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