/*..:....|....:....|....:....|....:....|....:....|....:....|....:....|...*/ /* Symbol ranking text compressor P M Fenwick 5 September 1996. V 0.2 9 Sep 1996 * uses 32-bit words as context, * move most globals into code/decode procedures to aid register allocation * adds variable coding order (with transmission to decoder) * run without parameters displays help messages. -S reports symbol-rank counts and percentages -F reports symbol-index counts from MTF -D specifies number of MTF dither bits V 0.5 uses 6-bit ASCII subsets to generate contexts. Unary coding for short runs V 1.0 Removes rank=3 symbol to allow shorter codes - negligible change. 1.1 Changes initialisation of Contexts array (10 Apr 97) The basis of symbol-ranking compression is that we have a list of symbols ordered according to their likelihood in the current context. On compression, each input symbol is translated into its index in that list, with the most likely symbol 'Rank-0', the next 'Rank-1', and so on. On expansion, the received index is used to get the symbol from the list. This version is intended for high speed rather than good compression. It is based on what is really a 4-way set associative cache, with LRU update. The preceding 3-symbol context is hashed and used to select a context, which has 3 symbols in LRU order. Each context has its own "cache line" -- usually 16--64K contexts are held. Each word in the "Contexts" array holds the three ranked symbols, from Rank-2 (Most Sig) to Rank-0 (least Sig) For a cache "hit" we encode the LRU position -- rank=0 for most recent through to rank=2 for least recent. For a miss we could encode the actual symbol, but rather do an approximate MTF to favour more frequent symbols. After coding, each symbol is moved to the front of the LRU list for its context The complete coding is -- 0 rank-0 10 xxxxx literal, MTF index < 32 110 rank-1 1110 rank-2 1111 xxxxxxxx literal, index >= 32 (also EOF, with code = 0) Short runs of rank-0 are emitted "as is". Longer runs are emitted as a run-length bit count emitted as a long literal followed by the length. (Such values cannot occur in a proper literal.) The "pseudo MTF" mechanism has a table of symbols in approximate MTF order. When one is referenced it is exchanged with one half-way to the front of the table. A mapping table is included to accelerate the operation, giving a constant 6 memory references for any symbol. A small dither is added to the exchange-position to improve performance. "Stats" should be 0 for normal use, but 1 to enable extended statistics reports. Enabling statistics slows execution slightly, but no other effect */ #define Stats 0 /* ########### 0 or 1 to control extended statistics */ #include #include #include #include #include #include #include #include #define srName "srank" #define srVersion "1" #define srLevel "0" #define srSuffix ".sr" #define outMode "wb" /* may have to be "w" on some computers */ #define usChar unsigned char #if (INT_MAX > 2000000L) /* an integer of 32 bits */ #define int32 int #define us32 unsigned int #else #define int32 long #define us32 unsigned long #endif #define runThresh 16 /* encode 0 runs longer than this */ FILE *inFile, /* the main input file */ *outFile; /* the main output file */ time_t sttTime, endTime; /* to time the operation */ us32 *Contexts, /* the table of contexts */ mask1, mask23; int ChToIx[260], /* for pseudo MTF */ IxToCh[260]; /* " " " " */ int report = 1, /* enable reporting */ Order = 3, /* encoding order */ error = 0; /* error indicator */ #if Stats int dithMask = 7; /* mask to dither the MTF index */ #else #define dithMask 7 #endif #define errFinish 1 /* just quietly finish */ #define errRemove 2 /* must remove output file */ #if Stats #define logSize 300 int Trigger, /* log from this position */ logCnt; /* symbols left to log */ #endif int32 ctxParam = 15, /* initially 2^15 contexts */ currContexts = -1, /* number of contexts */ ctxMask, /* get context index from hash */ grpBits = 3, /* sets contexts in group */ grpMask, compBytes, dataBytes, /* data counters */ runLength; /* rank-0 length */ char Header[20], /* the file header */ outSuffix[8], /* output suffix */ inName[80], outName[80]; /* file names */ /* ---------- statistics counters ---------- */ #if Stats long R0, R1, R2, shortLit, longLit, Runs, Ranks[260], Lengths[20]; #endif /* ---------- prototype declarations --------- */ void startoutputtingbits(void); void doneoutputtingbits(void); void startinputtingbits(void); int makeHeader(); void emitHeader(int V); int checkHeader(); void Initialize(); void emitRun(); void decodeFile(); void encodeFile(); void writeReport(); void printHelp(); char convByte(int byte); int prog(int argc, char *argv[]); /* used if program is run as a subroutine */ /* ===== Convert a symbol to a printable code (Macintosh specific) ===== */ #if Stats char convByte(int byte) { byte &= 0xff; if (byte >= ' ') return byte; /* normal character */ if (byte == '\n') return '©'; /* new-line LF */ if (byte == 0 ) return '¡'; /* zero NUL */ if (byte == '\r') return '¨'; /* return CR */ if (byte == '\t') return 'Æ'; /* hor tab HT */ return '¥'; /* something < space */ } #endif /* ========== input output routines ========== */ us32 BitBuffer, /* the bit output buffer */ BitsInBuf, /* bits in the buffer */ theBits, /* return value from getBits and lookBits */ Masks[17] = {0, 0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF}; void startoutputtingbits(void) { BitBuffer = 0; /* clear the buffer */ BitsInBuf = 0; /* and the bit count */ } #define putBits(Bits, N) \ { \ BitBuffer = (BitBuffer << N) | Bits; \ BitsInBuf += N; \ while (BitsInBuf >= 8) /* write buffer to file */ \ { \ putc((BitBuffer >> (BitsInBuf-8)), outFile); \ BitsInBuf -= 8; \ BitBuffer &= Masks[BitsInBuf]; /* remaining bits */ \ compBytes ++; \ } \ } void doneoutputtingbits(void) { while (BitsInBuf > 8) /* process high-order bytes */ { putc(BitBuffer >> (BitsInBuf - 8), outFile); BitsInBuf -= 8; compBytes ++; } if (BitsInBuf > 0) /* and the low-order bits */ { putc(BitBuffer << (8-BitsInBuf), outFile); compBytes ++; } } void startinputtingbits(void) { BitBuffer = 0; /* clear the buffer */ BitsInBuf = 0; /* and the bit count */ } /* ========== read N bits from the input buffer ========== */ /* the value is returned in the global variable 'theBits' */ #define getBits(N) \ { \ while (BitsInBuf < (N)) /* ensure enough bits */ \ { \ BitBuffer = (BitBuffer << 8) | getc(inFile); \ BitsInBuf += 8; /* another byte in buff */ \ compBytes ++; \ } \ theBits = (BitBuffer >> (BitsInBuf - N)); /* align */ \ BitsInBuf -= N; /* N bits taken */ \ BitBuffer &= Masks[BitsInBuf]; /* select bits */ \ } /* ========== inspect N bits from the input buffer ========== */ #define lookBits(N) \ { \ while (BitsInBuf < (N)) \ { \ BitBuffer = (BitBuffer << 8) | getc(inFile); \ BitsInBuf += 8; \ compBytes ++; \ } \ theBits = BitBuffer >> (BitsInBuf - N); \ } /* ========== process the file header ========== */ /* It has the form "srzp#VLnm", where "srzp" are those letters, V is a version number, L is a level number, n is a value indicating the context size, and m is the compression order (1, 2 or 3) */ int makeHeader() /* make header, return its length */ { strcpy(Header, srName); strcat(Header, "#"); strcat(Header, srVersion); strcat(Header, srLevel); return strlen(Header); } void emitHeader(int V1) /* emit the header bytes */ { int L, i; L = makeHeader(); for (i = 0; i < L; i ++) putBits(Header[i], 8); putBits(V1 & 0xFF, 8); /* context size */ putBits(Order, 8); /* compression order */ } int checkHeader() /* check the header */ { int i, L, V1; L = makeHeader(); for (i = 0; i < L; i++) { getBits(8); if (theBits != Header[i]) { error = errRemove; return -1; /* error value */ } } getBits(8); /* the parameter */ V1 = theBits; getBits(8); Order = theBits; /* set decompression order */ return V1; /* return context size info */ } /* ====== initialise the coding models etc, and emit values ====== */ void Initialize(int log2Ctx) { int i, ctxSize; ctxSize = 1 << log2Ctx; if (ctxSize != currContexts) /* check for changed context size */ { if (Contexts != NULL) free(Contexts); /* release old space */ Contexts = (us32 *) calloc(ctxSize+8, sizeof(us32)); if (Contexts == NULL) /* failure */ { fprintf(stderr, "Contexts allocation failure - %ld contexts\n", ctxSize); exit(1); } currContexts = ctxSize; /* remember number of contexts */ } for (i = 0; i < ctxSize; i++) Contexts[i] = 0x00652000; /* initialise to {NUL, ' ', 'e'} */ ctxMask = (ctxSize-1); /* get valid context index */ compBytes = dataBytes = runLength = 0; for (i = 0; i < 256; i++) ChToIx[i] = IxToCh[i] = i; /* set up pseudo MTF table */ #if Stats R0 = R1 = R2 = shortLit = longLit = Runs = 0; for (i = 0; i < 260; i++) Ranks[1] = 0; for (i = 0; i < 20; i++) Lengths[i] = 0; #endif } /* ========== ========== */ /* maintain a sort of MTF list by exchanging a character with one half-way to the start of the list. */ #define pseudoMTF(currCh, Dither) \ { \ int currIx, exchCh, exchIx; \ \ currIx = ChToIx[currCh]; /* position of current character */ \ exchIx = currIx >> 1; /* new posn of current character */ \ exchIx ^= ((Dither) & dithMask); \ exchCh = IxToCh[exchIx]; /* character to be exchanged */ \ \ ChToIx[currCh] = exchIx; /* new position of current char */ \ IxToCh[exchIx] = currCh; \ ChToIx[exchCh] = currIx; /* new posn of exchange char */ \ IxToCh[currIx] = exchCh; \ } /* ===== code for ISO Fletcher checksum ===== */ #define addCheckSum(C) \ { \ check1 += C; /* add character into check1 */ \ if (check1 >= 255) /* ... modulo 255 */ \ check1 -= 255; \ check2 += check1; /* add check1 into check2 */ \ if (check2 >= 255) /* ... modulo 255 */ \ check2 -= 255; \ } /* ========== report on compression performance ========== */ void writeReport() { float procTime, Rate; char self[8], vers[8]; time_t theTime; #if Stats int i, f; #endif procTime = (1.0*(endTime-sttTime))/CLOCKS_PER_SEC; Rate = (1.0e-6*dataBytes)/procTime; if (report > 1) { theTime = time(NULL); strcpy(self, srName); strcpy(vers, srVersion); strcat(vers, "."); strcat(vers, srLevel); fprintf(stderr, "'%s V%s' file '%s', %ld contexts, order %d, %s", self, vers, inName, currContexts, Order, ctime(&theTime)); } fprintf(stderr, "%ld data bytes, %ld comp. bytes, %5.3f bit/byte", dataBytes, compBytes,(8.0*compBytes)/dataBytes); fprintf(stderr, "%7.3f sec, %5.3f Mbyte/s\n", procTime, Rate); #if Stats if (report > 2) /* report rank statistics */ { fprintf(stderr, "%6ld R0, %ld R1, %ld R2, %ld short, %ld long, %ld runs\n", R0, R1, R2, shortLit, longLit, Runs); f = dataBytes/100; fprintf(stderr, "%5ld%% R0, %3ld%% R1, %2ld%% R2, %3ld%% short, %2ld%% long\n", R0/f, R1/f, R2/f, shortLit/f, longLit/f); fprintf(stderr, "Lengths "); for (i = 1; i < 11; i++) fprintf(stderr, " %d:%ld,", i, Lengths[i]); fprintf(stderr, "\n"); if (report > 3) /* report on symbol index frequencies */ for (i = 0; i < 256; i++) fprintf(stderr, "%ld\n", Ranks[i]); } #endif if (report > 1) fprintf(stderr, "\n"); } /* ========== expand a file ========== */ void decodeFile() { int headVal, /* value from header record */ rxCheck1=0, rxCheck2=0; /* checksums from trailer */ int SymIx, /* index in pseudo MTF table */ symbol, /* THE decoded symbol */ check1, check2; /* ISO checksums */ #if Stats int code; char rank; #endif us32 W, /* the selected context */ prev1, prev2, prev3, /* hash codes of prev symbols */ runLength, Hx; /* and its index */ prev1 = prev2 = prev3 = runLength = 0; check1 = check2 = 0; sttTime = clock(); startinputtingbits(); /* start input */ headVal = checkHeader(); /* check header */ if (headVal < 0) { fprintf(stderr, "Invalid file header\n"); exit(1); } Initialize(headVal); for (;;) /* start of main decoding loop */ { #if Stats if (dataBytes == Trigger) logCnt = logSize; /* start logging */ SymIx = -1; #endif Hx = (prev1 | prev2 | prev3) & mask23 & ctxMask; W = Contexts[Hx]; /* select context */ if (runLength > 0) { symbol = W & 0xFF; /* rank-0 symbol, in run */ runLength --; #if Stats R0++; rank = '0'; code = 0; #endif } else { lookBits(4); /* get code into 'theBits' */ if ((theBits & 8) == 0) /* 0xxx = Rank-0 */ { getBits(1); symbol = W & 0xFF; /* rank-0 symbol */ #if Stats R0++; rank = '0'; code = 0; #endif } else if ((theBits & 12) == 8) /* 10xx = Short literal */ { getBits(7); /* 2 prefix & 5 data */ SymIx = theBits & 0x1F; /* remove prefix */ symbol = IxToCh[SymIx]; /* get symbol from index */ Contexts[Hx] = (W << 8) | symbol; /* force as most recent symbol */ #if Stats shortLit++; Ranks[SymIx] ++; code = 10; rank = 'S'; #endif } else if ((theBits & 14) == 12) /* 110x = Rank-1 */ { getBits(3); symbol = (W >> 8) & 0xFF; /* fetch the rank-1 symbol */ Contexts[Hx] = (W & 0xFFFF0000) | ((W & 0xFF) << 8) | symbol; #if Stats R1++; rank = '1'; code = 110; #endif } else if ((theBits & 15) == 14) /* 1110 = Rank-2 */ { getBits(4); symbol = (W >> 16) & 0xFF; /* fetch the rank-2 symbol */ Contexts[Hx] = (W & 0xFF000000) | ((W & 0xFFFF) << 8) | symbol; #if Stats R2++; rank = '2'; code = 1110; #endif } else if (theBits == 15) /* 1111 = long Literal */ { getBits(12); /* get code & literal */ SymIx = theBits & 0xFF; /* remove prefix code */ if (SymIx == 0) /* end of file */ { getBits(8); /* get the checksums */ rxCheck1 = theBits; getBits(8); rxCheck2 = theBits; if ((check1 != rxCheck1) || (check2 != rxCheck2)) /* final check */ { fprintf(stderr, "File '%s' - invalid checksum\n", inName); error = errRemove; } break; } else if (SymIx < 32) /* encoded run */ { getBits(SymIx); /* SymIx has bit-length of run-length */ runLength = theBits + runThresh - 1; /* get length & adjust */ symbol = W & 0xff; /* first symbol of run */ #if Stats Runs++; if (logCnt > 0) fprintf(stderr, " Run %ld\n", runLength + 1); #endif } else /* long literal */ { symbol = IxToCh[SymIx]; Contexts[Hx] = (W << 8) | symbol; #if Stats longLit++; rank = 'L'; Ranks[SymIx] ++; code = 11111; #endif } } else { fprintf(stderr, "File '%s' -- decode failure - code = %d, dataBytes = %ld\n", inName, theBits, dataBytes); error = errRemove; break; } } /* end of main decode */ #if Stats if (logCnt-- > 0) { fprintf(stderr, "%7ld < %-7ld '%c' %02X %c\"%c%c%c\" rank %c, code %-6d", dataBytes, compBytes, convByte(symbol), symbol, convByte(W>>24), convByte(W), convByte(W>>8), convByte(W>>16), rank, code); if (SymIx >= 0) fprintf(stderr, " %3d", SymIx); fprintf(stderr, "\n"); } #endif dataBytes++; pseudoMTF(symbol, dataBytes); /* update pseudo MTF table */ prev3 = (prev2 << 6); /* move along hash codes */ prev2 = ((prev1 & 0x3F) << 6); prev1 = symbol & mask1; /* and get new one */ addCheckSum(symbol); /* build check sum */ putc(symbol,outFile); /* write symbol to file */ } /* end of decompressing a symbol */ endTime = clock(); fclose(outFile); if ((error & errRemove) != 0) remove(outName); else if (report > 0) writeReport(); } /* ========== emit the length of a run ========== */ void emitRun() { int L; int32 mask; #if Stats if (runLength < 20) Lengths[runLength] ++; #endif if (runLength <= runThresh) putBits(0, runLength) /* unary code for shorter runs */ else { #if Stats Runs++; if (logCnt > 0) fprintf(stderr, " Run %ld\n", runLength); #endif runLength -= runThresh; for (L = 0, mask = 1; mask <= runLength; L ++) mask <<= 1; putBits(15, 4); /* long Lit = 1111 */ putBits(L, 8); /* bits to encode the run length */ putBits(runLength, L); /* and the run length itself */ } runLength = 0; /* clear the length */ } /* ========== accept input symbols, handling run-encoding ========== */ void encodeFile() { us32 W; /* the selected context */ int SymIx, /* symbol index in MTF table */ symbol, /* the actual symbol */ check1, check2; /* ISO checksums */ #if Stats int code; char rank; #endif us32 prev1, prev2, prev3, /* hash codes of prev symbols */ Hx; /* and its index */ sttTime = clock(); startoutputtingbits(); emitHeader(ctxParam); Initialize(ctxParam); prev1 = prev2 = prev3 = 0; check1 = check2 = 0; for (;;) { symbol = getc(inFile); /* get next symbol */ if (symbol == EOF) break; /* exit if EOF */ dataBytes ++; addCheckSum(symbol); #if Stats if (dataBytes == Trigger) logCnt = logSize; /* start logging */ SymIx = -1; #endif Hx = (prev1 | prev2 | prev3) & mask23 & ctxMask; W = Contexts[Hx]; /* select context */ if((W & 0xFF) == symbol) /* rank-0 match */ { runLength ++; #if Stats R0++; rank = '0'; code = 0; #endif } else if (((W >> 8) & 0xFF) == symbol) /* rank-1 match */ { if (runLength > 0) emitRun(); /* emit any pending run */ putBits(6, 3); /* emit '110' */ Contexts[Hx] = (W & 0xFFFF0000) | ((W & 0xFF) << 8) | symbol; #if Stats R1++; rank = '1'; code = 110; #endif } else if (((W >> 16) & 0xFF) == symbol) /* rank-2 match */ { if (runLength > 0) emitRun(); /* emit any pending run */ putBits(14, 4); /* emit '1110' */ Contexts[Hx] = (W & 0xFF000000) | ((W & 0xFFFF) << 8) | symbol; #if Stats R2++; rank = '2'; code = 1110; #endif } else /* no match */ { if (runLength > 0) emitRun(); /* emit any pending run */ SymIx = ChToIx[symbol]; /* get index in MTF table */ #if Stats Ranks[SymIx] ++; #endif if (SymIx < 32) /* near symbol */ { putBits(2, 2); /* emit '10' short Lit */ putBits(SymIx, 5); /* emit index */ #if Stats shortLit++; code = 10; rank = 'S'; #endif } else /* remote symbol */ { putBits(15, 4); /* emit '1111' */ putBits(SymIx, 8); /* and the index */ #if Stats longLit++; code = 1111; rank = 'L'; #endif } Contexts[Hx] = (W & 0xFF000000) | ((W & 0xFFFF) << 8) | symbol; } #if Stats if ((logCnt-- > 0) || (Hx == 131840)) { fprintf(stderr, "%7ld > %-7ld '%c' %02X %c\"%c%c%c\" rank %c, code %-6d", dataBytes, compBytes, convByte(symbol), symbol, convByte(W>>24), convByte(W), convByte(W>>8), convByte(W>>16), rank, code); if (SymIx >= 0) fprintf(stderr, " %3d", SymIx); fprintf(stderr, "\n"); } #endif pseudoMTF(symbol, dataBytes & 0xFF); prev3 = (prev2 << 6); /* move along hash codes */ prev2 = ((prev1 & 0x3F) << 6); prev1 = symbol & mask1; /* and get new one */ } /* end of compression loop */ if (runLength > 0) emitRun(); /* emit any pending run */ putBits(15, 4); putBits( 0, 8); /* the EOF symbol */ putBits(check1, 8); /* and the checksum */ putBits(check2, 8); doneoutputtingbits(); endTime = clock(); fclose(inFile); fclose(outFile); if (report > 0) writeReport(); } /* =================================================== */ void printHelp() { char suff[20], self[8], vers[8]; strcpy(self, srName); strcpy(vers, srVersion); strcat(vers, "."); strcat(vers, srLevel); strcpy(suff, srSuffix); #if Stats fprintf(stderr, "\nFile compressor %s V%s - max rank = 3 (Statistics enabled)\n", self, vers); #else fprintf(stderr, "\nFile compressor %s V%s - max rank = 3\n", self, vers); #endif fprintf(stderr, "Run as per standard Unix conventions --\n"); fprintf(stderr, " %s [ options ] file [options ] file...\n", self); fprintf(stderr, "Options and file names may be intermixed, with options \n"); fprintf(stderr, " applying to all later files until overwritten.\n"); fprintf(stderr, "Options are preceded by a '-', with any number as a group\n"); fprintf(stderr, " -q disable summary report\n"); fprintf(stderr, " -v enable one line report (default)\n"); fprintf(stderr, " -V enable two line report\n"); #if Stats fprintf(stderr, " -S report rank statistics\n"); fprintf(stderr, " -F report symbol index statistics, after MTF\n"); #endif fprintf(stderr, " -Cn set number of compression contexts. 'n' is a digit (1 <= n <= 8)\n"); fprintf(stderr, " The number of contexts is 2^(10+n), so that the option\n"); fprintf(stderr, " -C5 sets 2^15 = 32768 contexts (the default).\n"); fprintf(stderr, " -oX expands files 'file%s' to 'file.X'\n", suff); fprintf(stderr, " (This is useful for testing.) X = '.' implies no suffix\n"); fprintf(stderr, " -On set compression order - 1, 2 or 3\n"); #if Stats fprintf(stderr, " -Dn set 'dither' mask to n bits - usually 3 bits\n"); fprintf(stderr, " -Lnnnn start logging from symbol nnnn (nnnn is a decimal number)\n"); #endif fprintf(stderr, "\n"); fprintf(stderr, "Compressed files have the suffix '%s' added to the file name.\n", suff); fprintf(stderr, "Files with the suffix %s are automatically expanded\n", suff); fprintf(stderr, "The input file is NOT deleted\n\n"); exit(0); } /* int prog(int argc, char *argv[]) /* */ int main(int argc, char *argv[]) /* */ { int n, i, L, Ls, v, compress, errval; char *P, c, suffix[8], wMode[8]; strcpy(outSuffix, ".n"); /* default output suffix */ error = 0; Order = 3; mask1 = 0x3F; /* order 3 -- 18 bits */ mask23 = 0x3FFFF; #if Stats Trigger = -1; dithMask = 7; /* 3 bits */ #endif if (argc == 1) printHelp(); for (n = 1; n < argc; n++) { P = argv[n]; /* the n-th parameter string */ if (P[0] == '-') /* it looks like an option */ { for (i = 1; P[i] != 0; i++) /* scan the option list */ switch (P[i]) { case 'q' : report = 0; /* no summary */ break; case 'v' : report = 1; /* brief report */ break; case 'V' : report = 2; /* complete report */ break; #if Stats case 'S' : report = 3; /* statistics report */ break; case 'F' : report = 4; /* MTF ranks report */ break; case 'L' : Trigger = 0; /* start logging */ while (((c = P[++i]) >= '0') && (c <= '9')) Trigger = Trigger*10 + (c - '0'); i--; break; #else case 'S' : case 'F' : case 'L' : fprintf(stderr, "Option '%c' requires compilation with \"Stats = 1\"\n", P[i]); break; #endif case 'o' : c = P[++i]; /* set recovered suffix */ if (c == '.') outSuffix[0] = 0; /* no suffix at all */ else { outSuffix[0] = '.'; /* build '.X' string */ outSuffix[1] = c; outSuffix[2] = 0; } break; case 'C' : c = P[++i]; if ((c > '0') && (c < '9')) ctxParam = c - '0' + 10; /* set context size */ else { fprintf(stderr, "C must be followed by digit 1 - 8\n"); error = errFinish; } break; case 'O' : c = P[++i]; if (( c >= '1') && (c <= '4')) Order = c - '0'; else { fprintf(stderr, "O must be followed by digit 1 - 4\n"); error = errFinish; } if (Order == 3) mask23 = 0x3FFFF; else if (Order == 2) mask23 = 0xFFF; else if (Order == 1) mask23 = 0x3F; break; #if Stats case 'D' : c = P[++i]; if (( c >= '0') && (c <= '9')) dithMask = (1 << (c - '0')) -1; else { fprintf(stderr, "D must be followed by digit 0 - 9\n"); error = errFinish; } break; #endif default : fprintf(stderr, "Invalid parameter '%c'\n", P[i]); error = errFinish; } } else if (P[0] == '?') printHelp(); else /* it looks like a file name */ { if (error != 0) break; strcpy(inName, P); /* set up the file names */ strcpy(outName, P); L = strlen(inName); /* and the name lengths */ strcpy(suffix, srSuffix); /* suffix for compressed files */ Ls = strlen(suffix); v = strcmp(&inName[L-Ls], suffix); /* is there a suffix? */ if (v == 0) { /* suffix matches -- must expand file */ outName[L-Ls] = 0; /* delete old suffix */ strcat(outName, outSuffix); /* append new suffix, if any */ compress = 0; strcpy(wMode, outMode); /* recover as plain file */ } else { /* no match - must compress */ strcat(outName, srSuffix); /* extend to compressed name */ compress = 1; strcpy(wMode, "wb"); /* write compressed as binary */ } inFile = fopen(inName, "rb"); /* try to open the input file */ errval = errno; if (inFile == NULL) /* open error */ { fprintf(stderr, "Error in opening input file '%s', error code = %d\n", inName, errval); error = errFinish; } outFile = fopen(outName, wMode); /* try to open the output file */ errval = errno; if (outFile == NULL) /* open error */ { fprintf(stderr, "Error in opening output file '%s', error code = %d\n", outName, errval); error = errFinish; } if (error != 0) break; else if (compress) /* now process the file */ encodeFile(); else decodeFile(); #if Stats logCnt = 0; Trigger = -1; /* reset the logging threshold */ #endif } /* end of handling file name */ } /* end of scanning parameters */ return 0; } /* main */