This is unsit.c in view mode; [Download] [Up]
/* unsit - Macintosh StuffIt file extractor Version 1.5f, for StuffIt 1.5 July 23, 1990 This program will unpack a Macintosh StuffIt file into separate files. The data fork of a StuffIt file contains both the data and resource forks of the packed files. The program will unpack each Mac file into either separate .data, .rsrc., and .info files that can be downloaded to a Mac using macput and MacTerminal over a tty line, or into a single MacBinary format file. The MacBinary format is generally more convenient for those with network connections and FTP capability. The program is much like the "unpit" program for breaking apart Packit archive files. ***** IMPORTANT ***** To extract StuffIt files that have been compressed with the Lempel-Ziv compression method, unsit pipes the data through the "compress" program with the appropriate switches, rather than incorporate the uncompression routines within "unsit". Therefore, it is necessary to have the "compress" program on the system and in the search path to make "unsit" work. "Compress" is available from the comp.sources.unix archives. The program syntax is much like unpit and macput/macget, with some added options: unsit [-rdulM] [-vqfm] stuffit-file.data Only one of the flags r, d, u, l, or M should be specified. The default mode is to create the three macput/MacTerminal compatible file. The -M flag will cause the output to be in MacBinary format (a single file). This can be swapped (default = MacBinary, -M = macput) by changing the definitions of DEFAULT_MODE and OTHER_MODE below. The -r and -d flags will cause only the resource and data forks to be written. The -u flag will cause only the data fork to be written and to have carriage return characters changed to Unix newline characters. The -l flag will make the program only list the files in the StuffIt file. The -v flag causes the program to list the names, sizes, type, and creators of the files it is writing. The -q flag causes it to list the name, type and size of each file and wait for a 'y' or 'n' for either writing that file or skipping it, respectively. The -m flag is used when the input file in in the MacBinary format instead of just the data fork. It causes the program to skip the 128 byte MacBinary header before looking for the StuffIt header. It is not necessary to specify the -m flag since the program now checks for MacBinary format input files and handles them correctly Version 1.5 of the unsit supports extracting files and folders as implemented by StuffIt 1.5's "Hierarchy Maintained Folder" feature. Each folder is extracted as a subdirectory on the Unix system with the files in the folder placed in the corresponding subdirectory. The -f option can be used to "flatten" out the hierarchy and unsit will store all the files in the current directory. If the query option (-q) is used and a "n" response is given to a folder name, none of the files or folders in that folder will be extraced. Some of the program is borrowed from the macput.c/macget.c programs. Many, many thanks to Raymond Lau, the author of StuffIt, for including information on the format of the StuffIt archives in the documentation. Several changes and enhancements supplied by David Shanks (cde@atelabs.UUCP) have been incorporated into the program for doing things like supporting System V and recognizing MacBinary files. Christopher Bingham <kb@umnstat.stat.umn.edu> supplied some Macbinary patches. Code was also borrowed from the macbin program by Jim Budler for convert macput format files to MacBinary. I'm always glad to receive advice, suggestions, or comments about the program so feel free to send whatever you think would be helpful Author: Allan G. Weber weber@sipi.usc.edu ...!usc!sipi!weber Date: July 23, 1990 */ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> typedef long OSType; #include "stuffit.h" /* * Define the following if your Unix can only handle 14 character file names * (e.g. Version 7 and System V). */ /* #define SHORTNAMES */ /* * The following defines the name of the compress program that is used for the * uncompression of Lempel-Ziv compressed files. If the path is set up to * include the right directory, this should work. */ #define COMPRESS "compress" #define IOBUFSIZ 4096 #define MACBINHDRSIZE 128L #define INIT_CRC 0L extern unsigned short updcrc(); #define INFOBYTES 128 #define BYTEMASK 0xff #define S_SIGNATURE 0 #define S_NUMFILES 4 #define S_ARCLENGTH 6 #define S_SIGNATURE2 10 #define S_VERSION 14 #define SITHDRSIZE 22 #define F_COMPRMETHOD 0 #define F_COMPDMETHOD 1 #define F_FNAME 2 #define F_FTYPE 66 #define F_CREATOR 70 #define F_FNDRFLAGS 74 #define F_CREATIONDATE 76 #define F_MODDATE 80 #define F_RSRCLENGTH 84 #define F_DATALENGTH 88 #define F_COMPRLENGTH 92 #define F_COMPDLENGTH 96 #define F_RSRCCRC 100 #define F_DATACRC 102 #define F_HDRCRC 110 #define FILEHDRSIZE 112 #define F_NAMELEN 63 #ifdef SHORTNAMES /* short file names */ # define I_NAMELEN 15 /* 14 char file names + '\0' terminator */ #else # define I_NAMELEN 69 /* 63 + strlen(".info") + 1 */ #endif /* The following are copied out of macput.c/macget.c */ #define I_NAMEOFF 1 /* 65 <-> 80 is the FInfo structure */ #define I_TYPEOFF 65 #define I_AUTHOFF 69 #define I_FLAGOFF 73 #define I_LOCKOFF 81 #define I_DLENOFF 83 #define I_RLENOFF 87 #define I_CTIMOFF 91 #define I_MTIMOFF 95 #define INITED_BUG #define INITED_OFF I_FLAGOFF /* offset to byte with Inited flag */ #define INITED_MASK (~1) /* mask to '&' with byte to reset it */ #define TEXT 0 #define DATA 1 #define RSRC 2 #define MACPUT 3 #define DUMP 4 #define MACBINARY 5 /* Swap the following definitions if you want the output to default to MacBinary, and the -M switch to create macput file (.data, .rsrc, .info) */ #define DEFAULT_MODE MACPUT #define OTHER_MODE MACBINARY /* #define ADDBIN */ /* add .bin to macbinary file names */ #define NODECODE 0 #define DECODE 1 #define H_ERROR -1 #define H_EOF 0 #define H_WRITE 1 #define H_SKIP 2 struct node { int flag, byte; struct node *one, *zero; } nodelist[512], *nodeptr, *read_tree(); /* 512 should be big enough */ sitHdr sithdr; char f_info[I_NAMELEN]; char f_data[I_NAMELEN]; char f_rsrc[I_NAMELEN]; char info[INFOBYTES]; char mname[F_NAMELEN+1]; char uname[F_NAMELEN+1]; char iobuf[IOBUFSIZ]; char zbuf[128]; /* buffer of zeros to pad MacBinary forks */ int mode, txtmode, listonly, verbose, query, flatten; int bit, chkcrc, numfiles, depth; int debug = 0; FILE *infp; long get4(); short get2(); unsigned short write_file(); main(argc, argv) int argc; char *argv[]; { int status; int c; extern int optind; extern char *optarg; int errflg; int macbin; mode = DEFAULT_MODE; errflg = 0; macbin = 0; flatten = 0; numfiles = 0; depth = 0; while ((c = getopt(argc, argv, "DMdflmqruvx")) != EOF) switch (c) { case 'r': /* extract resource fork only */ mode = RSRC; break; case 'd': /* extract data fork only */ mode = DATA; break; case 'u': /* extract data fork as Unix text file */ mode = TEXT; break; case 'l': /* list contents of archive */ listonly++; break; case 'q': /* query user on each extraction */ query++; break; case 'v': /* verbose mode */ verbose++; break; case 'x': /* don't decode data, just dump to files*/ mode = DUMP; break; case 'm': /* input file is in Macbinary format */ macbin = 1; break; case 'M': /* output file in OTHER_MODE */ mode = OTHER_MODE; break; case 'f': /* don't create flat directory tree */ flatten = 1; break; case 'D': /* debugging mode */ debug = 1; break; case '?': errflg++; break; } if (errflg) { usage(); exit(1); } if (optind == argc) { usage(); exit(1); } else { if ((infp = fopen(argv[optind], "r")) == NULL) { fprintf(stderr,"Can't open input file \"%s\"\n",argv[optind]); exit(1); } } if (macbin) { if (fseek(infp, MACBINHDRSIZE, 0) == -1) { fprintf(stderr, "Can't skip over MacBinary header\n"); exit(1); } } if (readsithdr(&sithdr) == 0) { fprintf(stderr, "Can't read file header\n"); exit(1); } if (debug) { printf("archive header (%d bytes):\n", SITHDRSIZE); printf("numFiles=%d, arcLength=%ld, version=%d\n", sithdr.numFiles, sithdr.arcLength, sithdr.version & 0xff); } status = extract("", 0); exit((status < 0) ? 1 : 0); } usage() { fprintf(stderr, "Usage: unsit [-rdulM] [-vqfm] filename\n"); } /* extract(parent, skip) - Extract all files from the current folder. char *parent; name of parent folder int skip; 1 to skip all files and folders in this one 0 to extract them returns 1 if came an endFolder record 0 if EOF -1 if error (bad fileHdr, bad file, etc.) */ extract(parent, skip) char *parent; int skip; { fileHdr filehdr; struct stat sbuf; int status, rstat, sstat, skipit; char name[256]; while (1) { rstat = readfilehdr(&filehdr, skip); if (rstat == H_ERROR || rstat == H_EOF) { status = rstat; break; } if (debug) { printf("file header (%d bytes):\n", FILEHDRSIZE); printf("compRMethod=%d, compDMethod=%d\n", filehdr.compRMethod, filehdr.compDMethod); printf("rsrcLength=%ld, dataLength=%ld\n", filehdr.rsrcLength, filehdr.dataLength); printf("compRLength=%ld, compDLength=%ld\n", filehdr.compRLength, filehdr.compDLength); printf("rsrcCRC=%d=0x%04x, dataCRC=%d=0x%04x\n", filehdr.rsrcCRC, filehdr.rsrcCRC, filehdr.dataCRC, filehdr.dataCRC); } skipit = (rstat == H_SKIP) ? 1 : 0; if (filehdr.compRMethod == endFolder && filehdr.compDMethod == endFolder) { status = 1; /* finished with this folder */ break; } else if (filehdr.compRMethod == startFolder && filehdr.compDMethod == startFolder) { if (!listonly && rstat == H_WRITE && !flatten) { sstat = stat(uname, &sbuf); if (sstat == -1) { /* directory doesn't exist */ if (mkdir(uname, 0777) == -1) { fprintf(stderr, "Can't create subdirectory %s\n", uname); return(-1); } } else { /* something exists with this name */ if ((sbuf.st_mode & S_IFMT) != S_IFDIR) { fprintf(stderr, "Directory name %s already in use\n", uname); return(-1); } } if (chdir(uname) == -1) { fprintf(stderr, "Can't chdir to %s\n", uname); return(-1); } sprintf(name,"%s:%s", parent, uname); } depth++; status = extract(name, skipit); depth--; if (status != 1) break; /* problem with folder */ if (depth == 0) /* count how many top-level files done */ numfiles++; if (!flatten) chdir(".."); } else { if ((status = extractfile(&filehdr, skipit)) != 1) break; if (depth == 0) /* count how many top-level files done */ numfiles++; } if (numfiles == sithdr.numFiles) break; } return(status); } extractfile(fh, skip) fileHdr *fh; int skip; { unsigned short crc; FILE *fp, *fp1; int n; f_data[0] = f_rsrc[0] = f_info[0] = '\0'; /* assume no output files */ /* figure out what file names to use and what to do */ if (!listonly && !skip) { switch (mode) { case MACPUT: /* do both rsrc and data forks */ sprintf(f_data, "%.*s.data", I_NAMELEN - 6, uname); sprintf(f_rsrc, "%.*s.rsrc", I_NAMELEN - 6, uname); sprintf(f_info, "%.*s.info", I_NAMELEN - 6, uname); break; case RSRC: /* rsrc fork only */ sprintf(f_rsrc, "%.*s.rsrc", I_NAMELEN - 6, uname); break; case DATA: /* data fork only */ case TEXT: sprintf(f_data, "%.*s", I_NAMELEN - 1, uname); break; case DUMP: /* for debugging, dump data as is */ sprintf(f_data, "%.*s.ddump", I_NAMELEN - 7, uname); sprintf(f_rsrc, "%.*s.rdump", I_NAMELEN - 7, uname); fh->compRMethod = fh->compDMethod = noComp; break; case MACBINARY: /* output file in MacBinary format */ sprintf(f_data, "%.*s.data", I_NAMELEN - 6, uname); sprintf(f_rsrc, "%.*s.rsrc", I_NAMELEN - 6, uname); #ifndef ADDBIN sprintf(f_info, "%.*s", I_NAMELEN - 1, uname); #else sprintf(f_info, "%.*s.bin", I_NAMELEN - 5, uname); #endif /*ADDBIN*/ break; } } fp = NULL; /* so we can tell if a file is open */ if (f_info[0] != '\0' && check_access(f_info) != -1) { fp = fopen(f_info, "w"); if (fp == NULL) { perror(f_info); exit(1); } if (mode == MACBINARY) { /* convert to MacBinary header */ /* taken from the macbin program */ if (info[74] & 0x40) info[81] = '\1'; /* protected */ info[74] = '\0'; /* clear zero2 */ info[82] = '\0'; /* force zero3 clear */ } fwrite(info, 1, INFOBYTES, fp); } if (f_rsrc[0] != '\0') { txtmode = 0; crc = write_file(f_rsrc, fh->compRLength, fh->rsrcLength, fh->compRMethod); if (chkcrc && fh->rsrcCRC != crc) { fprintf(stderr, "CRC error on resource fork: need 0x%04x, got 0x%04x\n", fh->rsrcCRC, crc); return(-1); } } else { fseek(infp, (long) fh->compRLength, 1); } if (f_data[0] != '\0') { txtmode = (mode == TEXT); crc = write_file(f_data, fh->compDLength, fh->dataLength, fh->compDMethod); if (chkcrc && fh->dataCRC != crc) { fprintf(stderr, "CRC error on data fork: need 0x%04x, got 0x%04x\n", fh->dataCRC, crc); return(-1); } } else { fseek(infp, (long) fh->compDLength, 1); } if (fp != NULL) { /* if Macbinary output, copy the data and resource forks to the end of the info file, and pad each to multiples of 128 bytes. */ if (mode == MACBINARY) { fp1 = fopen(f_data, "r"); /* re-open the file we just wrote */ if (fp1 == NULL) { perror(f_data); exit(1); } while ((n = fread(iobuf, 1, IOBUFSIZ, fp1)) > 0) fwrite(iobuf, 1, n, fp); /* append it to the info file */ /* pad out to multiple of 128 if in MacBinary format */ n = fh->dataLength % 128; if (n > 0) outc(zbuf, 128 - n, fp); fclose(fp1); unlink(f_data); fp1 = fopen(f_rsrc, "r"); /* re-open the file we just wrote */ if (fp1 == NULL) { perror(f_rsrc); exit(1); } while ((n = fread(iobuf, 1, IOBUFSIZ, fp1)) > 0) fwrite(iobuf, 1, n, fp); /* append it to the info file */ /* pad out to multiple of 128 if in MacBinary format */ n = fh->rsrcLength % 128; if (n > 0) outc(zbuf, 128 - n, fp); fclose(fp1); unlink(f_rsrc); } fclose(fp); } return(1); } readsithdr(s) sitHdr *s; { char temp[FILEHDRSIZE]; int count = 0; for (;;) { if (fread(temp, 1, SITHDRSIZE, infp) != SITHDRSIZE) { fprintf(stderr, "Can't read file header\n"); return(0); } if (strncmp(temp + S_SIGNATURE, "SIT!", 4) == 0 && strncmp(temp + S_SIGNATURE2, "rLau", 4) == 0) { s->numFiles = get2(temp + S_NUMFILES); s->arcLength = get4(temp + S_ARCLENGTH); return(1); } if (++count == 2) { fprintf(stderr, "Not a StuffIt file\n"); return(0); } if (fread(&temp[SITHDRSIZE], 1, FILEHDRSIZE - SITHDRSIZE, infp) != FILEHDRSIZE - SITHDRSIZE) { fprintf(stderr, "Can't read file header\n"); return(0); } if (strncmp(temp + I_TYPEOFF, "SIT!", 4) == 0 && strncmp(temp + I_AUTHOFF, "SIT!", 4) == 0) { /* MacBinary format */ fseek(infp, (long)(INFOBYTES-FILEHDRSIZE), 1); /* Skip over header */ } } } /* readfilehdr - reads the file header for each file and the folder start and end records. returns: H_ERROR = error H_EOF = EOF H_WRITE = write file/folder H_SKIP = skip file/folder */ readfilehdr(f, skip) fileHdr *f; int skip; { unsigned short crc; int i, n, write_it, isfolder; char hdr[FILEHDRSIZE]; char ch, *mp, *up; char *tp, temp[10]; for (i = 0; i < INFOBYTES; i++) info[i] = '\0'; /* read in the next file header, which could be folder start/end record */ n = fread(hdr, 1, FILEHDRSIZE, infp); if (n == 0) /* return 0 on EOF */ return(H_EOF); else if (n != FILEHDRSIZE) { fprintf(stderr, "Can't read file header\n"); return(H_ERROR); } /* check the CRC for the file header */ crc = INIT_CRC; crc = updcrc(crc, hdr, FILEHDRSIZE - 2); f->hdrCRC = get2(hdr + F_HDRCRC); if (f->hdrCRC != crc) { fprintf(stderr, "Header CRC mismatch: got 0x%04x, need 0x%04x\n", f->hdrCRC, crc); return(H_ERROR); } /* grab the name of the file or folder */ n = hdr[F_FNAME] & BYTEMASK; if (n > F_NAMELEN) n = F_NAMELEN; info[I_NAMEOFF] = n; copy(info + I_NAMEOFF + 1, hdr + F_FNAME + 1, n); strncpy(mname, hdr + F_FNAME + 1, n); mname[n] = '\0'; /* copy to a string with no illegal Unix characters in the file name */ mp = mname; up = uname; while ((ch = *mp++) != '\0') { if (ch <= ' ' || ch > '~' || strchr("/!()[]*<>?\\\"$\';&`", ch) != NULL) ch = '_'; *up++ = ch; } *up = '\0'; /* get lots of other stuff from the header */ f->compRMethod = hdr[F_COMPRMETHOD]; f->compDMethod = hdr[F_COMPDMETHOD]; f->rsrcLength = get4(hdr + F_RSRCLENGTH); f->dataLength = get4(hdr + F_DATALENGTH); f->compRLength = get4(hdr + F_COMPRLENGTH); f->compDLength = get4(hdr + F_COMPDLENGTH); f->rsrcCRC = get2(hdr + F_RSRCCRC); f->dataCRC = get2(hdr + F_DATACRC); /* if it's an end folder record, don't need to do any more */ if (f->compRMethod == endFolder && f->compDMethod == endFolder) return(H_WRITE); /* prepare an info file in case its needed */ copy(info + I_TYPEOFF, hdr + F_FTYPE, 4); copy(info + I_AUTHOFF, hdr + F_CREATOR, 4); copy(info + I_FLAGOFF, hdr + F_FNDRFLAGS, 2); #ifdef INITED_BUG info[INITED_OFF] &= INITED_MASK; /* reset init bit */ #endif copy(info + I_DLENOFF, hdr + F_DATALENGTH, 4); copy(info + I_RLENOFF, hdr + F_RSRCLENGTH, 4); copy(info + I_CTIMOFF, hdr + F_CREATIONDATE, 4); copy(info + I_MTIMOFF, hdr + F_MODDATE, 4); isfolder = f->compRMethod == startFolder && f->compDMethod == startFolder; /* list the file name if verbose or listonly mode, also if query mode */ if (skip) /* skip = 1 if skipping all in this folder */ write_it = 0; else { write_it = 1; if (listonly || verbose || query) { for (i = 0; i < depth; i++) putchar(' '); if (isfolder) printf("Folder: \"%s\"", uname); else printf("name=\"%s\", type=%4.4s, author=%4.4s, data=%ld, rsrc=%ld", uname, hdr + F_FTYPE, hdr + F_CREATOR, f->dataLength, f->rsrcLength); if (query) { /* if querying, check with the boss */ printf(" ? "); fgets(temp, sizeof(temp) - 1, stdin); tp = temp; write_it = 0; while (*tp != '\0') { if (*tp == 'y' || *tp == 'Y') { write_it = 1; break; } else tp++; } } else /* otherwise, terminate the line */ putchar('\n'); } } return(write_it ? H_WRITE : H_SKIP); } check_access(fname) /* return 0 if OK to write on file fname, -1 otherwise */ char *fname; { char temp[10], *tp; if (access(fname, 0) == -1) { return(0); } else { printf("%s exists. Overwrite? ", fname); fgets(temp, sizeof(temp) - 1, stdin); tp = temp; while (*tp != '\0') { if (*tp == 'y' || *tp == 'Y') { return(0); } else tp++; } } return(-1); } unsigned short write_file(fname, ibytes, obytes, type) char *fname; unsigned long ibytes, obytes; unsigned char type; { unsigned short crc; int i, n, ch, lastch; FILE *outf; char temp[256]; crc = INIT_CRC; chkcrc = 1; /* usually can check the CRC */ if (check_access(fname) == -1) { fseek(infp, ibytes, 1); chkcrc = 0; /* inhibit crc check if file not written */ return(-1); } switch (type) { case noComp: /* no compression */ if (verbose) printf("compression=none\n"); outf = fopen(fname, "w"); if (outf == NULL) { perror(fname); exit(1); } while (ibytes > 0) { n = (ibytes > IOBUFSIZ) ? IOBUFSIZ : ibytes; n = fread(iobuf, 1, n, infp); if (n == 0) break; crc = updcrc(crc, iobuf, n); outc(iobuf, n, outf); ibytes -= n; } fclose(outf); break; case rleComp: /* run length encoding */ if (verbose) printf("compression=rle\n"); outf = fopen(fname, "w"); if (outf == NULL) { perror(fname); exit(1); } while (ibytes > 0) { ch = getc(infp) & 0xff; ibytes--; if (ch == 0x90) { /* see if its the repeat marker */ n = getc(infp) & 0xff; /* get the repeat count */ ibytes--; if (n == 0) { /* 0x90 was really an 0x90 */ iobuf[0] = 0x90; crc = updcrc(crc, iobuf, 1); outc(iobuf, 1, outf); } else { n--; for (i = 0; i < n; i++) iobuf[i] = lastch; crc = updcrc(crc, iobuf, n); outc(iobuf, n, outf); } } else { iobuf[0] = ch; crc = updcrc(crc, iobuf, 1); lastch = ch; outc(iobuf, 1, outf); } } fclose(outf); break; case lzwComp: /* LZW compression */ if (verbose) printf("compression=lzw\n"); sprintf(temp, "%s%s", COMPRESS, " -d -c -n -b 14 "); if (txtmode) { strcat(temp, "| tr \'\\015\' \'\\012\' "); chkcrc = 0; /* can't check CRC in this case */ } strcat(temp, "> '"); strcat(temp, fname); strcat(temp, "'"); outf = popen(temp, "w"); if (outf == NULL) { perror(fname); exit(1); } while (ibytes > 0) { n = (ibytes > IOBUFSIZ) ? IOBUFSIZ : ibytes; n = fread(iobuf, 1, n, infp); if (n == 0) break; fwrite(iobuf, 1, n, outf); ibytes -= n; } pclose(outf); if (chkcrc) { outf = fopen(fname, "r"); /* read the file to get CRC value */ if (outf == NULL) { perror(fname); exit(1); } while (1) { n = fread(iobuf, 1, IOBUFSIZ, outf); if (n == 0) break; crc = updcrc(crc, iobuf, n); } fclose(outf); } break; case hufComp: /* Huffman compression */ if (verbose) printf("compression=Huffman\n"); outf = fopen(fname, "w"); if (outf == NULL) { perror(fname); exit(1); } nodeptr = nodelist; bit = 0; /* put us on a byte boundary */ read_tree(); while (obytes > 0) { n = (obytes > IOBUFSIZ) ? IOBUFSIZ : obytes; for (i = 0; i < n; i++) iobuf[i] = gethuffbyte(DECODE); crc = updcrc(crc, iobuf, n); outc(iobuf, n, outf); obytes -= n; } fclose(outf); break; default: fprintf(stderr, "Unknown compression method\n"); chkcrc = 0; /* inhibit crc check if file not written */ return(-1); } return(crc & 0xffff); } outc(p, n, fp) char *p; int n; FILE *fp; { register char *p1; register int i; if (txtmode) { for (i = 0, p1 = p; i < n; i++, p1++) if ((*p1 & BYTEMASK) == '\r') *p1 = '\n'; } fwrite(p, 1, n, fp); } long get4(bp) char *bp; { register int i; long value = 0; for (i = 0; i < 4; i++) { value <<= 8; value |= (*bp & BYTEMASK); bp++; } return(value); } short get2(bp) char *bp; { register int i; int value = 0; for (i = 0; i < 2; i++) { value <<= 8; value |= (*bp & BYTEMASK); bp++; } return(value); } copy(p1, p2, n) char *p1, *p2; int n; { while (n-- > 0) *p1++ = *p2++; } /* This routine recursively reads the Huffman encoding table and builds and decoding tree. */ struct node *read_tree() { struct node *np; np = nodeptr++; if (getbit() == 1) { np->flag = 1; np->byte = gethuffbyte(NODECODE); } else { np->flag = 0; np->zero = read_tree(); np->one = read_tree(); } return(np); } /* This routine returns the next bit in the input stream (MSB first) */ getbit() { static char b; if (bit == 0) { b = getc(infp) & 0xff; bit = 8; } bit--; return((b >> bit) & 1); } /* This routine returns the next 8 bits. If decoding is on, it finds the byte in the decoding tree based on the bits from the input stream. If decoding is not on, it either gets it directly from the input stream or puts it together from 8 calls to getbit(), depending on whether or not we are currently on a byte boundary */ gethuffbyte(decode) int decode; { register struct node *np; register int i, b; if (decode == DECODE) { np = nodelist; while (np->flag == 0) np = (getbit()) ? np->one : np->zero; b = np->byte; } else { if (bit == 0) /* on byte boundary? */ b = getc(infp) & 0xff; else { /* no, put a byte together */ b = 0; for (i = 8; i > 0; i--) { b = (b << 1) + getbit(); } } } return(b); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.