This is freeze.c in view mode; [Download] [Up]
#include "freeze.h" #include "lz.h" #include "huf.h" #include "patchlevel.h" /* * Freeze - data freezing program * Version 1.0: * This program is made from GNU compress.c and Yoshizaki/Tagawa's * lzhuf.c. (Thanks to all of them.) * The algorithm is modified for using in pipe * (added ENDOF symbol in Huffman table). * Version 1.1: * Check for lack of bytes in frozen file when melting. * Put the GetBit routine into DecodeChar for reduce function- * call overhead when melting. * Version 1.2: * Added delayed coding a la COMIC. * Now freeze works on Intels (*NIX, Microsoft, Turbo), * Sun (SunOS). * Version 2.0: * Buffer size is now 8192 bytes, maximum match length - 256 bytes. * Improved hash function (with tuning of hash-table) * Version 2.1: Noticeable speedup: Insert_Node and Get_Next_Match * are now separated. (Boyer-Moore string matching) * Version 2.2: Tunable static Huffman table for position information, * this info may be given in the command string now. * Version 2.2.3: Bug fixes, 10% freezing speedup. * Version 2.3: Minor bug fixes (DOS filenames handling, backward * compatibility feature improved, "bits" compression ratio display, * preventive check for special files), speedups, more comments added. */ static char ident[] = "@(#) freeze.c 2.3.%d %s leo@s514.ipmce.su\n"; int exit_stat = 0; void Usage() { #ifdef DEBUG # ifdef MSDOS fprintf(stderr,"Usage: freeze [-cdDfitvVg] [file | +type ...]\n"); # else fprintf(stderr,"Usage: freeze [-cdDfvVg] [file | +type ...]\n"); # endif /* MSDOS */ #else # ifdef MSDOS fprintf(stderr,"Usage: freeze [-cdfitvVg] [file | +type ...]\n"); # else fprintf(stderr,"Usage: freeze [-cdfvVg] [file | +type ...]\n"); # endif /* MSDOS */ #endif /* DEBUG */ } void (*meltfunc)(); /* To call something for melting */ short topipe = 0, /* Write output on stdout, suppress messages */ precious = 1, /* Don't unlink output file on interrupt */ quiet = 1, /* Don't tell me about freezing */ do_melt = 0, /* freeze means "freeze" */ greedy = 0, /* GREEDY parsing */ force = 0; /* "Force" flag */ char ofname [MAXNAMLEN]; struct stat statbuf; /* Used by 'main' and 'copystat' routines */ #ifdef MSDOS char *last_sep(); /* last slash, backslash, or colon */ char tail[2]; /* 2nd and 3rd chars of file extension */ # ifdef BIN_DEFAULT short image = O_BINARY; # else short image = O_TEXT; # endif #else # define last_sep(s) rindex((s), '/') /* Unix always uses slashes */ char deffile[] = "/etc/default/freeze"; #endif #ifdef DEBUG short debug = 0; short verbose = 0; char * pr_char(); long symbols_out = 0, refers_out = 0; #endif /* DEBUG */ /* Do not sleep when freeze works :-) */ long indc_count, indc_threshold; #ifdef INT_SIG int #else void #endif (*bgnd_flag)(); void writeerr(), copystat(), version(), tune_table(); /***************************************************************** * * Usage: freeze [-cdfivV] [-t type] [file ...] * Inputs: * * -c: Write output on stdout, don't remove original. * * -d: If given, melting is done instead. * * -g: Use "greedy" parsing (1.5% worse, 40% faster). * (Means nothing when melting) * * -f: Forces output file to be generated, even if one already * exists, and even if no space is saved by freezeing. * If -f is not used, the user will be prompted if stdin is * a tty, otherwise, the output file will not be overwritten. * * -i: Image mode (defined only under MS-DOS). Prevents * conversion between UNIX text representation (LF line * termination) in frozen form and MS-DOS text * representation (CR-LF line termination) in melted * form. Useful with non-text files. Default if * BIN_DEFAULT specified. * * -b: Binary mode. Synonym for -i. MS-DOS only. * * -t: Text mode (defined only under MS-DOS). Treat file * as text (CR-LF and ^Z special) in melted form. Default * unless BIN_DEFAULT specified. * * -v: Write freezing statistics * * -V: Write version and compilation options. * * file ...: Files to be frozen. If none specified, stdin * is used. * Outputs: * file.F: Frozen form of file with same mode, owner, and utimes * or stdout (if stdin used as input) * * Assumptions: * When filenames are given, replaces with the frozen version * (.F suffix) only if the file decreases in size. * Algorithm: * Modified Lempel-Ziv-SS method (LZSS), adaptive Huffman coding * for literal symbols and length info. * Static Huffman coding for position info. (Is it optimal ?) * Lower bits of position info are put in output * file without any coding because of their random distribution. */ /* From compress.c. Replace .Z --> .F etc */ void main( argc, argv ) register int argc; char **argv; { short overwrite = 0; /* Do not overwrite unless given -f flag */ char tempname[100]; char **filelist, **fileptr; char *cp, *rindex(); #ifndef MSDOS char *malloc(); #endif #if defined(__TURBOC__) || !defined(INT_SIG) extern void onintr(); #else extern onintr(); #endif #ifdef MSDOS char *sufp; #else #ifdef INT_SIG extern oops(); #else extern void oops(); #endif #endif #ifndef MSDOS if ( (bgnd_flag = signal ( SIGINT, SIG_IGN )) != SIG_IGN ) #endif { signal ( SIGINT, onintr ); #ifdef __TURBOC__ setcbrk(1); #endif #ifndef MSDOS signal ( SIGSEGV, oops ); #endif } filelist = fileptr = (char **)(malloc(argc * sizeof(*argv))); *filelist = NULL; if((cp = last_sep(argv[0])) != 0) { cp++; } else { cp = argv[0]; } #ifdef MSDOS /* use case-insensitive match: parent may not be command.com */ if(stricmp(cp, "melt.exe") == 0) { #else if(strcmp(cp, "melt") == 0) { #endif do_melt = 1; #ifdef MSDOS } else if(stricmp(cp, "fcat.exe") == 0) { #else } else if(strcmp(cp, "fcat") == 0) { #endif do_melt = 1; topipe = 1; } else { /* Freezing */ #ifndef MSDOS defopen(deffile); #else cp = rindex(cp, '.'); *++cp = 'C'; *++cp = 'N'; *++cp = 'F'; *++cp = '\0'; defopen(argv[0]); #endif } #ifdef BSD4_2 /* 4.2BSD dependent - take it out if not */ setlinebuf( stderr ); #endif /* BSD4_2 */ /* Argument Processing * All flags are optional. * -D => debug * -V => print Version; debug verbose * -d => do_melt * -v => unquiet * -g => greedy * -f => force overwrite of output file * -c => cat all output to stdout * if a string is left, must be an input filename. */ for (argc--, argv++; argc > 0; argc--, argv++) { if (**argv == '-') { /* A flag argument */ while (*++(*argv)) { /* Process all flags in this arg */ switch (**argv) { #ifdef DEBUG case 'D': debug = 1; break; case 'V': verbose = 1; #else case 'V': version(); #endif /* DEBUG */ break; #ifdef MSDOS case 'i': case 'b': image = O_BINARY; /* binary (aka image) mode */ break; case 't': /* text mode */ image = O_TEXT; break; #endif case 'v': quiet--; break; case 'g': greedy = 1; break; case 'd': do_melt = 1; break; case 'f': case 'F': overwrite = 1; force = 1; break; case 'c': topipe = 1; break; case 'q': quiet = 1; break; default: fprintf(stderr, "Unknown flag: '%c'; ", **argv); Usage(); exit(1); } } } else { /* Input file name */ *fileptr++ = *argv; /* Build input file list */ *fileptr = NULL; } } # ifdef DEBUG if (verbose && !debug) version(); #endif if (*filelist != NULL) { for (fileptr = filelist; *fileptr; fileptr++) { if (**fileptr == '+' && do_melt == 0) { tune_table(*fileptr + 1); /* If a file type is given, but no file names */ if (filelist[1] == NULL) goto Pipe; continue; } exit_stat = 0; if (do_melt != 0) { /* MELTING */ #ifdef MSDOS /* Check for .F or XF suffix; add one if necessary */ cp = *fileptr + strlen(*fileptr) - 2; if ((*cp != '.' && *cp != 'X' && *cp != 'x') || (*(++cp) != 'F' && *cp != 'f')) { strcpy(tempname, *fileptr); *tail = '\0'; if ((cp=rindex(tempname,'.')) == NULL) strcat(tempname, ".F"); else if(*(++cp) == '\0') /* pseudo-extension: FOOBAR. */ strcat(tempname, "F"); else { /* cp now points to file extension */ tail[0] = cp[1]; /* save two chars */ tail[1] = cp[2]; *(++cp) = '\0'; strcat(tempname, "XF"); } *fileptr = tempname; } #else /* Check for .F suffix */ if (strcmp(*fileptr + strlen(*fileptr) - 2, ".F") != 0) { /* No .F: tack one on */ strcpy(tempname, *fileptr); strcat(tempname, ".F"); *fileptr = tempname; } #endif /*MSDOS */ /* Open input file for melting */ if (checkstat(*fileptr)) continue; #ifdef MSDOS if ((freopen(*fileptr, "rb", stdin)) == NULL) #else if ((freopen(*fileptr, "r", stdin)) == NULL) #endif { perror(*fileptr); continue; } /* Check the magic number */ if (getchar() != MAGIC1) goto reject; switch (getchar()) { #ifdef COMPAT case MAGIC2_1: meltfunc = melt1; break; #endif case MAGIC2_2: meltfunc = melt2; break; default: reject: fprintf(stderr, "%s: not in frozen format\n", *fileptr); continue; } /* Generate output filename */ strcpy(ofname, *fileptr); ofname[strlen(*fileptr) - 2] = '\0'; /* Strip off .F */ #ifdef MSDOS strcat(ofname, tail); #endif } else { /* FREEZING */ #ifdef MSDOS cp = *fileptr + strlen(*fileptr) - 2; if ((*cp == '.' || *cp == 'X' || *cp == 'x') && (*(++cp) == 'F' || *cp == 'f')) { fprintf(stderr,"%s: already has %s suffix -- no change\n", *fileptr,--cp); /* } */ #else if (strcmp(*fileptr + strlen(*fileptr) - 2, ".F") == 0) { fprintf(stderr, "%s: already has .F suffix -- no change\n", *fileptr); #endif /* MSDOS */ continue; } /* Open input file for freezing */ if (checkstat(*fileptr)) continue; #ifdef MSDOS if ((freopen(*fileptr, image == O_TEXT ? "rt" : "rb", stdin)) == NULL) #else if ((freopen(*fileptr, "r", stdin)) == NULL) #endif { perror(*fileptr); continue; } /* Generate output filename */ strcpy(ofname, *fileptr); #ifndef BSD4_2 /* Short filenames */ if ((cp = last_sep(ofname)) != NULL) cp++; else cp = ofname; # ifdef MSDOS if (topipe == 0 && (sufp = rindex(cp, '.')) != NULL && strlen(sufp) > 2) fprintf(stderr, "%s: part of filename extension will be replaced by XF\n", cp); # else if (topipe == 0 && strlen(cp) > 12) { fprintf(stderr,"%s: filename too long to tack on .F\n",cp); continue; } # endif /* MSDOS */ #endif /* BSD4_2 Long filenames allowed */ #ifdef MSDOS /* There is no difference between FOOBAR and FOOBAR. names */ if ((cp = rindex(ofname, '.')) == NULL) strcat(ofname, ".F"); else if (cp[1] == '\0') /* FOOBAR. case */ strcat(ofname, "F"); else { cp[2] = '\0'; strcat(ofname, "XF"); } #else strcat(ofname, ".F"); #endif /* MSDOS */ } precious = 0; /* Check for overwrite of existing file */ if (overwrite == 0 && topipe == 0) { if (stat(ofname, &statbuf) == 0) { char response[2]; response[0] = 'n'; fprintf(stderr, "%s already exists;", ofname); #ifndef MSDOS if (foreground()) { #endif fprintf(stderr, " do you wish to overwrite %s (y or n)? ", ofname); fflush(stderr); read(2, response, 2); while (response[1] != '\n') { if (read(2, response+1, 1) < 0) { /* Ack! */ perror("stderr"); break; } } #ifndef MSDOS } #endif if (response[0] != 'y') { fprintf(stderr, "\tnot overwritten\n"); continue; } } } if(topipe == 0) { /* Open output file */ #ifdef DEBUG if (do_melt == 0 || debug == 0) { #endif #ifdef MSDOS if (freopen(ofname, do_melt && image == O_TEXT ? "wt" : "wb", stdout) == NULL) #else if (freopen(ofname, "w", stdout) == NULL) #endif { perror(ofname); continue; } #ifdef DEBUG } #endif if(quiet != 1) { fprintf(stderr, "%s:", *fileptr); indc_threshold = 2048; indc_count = 1024; } } else { /* output is to stdout */ #ifdef MSDOS /* freeze output always binary; melt output is binary if image == O_BINARY */ if (do_melt == 0 || image == O_BINARY) setmode(fileno(stdout), O_BINARY); #endif } /* Actually do the freezing/melting */ if (do_melt == 0) freeze(); #ifndef DEBUG else meltfunc(); #else else if (debug && verbose) printcodes(meltfunc == (void(*)()) melt2); else meltfunc(); #endif /* DEBUG */ /* check output status, and close to make sure data is written */ if ( ferror(stdout) || (!topipe && fclose(stdout) < 0)) writeerr(); if(topipe == 0) copystat(*fileptr); /* Copy stats */ } } else { /* Standard input */ Pipe: if (fstat(fileno(stdin), &statbuf)) { perror("stdin"); exit(1); } file_length = statbuf.st_size; indc_threshold = file_length / 100; if (indc_threshold < 4096) indc_threshold = 4096; #ifdef DEBUG fprintf(stderr, "File length: %ld\n", file_length); #endif topipe = 1; if (do_melt == 0) { #ifdef MSDOS /* freeze output always binary */ /* freeze input controlled by -i -t -b switches */ setmode(fileno(stdout), O_BINARY); setmode(fileno(stdin), image); #endif freeze(); if(quiet != 1) putc('\n', stderr); } else { #ifdef MSDOS /* melt input always binary */ /* melt output to stdout binary if so requested */ setmode(fileno(stdin), O_BINARY); setmode(fileno(stdout), image); #endif /* Check the magic number */ if (getchar() != MAGIC1) goto badstdin; switch (getchar()) { #ifdef COMPAT case MAGIC2_1: meltfunc = melt1; break; #endif case MAGIC2_2: meltfunc = melt2; break; default: badstdin: fprintf(stderr, "stdin: not in frozen format\n"); exit(1); } #ifndef DEBUG meltfunc(); #else if (debug && verbose) printcodes(meltfunc == (void(*)()) melt2); else meltfunc(); #endif /* DEBUG */ } } exit(exit_stat); /*NOTREACHED*/ } long in_count = 1; /* length of input */ long bytes_out; /* length of frozen output */ long file_length = 0; /* initial length of file */ /* Calculates and prints the compression ratio w/o floating point OPs */ void prratio(stream, was, is) FILE *stream; long was, is; { register long q; /* This works everywhere */ if (!is) is++; if(was > 214748L) { /* 2147483647/10000 */ q = was / (is / 10000L); } else { q = 10000L * was / is; /* Long calculations, though */ } if (q < 0) { putc('-', stream); q = -q; } fprintf(stream, "%d.%02d%%", (int)(q / 100), (int)(q % 100)); #ifdef GATHER_STAT fprintf(stream, "(%ld / %ld)", was, is); #endif } /* Calculates and prints bits/byte compression ratio as above */ void prbits(stream, was, is) FILE *stream; long was, is; { register long q; if (!was) was++; if(is > 2684354L) { /* 2147483647/800 */ q = is / (was / 800L); } else { q = 800L * is / was; } fprintf(stream, " (%d.%02d bits)", (int)(q / 100), (int)(q % 100)); } /* From compress.c */ char * rindex(s, c) /* For those who don't have it in libc.a */ register char *s, c; { char *p; for (p = NULL; *s; s++) if (*s == c) p = s; return(p); } /* There was an error when reading or writing files */ void writeerr() { if (!topipe) { perror ( ofname ); unlink ( ofname ); } exit ( 1 ); } void copystat(ifname) char *ifname; { #ifdef __TURBOC__ struct ftime utimbuf; #endif int mode; #ifndef __TURBOC__ time_t timep[2]; #endif #ifdef MSDOS if (_osmajor < 3) freopen("CON","at",stdout); else /* MS-DOS 2.xx bug */ #endif fclose(stdout); if (exit_stat == 2 && (!force)) { /* No freezing: remove file.F */ if(quiet != 1) fprintf(stderr, "-- file unchanged\n"); } else { /* ***** Successful Freezing ***** */ if (stat (ifname, &statbuf)) { /* file disappeared ?! */ perror(ifname); exit_stat = 1; return; } exit_stat = 0; mode = statbuf.st_mode & 07777; if (chmod(ofname, mode)) /* Copy modes */ perror(ofname); #ifndef MSDOS /* Copy ownership */ chown(ofname, (int) statbuf.st_uid, (int) statbuf.st_gid); #endif #ifdef __TURBOC__ getftime(fileno(stdin),&utimbuf); freopen(ofname,"rb",stdout); setftime(fileno(stdout),&utimbuf); fclose(stdout); #else timep[0] = statbuf.st_atime; timep[1] = statbuf.st_mtime; utime(ofname, timep); /* Update last accessed and modified times */ #endif precious = 1; if (unlink(ifname)) /* Remove input file */ perror(ifname); if(quiet != 1) fprintf(stderr, " -- replaced with %s\n", ofname); return; /* Successful return */ } /* Unsuccessful return -- one of the tests failed */ if (unlink(ofname)) perror(ofname); } /* Checks status of a file, returns 0 if the file may be frozen, or 1 otherwise; assigns this value to exit_stat */ int checkstat(ifname) char *ifname; { if (stat (ifname, &statbuf)) { perror(ifname); return exit_stat = 1; } /* Do NOT try to freeze /dev/null or /dev/tty ... */ #ifndef MSDOS if ((statbuf.st_mode & S_IFMT) != S_IFREG) { fprintf(stderr, "%s: ", ifname); fprintf(stderr, " not a regular file -- unchanged\n"); return exit_stat = 1; } else if (statbuf.st_nlink > 1) { fprintf(stderr, "%s: ", ifname); fprintf(stderr, " has %d other links -- unchanged\n", statbuf.st_nlink - 1); return exit_stat = 1; } #endif /* MSDOS */ file_length = statbuf.st_size; indc_threshold = file_length / 100; if (indc_threshold < 4096) indc_threshold = 4096; return exit_stat = 0; } #ifndef MSDOS /* * This routine returns 1 if we are running in the foreground and stderr * is a tty. (as in compress(1)) */ int foreground() { if(bgnd_flag != SIG_DFL) /* background? */ return(0); else { /* foreground */ if(isatty(2)) { /* and stderr is a tty */ return(1); } else { return(0); } } } #endif #if defined(__TURBOC__) || !defined(INT_SIG) void #endif /* Exception handler (SIGINT) */ onintr ( ) { if (!precious) { fclose(stdout); unlink ( ofname ); } exit ( 1 ); } #if defined(__TURBOC__) || !defined(INT_SIG) void #endif /* Exception handler (SIGSEGV) */ oops ( ) /* file is corrupt or internal error */ { fflush(stdout); fprintf(stderr, "Segmentation violation occured...\n"); exit ( 1 ); } /* Prints version and compilation options */ void version() { fprintf(stderr, ident, PATCHLEVEL, PATCHDATE); fprintf(stderr, "LZSS 8192/256 + Huffman coding\nOptions: "); #ifdef COMPAT fprintf(stderr, "compatible with vers. 1.0, "); #endif #ifdef DEBUG fprintf(stderr, "DEBUG, "); #endif #ifdef BSD4_2 fprintf(stderr, "BSD4_2, "); #endif #ifdef __XENIX__ fprintf(stderr, "XENIX, "); #endif #ifdef __TURBOC__ fprintf(stderr, "TURBO, "); #endif #ifdef GATHER_STAT fprintf(stderr, "GATHER_STAT, "); #endif fprintf(stderr, "HASH: %d bits\n", BITS); fprintf(stderr, "Static Huffman table: %d %d %d %d %d %d %d %d\n", Table2[1], Table2[2], Table2[3], Table2[4], Table2[5], Table2[6], Table2[7], Table2[8]); #ifdef MSDOS fprintf(stderr, "Default melted mode: %s\n", image == O_BINARY ? "binary" : "text"); #endif exit(0); } /* Deals with static Huffman table parameters. Input: command line option w/o leading `+'. Output: fills the array `Table2' if OK, exit(1) otherwise. */ void tune_table(type) char *type; { extern char * defread(); register char *s = defread(type), *t; static int v[8]; int i, is_list = 0; if(!s) { /* try to consider 'type' as a list of values: n1,n2, ... */ if(rindex(type, ',')) is_list = 1; else { fprintf(stderr, "\"%s\" - no such file type\n", type); exit(1); } if(sscanf(type, "%d,%d,%d,%d,%d,%d,%d,%d", v, v+1, v+2, v+3, v+4, v+5, v+6, v+7) != 8) { fprintf(stderr, "%s - a list of 8 numbers expected\n", type); exit(1); } } if(!is_list && (!(t = rindex(s, '=')) || sscanf(++t, "%d %d %d %d %d %d %d %d", v, v+1, v+2, v+3, v+4, v+5, v+6, v+7) != 8)) { fprintf(stderr, "\"%s\" - invalid entry\n", type); exit(1); } for(i = 0; i < 8; i++) Table2[i+1] = v[i]; if(quiet < 0) { if(!is_list) { t = s; /* make full word */ while(*s != '=' && *s != ' ' && *s != '\t') s++; *s = '\0'; } else t = ""; fprintf(stderr, "Using \"%s%s\" type\n", type, t); } } #ifdef MSDOS /* MSDOS typically has ':' and '\\' separators, but some command processors (and the int 21h function handler) support '/' too. Find the last of these. */ char * last_sep(s) register char *s; { char *p; for (p = NULL; *s; s++) if (*s == '/' || *s == '\\' || *s == ':') p = s; return(p); } #endif /* MSDOS */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.