home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume22 / nn6.4 / part17 / decode.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-06-07  |  10.6 KB  |  514 lines

  1. /*
  2.  * Decode one or more uuencoded article back to binary form.
  3.  *
  4.  * UNIX/NN VERSION
  5.  *    This version cannot be used as a stand-alone uud!
  6.  *     This version is made: 16 June 1989.
  7.  *
  8.  * From the Berkeley original, modified by MSD, RDR, JPHD & WLS.
  9.  */
  10.  
  11. #include "config.h"
  12.  
  13. /* #define DEC_DEBUG    /* never define this */
  14.  
  15. export char *decode_header_file = "Decode.Headers";
  16. export int decode_skip_prefix = 2;
  17.  
  18. #define MAXCHAR 256
  19. #define LINELEN 256
  20. #define NORMLEN 60    /* allows for 80 encoded chars per line */
  21.  
  22. #define SEQMAX 'z'
  23. #define SEQMIN 'a'
  24.  
  25. static char seqc, partn;
  26. static int first, secnd, check;
  27.  
  28. #define MAX_PREFIX 10
  29. static int prefix_lgt, set_prefix;
  30. static char prefix_str[MAX_PREFIX];
  31.  
  32. static FILE *out;
  33. static char *target;
  34. static char blank;
  35. static int chtbl[MAXCHAR], cdlen[NORMLEN + 3];
  36. static char ofname[FILENAME], arcname[FILENAME];
  37. static int state, arcpart;
  38.  
  39. #define    NO_ADVANCE        0x10
  40.  
  41. #define    DECODE_TEXT            1
  42. #define    FIND_BEGIN            2
  43. #define    FIND_BEGIN_AFTER_ERROR        3
  44. #define FIND_BEGIN_AFTER_INCLUDE    4
  45. #define NEW_BEGIN            (5 | NO_ADVANCE)
  46. #define    FOUND_END                   (6 | NO_ADVANCE)
  47. #define FOUND_INCLUDE            (7 | NO_ADVANCE)
  48. #define    SKIP_LEADING            8
  49. #define    SKIP_TRAILING            (9 | NO_ADVANCE)
  50. #define DECODE_ERROR            (10 | NO_ADVANCE)
  51. #define OTHER_ERROR            (11 | NO_ADVANCE)
  52.  
  53. #define MIN_DECODE_LEADING 8 /* lines to decode ok when doing skip-leading */
  54.  
  55. #ifdef DEC_DEBUG
  56.     char *state_tbl[] = {
  57.     "-", "decode", "find begin", "find a/error", "find a/include",
  58.     "new begin", "found end", "found include", "skip leading",
  59.     "skip trail", "error", "other error"
  60.     };
  61.  
  62. #endif
  63.  
  64. /*
  65.  * decode one line, write on out file
  66.  */
  67.  
  68. static strncmp_skip(buf, str, n)
  69. char *buf, *str;
  70. int n;
  71. {
  72.     register int i;
  73.     register char *line = buf;
  74.  
  75.     if (!set_prefix)
  76.     return strncmp(line, str, n);
  77.  
  78.     if (decode_skip_prefix > MAX_PREFIX) decode_skip_prefix = MAX_PREFIX;
  79.  
  80.     for (i = 0; i <= decode_skip_prefix; i++, line++) {
  81.     if (*line == NUL) break;
  82.     if (*line == '#' || *line == ':') break;
  83.     if (strncmp(line, str, n)) continue;
  84.     prefix_lgt = i;
  85.     if (i) strncpy(prefix_str, buf, i);
  86.     set_prefix = 0;
  87. #ifdef DEC_DEBUG
  88.     msg("match %s", str);
  89.     user_delay(1);
  90. #endif
  91.     return 0;
  92.     }
  93.  
  94.     return 1;
  95. }
  96.  
  97. static decode_line(buf, len, dont_write)
  98. char *buf;
  99. register int len;        /* actual input line length */
  100. int dont_write;            /* doing leading check */
  101. {
  102.     char outl[LINELEN];
  103.     register char *bp, *ut;
  104.     register int *trtbl = chtbl;
  105.     register int n;
  106.     register int blen;        /* binary length (from decoded file) */
  107.     register int rlen;        /* calculated input line length */
  108.  
  109.     /*
  110.      * Get the binary line length.
  111.      */
  112.     if ((blen = trtbl[buf[0]]) < 0) {
  113.     if (strncmp(buf, "begin", 5) == 0 || strncmp(buf, "table", 5) == 0)
  114.         return NEW_BEGIN;
  115.  
  116.     if (state == SKIP_LEADING) return SKIP_LEADING;
  117.  
  118.     /*
  119.      * end of uuencoded file ?
  120.      */
  121.     if (strncmp(buf, "end", 3) == 0)
  122.         return FOUND_END;
  123.  
  124.     /*
  125.      * end of current file ? : get next one.
  126.      */
  127.     if (strncmp(buf, "include", 7) == 0)
  128.         return FOUND_INCLUDE;
  129.  
  130.     /*
  131.      * trailing garbage
  132.      */
  133.     return SKIP_TRAILING;
  134.     }
  135.  
  136.     rlen = cdlen[blen];
  137.     if (len < rlen) goto d_err;
  138.  
  139.     /*
  140.      * Is it the empty line before the end line ?
  141.      */
  142.     if (blen == 0) return state;
  143.  
  144.     /*
  145.      * Pad with blanks.
  146.      */
  147.     for (bp = buf + len, n = rlen - len; --n >= 0; ) *bp++ = blank;
  148.  
  149.     /*
  150.      * Verify
  151.      */
  152.     for (n = rlen, bp = buf; --n >= 0; bp++)
  153.     if (trtbl[*bp] < 0) {
  154. #ifdef DEC_DEBUG
  155.         msg("%s - verify failed %d '%.30s'",
  156.         state_tbl[state&0xf], rlen - n, buf);
  157.         user_delay(2);
  158. #endif
  159.         goto d_err;
  160.     }
  161.  
  162.     /*
  163.      * Check for uuencodes that append a 'z' to each line....
  164.      */
  165.     if (check)
  166.     if (secnd) {
  167.         secnd = 0;
  168.         if (buf[rlen] == SEQMAX) check = 0;
  169.     } else if (first) {
  170.         first = 0;
  171.         secnd = 1;
  172.         if (buf[rlen] != SEQMAX) check = 0;
  173.     }
  174.  
  175.     /*
  176.      * There we check.
  177.      */
  178.     if (check) {
  179.     if (buf[rlen] != seqc) {
  180. #ifdef DEC_DEBUG
  181.         msg("check failed %d != %d", buf[rlen], seqc); user_delay(1);
  182. #endif
  183.         goto d_err;
  184.     }
  185.  
  186.     if (--seqc < SEQMIN) seqc = SEQMAX;
  187.     }
  188.  
  189.     if (dont_write) return DECODE_TEXT;
  190.  
  191.     /*
  192.      * output a group of 3 bytes (4 input characters).
  193.      */
  194.     ut = outl;
  195.     n = blen;
  196.     bp = &buf[1];
  197.     while (--n >= 0) {
  198.     *(ut++) = trtbl[*bp] << 2 | trtbl[bp[1]] >> 4;
  199.     if (n > 0) {
  200.         *(ut++) = (trtbl[bp[1]] << 4) | (trtbl[bp[2]] >> 2);
  201.         n--;
  202.     }
  203.     if (n > 0) {
  204.         *(ut++) = trtbl[bp[2]] << 6 | trtbl[bp[3]];
  205.         n--;
  206.     }
  207.     bp += 4;
  208.     }
  209.     if (fwrite(outl, 1, blen, out) <= 0) {
  210.     msg("Error on writing decoded file");
  211.     return OTHER_ERROR;
  212.     }
  213.  
  214.     return DECODE_TEXT;
  215.  
  216.  d_err:
  217.     if (state == SKIP_LEADING) return SKIP_LEADING;
  218.     return DECODE_ERROR;
  219.  
  220. }
  221.  
  222.  
  223.  
  224. /*
  225.  * Install the table in memory for later use.
  226.  */
  227. static inittbls()
  228. {
  229.     register int i, j;
  230.  
  231.     /*
  232.      * Set up the default translation table.
  233.      */
  234.     for (i = 0; i < ' '; i++) chtbl[i] = -1;
  235.     for (i = ' ', j = 0; i < ' ' + 64; i++, j++) chtbl[i] = j;
  236.     for (i = ' ' + 64; i < MAXCHAR; i++) chtbl[i] = -1;
  237.     chtbl['`'] = chtbl[' '];    /* common mutation */
  238.     chtbl['~'] = chtbl['^'];    /* an other common mutation */
  239.     blank = ' ';
  240.     /*
  241.      * set up the line length table, to avoid computing lotsa * and / ...
  242.      */
  243.     cdlen[0] = 1;
  244.     for (i = 1, j = 5; i <= NORMLEN; i += 3, j += 4)
  245.     cdlen[i] = (cdlen[i + 1] = (cdlen[i + 2] = j));
  246. }
  247.  
  248. static gettable(in)
  249. FILE *in;
  250. {
  251.     char buf[LINELEN], *line;
  252.     register int c, n = 0;
  253.     register char *cpt;
  254.  
  255.     for (c = 0; c <= MAXCHAR; c++) chtbl[c] = -1;
  256.  
  257.     for (;;) {
  258.     if (fgets(buf, sizeof buf, in) == NULL) {
  259.         msg("EOF while in translation table.");
  260.         return -1;
  261.     }
  262.     line = buf + prefix_lgt;
  263.     if ((prefix_lgt > 0 && strncmp(buf, prefix_str, prefix_lgt)) ||
  264.         strncmp(line, "begin", 5) == 0) {
  265.         msg("Incomplete translation table.");
  266.         return -1;
  267.     }
  268.     cpt = line + strlen(line) - 1;
  269.     *cpt = ' ';
  270.     while (*(cpt) == ' ') {
  271.         *cpt = 0;
  272.         cpt--;
  273.     }
  274.     cpt = line;
  275.     while (c = *cpt) {
  276.         if (chtbl[c] != -1) {
  277.         msg("Duplicate char in translation table.");
  278.         return -1;
  279.         }
  280.         if (n == 0) blank = c;
  281.         chtbl[c] = n++;
  282.         if (n >= 64) return 0;
  283.         cpt++;
  284.     }
  285.     }
  286. }
  287.  
  288. static new_file()
  289. {
  290.     out = NULL;
  291.     seqc = SEQMAX;
  292.     partn = 'a';
  293.     check = 1;
  294.     first = 1;
  295.     secnd = 0;
  296.     state = FIND_BEGIN;
  297.     prefix_lgt = 0;
  298.     set_prefix = decode_skip_prefix;
  299.     arcpart = 0;
  300.     inittbls();
  301. }
  302.  
  303. uud_start(dir)
  304. char *dir;
  305. {
  306.     target = dir;
  307.     new_file();
  308. }
  309.  
  310. uud_end()
  311. {
  312.     if (out != NULL) {
  313.     fclose(out);
  314.     msg("%s INCOMPLETE -- removed", arcname);
  315.     unlink(ofname);
  316.     out = NULL;
  317.     }
  318. }
  319.  
  320.  
  321. uudecode(ah, in)
  322. register article_header *ah;
  323. FILE *in;
  324. {
  325.     int mode, onedone, len, lead_check = 0;
  326.     int ostate = 0;
  327.     char buf[LINELEN], part[2], *line;
  328.     off_t real_size, start_offset;
  329.     long expect_size;
  330.  
  331.     onedone = 0;
  332.  
  333.     /*
  334.      * search for header or translation table line.
  335.      */
  336.     start_offset = ftell(in);
  337.  
  338.     for (;;) {
  339.     if ((state & NO_ADVANCE) == 0) {
  340.         if (ftell(in) >= ah->lpos) break;
  341.         if (fgets(buf, sizeof buf, in) == NULL) break;
  342.     }
  343.  
  344.     if (s_keyboard) return -1;
  345.  
  346.     len=strlen(buf);
  347.     if (len > 0 && buf[len - 1] == NL) buf[--len] = NUL;
  348.  
  349. #ifdef DEC_DEBUG
  350.     if (state != ostate) {
  351.         msg("%s->%s - '%.30s'", state_tbl[ostate&0xf], state_tbl[state&0xf], buf);
  352.         user_delay(2);
  353.         ostate = state;
  354.     }
  355. #endif
  356.     switch (state) {
  357.  
  358.      case NEW_BEGIN:
  359.         if (out != NULL) {
  360.         uud_end();
  361.         user_delay(5);
  362.         }
  363.         new_file();
  364.         /* fall thru */
  365.  
  366.      case FIND_BEGIN:
  367.      case FIND_BEGIN_AFTER_ERROR:
  368.      case FIND_BEGIN_AFTER_INCLUDE:
  369.         set_prefix = decode_skip_prefix;
  370.         if (strncmp_skip(buf, "table", 5) == 0) {
  371.         gettable(in);
  372.         continue;
  373.         }
  374.  
  375.         if (strncmp_skip(buf, "begin", 5)) continue;
  376.  
  377.         line = buf + prefix_lgt;
  378.  
  379.         if (state == FIND_BEGIN_AFTER_INCLUDE) {
  380.         if(sscanf(line,"begin part %1s%s", part, arcname) != 2) {
  381.             msg("Invalid 'begin' line after 'include'");
  382.             continue;
  383.         }
  384.         partn++;
  385.         if (partn > 'z') partn = 'a';
  386.         if (part[0] != partn) {
  387.             msg("PARTS NOT IN SEQUENCE: %s -- removed", arcname);
  388.             user_delay(5);
  389.             fclose(out);
  390.             unlink(ofname);
  391.             new_file();
  392.             state = FIND_BEGIN_AFTER_ERROR;
  393.             goto err;
  394.         }
  395.         } else {
  396.         if(sscanf(line,"begin%o%s", &mode, arcname) != 2)
  397.             continue;
  398.  
  399.         if (target != NULL)
  400.             sprintf(ofname, "%s%s", target, arcname);
  401.         else
  402.             strcpy(ofname, arcname);
  403.  
  404.         if ((out = open_file(ofname, OPEN_CREATE)) == NULL) {
  405.             msg("Cannot create file: %s", ofname);
  406.             goto err;
  407.         }
  408.         chmod(ofname, mode);
  409.  
  410.         if (decode_header_file)
  411.             store_header(ah, in, target, decode_header_file);
  412.         }
  413.  
  414.         state = DECODE_TEXT;
  415.         continue;
  416.  
  417.      case SKIP_LEADING:
  418.         if (len > prefix_lgt &&
  419.         (!prefix_lgt || strncmp(buf, prefix_str, prefix_lgt) == 0)) {
  420.         state = decode_line(buf + prefix_lgt, len - prefix_lgt, 1);
  421.         if (state == DECODE_TEXT) {
  422.             if (++lead_check == MIN_DECODE_LEADING) {
  423.             fseek(in, start_offset, 0);
  424.             continue;
  425.             }
  426.             state = SKIP_LEADING;
  427.             continue;
  428.         }
  429.         } else {
  430.         set_prefix = decode_skip_prefix;
  431.         if (strncmp_skip(buf, "begin", 5) == 0 ||
  432.             strncmp_skip(buf, "table", 5) == 0)
  433.             state = NEW_BEGIN;
  434.         }
  435.  
  436.         lead_check = 0;
  437.         start_offset = ftell(in);
  438.         continue;
  439.  
  440.      case DECODE_TEXT:
  441.         if (len <= prefix_lgt ||
  442.         (prefix_lgt > 0 && strncmp(buf, prefix_str, prefix_lgt))) {
  443.         state = SKIP_TRAILING;
  444.         continue;
  445.         }
  446.         if (onedone == 0) {
  447.         msg("Decoding%s: %s (part %d)",
  448.             prefix_lgt ? " & Unsharing" : "", arcname, ++arcpart);
  449.  
  450.         onedone = 1;
  451.         }
  452.         state = decode_line(buf + prefix_lgt, len - prefix_lgt, 0);
  453.         continue;
  454.  
  455.      case FOUND_END:
  456.         real_size = ftell(out);
  457.         fclose(out);
  458.  
  459.         if (ftell(in) >= ah->lpos || fgets(buf, sizeof buf, in) == NULL) {
  460.         new_file();
  461.         break;
  462.         }
  463.  
  464.         if ((!prefix_lgt || strncmp(buf, prefix_str, prefix_lgt) == 0) &&
  465.         sscanf(buf + prefix_lgt, "size%ld", &expect_size) == 1 &&
  466.         real_size != expect_size) {
  467.  
  468.         msg("%s decoded with wrong size %ld (exp. %ld)",
  469.             arcname, real_size, expect_size);
  470.         user_delay(3);
  471.         } else {
  472.         msg("%s complete", arcname);
  473.         user_delay(1);
  474.         }
  475.  
  476.         new_file();
  477.         state = NEW_BEGIN;
  478.         continue;
  479.  
  480.      case FOUND_INCLUDE:
  481.         state = FIND_BEGIN_AFTER_INCLUDE;
  482.         return 0;
  483.  
  484.      case SKIP_TRAILING:
  485.         state = SKIP_LEADING;
  486.         return 0;
  487.  
  488.      case DECODE_ERROR:
  489.         state = SKIP_TRAILING;
  490.         continue;
  491.  
  492.      case OTHER_ERROR:
  493.         fclose(out);
  494.         new_file();
  495.         state = FIND_BEGIN_AFTER_ERROR;
  496.         goto err;
  497.     }
  498.  
  499.     break;    /* break in switch => break in loop */
  500.     }
  501.  
  502.     if (onedone) {
  503.     if (state == DECODE_TEXT) state = SKIP_LEADING;
  504.     return 0;
  505.     }
  506.  
  507.     if (state == FIND_BEGIN_AFTER_ERROR) return -1;
  508.     msg("No 'begin' line");
  509.  
  510.  err:
  511.     user_delay(2);
  512.     return -1;
  513. }
  514.