/* * $Header: /usr/people/tcl/src/uutar/RCS/decode.c,v 1.1.1.3 1993/09/11 18:42:17 tcl Exp $ * Tom Lawrence * tcl@sgi.com */ #include #include #include #include #include #include #include "codes.h" /* * given a string with n tokens separated by white space in it, and a * pointer to a char vector, create a vector with each pointer pointing * to a successive token and null terminate the tokens. Return the * number of tokens or -1 on error. This routine is destructive to the * passed string */ #define IS_WHITE_SPACE(c) (c == ' ' || c == '\t') #define VECLEN 10 FILE *infp, *outfp; static int tokenize(string, vector) char *string; char **vector; { int tokens; enum { WHITE_SPACE, TOKEN } state; char *c; /* scan through the string setting up the vector pointers and * null terminating the tokens */ tokens = 0; state = WHITE_SPACE; for(c = string; *c; c++) { if (state == WHITE_SPACE && !IS_WHITE_SPACE(*c)) { /* just hit beginning of a token */ vector[tokens] = c; tokens++; state = TOKEN; if (tokens >= VECLEN) { fprintf(stderr, "too many tokens in input\n"); exit(1); } } else if (state == TOKEN && IS_WHITE_SPACE(*c)) { /* just ended a token */ *c = 0; state = WHITE_SPACE; } } return(tokens); } /* normally I'd use strtol for this, but strtol can't handle * unsigned values greater than 0x7FFFFFFF on some machines. */ static unsigned int hex2long(str) char *str; { unsigned long ret = 0; char *c, c1; for(c = str; *c; c++) { c1 = *c; if (c1 >= '0' && c1 <= '9') c1 -= '0'; else if (c1 >= 'a' && c1 <= 'f') c1 -= ('a' - 10); else if (c1 >= 'A' && c1 <= 'F') c1 -= ('A' - 10); ret = (ret << 4) + c1; } return(ret); } static void usage() { printf("options:\n"); printf("-i \n"); printf("-o \n"); exit(1); } /* parse command line arguments */ static void parse(argc, argv) int argc; char **argv; { char *infile, *outfile; infile = outfile = 0; while(--argc) { argv++; if (!strcmp(*argv, "-i")) { if (argc < 2) usage(); argc--; argv++; infile = *argv; } else if (!strcmp(*argv, "-o")) { if (argc < 2) usage(); argc--; argv++; outfile = *argv; } else usage(); } /* open input stream */ if (infile) { if ((infp = fopen(infile, "r")) == 0) { perror(infile); exit(1); } } else infp = stdin; /* open output stream or leave it for later if no output file * was specified */ if (outfile) { if ((outfp = fopen(outfile, "w")) == 0) { perror(outfile); exit(1); } } else outfp = 0; } main(argc, argv) int argc; char **argv; { char buffer[1024], *tokens[VECLEN], *c, out; int state, numtokens, outfd, buf_offset, lookforend; unsigned int cksum; unsigned short buf; /* parse command line arguments */ parse(argc, argv); state = 0; /* clear the output buffer */ buf = 0; buf_offset = 16; cksum = 0; lookforend = 0; /* scan the input file */ while(fgets(buffer, sizeof(buffer), infp)) { /* remove any newlines */ if (c = index(buffer, '\n')) *c = 0; /* if this line is blank, check for and END keyword * on the next line */ if (*buffer == 0) { lookforend = 1; continue; } /* state 0 == haven't seen BEGIN yet */ if (state == 0) { if (!strncmp(buffer, "BEGIN ", 6)) { state = 1; numtokens = tokenize(buffer, tokens); if (numtokens < 4) { fprintf(stderr, "incomplete BEGIN line in encoded file\n"); exit(1); } /* if output file wasn't specified on command line, use the * one encoded in the input file */ if (outfp == 0) { /* use open() so we can specify the mode */ if ((outfd = open(tokens[2], O_WRONLY | O_CREAT | O_TRUNC, strtol(tokens[1], 0, 8))) < 0) { perror(tokens[2]); exit(1); } outfp = fdopen(outfd, "w"); } /* parse the character set and initialize the * codes accordingly */ parse_charval_list(tokens[3]); init_codes(DECODE); } } /* state != 0 and we're looking for the END token */ else if (lookforend && !strncmp(buffer, "END ", 4)) { numtokens = tokenize(buffer, tokens); /* issue checksum error if there's a mismatch */ if (numtokens < 2 || hex2long(tokens[1]) != cksum) { fprintf(stderr, "checksum error.\n"); fprintf(stderr, "saw %X, computed %X\n", hex2long(tokens[1]), cksum); exit(1); } exit(0); } /* state != 0 so this is a data line. Decode it */ else { for(c = buffer; *c; c++) { /* check for garbage characters in the input */ if (!codes[*c].inuse) { fprintf(stderr, "invalid char "); if (isprint(*c)) fprintf(stderr, "\'%c\' ", *c); fprintf(stderr, "(%d) in input", *c); exit(1); } /* append the variable length bitfield that maps to * this input character to the output bitstream */ buf_offset -= codes[*c].len; buf |= (((unsigned short)(codes[*c].code) << buf_offset)); /* if we've got an entire byte available in the output * buffer, append it to the output file */ if (buf_offset < 9) { out = (char)(buf >> 8); putc(out, outfp); cksum = ((cksum << 7) | (cksum >> 25)) ^ (unsigned char)out; /* advance the output buffer */ buf_offset += 8; buf <<= 8; } } } lookforend = 0; } }