├── .gitignore ├── AppleFSCompression.framework ├── AppleFSCompression.tbd └── Headers │ └── AppleFSCompression.h ├── LICENSE ├── Makefile ├── README.md └── src └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /fscmp 3 | -------------------------------------------------------------------------------- /AppleFSCompression.framework/AppleFSCompression.tbd: -------------------------------------------------------------------------------- 1 | --- !tapi-tbd 2 | tbd-version: 4 3 | targets: [ arm64-macos, arm64e-macos, arm64-ios, arm64e-ios, x86_64-macos, x86_64h-macos ] 4 | install-name: '/System/Library/PrivateFrameworks/AppleFSCompression.framework/AppleFSCompression' 5 | current-version: 145 6 | exports: 7 | - targets: [ arm64-macos, arm64e-macos, arm64-ios, arm64e-ios, x86_64-macos, x86_64h-macos ] 8 | symbols: [ _AFSCLockFileFd, _AFSCLockFilePath, _AFSCUnlockFile, _CloseStreamCompressor, 9 | _CompressFile, _CreateCompressionQueue, _CreateStreamCompressor, 10 | _CreateStreamCompressorQueue, _CreateStreamCompressorQueueWithOptions, 11 | _DrainStreamCompressorQueue, _FinishCompressionAndCleanUp, 12 | _FinishStreamCompressorQueue, _VolumeSupportsCompression, 13 | _WriteToStreamCompressor, _catFile, _decompressFile, _decompressWithCallback, 14 | _fqueryCompressionInfo, _fqueryCompressionType, _kAFSCAllowLargeResourceForks, 15 | _kAFSCAllowStoringDataInXattr, _kAFSCCompressionLevel, _kAFSCCompressionRulesKey, 16 | _kAFSCCompressionSystemRulesKey, _kAFSCCompressionTypes, _kAFSCDispatchQueuePriority, 17 | _kAFSCFlagsKey, _kAFSCIgnoreXattrErrors, _kAFSCInverseFlags, 18 | _kAFSCPathBeginsWithStringKey, _kAFSCPathContainsStringKey, 19 | _kAFSCPathEndsWithStringKey, _kAFSCPathExactlyEqualsStringKey, 20 | _kAFSCRuleTypeExclude, _kAFSCRuleTypeInclude, _kAFSCRuleTypeKey, 21 | _kAFSCSkipMachOFileTypes, _kAFSCSkipMachOOverridePaths, _kAFSCSynchronous, 22 | _kAFSCThrottledIO, _queryCompressionInfo, _queryCompressionType, 23 | _type5_handler_function ] 24 | ... 25 | -------------------------------------------------------------------------------- /AppleFSCompression.framework/Headers/AppleFSCompression.h: -------------------------------------------------------------------------------- 1 | #ifndef _APPLEFSCOMPRESSION_PRIVATEFRAMEWORK 2 | #define _APPLEFSCOMPRESSION_PRIVATEFRAMEWORK 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | // Keys for the "options" dict passed to CreateCompressionQueue() and CreateStreamCompressorQueueWithOptions(). 14 | extern const _Nonnull CFStringRef kAFSCSkipMachOFileTypes; // CFSTR("SkipMachOFileTypes") 15 | extern const _Nonnull CFStringRef kAFSCSkipMachOOverridePaths; // CFSTR("SkipMachOOverridePaths") 16 | extern const _Nonnull CFStringRef kAFSCCompressionTypes; // CFSTR("CompressionTypes") 17 | extern const _Nonnull CFStringRef kAFSCCompressionLevel; // CFSTR("CompressionLevel") 18 | extern const _Nonnull CFStringRef kAFSCAllowLargeResourceForks; // CFSTR("AllowLargeResourceForks") 19 | extern const _Nonnull CFStringRef kAFSCSynchronous; // CFSTR("Synchronous") 20 | extern const _Nonnull CFStringRef kAFSCThrottledIO; // CFSTR("AFSCThrottledIO") 21 | extern const _Nonnull CFStringRef kAFSCIgnoreXattrErrors; // CFSTR("IgnoreXattrErrors") 22 | extern const _Nonnull CFStringRef kAFSCAllowStoringDataInXattr; // CFSTR("AllowStoringDataInXattr") 23 | extern const _Nonnull CFStringRef kAFSCDispatchQueuePriority; // CFSTR("DispatchQueuePriority") 24 | extern const _Nonnull CFStringRef kAFSCCompressionRulesKey; // CFSTR("CompressionRules") 25 | extern const _Nonnull CFStringRef kAFSCCompressionSystemRulesKey; // CFSTR("SystemCompressionRules") 26 | extern const _Nonnull CFStringRef kAFSCRuleTypeKey; // CFSTR("RuleType") 27 | extern const _Nonnull CFStringRef kAFSCRuleTypeInclude; // CFSTR("Include") 28 | extern const _Nonnull CFStringRef kAFSCRuleTypeExclude; // CFSTR("Exclude") 29 | extern const _Nonnull CFStringRef kAFSCPathEndsWithStringKey; // CFSTR("PathEndsWithString") 30 | extern const _Nonnull CFStringRef kAFSCPathBeginsWithStringKey; // CFSTR("PathBeginsWithString") 31 | extern const _Nonnull CFStringRef kAFSCPathContainsStringKey; // CFSTR("PathContainsString") 32 | extern const _Nonnull CFStringRef kAFSCPathExactlyEqualsStringKey; // CFSTR("PathExactlyEqualsString") 33 | extern const _Nonnull CFStringRef kAFSCFlagsKey; // CFSTR("Flags") 34 | extern const _Nonnull CFStringRef kAFSCInverseFlags; // CFSTR("InverseFlags") 35 | 36 | // "CompressionTypes" values 37 | enum 38 | { 39 | kAFSCTypeZLibChunk = 3, 40 | kAFSCTypeZLib = 4, 41 | kAFSCTypeLZVNChunk = 7, 42 | kAFSCTypeLZVN = 8, 43 | kAFSCTypeRawChunk = 9, 44 | kAFSCTypeRaw = 10, 45 | kAFSCTypeLZFSEChunk = 11, 46 | kAFSCTypeLZFSE = 12, 47 | kAFSCTypeLZBitmapChunk = 13, 48 | kAFSCTypeLZBitmap = 14, 49 | }; 50 | 51 | typedef struct 52 | { 53 | uint32_t type; 54 | uint32_t chunkCount; 55 | uint64_t compressedSize; 56 | uint64_t uncompressedSize; 57 | uint64_t f0x18; // XXX: unknown purpose, field name will change when I find out 58 | } AppleFSCompressionInfo_t; 59 | 60 | struct _AppleFSCompressionQueue; // opaque 61 | typedef struct _AppleFSCompressionQueue *AppleFSCompressionQueue; 62 | struct _AppleFSCompressionStreamCompressor; // opaque 63 | typedef struct _AppleFSCompressionStreamCompressor *AppleFSCompressionStreamCompressor; 64 | 65 | typedef const void * _Nullable _UNKNOWN; 66 | 67 | _Nullable AppleFSCompressionQueue CreateCompressionQueue(_UNKNOWN, _UNKNOWN, _UNKNOWN, _UNKNOWN, _Nullable CFDictionaryRef options); 68 | _Nullable AppleFSCompressionQueue CreateStreamCompressorQueue(void); 69 | _Nullable AppleFSCompressionQueue CreateStreamCompressorQueueWithOptions(_Nullable CFDictionaryRef options); 70 | int DrainStreamCompressorQueue(_Nonnull AppleFSCompressionQueue queue); 71 | int FinishStreamCompressorQueue(_Nonnull AppleFSCompressionQueue queue); 72 | void FinishCompressionAndCleanUp(_Nonnull AppleFSCompressionQueue queue); 73 | 74 | _Nullable AppleFSCompressionStreamCompressor CreateStreamCompressor(int fd, const char * _Nonnull path, const char * _Nullable prefix, _Nonnull AppleFSCompressionQueue queue); 75 | size_t WriteToStreamCompressor(_Nonnull AppleFSCompressionStreamCompressor compressor, const void * _Nonnull buffer, size_t length); 76 | int CloseStreamCompressor(_Nonnull AppleFSCompressionStreamCompressor compressor); 77 | 78 | int queryCompressionType(const char * _Nonnull path, uint32_t * _Nonnull type); 79 | int fqueryCompressionType(int fd, uint32_t * _Nonnull type); 80 | int queryCompressionInfo(const char * _Nonnull path, AppleFSCompressionInfo_t * _Nonnull info); 81 | int fqueryCompressionInfo(int fd, AppleFSCompressionInfo_t * _Nonnull info); 82 | 83 | bool CompressFile(_Nonnull AppleFSCompressionQueue queue, const char * _Nonnull path, const char * _Nullable prefix); 84 | int decompressFile(const char * _Nonnull path, bool verifyData, bool _unknown); 85 | 86 | bool VolumeSupportsCompression(const char * _Nonnull mountpoint); 87 | 88 | #ifdef __cplusplus 89 | } 90 | #endif 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Siguza 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET = fscmp 2 | FW = AppleFSCompression 3 | 4 | .PHONY: all clean 5 | 6 | all: $(TARGET) 7 | 8 | $(TARGET): src/*.c $(FW).framework/Headers/*.h Makefile 9 | $(CC) -o $@ -Wall -O3 $(CFLAGS) src/*.c -F. -framework $(FW) -framework CoreFoundation $(LDFLAGS) 10 | 11 | clean: 12 | rm -f $(TARGET) 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fscmp 2 | 3 | CLI frontend for com.apple.decmpfs / AppleFSCompression.framework. 4 | 5 | ### About 6 | 7 | HFS and APFS support "decmpfs" attributes, which allow the file contents to be compressed on disk but be decompressed transparently on the fly when the file is read. Writing to such files will cause them to be decompressed. 8 | 9 | Apple implements code for dealing with this layer in their "AppleFSCompression" PrivateFramework. This repository contains a reverse engineered header as well as other files required to link against that framework. 10 | 11 | All code in this repository should be considered experimental, and is used at your own risk. The interface to AppleFSCompression.framework is not stable and may be changed at any time. 12 | 13 | ### Building 14 | 15 | make 16 | 17 | macOS-only for obvious reasons. Maybe @saagarjha can be motivated to write a manual implementation for other OSes. 18 | 19 | ### Usage 20 | 21 | Usage: fscmp action [...] 22 | 23 | Actions: 24 | c[ompress] [options] file [file...] 25 | i[nfo] [options] file [file...] 26 | 27 | Options for compress: 28 | -- Treat all following arguments as path literals 29 | -1 Compression level 1 30 | -9 Compression level 9 (default) 31 | -B Compression type LZBitmap 32 | -F Compression type LZFSE (default) 33 | -R Compression type raw (uncompressed, just store) 34 | -V Compression type LZVN 35 | -Z Compression type ZLib 36 | -p Show progress (between files only) 37 | 38 | Options for info: 39 | -- Treat all following arguments as path literals 40 | -q Quiet, just exit with 0 if all files are compressed, non-zero otherwise 41 | -t Print only the compression type ID, one line per file 42 | 43 | If you want to interface with AppleFSCompression.framework yourself, see [`main.c`](https://github.com/Siguza/fscmp/blob/master/src/main.c) for examples. That's as much as I know. 44 | 45 | ### License 46 | 47 | [MIT](https://github.com/Siguza/fscmp/blob/master/LICENSE). 48 | 49 | The heavy lifting is done by Apple code anyway. 50 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static void formatBytes(uint64_t bytes, char buf[10]) 8 | { 9 | const char *suffix[] = { "", "K", "M", "G", "T", "P", "E" }; 10 | long double d = bytes; 11 | size_t i = 0; 12 | while(d >= 999.995) 13 | { 14 | d /= 1000.0; 15 | ++i; 16 | } 17 | snprintf(buf, 10, "%.*Lf %sB", i == 0 ? 0 : 2, d, suffix[i]); 18 | } 19 | 20 | int main(int argc, const char **argv) 21 | { 22 | if(argc < 2) 23 | { 24 | goto help; 25 | } 26 | 27 | const char *act = argv[1]; 28 | size_t n = strlen(act); 29 | if(!n) 30 | { 31 | fprintf(stderr, "No action given.\n\n"); 32 | goto help; 33 | } 34 | int aoff = 2; 35 | 36 | if(strncmp(act, "compress", n) == 0) 37 | { 38 | bool progress = false; 39 | int compressionLevel = 9; 40 | int compressionType = kAFSCTypeLZFSE; 41 | 42 | for(; aoff < argc; ++aoff) 43 | { 44 | const char *opt = argv[aoff]; 45 | if(opt[0] != '-') 46 | { 47 | break; 48 | } 49 | if(opt[1] == '-') 50 | { 51 | ++aoff; 52 | break; 53 | } 54 | char c; 55 | for(size_t i = 1; (c = opt[i]) != '\0'; ++i) 56 | { 57 | switch(c) 58 | { 59 | case '1': 60 | case '2': 61 | case '3': 62 | case '4': 63 | case '5': 64 | case '6': 65 | case '7': 66 | case '8': 67 | case '9': 68 | compressionLevel = c - '0'; 69 | break; 70 | 71 | case 'B': 72 | compressionType = kAFSCTypeLZBitmap; 73 | break; 74 | 75 | case 'F': 76 | compressionType = kAFSCTypeLZFSE; 77 | break; 78 | 79 | case 'R': 80 | compressionType = kAFSCTypeRaw; 81 | break; 82 | 83 | case 'V': 84 | compressionType = kAFSCTypeLZVN; 85 | break; 86 | 87 | case 'Z': 88 | compressionType = kAFSCTypeZLib; 89 | break; 90 | 91 | case 'p': 92 | progress = true; 93 | break; 94 | 95 | default: 96 | fprintf(stderr, "Bad compress option: -%c\n\n", c); 97 | goto help; 98 | } 99 | } 100 | } 101 | if(aoff >= argc) 102 | { 103 | fprintf(stderr, "Missing file argument.\n\n"); 104 | goto help; 105 | } 106 | 107 | CFNumberRef cfCompressionLevel = CFNumberCreate(NULL, kCFNumberIntType, &compressionLevel); 108 | if(!cfCompressionLevel) 109 | { 110 | fprintf(stderr, "Failed to allocate compression level number.\n"); 111 | return -1; 112 | } 113 | 114 | CFStringRef cfCompressionType = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), compressionType); 115 | if(!cfCompressionType) 116 | { 117 | CFRelease(cfCompressionLevel); 118 | fprintf(stderr, "Failed to allocate compression type string.\n"); 119 | return -1; 120 | } 121 | 122 | CFMutableDictionaryRef options = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 123 | if(!options) 124 | { 125 | CFRelease(cfCompressionLevel); 126 | CFRelease(cfCompressionType); 127 | fprintf(stderr, "Failed to allocate options dict.\n"); 128 | return -1; 129 | } 130 | 131 | CFDictionarySetValue(options, kAFSCCompressionLevel, cfCompressionLevel); 132 | CFDictionarySetValue(options, kAFSCCompressionTypes, cfCompressionType); 133 | CFRelease(cfCompressionLevel); 134 | CFRelease(cfCompressionType); 135 | 136 | AppleFSCompressionQueue queue = CreateCompressionQueue(NULL, NULL, NULL, NULL, options); 137 | if(!queue) 138 | { 139 | CFRelease(options); 140 | fprintf(stderr, "Failed to allocate compression queue.\n"); 141 | return -1; 142 | } 143 | 144 | int ret = 0; 145 | for(int i = aoff; i < argc; ++i) 146 | { 147 | if(progress) 148 | { 149 | printf("\r%d/%d", i - aoff, argc - aoff); 150 | fflush(stdout); 151 | } 152 | const char *file = argv[i]; 153 | if(!CompressFile(queue, file, NULL)) 154 | { 155 | int r = errno; 156 | fprintf(stderr, "%s: %s (%d)\n", file, strerror(r), r); 157 | if(ret == 0) 158 | { 159 | ret = r == 0 ? -1 : r; 160 | } 161 | } 162 | } 163 | if(progress) 164 | { 165 | printf("\r%d/%d\n", argc - aoff, argc - aoff); 166 | } 167 | 168 | FinishCompressionAndCleanUp(queue); 169 | CFRelease(options); 170 | 171 | return ret; 172 | } 173 | else if(strncmp(act, "decompress", n) == 0) 174 | { 175 | bool progress = false; 176 | bool verify = true; 177 | 178 | for(; aoff < argc; ++aoff) 179 | { 180 | const char *opt = argv[aoff]; 181 | if(opt[0] != '-') 182 | { 183 | break; 184 | } 185 | if(opt[1] == '-') 186 | { 187 | ++aoff; 188 | break; 189 | } 190 | char c; 191 | for(size_t i = 1; (c = opt[i]) != '\0'; ++i) 192 | { 193 | switch(c) 194 | { 195 | case 'f': 196 | verify = false; 197 | break; 198 | 199 | case 'p': 200 | progress = true; 201 | break; 202 | 203 | default: 204 | fprintf(stderr, "Bad decompress option: -%c\n\n", c); 205 | goto help; 206 | } 207 | } 208 | } 209 | if(aoff >= argc) 210 | { 211 | fprintf(stderr, "Missing file argument.\n\n"); 212 | goto help; 213 | } 214 | 215 | int ret = 0; 216 | for(int i = aoff; i < argc; ++i) 217 | { 218 | if(progress) 219 | { 220 | printf("\r%d/%d", i - aoff, argc - aoff); 221 | fflush(stdout); 222 | } 223 | const char *file = argv[i]; 224 | if(decompressFile(file, verify, false) != 0) // XXX: setting the second param to "true" causes failure 225 | { 226 | int r = errno; 227 | fprintf(stderr, "%s: %s (%d)\n", file, strerror(r), r); 228 | if(ret == 0) 229 | { 230 | ret = r == 0 ? -1 : r; 231 | } 232 | } 233 | } 234 | if(progress) 235 | { 236 | printf("\r%d/%d\n", argc - aoff, argc - aoff); 237 | } 238 | 239 | return ret; 240 | } 241 | else if(strncmp(act, "info", n) == 0) 242 | { 243 | int mode = 0; 244 | 245 | for(; aoff < argc; ++aoff) 246 | { 247 | const char *opt = argv[aoff]; 248 | if(opt[0] != '-') 249 | { 250 | break; 251 | } 252 | if(opt[1] == '-') 253 | { 254 | ++aoff; 255 | break; 256 | } 257 | char c; 258 | for(size_t i = 1; (c = opt[i]) != '\0'; ++i) 259 | { 260 | switch(c) 261 | { 262 | case 'q': 263 | mode = 1; 264 | break; 265 | 266 | case 't': 267 | mode = 2; 268 | break; 269 | 270 | default: 271 | fprintf(stderr, "Bad info option: -%c\n\n", c); 272 | goto help; 273 | } 274 | } 275 | } 276 | if(aoff >= argc) 277 | { 278 | fprintf(stderr, "Missing file argument.\n\n"); 279 | goto help; 280 | } 281 | 282 | int ret = 0; 283 | bool first = true; 284 | for(int i = aoff; i < argc; ++i) 285 | { 286 | const char *file = argv[i]; 287 | AppleFSCompressionInfo_t info = {}; 288 | if(queryCompressionInfo(file, &info) == 0) 289 | { 290 | switch(mode) 291 | { 292 | case 0: 293 | { 294 | char usize[10] = {}; 295 | formatBytes(info.uncompressedSize, usize); 296 | if(info.type == 0) 297 | { 298 | printf("%s%s:\n" 299 | " Type: Uncompressed (0)\n" 300 | " Size: %s (0x%llx)\n" 301 | , first ? "" : "\n", file, usize, info.uncompressedSize); 302 | } 303 | else 304 | { 305 | const char *type = NULL; 306 | switch(info.type) 307 | { 308 | case kAFSCTypeZLibChunk: type = "ZLibChunk"; break; 309 | case kAFSCTypeZLib: type = "ZLib"; break; 310 | case kAFSCTypeLZVNChunk: type = "LZVNChunk"; break; 311 | case kAFSCTypeLZVN: type = "LZVN"; break; 312 | case kAFSCTypeRawChunk: type = "RawChunk"; break; 313 | case kAFSCTypeRaw: type = "Raw"; break; 314 | case kAFSCTypeLZFSEChunk: type = "LZFSEChunk"; break; 315 | case kAFSCTypeLZFSE: type = "LZFSE"; break; 316 | case kAFSCTypeLZBitmapChunk: type = "LZBitmapChunk"; break; 317 | case kAFSCTypeLZBitmap: type = "LZBitmap"; break; 318 | default: type = "Unknown"; break; 319 | } 320 | char csize[10] = {}; 321 | formatBytes(info.compressedSize, csize); 322 | printf("%s%s:\n" 323 | " Type: %s (%u)\n" 324 | " Chunk count: %u (0x%x)\n" 325 | " Compressed size: %s (0x%llx)\n" 326 | " Uncompressed size: %s (0x%llx)\n" 327 | " 0x18: 0x%llx\n" 328 | , first ? "" : "\n", file, type, info.type, info.chunkCount, info.chunkCount, csize, info.compressedSize, usize, info.uncompressedSize, info.f0x18); 329 | } 330 | break; 331 | } 332 | 333 | case 1: 334 | { 335 | if(ret == 0 && info.type == 0) 336 | { 337 | ret = -1; 338 | } 339 | break; 340 | } 341 | 342 | case 2: 343 | { 344 | printf("%u\n", info.type); 345 | break; 346 | } 347 | } 348 | 349 | first = false; 350 | } 351 | else 352 | { 353 | int r = errno; 354 | fprintf(stderr, "%s: %s (%d)\n", file, strerror(r), r); 355 | if(ret == 0) 356 | { 357 | ret = r == 0 ? -1 : r; 358 | } 359 | } 360 | } 361 | 362 | return ret; 363 | } 364 | else 365 | { 366 | fprintf(stderr, "Bad action: %s\n\n", act); 367 | goto help; 368 | } 369 | 370 | help:; 371 | fprintf(stderr, "Usage: %s action [...]\n" 372 | "\n" 373 | "Actions:\n" 374 | " c[ompress] [options] file [file...]\n" 375 | //" d[ecompress] [options] file [file...]\n" 376 | " i[nfo] [options] file [file...]\n" 377 | "\n" 378 | "Options for compress:\n" 379 | " -- Treat all following arguments as path literals\n" 380 | " -1 Compression level 1\n" 381 | " -9 Compression level 9 (default)\n" 382 | " -B Compression type LZBitmap\n" 383 | " -F Compression type LZFSE (default)\n" 384 | " -R Compression type raw (uncompressed, just store)\n" 385 | " -V Compression type LZVN\n" 386 | " -Z Compression type ZLib\n" 387 | " -p Show progress (between files only)\n" 388 | "\n" 389 | //"Options for decompress:\n" 390 | //" -- Treat all following arguments as path literals\n" 391 | //" -f Fast (skip data verification)\n" 392 | //" -p Show progress (between files only)\n" 393 | //"\n" 394 | "Options for info:\n" 395 | " -- Treat all following arguments as path literals\n" 396 | " -q Quiet, just exit with 0 if all files are compressed, non-zero otherwise\n" 397 | " -t Print only the compression type ID, one line per file\n" 398 | , argv[0]); 399 | return -1; 400 | } 401 | --------------------------------------------------------------------------------