home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1993 #2 / Image.iso / unix / uuexplod.zip / SOURCE
Text File  |  1993-05-26  |  23KB  |  713 lines

  1.  
  2. I have now completed version 1.5 of uuexplode, this time the secure
  3. option is really in there and it doesn't slow down the decoding.
  4.  
  5. uuexplode is YASMU (Yet Another Smart Multipart Uudecoder).
  6.  
  7. So, you say, do we really need another one of these? Yes, that's why
  8. I wrote it. In essence it behaves much like unpost in sorted mode, if
  9. you're familiar with that utility.
  10. Anyway, this compiles with any K & R compiler or GCC or any other
  11. you might prefer.
  12. It is meant for UNIX but should not be very difficult to port.
  13.  
  14. I CANNOT PORT THIS TO MS-DOS OR ANY OTHER NON-BSD UNIX SYSTEM.
  15. YOU'LL HAVE TO DO THAT YOURSELF.
  16.  
  17. Basically it assumes that there are an arbitary number of uuencoded
  18. files in its input stream, uudecodes all the lines that really are
  19. uucode and writes the appropriate output files.
  20. Naturally all parts of a file must be there and they must be *in order*.
  21. Ideally you should tag articles in order (using a newsreader that can
  22. to that, e.g. tin) and then pipe everything through uuexplode and that's it.
  23.  
  24. Features:
  25.  
  26.  1. Fast (~ 350000 lines/min on a Sun SPARC 2).
  27.  2. Small.
  28.  3. Quite verbose, warns when it suspects it may have produced corrupt
  29.     output. Warning/errors always give line numbers.
  30.  4. Takes multiple input files or stdin (you can pipe through it).
  31.  5. Quiet option and secure option.
  32.  6. Handles garbage between all the last 3 lines of a uucode block
  33.  7. Handles bogus space/` lines between parts (and at the end) of the
  34.     same block. This would otherwise cause truncation of the output since
  35.     this line normally marks the end of a uucode block.
  36.  8. Does not assume *anything* about headers or other garbage
  37.  9. Handles short or even empty uucode blocks correctly
  38. 10. Tolerates optional checksum characters at the end of data lines that
  39.     would be transparent to uudecode(1).
  40.  
  41.  
  42. All in all this program is about as intelligent as it can be without assuming
  43. anything at all about the format of the garbage in the input.
  44. The strategies used are fully documented in the source code for anyone
  45. who is interested :-)
  46.  
  47. Enjoy!
  48.  
  49. M
  50.  
  51. --
  52. Michael Bergman            Email: euambn@eua.ericsson.se    
  53. EUA/XFI                Phone: +46 8 7275709
  54. Ellemtel Telecom Systems Labs
  55. Armborstv 14,  S-125 25 Alvsjo, Sweden
  56.  
  57.  
  58. /*
  59.  * uuexplode-1.5.c (based on kiss 1.0)
  60.  * Author kiss 1.0: Kevin Yang
  61.  * Code cleanup and v1.5: Michael Bergman  (euambn@eua.ericsson.se)
  62.  * Released to the Public Domain, no warranty whatsoever implied.
  63.  * 
  64.  * This C program (UNIX) takes an unlimited number of uuencoded files
  65.  * and removes garbage lines. The uuencoded lines are decoded and written 
  66.  * to specific output files. It is quite verbose as opposed to e.g.
  67.  * uuconvert and tells you when things go well and when they don't.
  68.  * It can also handle garbage between the `/SPC line and the end line and
  69.  * warns if it suspects there is garbage between the last short data line
  70.  * and the SPC-line.
  71.  *
  72.  * New in v 1.3:
  73.  * Better format of warning messages, e.g. line numbers provided.
  74.  * Bogus space lines in data and at end of a uucode block is handled.
  75.  * See details below in ExplodeFile() if you're interested in exactly how
  76.  * this is done.
  77.  * New is v 1.4:
  78.  * Secure option implemented.
  79.  * New in v 1.5:
  80.  * Changed string and I/O buffer size. 128 char string buffers
  81.  * is not enough for long path names in news headers. This made
  82.  * the line numbers in warning messages be wrong.
  83.  *
  84.  * WARNING:
  85.  * If there is garbage between the short data line (if any) and the
  86.  * SPC-line, the output file *might* be corrupted, but most likely not!
  87.  *
  88.  * The format of the uucode is assumed to be more or less as described
  89.  * in uuencode(5) in the online UNIX manual with some exceptions:
  90.  * An M-line can be longer than necessary. Some coders put an extra
  91.  * checksum (character) after each line, this is ignored by uuexplode.
  92.  * It is assumed though that all lines have the same number of checksum
  93.  * chars after them.
  94.  * The last data line before end-line can be either SPC\n or `\n
  95.  * since SPC is equivalent to ` when decoding (both give 0).
  96.  *
  97.  * Any char in the range [a-~] is illegal uucode to this program
  98.  * until someone has shown that there are COMMONLY used uuencode 
  99.  * programs that produce correct uucode with small letters in it.
  100.  * There are such uuencode, e.g. for the Macintosh(?), which use the
  101.  * the range [>-}] by adding 96 to [0-29] and 32 to [30-63].
  102.  * Note that this code is transparent to the standard uudecode.
  103.  *
  104.  * It is assumed that the end line of a uucode-block is in the
  105.  * same input file as the corresponding begin and that the parts
  106.  * are in correct order (of course).
  107.  * 
  108.  * Some diagnostics mean that the current output file is removed.
  109.  * Others leave the output file, but check the input to be sure!
  110.  *
  111.  * To speed up the file handling, the file I/O buffers are re-set.
  112.  * I chose 16 KB because it is 4 whole pages in a SunOS 4.x system.
  113.  * The file I/O is by default buffered. If it is not in your system,
  114.  * change to setvbuf() instead. This is also the appropriate action
  115.  * to take if you don't have setbuffer().
  116.  *
  117.  * To compile: "cc -O -o uuexplode uuexplode-1.x.c".
  118.  *
  119.  */
  120.  
  121. #include <stdio.h>
  122. #include <ctype.h>
  123. #include <malloc.h>
  124.  
  125. /* Some garbage lines such as paths are very long, almost 200 chars */
  126. #define LEN 256
  127. #define BUFFER_SIZE 32768
  128.  
  129. #define D(x)
  130. #define DISPLAY if (verbose) printf
  131. /* #define DISPLAY(X) if (verbose) printf(X) */
  132.  
  133. /* Most variables are declared globally, in order to
  134.  * speed up function calls.
  135.  */
  136.  
  137. static char input_buf[LEN],
  138.             b[LEN] = " ";   /* Empty marker. If b is decoded when it is empty,
  139.                    nothing will happen since length is 0 */
  140. static char out_buf[LEN], dest[LEN], dummy[LEN];
  141.  
  142. static char *ibuf = input_buf, *bp = b, *obuf = out_buf, *tmp, *p;
  143. static char *filebuf1 = NULL,
  144.             *filebuf2 = NULL;   /* Used for I/O buffering */
  145. static int line, len, mlen, dlen, count,
  146.        mode, newdata, hasdata, bquote, bquoteline, end,
  147.        verbose=1, secure=0, ambiguous, truncation=0,
  148.        t, i, x, y, z;
  149. static int siz = LEN;
  150. static FILE *ofp;
  151.  
  152. /* Forward declaratons, defined after main() */
  153. int ExplodeFile();
  154. int ExplodeLine();
  155. int ExplodeLineCheck();
  156.  
  157.  
  158. /* This function returns 1 if the string s only has
  159.  * chars which are valid in uuencoded data, 0 otherwise.
  160.  * The string is assumed not to have any ASCII < 32.
  161.  */
  162. int uuchk(s)
  163. char *s;
  164. {
  165.   int i, top;
  166.  
  167.   top = len-(mlen-62); 
  168.   for(i=1; i<top; i++)           /* Check only those chars that are data */
  169.     if (*s++ >= 'a') return 0;   /* This string is normally not valid uucode */
  170.   return 1;
  171. }
  172.  
  173.  
  174. main(argc, argv)
  175. int argc;
  176. char *argv[];
  177. {
  178.   FILE *ifp;
  179.   char *pname;
  180.  
  181.   pname = argv[0];
  182.   while (argc > 1 && *argv[1] == '-')   /* There is an option */
  183.     {
  184.       if (*(argv[1]+1) == 'q')          /* Quiet option */
  185.     {
  186.       verbose = 0;
  187.       argc--;
  188.       argv++;
  189.     }
  190.       else if (*(argv[1]+1) == 'h')   /* Help option, print usage and quit */
  191.     {
  192.       printf("Usage: %s [-q | -h | -s] [file1 [file2...]]\n", argv[0]);
  193.       printf("-q  quiet\n-h  usage\n-s  secure, check all data lines\n");
  194.       printf("When you get warnings check the input.\n");
  195.       return;
  196.     }
  197.       else if (*(argv[1]+1) == 's')   /* Secure option  */
  198.         {
  199.       secure = 1;
  200.       argc--;
  201.       argv++;
  202.        }
  203.     }
  204.   D(printf("verbose=%d, secure=%d\n", verbose, secure));
  205.   if (argc < 2)
  206.     {
  207.       line = 0;   /* Not that one has much use of linenumbers in stdin... */
  208.       ExplodeFile(stdin, "stdin");    /* Standard input, probably a pipe */
  209.     }
  210.   else 
  211.     {
  212.       if ((filebuf1 = malloc(BUFFER_SIZE)) == NULL
  213.       || (filebuf2 = malloc(BUFFER_SIZE)) == NULL)
  214.     fprintf(stderr, "%s: buffer allocation failed.\n", pname);
  215.       
  216.       for (; --argc && ++argv; )
  217.     {
  218.       if ((ifp = fopen(*argv,"r")) == NULL)
  219.         {
  220.           perror(*argv);
  221.           continue;
  222.         }
  223.       if (filebuf1 != NULL)
  224.         setbuffer(ifp, filebuf1, BUFFER_SIZE);
  225.       line = 0;                 /* reset line number */
  226.       ExplodeFile(ifp, *argv);  /* uudecodes the data in this input file*/
  227.       
  228.       /* Uncomment the next line when you want to save  */
  229.       /* disk space, it simply deletes the input file   */
  230.       /*    unlink(*argv);  */
  231.       fclose(ifp);
  232.     }
  233.     }
  234. }
  235.  
  236.  
  237. /* This function scans the input file until all parts are decoded. */
  238.  
  239. int ExplodeFile(ifp, fname)
  240. FILE *ifp;
  241. char *fname;
  242. {
  243.   hasdata = mlen = 0;
  244.  
  245.   /* On EOF, fgets returns NULL, not its first argument */
  246.   while (fgets(ibuf, siz, ifp) != NULL)
  247.     {
  248.       while (1)
  249.     {
  250.       line++;
  251.       if (! strncmp(ibuf, "begin ", 6)
  252.           && isdigit(ibuf[6])
  253.           && isdigit(ibuf[7])
  254.           && isdigit(ibuf[8]))
  255.         {
  256.           break;   /* Found a begin line */
  257.         }
  258.       if (fgets(ibuf, siz, ifp) == NULL)   /* EOF was reached */
  259.         {
  260.           if (! hasdata) printf("%s: no begin line\n", fname);
  261.           return;
  262.         }
  263.     }
  264.  
  265.       /* Extract filename and file mode    */    
  266.       if ((sscanf(ibuf, "begin %o%[ ]%s", &mode, dummy, dest)) != 3)
  267.     {
  268.       len = strlen(ibuf);
  269.       ibuf[len-1] = 0;      /* Replace the \n with a 0 */
  270.       printf("%s: %s: Invalid mode or filename\n", fname, ibuf);
  271.       continue;     /* Search for next begin line */
  272.     }
  273.       hasdata = 1;      /* Found first valid begin line */
  274.       if (verbose) 
  275.     {
  276.       printf("Extracting <%s>\t", dest);
  277.       /* By default stdout is line buffered so I must flush */
  278.       fflush(stdout);
  279.     }
  280.  
  281.       /* prepare output file */
  282.       if ((ofp = fopen(dest, "w")) == NULL)
  283.     {
  284.       perror(dest);
  285.       continue;
  286.     }
  287.       if (chmod(dest, mode) == -1)     /* Set file-mode */
  288.     perror(dest);
  289.       if (filebuf2 != NULL)
  290.     setbuffer(ofp, filebuf2, BUFFER_SIZE);
  291.       
  292.       /* The line after the begin is assumed to be a valid data line.
  293.        * If it is an M-line, the length is saved, unless it is < 62, which
  294.        * is considered a fatal error.
  295.        * If it begins with [!-L], it is decoded as the short data line,
  296.        * if the next line is not SPC/`, a warning is issued.
  297.        * If it is a SPC or `, the outputfile is simply closed.
  298.        * (The code below was added to handle very small uuencoded files.)
  299.        */
  300.       if (fgets(ibuf, siz, ifp) == NULL)  /* EOF encountered */
  301.     {
  302.       DISPLAY("\nEOF after begin\n");
  303.       fclose(ofp);
  304.       unlink(dest);    /* Remove the output file and */
  305.       return;          /* proceed with next input file */
  306.     }
  307.       line++;
  308.       len = strlen(ibuf);
  309.       if (*ibuf == 'M')
  310.     {
  311.       if ((mlen = len) < 62)
  312.         {
  313.           DISPLAY("\ninvalid first data line %d\n", line);
  314.           fclose(ofp);
  315.           unlink(dest);
  316.           continue;     /* Search for next begin line */
  317.         }
  318.       if (! secure)
  319.         ExplodeLine(ibuf);
  320.       else if (! ExplodeLineCheck(ibuf))
  321.         {
  322.           DISPLAY("\nillegal chars in first data line %d\n", line);
  323.           fclose(ofp);
  324.           unlink(dest);
  325.           continue;     /* Search for next begin line */
  326.         }
  327.         }
  328.       else if (*ibuf <= 'L' && *ibuf > ' ')   /* No special checks here! */
  329.     {
  330.       if (! secure)
  331.         ExplodeLine(ibuf);
  332.       else if (! ExplodeLineCheck(ibuf))
  333.         {
  334.           DISPLAY("\nillegal chars in short data line %d\n", line);
  335.           fclose(ofp);
  336.           continue;     /* Search for next begin line */
  337.         }
  338.  
  339.       /* Now read in line after short data line */
  340.       if (fgets(ibuf, siz, ifp) == NULL)
  341.         {
  342.           DISPLAY("\nwarning: EOF after short data line\n");
  343.           fclose(ofp);
  344.           return;         /* Proceed with next input file */
  345.         }
  346.       len = strlen(ibuf);
  347.       /* This next line depends on lazy evaluation */
  348.       if (len != 2 && len != 3 || *ibuf != ' ' && *ibuf != '`')
  349.         {
  350.           DISPLAY("\nwarning: garbage at end %d\n", line+1);
  351.           /* Back up, it may have been a new begin line */
  352.           fseek(ifp, (long)-strlen(ibuf), 1);
  353.           fclose(ofp);
  354.         }
  355.       else
  356.         {
  357.           line++;
  358.           DISPLAY("-- done\n");
  359.           fclose(ofp);
  360.         }
  361.       continue;       /* Search for next begin line */
  362.     }
  363.       else if ((*ibuf == ' ' || *ibuf == '`') && (len == 2 || len == 3))
  364.     /* uuencoded empty file */
  365.     {
  366.       DISPLAY("-- done\n");
  367.       fclose(ofp);
  368.       continue;       /* Search for next begin line */
  369.     }
  370.       else
  371.     {
  372.       DISPLAY("\ngarbage at first line %d\n", line);
  373.       fclose(ofp);
  374.       unlink(dest);
  375.       continue;       /* Search for next begin line */
  376.     }
  377.       
  378.       /* When a [!-L] line is found, a length and char check is made to
  379.        * validate it as the short data line. If the next valid uucode line
  380.        * is SPC, the line is decoded. If it is not, a warning is printed,
  381.        * but the line is still decoded (most likely correctly). Check the
  382.        * input file to be sure.
  383.        *
  384.        * Whenever a valid M-line is found, it is assumed that any previously
  385.        * seen potentially valid short lines were in fact garbage, this is
  386.        * done by setting ambiguous=0.
  387.        *
  388.        * When a SPC is found, one of two strategies are used:
  389.        * 1. If a short line has been seen, this SPC is assumed valid and I
  390.        *    never bother about the end line.
  391.        * 2. If not, look for the end-line. If next valid uucode line is
  392.        *    end        -- finish output file, no warnings
  393.        *    M-line    -- SPC was bogus, continue & issue warning.
  394.        *    SPC        -- continue & issue warning
  395.        *    EOF or begin -- finish output file with "end line not found"
  396.        *    short data line -- SPC was bogus, continue & issue warning
  397.        *
  398.        * The reason for the last behaviour is that P(no short data line) ~= 2%
  399.        * (see below) and P(bogus short line) is very small. So if a valid
  400.        * short line is seen after a SPC, it is more likely that it is valid
  401.        * and the SPC was bogus.
  402.        * A problem here is how to know if the file has ` or SPC as last
  403.        * 0 length data line. It is one or the other and if it is SPC, I can
  404.        * always just ignore spurious '-lines and vice versa. I decided not
  405.        * to distinguish between them. This is a bit vaulnerable, but saves
  406.        * overhead.
  407.        *
  408.        * The above SPC-line checking is new in v 1.3 and was added to handle
  409.        * cases like this (which do occur sometimes):
  410.        * M-lines
  411.        * [garbage]
  412.        * bogus SPC
  413.        * [garbage]
  414.        * M-lines   (belonging to the same file)
  415.        *
  416.        * As a bonus, it now also handles (I've never seen this happen):
  417.        * M-lines
  418.        * [garbage]
  419.        * bogus SPC
  420.        * [garbage]
  421.        * short data line
  422.        * SPC
  423.        * 
  424.        * Of course things like this cannot be correctly decoded:
  425.        * M-line
  426.        * bogus SPC
  427.        * [garbage]
  428.        * bogus short line
  429.        * [garbage]
  430.        * M-line    (belonging to the same file)
  431.        * but the prob. that this would happen is rather small!
  432.        * 
  433.        * There is still only *one* pathological case where this function
  434.        * would issue no warning at all and still produce corrupted output:
  435.        * M-lines
  436.        * bogus short-line (exactly one, falling through the checks)
  437.        * space/`
  438.        * [garbage]
  439.        * M-lines   (belonging to the same file)
  440.        * This case is obviously theoretically undetectable, so in this case I
  441.        * don't gain anything by looking for the end-line.
  442.        *
  443.        * X = original filesize % 45
  444.        * Assumption: X is uniform(0..44)  (This seems reasonable)
  445.        * P(no short-line) = P(X=0) = 1/45 ~= 2%
  446.        * Even if no short line is found in a block, no warning about this
  447.        * is printed. This was changed because in this case I always look for
  448.        * the end line. If it is found, the output is very likely correct.
  449.        * If not, a warning "no end line" is given, so the previuos warning
  450.        * is now obsolete.
  451.        * 
  452.        * If there is a garbage line beginning with M and of correct length,
  453.        * the output will be corrupted. A check for this is turned on with
  454.        * -s which looks for any chars that cannot belong to a valid M-line.
  455.        * This check is done in ExplodeLineCheck() writing to an array
  456.        * instead of directly to the file. If all goes well, the array is
  457.        * written to the file.
  458.        * This solution costs less if the M-line is valid (which is probable)
  459.        * than checking the line first and then uudecoding it.
  460.        */
  461.  
  462.       /* Reset the buffer for short data lines to empty */
  463.       *bp = ' ';
  464.       /* Reset a couple of indicators */
  465.       count = ambiguous = truncation = 0;
  466.       bquote = bquoteline = 0;
  467.       end = 0;
  468.       while (fgets(ibuf, siz, ifp) != NULL)
  469.     {
  470.       if ((newdata = ! strncmp(ibuf, "begin ", 6)
  471.            && isdigit(ibuf[6])
  472.            && isdigit(ibuf[7])
  473.            && isdigit(ibuf[8])) )     /* No SPC line in this block, or
  474.                          no end line */
  475.         {        /* Return the begin line to input stream */
  476.           fseek(ifp, (long)-strlen(ibuf), 1);
  477.           ExplodeLine(bp);
  478.           break;     /* get out of inner while loop */
  479.         }
  480.       line++;
  481.       len = strlen(ibuf);
  482.       if (*ibuf <= 'L' && *ibuf > ' ')   /* It may be a short data line */
  483.         {
  484.           dlen = *ibuf-32;   /* Actual data length, bytes */
  485.           t = dlen/3;
  486.           if (dlen%3) t++;
  487.           t = t << 2;
  488.           if (len == t+2+(mlen-62))  /* Allows for extra chars checksum? */
  489.         if (uuchk(ibuf))     /* All chars are valid */
  490.           {
  491.             tmp = bp;    /* Keep this line in b[] */
  492.             bp = ibuf;
  493.             ibuf = tmp;
  494.             if (bquote)  /* There is a bogus SPC line above */
  495.               {
  496.                 DISPLAY("\nwarning: bogus SPC-line %d  ", bquoteline);
  497.                        bquote = bquoteline = 0;
  498.             /* I must also reset the truncation flag in
  499.              * case "truncated" message has been displayed.
  500.              */
  501.             truncation = 0;
  502.               }
  503.             ambiguous++; /* No of seen valid short-lines between */
  504.                          /* last M-line and first SPC-line       */
  505.             continue;    /* Get next input line */
  506.           }
  507.           if (*bp != ' ') count++;
  508.           /*
  509.            * This was a garbage line after a potential short line,
  510.            *  get next input line
  511.            */
  512.           continue;
  513.         }
  514.       else if ((*ibuf == ' ' || *ibuf == '`' ) && (len == 2 || len == 3))
  515.         {
  516.           bquote = 1;         /* Mark that a SPC line has been seen */
  517.           if (bquoteline)     /* There was a previous SPC line */
  518.             {
  519.           DISPLAY("\nwarning: bogus SPC-line %d  ", bquoteline);
  520.           truncation = 0;
  521.             }
  522.           bquoteline = line;  /* Save the line number */
  523.           if (ambiguous)      /* At least one short line has been seen */
  524.             {
  525.           if (count > 0)
  526.              DISPLAY("\nwarning: garbage at end %d  ", line);
  527.           if (ambiguous > 1)
  528.              DISPLAY("\nambiguous data at end %d  ", line);
  529.           ExplodeLine(bp);
  530.           break;            /* Finished, ignore the end line */
  531.             }
  532.         }
  533.       else if ((end = !strncmp(ibuf, "end\n", 4)) && ! ambiguous && bquote)
  534.         {
  535.           /* Mark that the end line has been seen, end==1 */
  536.           break;      /* Finished, get out */
  537.         }
  538.       else if (*ibuf == 'M')     /* This is probably a valid data line */
  539.         {
  540.           if (len != mlen)
  541.         {
  542.           if (verbose && uuchk(ibuf))   /* All chars are valid */
  543.             {
  544.               if (! truncation)
  545.                 {
  546.               printf("\nwarning: truncated data line %d ", line);
  547.               fflush(stdout);
  548.               truncation = 1;
  549.                 }
  550.               else
  551.                 {
  552.               printf("%d ", line);
  553.               fflush(stdout);
  554.                 }
  555.             }
  556.         }
  557.           else
  558.         {
  559.           mode = 1;    /* Used temporary as a flag */
  560.           if (! secure)
  561.             ExplodeLine(ibuf);
  562.           else
  563.           {
  564.              mode = ExplodeLineCheck(ibuf);
  565.              D(if (! mode) printf("\nIllegal char %d", line));
  566.           }
  567.           if (mode)
  568.           {
  569.             /* Reset the garbage indicators */
  570.             count = ambiguous = 0;
  571.             if (bquote)       /* There is a bogus SPC line above */
  572.             {
  573.                DISPLAY("\nwarning: bogus SPC-line %d  ", bquoteline);
  574.                bquoteline = bquote = 0;
  575.                /* I must also reset the truncation flag in case
  576.             * the "truncated" message has been displayed. It
  577.             * would otherwise be "broken off" by this message.
  578.             */
  579.                truncation = 0;
  580.             }
  581.           }
  582.            }
  583.         }
  584.     } /*while*/
  585.       fclose(ofp);
  586.       
  587.       if (! ambiguous && ! end) DISPLAY("\nwarning: no end line  ");
  588.       if (! bquote)
  589.     {DISPLAY("\nend of uucode not found\n");}
  590.       else
  591.     {DISPLAY("-- done\n");}
  592.     } /*while*/
  593. }
  594.  
  595.  
  596. /* This function decodes one line of information, written by Kevin Yang.
  597.  * The code is slightly different from that of uuconvert.c
  598.  * or uudecode.c. I use pointers instead of array indices
  599.  * to avoid time consuming address multiplications.  The 
  600.  * condition tests are modified and eliminates 2 tests
  601.  * for every 4 bytes. Characters are decoded only when
  602.  * one of the three conditions matches, in which complicated
  603.  * mathematical computation are eliminated for the last few
  604.  * bytes.
  605.  * I hope this can speed up the decoding!
  606.  */
  607. #define DECODE(C) (((C) - ' ') & 077)
  608.  
  609. ExplodeLine(str)
  610. char *str;
  611. {
  612.   i = DECODE(*str);    /* Always 0 for a line beginning with SPC or ` */
  613.   p = ++str;
  614.   
  615.   while (i > 0)
  616.     {
  617.       if (i >= 3)
  618.     {
  619.       x  = (DECODE(*p) << 2); p++;
  620.       x |= (DECODE(*p) >> 4);
  621.       y  = (DECODE(*p) << 4); p++;
  622.       y |= (DECODE(*p) >> 2);
  623.       z  = (DECODE(*p) << 6); p++;
  624.       z |= (DECODE(*p));
  625.       putc(x, ofp);
  626.       putc(y, ofp);
  627.       putc(z, ofp);
  628.     }
  629.       else if (i >= 2)
  630.     {
  631.       x  = (DECODE(*p) << 2); p++;
  632.       x |= (DECODE(*p) >> 4);
  633.       y  = (DECODE(*p) << 4); p++;
  634.       y |= (DECODE(*p) >> 2);
  635.       putc(x, ofp);
  636.       putc(y, ofp);
  637.     }
  638.       else if (i >= 1)
  639.     {
  640.       x  = (DECODE(*p) << 2); p++;
  641.       x |= (DECODE(*p) >> 4);
  642.       putc(x, ofp);
  643.     }
  644.       
  645.       str += 4;
  646.       p = str;
  647.       i -= 3;
  648.     }  /*while*/
  649. }
  650.  
  651.  
  652. /* This array is used to hold decoded data. The entire buffer is
  653.  * written to the output file when the line is decoded.
  654.  * It is assumed that a full data line is 45 bytes.
  655.  */
  656. unsigned char data[50];
  657.  
  658. /*
  659.  * Returns:
  660.  *    1 -- OK, wrote to file
  661.  *      0 -- this is no data line, illegal chars found
  662.  */
  663. int ExplodeLineCheck(str)
  664. char *str;
  665. {
  666.   unsigned char *d;
  667.  
  668.   i = DECODE(*str);    /* Always 0 for a line beginning with SPC or ` */
  669.   p = ++str;
  670.   d = data;
  671.   while (i > 0)
  672.     {
  673.       if (i >= 3)
  674.     {
  675.       x  = (DECODE(*p) << 2); p++;
  676.       x |= (DECODE(*p) >> 4);
  677.       y  = (DECODE(*p) << 4); p++;
  678.       y |= (DECODE(*p) >> 2);
  679.       z  = (DECODE(*p) << 6); p++;
  680.       z |= (DECODE(*p));
  681.       *d++ = x; *d++ = y; *d++ = z;
  682.       /* Check if these chars were valid uucode */
  683.       if (*p >= 'a' || *(p-1) >= 'a' || *(p-2) >= 'a' || *(p-3) >= 'a')
  684.         return(0);
  685.     }
  686.       else if (i >= 2)
  687.     {
  688.       x  = (DECODE(*p) << 2); p++;
  689.       x |= (DECODE(*p) >> 4);
  690.       y  = (DECODE(*p) << 4); p++;
  691.       y |= (DECODE(*p) >> 2);
  692.       *d++ = x; *d++ = y;
  693.       /* Check if these chars were valid uucode */
  694.       if (*p >= 'a' || *(p-1) >= 'a' || *(p-2) >= 'a')
  695.         return(0);
  696.     }
  697.       else if (i >= 1)
  698.     {
  699.       x  = (DECODE(*p) << 2); p++;
  700.       x |= (DECODE(*p) >> 4);
  701.       *d++ = x;
  702.       if (*p >= 'a' || *(p-1) >= 'a')
  703.         return(0);
  704.     }
  705.       str += 4;
  706.       p = str;
  707.       i -= 3;
  708.     }  /*while*/
  709.     *d = 0;   /* End the string */
  710.     fwrite(data, 1, d-data, ofp);
  711.     return(1);
  712. }
  713.