home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / packer / uugrab / uugrab.c < prev    next >
C/C++ Source or Header  |  1992-12-07  |  24KB  |  967 lines

  1. /* uugrab.c   (c) 1992 by Harald Boegeholz */
  2.  
  3. /* contains parts of the program uucat which was placed in the public
  4.    domain by d84sp@efd.lth.se (Stefan Parmark) */
  5.  
  6.  
  7. /* customize these for use with different shells */
  8. #define SHELL_COMMAND_ECHO    "echo"
  9. #ifdef OS2
  10. #define SHELL_COMMAND_COMMENT "REM"
  11. #else
  12. #define SHELL_COMMAND_COMMENT "#"
  13. #endif
  14.  
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <stdarg.h>
  18. #include <string.h>
  19. #include <ctype.h>
  20.  
  21. #ifdef OS2
  22. #define INCL_DOSFILEMGR
  23. #define INCL_DOSERRORS
  24. #include <os2.h>
  25. #endif
  26.  
  27. #if !defined(OS2)
  28. #define _popen popen
  29. #define _pclose pclose
  30. #endif
  31.       
  32.  
  33. #define BUFFER_SIZE  1000
  34. #define MAX_LINE_LEN 1042
  35.  
  36. typedef int boolean;
  37. #define TRUE  1
  38. #define FALSE 0
  39.  
  40. #define UFIRST 'M'
  41. #define UQUOTE '`'
  42. #define LENGTH    150
  43.  
  44.  
  45. struct _part_list
  46. {
  47.   char *filename;
  48.   char *msgid;
  49.   int partno;
  50.   struct _part_list *next;
  51. };
  52.  
  53. static struct _subj_list
  54. {
  55.   char *subject;
  56.   int nparts;
  57.   struct _part_list *partlist;
  58.   struct _subj_list *next;
  59. } *subject_list=NULL;
  60.  
  61. static struct _msgid_list
  62. {
  63.   char *msgid;
  64.   boolean visited;
  65.   struct _msgid_list *left;
  66.   struct _msgid_list *right;
  67.   struct _msgid_list *next;
  68. } *old_msgid_list=NULL;
  69.  
  70. static struct _msgid_list *dummy_next;
  71. static struct _msgid_list **last_msgid_next_ptr=&dummy_next;
  72.  
  73.  
  74. #if defined(OS2)
  75. static char history_file_name[]="uugrab.rc";
  76. #else
  77. static char history_file_name[]=".uugrabrc";
  78. #endif
  79.  
  80.  
  81. static void error(char *, ...);
  82. int main(int, char *[]);
  83.  
  84. static void read_history(void);
  85. static void save_history(void);
  86. static boolean already_processed(char *msgid, boolean add, boolean setvisited);
  87.  
  88. static void read_subjects(char *filespec);
  89. static void find_subject(char *filename);
  90. static void parse_subject(char *filename, char *subject, char *msgid);
  91. static void store_part(char *filename, char *subject, char *msgid,
  92.                        int partno, int nparts);
  93. static void add_article(struct _part_list **partlist, char *filename, 
  94.                         char *msgid, int partno);
  95. static void output_results(void);
  96. static void print_malformed(struct _subj_list *p);
  97. static void begin_uucat(void);
  98. static void end_uucat(void);
  99. static void uucat_file(char *filename);
  100. static int is_begin_line(char *s);
  101.  
  102.  
  103. static boolean option_decode_all=FALSE;
  104. static boolean option_dont_execute=FALSE;
  105. static boolean option_save_descriptions=FALSE;
  106. static boolean option_verbose=FALSE;
  107. static int ignore_fields=0;
  108. static char *command=NULL;
  109. static FILE *uudecode;
  110. static FILE *description_file;
  111. static char *description_file_name;
  112. static boolean echo_on, started, just_finished;
  113. static int line_length, lines_to_go;
  114. static boolean echo_description;
  115. static char uu_name[LENGTH];
  116.  
  117. #ifdef OS2
  118. static void check_error(int err, int line, char *file);
  119.  
  120. #define chk(err) check_error(err, __LINE__, __FILE__)
  121.  
  122. static void check_error(int err, int line, char *file)
  123. {
  124.   if (err==0)
  125.     return;
  126.   error("Unexpected OS/2 error occured: code %d. Sourcefile %s (line %d)\n", 
  127.         err, file, line);
  128. }
  129. #endif
  130.  
  131.  
  132. #if defined(NOSTRICMP)
  133. /* stricmp.c (emx/gcc) -- Copyright (c) 1990-1992 by Eberhard Mattes */
  134.  
  135. int stricmp (const char *string1, const char *string2)
  136.     {
  137.     int d;
  138.  
  139.     for (;;)
  140.         {
  141.         d = tolower((unsigned char)*string1) -
  142.             tolower((unsigned char)*string2);
  143.         if (d != 0 || *string1 == 0 || *string2 == 0)
  144.             return (d);
  145.         ++string1; ++string2;
  146.         }
  147.     }
  148.  
  149. /* strnicmp.c (emx/gcc) -- Copyright (c) 1990-1992 by Eberhard Mattes */
  150.  
  151. int strnicmp (const char *string1, const char *string2, size_t count)
  152.     {
  153.     int d;
  154.  
  155.     while (count != 0)
  156.         {
  157.         d = tolower((unsigned char)*string1) -
  158.             tolower((unsigned char)*string2);
  159.         if (d != 0 || *string1 == 0 || *string2 == 0)
  160.             return (d);
  161.         ++string1; ++string2;
  162.         --count;
  163.         }
  164.     return (0);
  165.     }
  166.  
  167. #endif
  168.  
  169.  
  170. static void error(char *fmt, ...)
  171. {
  172.   va_list arg_ptr;
  173.  
  174.   va_start(arg_ptr,fmt);
  175.   vprintf(fmt,arg_ptr);
  176.   va_end(arg_ptr);
  177.   exit(2);
  178. }
  179.  
  180.  
  181. static void read_history(void)
  182. {
  183.   FILE *fp;
  184.   char line[MAX_LINE_LEN];
  185.   
  186.   if (NULL==(fp=fopen(history_file_name, "r")))
  187.   {
  188.     fprintf(stderr, "*** Warning: History file '%s' not found\n", history_file_name);
  189.     return;
  190.   }
  191.   
  192.   while (fgets(line, MAX_LINE_LEN, fp))
  193.   {
  194.     if (already_processed(line, TRUE, FALSE))
  195.       error("*** History file corrupt. Aborting.\n");
  196.   }
  197.  
  198.   if (ferror(fp))
  199.     error("*** error reading history file\n");
  200.   
  201.   fclose(fp);
  202.  
  203. } /* read_history */
  204.  
  205.  
  206. static boolean already_processed(char *msgid, boolean add, boolean setvisited)
  207. {
  208.   struct _msgid_list **p=&old_msgid_list;
  209.   int d;
  210.   
  211.   while (*p)
  212.   {
  213.     d=strcmp(msgid, (*p)->msgid);
  214.     if (d==0)
  215.     {
  216.       (*p)->visited=setvisited;
  217.       return TRUE;
  218.     }
  219.     else if (d<0)
  220.       p=&(*p)->left;
  221.     else
  222.       p=&(*p)->right;
  223.   }
  224.  
  225.   if (add)
  226.   {
  227.     if (NULL==(*p=malloc(sizeof (struct _msgid_list))))
  228.       error("*** Out of memory (alloc msgid_list)\n");
  229.     
  230.     if (NULL==((*p)->msgid=malloc(strlen(msgid)+1)))
  231.       error("*** Out of memory (alloc msgid)\n");
  232.     
  233.     strcpy((*p)->msgid, msgid);
  234.     (*p)->visited=setvisited;
  235.     (*p)->left = (*p)->right = (*p)->next = NULL;
  236.     *last_msgid_next_ptr=*p;
  237.     last_msgid_next_ptr=&(*p)->next;
  238.   }
  239.   
  240.   return FALSE;
  241. } /* already_processed */
  242.  
  243.  
  244. static void save_history(void)
  245. {
  246.   FILE *fp;
  247.   struct _msgid_list *p=old_msgid_list;
  248.   
  249.   if (NULL==(fp=fopen(history_file_name, "w")))
  250.   {
  251.     fprintf(stderr, "*** Warning: cannot open history file '%s' for writing\n", history_file_name);
  252.     return;
  253.   }
  254.   
  255.   while (p)
  256.   {
  257.     if (p->visited)
  258.       fputs(p->msgid, fp);
  259.     p=p->next;
  260.   }
  261.  
  262.   if (ferror(fp))
  263.     error("*** error writing history file.\n");
  264.   
  265.   fclose(fp);
  266.  
  267. } /* save_history */
  268.  
  269.  
  270. static void read_subjects(char *filespec)
  271. {
  272. #ifdef OS2
  273.   USHORT res;
  274.   HDIR hdir=HDIR_SYSTEM;
  275.   FILEFINDBUF *buffer;
  276.   USHORT usCount=1;
  277.   int i;
  278.   char pathspec[_MAX_FNAME], foundfile[_MAX_FNAME];
  279.   
  280.   if (NULL == (buffer= (FILEFINDBUF *) malloc(BUFFER_SIZE)))
  281.     error("*** Out of memory (alloc filefindbuf)\n");
  282.   
  283.   for (i=strlen(filespec); --i>=0 && filespec[i]!='\\' && filespec[i]!=':';);
  284.   strcpy(pathspec, filespec);
  285.   pathspec[i+1]='\0';
  286.  
  287.   res=DosFindFirst(filespec, &hdir, FILE_NORMAL, buffer, BUFFER_SIZE, 
  288.                    &usCount, 0L);
  289.   
  290.   while (res==0 && usCount>0)
  291.   {
  292.     find_subject(strcat(strcpy(foundfile, pathspec), &buffer->achName[0]));
  293.     /* usCount=1; anyway */
  294.     res=DosFindNext(hdir, buffer, BUFFER_SIZE, &usCount);
  295.   }
  296.   
  297.   if (res != ERROR_NO_MORE_FILES)
  298.     chk(res);
  299.   
  300.   DosFindClose(hdir);
  301.   free(buffer);
  302. #else
  303.   find_subject(filespec);
  304. #endif
  305. } /* read_subjects */
  306.  
  307.  
  308. static void find_subject(char *filename)
  309. {
  310.   FILE *article;
  311.   char line[MAX_LINE_LEN];
  312.   char subject[MAX_LINE_LEN];
  313.   char msgid[MAX_LINE_LEN];
  314.   boolean found_subject=FALSE, found_msgid=FALSE;
  315.   
  316.   if (NULL==(article=fopen(filename, "r")))
  317.   {
  318.     fprintf(stderr, "Warning: can't open '%s'\n", filename);
  319.     return;
  320.   }
  321.   
  322.   while (!feof(article) && !ferror(article) && (!found_subject || !found_msgid))
  323.   {
  324.     fgets(line, MAX_LINE_LEN, article);
  325.     if (strlen(line) <= 1)   /* empty line signalizes end of headers */
  326.       break;
  327.     if (!found_subject && strnicmp(line, "Subject: ", 9) == 0)
  328.     {
  329.       strcpy(subject, &line[9]);
  330.       found_subject=TRUE;
  331.     }
  332.     if (!found_msgid && strnicmp(line, "Message-ID: ", 12) == 0)
  333.     {
  334.       strcpy(msgid, &line[12]);
  335.       found_msgid=TRUE;
  336.     }
  337.   }
  338.  
  339.   if (!found_subject)
  340.     fprintf(stderr, "Article '%s' has no Subject\n", filename);
  341.   else if (!found_msgid)
  342.     fprintf(stderr, "Article '%s' has no Message-ID\n", filename);
  343.   else if (already_processed(msgid, FALSE, TRUE))
  344.   {
  345.     if (option_verbose)
  346.       fprintf(stderr, "Skipping old article '%s'...\n", filename);
  347.   }
  348.   else
  349.   {
  350.     if (subject[strlen(subject)-1] == '\n')
  351.       subject[strlen(subject)-1] = '\0';  /* remove trailing linefeed */
  352.     parse_subject(filename, subject, msgid);
  353.   }
  354.   
  355.   fclose(article);
  356. } /* find_subject */
  357.  
  358.  
  359. static void parse_subject(char *filename, char *subject, char *msgid)
  360. {
  361.   char *beg_partno, *beg_nparts, *end_nparts;
  362.   char *p;
  363.   int i;
  364.   int partno, nparts;
  365.   char *new_subject;
  366.  
  367. #ifdef DEBUG
  368.   fprintf(stderr, " Article '%s' has subject '%s'.\n", filename, subject);
  369. #endif
  370.  
  371.   for (i=0; i<ignore_fields; ++i)
  372.   {
  373.     while (*subject && *subject != ' ' && *subject != '\t') ++subject;
  374.     while (*subject && (*subject==' ' || *subject == '\t')) ++subject;
  375.     if (! *subject)
  376.     {
  377.       fprintf(stderr, "Subject of article '%s' has too few fields\n", filename);
  378.       already_processed(msgid, TRUE, TRUE);  /* don't parse this article again */
  379.       return;
  380.     }
  381.   }
  382.  
  383.   p=&subject[strlen(subject)-1];
  384.   
  385. /* no spaghetti, but a finite state machine :-) looking for 
  386.      <digits> [<whitespace>] ( "of" | "/" ) [<whitespace>] <digits>
  387.    working backwards from the end of the subject */
  388.   
  389.   state_nothing:
  390.     if (p==subject) goto not_found;
  391.     while (!isdigit(*p) && p != subject) --p;
  392.     if (p==subject) goto not_found;
  393.     end_nparts=p;
  394.       
  395.   /* found a digit, looking for number of parts */
  396.     while (isdigit(*p) && p != subject) --p;
  397.     if (p==subject) goto not_found;
  398.     beg_nparts=p+1;
  399.   
  400.   /* found number of parts, skip whitespace */
  401.     while ((*p == ' ' || *p == '\t') && p != subject) --p;
  402.     if (p==subject) goto not_found;
  403.     
  404.     if (*p != '/')
  405.     {
  406.       if ((*p!='f' && *p!='F' || *(p-1)!='o' && *(p-1)!='O'))
  407.       {
  408.         p=end_nparts - 1;
  409.         goto state_nothing;
  410.       }
  411.       else
  412.         if (--p == subject) goto not_found;
  413.     }
  414.     if (--p == subject) goto not_found;
  415.  
  416.   /* found "of" or "/", skip whitespace */
  417.     while ((*p == ' ' || *p == '\t') && p != subject) --p;
  418.     
  419.     if (!isdigit(*p))
  420.     {
  421.       p=end_nparts - 1;
  422.       goto state_nothing;
  423.     }
  424.  
  425.     while (p!=subject && isdigit(*(p-1))) --p;
  426.     beg_partno=p;
  427.   
  428.   partno=atoi(beg_partno);
  429.   nparts=atoi(beg_nparts);
  430.  
  431.   if (NULL == (new_subject=malloc(strlen(subject)+1)))
  432.     error("*** Out of memory (malloc new_subject)\n");
  433.   strcpy(new_subject, subject);
  434.   strcpy(new_subject+(beg_partno-subject), end_nparts+1);
  435.   store_part(filename, new_subject, msgid, partno, nparts);
  436.   free(new_subject);
  437.  
  438.   return;
  439.   
  440. not_found:
  441.   if (option_decode_all)
  442.     store_part(filename, subject, msgid, 1, 1);
  443.   else
  444.   {
  445.     fprintf(stderr, " Article '%s' has no part numbers: '%s'\n", filename, subject);
  446.     already_processed(msgid, TRUE, TRUE);  /* don't parse this article again */
  447.   }
  448.   return;
  449. } /* parse_subject */
  450.  
  451.  
  452. static void store_part(char *filename, char *subject, char*msgid,
  453.                        int partno, int nparts)
  454. {
  455.   struct _subj_list **p;
  456.  
  457. #ifdef DEBUG
  458.   fprintf(stderr,
  459.           " Article '%s' contains '%s' part %d of %d.\n",
  460.           filename, new_subject, partno, nparts);
  461. #endif
  462.   
  463.   p=&subject_list;
  464.   
  465.   while (*p)
  466.   {
  467.     if (0==strcmp((*p)->subject, subject) && (*p)->nparts==nparts)
  468.     {
  469.       add_article(&(*p)->partlist, filename, msgid, partno);
  470.       return;
  471.     }
  472.     p=&(*p)->next;
  473.   }
  474.  
  475.   if (NULL==(*p=(struct _subj_list *)malloc(sizeof (struct _subj_list))))
  476.     error("*** Out of memory (malloc sub_list)\n");
  477.   (*p)->next=NULL;
  478.   if (NULL==((*p)->subject=(char *)malloc(strlen(subject)+1)))
  479.     error("*** Out of memory (malloc subject)\n");
  480.   strcpy((*p)->subject, subject);
  481.   (*p)->nparts=nparts;
  482.   (*p)->partlist=NULL;
  483.   add_article(&(*p)->partlist, filename, msgid, partno);
  484.  
  485. } /* store_part */
  486.  
  487.  
  488. static void add_article(struct _part_list **partlist, char *filename, 
  489.                         char *msgid, int partno)
  490. {
  491.   struct _part_list *p;
  492.  
  493.   while (*partlist)
  494.   {
  495.     if ((*partlist)->partno == partno)
  496.     {
  497.       fprintf(stderr, "Duplicate article '%s' ignored\n", filename);
  498.       already_processed(msgid, TRUE, TRUE);  /* don't parse this article again */
  499.       return;
  500.     }
  501.     else if ((*partlist)->partno > partno)
  502.       break; /* insert here */
  503.     partlist=&(*partlist)->next;
  504.   }
  505.   
  506.   p=*partlist;
  507.   if (NULL==(*partlist=(struct _part_list *)malloc(sizeof (struct _part_list))))
  508.     error("*** Out of memory (malloc partlist)\n");
  509.   (*partlist)->next=p;
  510.   (*partlist)->partno=partno;
  511.   if (NULL==((*partlist)->filename=malloc(strlen(filename)+1)))
  512.     error("*** Out of memory (malloc filename)\n");
  513.   strcpy((*partlist)->filename, filename);
  514.   if (NULL==((*partlist)->msgid=malloc(strlen(msgid)+1)))
  515.     error("*** Out of memory (malloc msgid(partlist))\n");
  516.   strcpy((*partlist)->msgid, msgid);
  517. } /* add_article */
  518.  
  519.  
  520. static void output_results(void)
  521. {
  522.   struct _subj_list *p;
  523.   struct _part_list *q;
  524.   int i;
  525.   char *cmdline=NULL;
  526.   
  527.   for (p=subject_list; p; p=p->next)
  528.   {
  529.     for (i=1, q=p->partlist; i<=p->nparts; ++i, q=q->next)
  530.       if (!q || q->partno != i)
  531.       {
  532.         fprintf(stderr,
  533.                 "Subject '%s' (%d parts) is incomplete.\n", 
  534.                 p->subject, p->nparts);
  535.         print_malformed(p);
  536.         goto next_subject;
  537.       }
  538.     if (q)
  539.     {
  540.       fprintf(stderr,
  541.               "Subject '%s' (%d parts) has too many parts.\n",
  542.               p->subject, p->nparts);
  543.       print_malformed(p);
  544.       goto next_subject;
  545.     }
  546.     if (option_dont_execute)
  547.       printf(SHELL_COMMAND_ECHO " ");
  548.     if (command)
  549.       printf("Executing %s for '%s'...\n", command, p->subject);
  550.     else
  551.       printf("Decoding '%s'...\n", p->subject);
  552.     
  553.     if (option_dont_execute)
  554.     {
  555.       if (command)
  556.         fputs(command, stdout);
  557.       else
  558.         fputs(SHELL_COMMAND_COMMENT
  559.               " internal_decoder", stdout);
  560.       for (q=p->partlist; q; q=q->next)
  561.         printf(" %s", q->filename);
  562.       printf("\n");
  563.     }
  564.     else
  565.     {
  566.       if (command)
  567.       {
  568.         cmdline=(char *)malloc(strlen(command)+p->nparts*(strlen(p->partlist->filename)+5)+10);
  569.         if (cmdline==NULL)
  570.           error("*** out of memory (malloc cmdline)\n");
  571.         strcpy(cmdline, command);
  572.         for (q=p->partlist; q; q=q->next)
  573.         {
  574.           strcat(cmdline, " ");
  575.           strcat(cmdline, q->filename);
  576.           already_processed(q->msgid, TRUE, TRUE);  /* don't parse this article again */
  577.         }
  578. #if defined(DEBUG)
  579.         fprintf(stderr, "Executing: '%s'\n", cmdline);
  580. #endif
  581.         system(cmdline);
  582.         free(cmdline);
  583.       }
  584.       else
  585.       {
  586.         begin_uucat();
  587.         for (q=p->partlist; q; q=q->next)
  588.         {
  589.           uucat_file(q->filename);
  590.           already_processed(q->msgid, TRUE, TRUE);  /* don't parse this article again */
  591.         }
  592.         end_uucat();  
  593.       }  
  594.     }
  595.         
  596.   next_subject:;
  597.   }
  598. } /* output_results */      
  599.  
  600.  
  601. static void print_malformed(struct _subj_list *p)
  602. {
  603.   struct _part_list *q;
  604.   int prev_part;
  605.   boolean in_range=FALSE;
  606.   int n_expected=0, n_unexpected=0;
  607.   
  608.   if (option_verbose)
  609.   {
  610.     for (q=p->partlist; q; q=q->next)
  611.       fprintf(stderr,
  612.               "     part %d: '%s'\n", q->partno, q->filename);
  613.   }
  614.   else
  615.   {
  616.     fprintf(stderr, "Available parts: %d",p->partlist->partno);
  617.     prev_part=p->partlist->partno;
  618.     if (1 <= p->partlist->partno && p->partlist->partno <= p->nparts)
  619.       ++n_expected;
  620.     else
  621.       ++n_unexpected;
  622.     
  623.     for (q=p->partlist->next; q; q=q->next)
  624.     {
  625.       if (1 <= q->partno && q->partno <= p->nparts)
  626.         ++n_expected;
  627.       else
  628.         ++n_unexpected;
  629.       
  630.       if (q->partno == ++prev_part)
  631.         in_range=TRUE;
  632.       else
  633.       {
  634.         if (in_range)
  635.           fprintf(stderr, "-%d", prev_part-1);
  636.         fprintf(stderr, ",%d", q->partno);
  637.         prev_part=q->partno;
  638.         in_range=FALSE;
  639.       }
  640.     }
  641.     if (in_range)
  642.       fprintf(stderr, "-%d", prev_part);
  643.     if (n_unexpected == 0)
  644.       fprintf(stderr, ". (%d part%s)\n", n_expected, (n_expected==1?"":"s"));
  645.     else
  646.       fprintf(stderr, ". (%d part%s + %d unexpected part%s)\n", 
  647.         n_expected, (n_expected==1?"":"s"), 
  648.         n_unexpected, (n_unexpected==1?"":"s"));
  649.   }
  650. } /* print_malformed */
  651.  
  652. /* -- internal uucat decoder. Adapted from a public domain source ---- */
  653.  
  654. /*
  655. *From: d84sp@efd.lth.se (Stefan Parmark)
  656. Subject: Decoding pictures
  657. Summary: uudecode
  658. Date: 12 Aug 90 07:54:17 GMT
  659. Organization: Lund Institute of Technology, Sweden
  660.  
  661. The other day I wrote a simple C program which I use when
  662. uudecoding multi-part uuencoded files. It has worked for
  663. all newsgroups I have tested so far. It saves you the effort
  664. of having to edit the files. To use it, compile it
  665. and to decode files, type
  666. > uucat file1 file2 ... filen | uudecode
  667. or
  668. > cat file1 file2 ... filen | uucat | uudecode
  669. or, if you already have concatenated them into one file
  670. > uucat file | uudecode
  671. . I place uucat in the public domain, and take no responsibility
  672. for its usage.
  673.  
  674. */
  675.  
  676. static char first = UFIRST, quote = UQUOTE;
  677.  
  678. static void begin_uucat(void)
  679. {
  680.   uudecode = _popen("uudecode", "w");
  681.   echo_on = FALSE, started = FALSE, just_finished = FALSE;
  682.   line_length = 0, lines_to_go = 0;
  683.   uu_name[0]='\0';
  684.  
  685.   if (option_save_descriptions)
  686.   {
  687.     description_file_name="$UUGRAB$.TMP";
  688.     if (NULL==(description_file=fopen(description_file_name, "w")))
  689.       error("Can't open description file '%s' for output.\n", description_file_name);
  690.     echo_description=TRUE;
  691.   }
  692.   else
  693.     echo_description=FALSE;
  694.  
  695. } /* begin_uucat */
  696.  
  697.  
  698. static void end_uucat(void)
  699. {
  700.   char *last_dot;
  701.  
  702.   _pclose(uudecode);
  703.   if (option_save_descriptions)
  704.   {
  705.     fclose(description_file);
  706.     
  707.     if (*uu_name=='\0')  /* uu_name is set as a side effect of is_begin_line */
  708.     {
  709.       fprintf(stderr, "Description lost (no filename available)\n");
  710.       unlink(description_file_name);
  711.     }
  712.     else
  713.     {
  714.       /* replace the last extension with .des or append .des if none */
  715.       last_dot=strrchr(uu_name, '.');
  716.       if (!last_dot)
  717.       {
  718.         last_dot=&uu_name[strlen(uu_name)];
  719.         *last_dot='.';
  720.       }
  721.       *++last_dot='d';
  722.       *++last_dot='e';
  723.       *++last_dot='s';
  724.       *++last_dot='\0';
  725.     
  726.       if (rename(description_file_name, uu_name))
  727.       {
  728.         fprintf(stderr, "Description lost: could not rename '%s' to '%s'.\n",
  729.                         description_file_name, uu_name);
  730.         unlink(description_file_name);
  731.       }
  732.     }
  733.   }
  734. } /* end_uucat */
  735.  
  736.  
  737. static void uucat_file(char *filename)
  738. {
  739.   FILE *infile;
  740.   char *s2, *s1, *s0, *tmp_s;
  741.   int length;
  742.   static char s[3 * (LENGTH + 1)];
  743.   
  744.   if ((infile = fopen(filename, "r")) == NULL)
  745.   {
  746.     fprintf(stderr, "*** Can't open '%s' for input.\n", filename);
  747.     return;
  748.   }
  749.  
  750.   s0 = s;
  751.   s1 = s0 + (LENGTH + 1);
  752.   s2 = s1 + (LENGTH + 1);
  753.  
  754.   s0[0] = s1[0] = s2[0] = '\0';  /* Clear strings */
  755.  
  756.   while (fgets(s0, LENGTH, infile) != NULL)
  757.   {
  758.     s0[LENGTH] = '\0';  /* Make sure string is terminated */
  759.  
  760.     if (just_finished)
  761.     {
  762.       if (strncmp(s0, "size ", 5) == 0)
  763.       {
  764.         fputs(s0, uudecode);
  765.     s0[0] = '\0';
  766.       }
  767.       just_finished = FALSE;
  768.     }
  769.  
  770.     if (!started)
  771.     {
  772.       if (is_begin_line(s0))
  773.       {
  774.     started = echo_on = TRUE;
  775.     line_length = 0;
  776.         lines_to_go = 0;
  777.         echo_description=FALSE;
  778.       }
  779.     }
  780.     else  /* started */
  781.     {
  782.       length = strlen(s0);
  783.       if (line_length == 0)
  784.     line_length = length;
  785.  
  786.       if (echo_on)
  787.       {
  788.     lines_to_go = 0;
  789.     if (s0[0] != first || length != line_length)
  790.     {
  791.       echo_on = FALSE;
  792.       lines_to_go = 2;  /* Lines to go before 'end' is expected */
  793.     }
  794.       }
  795.       else  /* !echo_on */
  796.       {
  797.     if (s0[0] == first && length == line_length)
  798.       echo_on = TRUE;
  799.     else if (lines_to_go > 0)
  800.     {
  801.       if (lines_to_go == 2)
  802.       {
  803.         if (s0[0] == ' ' || s0[0] == quote)
  804.           lines_to_go = 1;
  805.         else
  806.           lines_to_go = 0;  /* Unexpected line, so break off */
  807.       }
  808.       else if (lines_to_go == 1)
  809.       {
  810.         if (strcmp(s0, "end\n") == 0)
  811.         {
  812.               fputs(s2, uudecode);
  813.               fputs(s1, uudecode);
  814.               fputs(s0, uudecode);
  815.           lines_to_go = 0;  /* Done. Break off */
  816.           just_finished = TRUE;
  817.           started = FALSE;
  818.         }
  819.         else
  820.           lines_to_go = 0;  /* Unexpected line, so break off */
  821.       }
  822.     }
  823.       }
  824.     }
  825.  
  826.     if (echo_description)
  827.       fputs(s0, description_file);
  828.     if (echo_on)
  829.     {
  830.       fputs(s0, uudecode);
  831.       s0[0]='\0';
  832.     }
  833.   
  834.     tmp_s = s2;
  835.     s2 = s1;
  836.     s1 = s0;
  837.     s0 = tmp_s;
  838.   }
  839.  
  840.   fclose(infile);
  841. } /* uucat_file */
  842.  
  843.  
  844. /* check (thoroughly) if a line is the begin line of a uuencoded file */
  845. /* SIDE EFFECT: store name of file to be uudecoded in uu_name which
  846.    must be an already allocated array of characters                   */
  847.  
  848. static int is_begin_line(char *s)
  849. {
  850.   char *name_begin;
  851.  
  852.   if (strncmp(s, "begin ", 6) != 0)
  853.     return FALSE;
  854.   
  855.   s+=6;
  856.   
  857.   while (isspace(*s)) ++s;
  858.   
  859.   if (*s<'0' || *s>'7')
  860.     return FALSE;
  861.   
  862.   while (*s>='0' && *s<='7') ++s;
  863.   
  864.   if (*s != ' ') return FALSE;
  865.     
  866.   while (isspace(*s)) ++s;
  867.   
  868.   if (!*s)
  869.     return FALSE;
  870.   
  871.   name_begin=s;
  872.  
  873.   while (*s && !isspace(*s)) ++s;
  874.     
  875.   if (*s=='\n')
  876.   {
  877.     strcpy(uu_name, name_begin);
  878.     uu_name[strlen(uu_name)]='\0';  /* remove trailing newline */
  879.     return TRUE;
  880.   }
  881.   else
  882.     return FALSE;
  883. } /* is_begin_line */
  884.  
  885.  
  886. /* ------------------------------------------------------------------- */
  887.  
  888.  
  889. static void usage(void)
  890. {    
  891.   fprintf(stderr, 
  892.     "Usage: uugrab [-a] [-c <command>] [-d] [-i <n>] [-n] [-v] files...\n"
  893.     "  -a  decode all files, even if there is no part number\n"
  894.     "  -c  execute <command> for each binary (default: decode binary)\n"
  895.     "  -d  save descriptions of binaries in separate files\n"
  896.     "  -i  ignore <n> fields at beginning of subject\n"
  897.     "  -n  don't do anything; just display what would be done\n"
  898.     "  -v  verbose: display some more messages\n"
  899.     "Hints: Use the -n option if unsure. Use -i 1 for comp.binaries.os2.\n"
  900.     "       If all else fails, read the documentation.\n"
  901.     );
  902.   exit(1);
  903. } /* usage */
  904.  
  905.  
  906. int main(int argc, char *argv[])
  907. {
  908.   int i;
  909.   
  910. #ifdef OS2
  911.   fprintf(stderr, "uugrab Version 1.6  (c)1992 by Harald Bögeholz\n");
  912. #else
  913.   fprintf(stderr, "uugrab Version 1.6  (c)1992 by Harald Boegeholz\n");
  914. #endif
  915.   
  916.   for (i=1; i<argc; ++i)
  917.   {
  918.     if (argv[i][0] != '-')
  919.       break;
  920.     
  921.     if (0==stricmp(argv[i], "-a"))
  922.       option_decode_all=TRUE;
  923.     else if (0==stricmp(argv[i], "-d"))
  924.       option_save_descriptions=TRUE;
  925.     else if (0==stricmp(argv[i], "-n"))
  926.       option_dont_execute=TRUE;
  927.     else if (0==stricmp(argv[i], "-v"))
  928.       option_verbose=TRUE;
  929.     else if (0 == stricmp(argv[i], "-i"))
  930.     {
  931.       if (++i<argc)
  932.         ignore_fields=atoi(argv[i]);
  933.       else
  934.         usage();
  935.     }
  936.     else if (0 == stricmp(argv[i], "-c"))
  937.     {
  938.       if (++i<argc)
  939.         command=argv[i];
  940.       else
  941.         usage();
  942.     }
  943.     else
  944.       usage();  
  945.   }
  946.  
  947.   if (i>=argc)
  948.     usage();
  949.   
  950.   if (command && option_save_descriptions)
  951.     error("The -c and -d options are mutually exclusive.\n");
  952.  
  953.   read_history();
  954.  
  955.   for (; i<argc; ++i)
  956.     read_subjects(argv[i]);
  957.   
  958.   output_results();
  959.   
  960.   if (!option_dont_execute)
  961.     save_history();
  962.  
  963.   return 0;
  964. } /* main */
  965.  
  966.  
  967.