home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume16 / narc / narc.c < prev    next >
C/C++ Source or Header  |  1989-01-17  |  12KB  |  539 lines

  1. /*
  2. **    Narc -- archive NetNews articles
  3. **
  4. **    Geoffrey Leach
  5. **    LatiCorp Inc.    
  6. **    {att,bellcore,sun,ames,pyramid}!pacbell!laticorp!geoff
  7. */
  8.  
  9. #include <stdio.h>
  10. #include <strings.h>
  11. #include <sys/file.h>
  12. #include "version.h"
  13. #include "patchlevel.h"
  14.  
  15. #define    TRUE        1
  16. #define FALSE        0
  17. #define MATCH        0
  18. #define EXISTS        0
  19. #define PROMPT        0
  20. #define SUBJECT        1
  21. #define INDEX        2
  22. #define PNULL        (char *)0
  23. #define FNULL        (FILE *)0
  24. #define NGNULL        (sNewsGroup *)0
  25. #define INDEXR        "Indexr"
  26.  
  27. typedef struct
  28. {
  29.     char *    name;
  30.     char *    archive;
  31.     char *    volume_tag;
  32.     int        moderated;
  33. } sNewsGroup;
  34.  
  35. extern char *    fgets();
  36. extern char *    malloc();
  37. extern char *    getenv();
  38. extern char *    strpbrk();
  39. extern char *    optarg;
  40. extern int    optind;
  41.  
  42. sNewsGroup *    NewsGroups[100];
  43. sNewsGroup     prompted = {PNULL, PNULL, PNULL, FALSE};
  44. FILE *        rc;
  45. FILE *        tty;
  46. FILE *        out;
  47. FILE *        aindex;
  48. char         arcdir[1024];
  49. char         descr[1024];
  50. char         arc_dir[1024];
  51. char        line[1024];
  52. char        head_buf[10240];
  53. char *        head_buf_ptr = head_buf;
  54. char        tmp_file[] = {"Narc.XXXXXX"};
  55. char        arc_file[256];
  56. char        arc_name[256];
  57. char        p_archive[256];
  58. int        debug = FALSE;
  59. int        named = FALSE;
  60. int        repost = FALSE;
  61. int        selected = FALSE;
  62. int        subjected = FALSE;
  63. int        head_buf_lines = 0;
  64.  
  65. sNewsGroup*
  66. lookup(name)
  67.     char    *name;
  68. {
  69.     int      i = -1;
  70.  
  71.     /*
  72.      *  A little surgery: get rid of the  leading "Newsgroups: "
  73.      *  and remove any secondary newsgroups
  74.      */
  75.     name += 12;
  76.     *(strpbrk(name, ",\n")) = '\0';
  77.  
  78.     /*
  79.      *  See if this newsgroup was listed in the user's rc file
  80.      */
  81.     while ( NewsGroups[++i] )
  82.     {
  83.     if ( strcmp(name, NewsGroups[i]->name) == MATCH )
  84.         return NewsGroups[i];
  85.     }
  86.     return NGNULL;
  87. }
  88.  
  89. void
  90. rename_article(tmp_file, name)
  91.     char *tmp_file;
  92.     char *name;
  93. {
  94.     char  ans[10];
  95.     char  rcmd[1024];
  96.  
  97.     if ( access(name, R_OK) == EXISTS )
  98.     {
  99.     printf("%s exists! (a)ppend, (i)gnore or (r)eplace [r]? ", arc_file);
  100.     fflush(stdout);
  101.     fgets(ans, 10, tty);
  102.     switch ( ans[0] )
  103.     {
  104.         default:
  105.         case 'r':
  106.         break;
  107.         case 'a':
  108.         sprintf(rcmd, "cat %s >> %s", tmp_file, name);
  109.         if ( debug )
  110.             printf("%s\n", rcmd);
  111.         system(rcmd);
  112.         unlink(tmp_file);
  113.         return;
  114.         case 'i':
  115.         return;
  116.     }
  117.     }
  118.  
  119.     if ( rename(tmp_file, name) )
  120.     {
  121.     sprintf(rcmd, "Unable to rename %s", name);
  122.     perror(rcmd);
  123.     exit(1);
  124.     }
  125. }
  126.  
  127. void
  128. close_article()
  129. {
  130.     /*
  131.      *  The beginning of an article, and not the first.
  132.      *  Therefore, we have an article open that must be closed.
  133.      */
  134.     named     = FALSE;
  135.     repost    = FALSE;
  136.     subjected = FALSE;
  137.     if ( strlen(arc_name) )
  138.     {
  139.     fputs(arc_name, aindex);
  140.     fputs(":  ",    aindex);
  141.     fputs(descr,    aindex);
  142.     }
  143.     fclose(out);
  144.  
  145.     if ( selected )
  146.     {
  147.     rename_article(tmp_file, arc_file);
  148.     out = fopen(tmp_file,"w");
  149.     selected = FALSE;
  150.     }
  151.     else
  152.     rewind(out);
  153. }
  154.  
  155. void
  156. chop(str)
  157.     char *    str;
  158. {
  159.     *(str +strlen(str) - 1) = '\0';
  160. }
  161.  
  162. void
  163. fgets_buffer()
  164. {
  165.     if ( (head_buf_ptr + strlen(line)) >= (head_buf + sizeof(head_buf)) )
  166.     {
  167.     printf("Could not find newsgroup (or volume) within %d lines\n",
  168.            head_buf_lines);
  169.     exit(1);
  170.     }
  171.  
  172.     fgets(line, sizeof(line), stdin);
  173.     strcpy(head_buf_ptr, line);
  174.     head_buf_ptr += strlen(line) + 1;
  175.     head_buf_lines++;
  176. }
  177.  
  178. char *
  179. fgets_unbuffer()
  180. {
  181.     if ( head_buf_lines )
  182.     {
  183.     strcpy(line, head_buf_ptr);
  184.     head_buf_ptr += strlen(line) + 1;
  185.     head_buf_lines--;
  186.     return line;
  187.     }
  188.     return fgets(line, sizeof(line), stdin);
  189. }
  190.  
  191. void
  192. main(argc, argv)
  193.     int         argc;
  194.     char    *argv[];
  195. {
  196.     int         i;
  197.     int         opt;
  198.     int          vol;
  199.     int          mode = INDEX;
  200.     int          name_fd = 0;
  201.     int         article_name = 0;
  202.     char *     cmd;
  203.     char *     name;
  204.     char *     subj;
  205.     sNewsGroup * NewsGroup;
  206.  
  207.     while ( (opt = getopt(argc, argv, "a:px")) != EOF )
  208.     {
  209.     switch ( opt )
  210.     {
  211.         case 'a':
  212.         prompted.archive = optarg;
  213.         break;
  214.         case 'p':
  215.         mode = PROMPT;
  216.         break;
  217.         case 'x':
  218.         debug = TRUE;
  219.         break;
  220.         default:
  221.         fprintf(stderr, "Usage: narc [-px] [-a archive]\n");
  222.         fprintf(stderr, "       version %s level %d\n", VERSION, PATCHLEVEL);
  223.         exit(1);
  224.     }
  225.     }
  226.  
  227.     /*
  228.      * Get the archive directory from the environment
  229.      */
  230.     strcpy(arcdir, getenv("ARCHIVE"));
  231.  
  232.     /*
  233.      * Read the user's ~/.narc file (if he has one) for the 
  234.      * newsgroups to watch.  What we are looking for is 
  235.      * The base of the archive directory (full path) this is the
  236.      * prefix for the archive directory specified for each newsgroup.
  237.      * Then, tab separated fields as follows:
  238.      *        newsgroup:  what fillows Newsgroups: in the header.
  239.      *        archive:    the directory under arcdir.
  240.      *        volume_tag: the tag for the line that says what the volume is 
  241.      *            (moderated newsgroups only).
  242.      *        moderated:  is this a moderated newsgroup? (0/1)
  243.      */
  244.     sprintf(line, "%s/.narcrc", getenv("HOME"));
  245.     if ( (rc = fopen(line, "r")) != FNULL )
  246.     {
  247.     i = 0;
  248.     while ( fgets(line, sizeof(line), rc ) != PNULL )
  249.     {
  250.         NewsGroup       =
  251.         NewsGroups[i++] = (sNewsGroup *)malloc(sizeof(sNewsGroup));
  252.  
  253.         *(name = index(line, ':')) = '\0';
  254.         NewsGroup->name = malloc(strlen(line) + 1);
  255.         strcpy(NewsGroup->name, line);
  256.  
  257.         subj = ++name;
  258.         if ( (name = index(subj, ':')) == PNULL )
  259.         {
  260.         NewsGroup->moderated = FALSE;
  261.         subj[strlen(subj) - 1] = '\0';
  262.         }
  263.         else
  264.         {
  265.         NewsGroup->moderated = TRUE;
  266.         *name = '\0';
  267.         }
  268.         NewsGroup->archive = malloc(strlen(subj) + 1);
  269.         strcpy(NewsGroup->archive, subj);
  270.  
  271.         if ( NewsGroup->moderated )
  272.         {
  273.         subj = ++name;
  274.         NewsGroup->volume_tag = malloc(strlen(subj));
  275.         strncpy(NewsGroup->volume_tag, subj, strlen(subj) - 1);
  276.         }
  277.  
  278.         if ( debug )
  279.         printf("%s\t%s\t%s\t%d\n", NewsGroup->name,
  280.                        NewsGroup->archive,
  281.                        NewsGroup->volume_tag,
  282.                        NewsGroup->moderated);
  283.     }
  284.     fclose(rc);
  285.     }
  286.  
  287.     if ( (tty = fopen("/dev/tty", "r")) == FNULL )
  288.     {
  289.     perror("Error opening tty");
  290.     exit(1);
  291.     };
  292.  
  293.     /*
  294.      *  If the user has not forced an archive, find one.
  295.      *  As we will skip past the first subject line, we need
  296.      *  to buffer the head of the file.
  297.      */
  298.     if ( prompted.archive )
  299.     NewsGroup = &prompted;
  300.     else
  301.     {
  302.     /*
  303.      *  Look for the Newsgroups line in the header
  304.      */
  305.     do 
  306.     {
  307.         fgets_buffer();
  308.     }
  309.     while ( strncmp(line, "Newsgroups: ", 12) != MATCH );
  310.  
  311.     /*
  312.      *  Is the newsgroup on the list?  Note that we only look
  313.      *  at the first newsgroup that's specified.  
  314.      */
  315.     if ( (NewsGroup = lookup(line)) == NGNULL )
  316.     {
  317.         printf("Archive directory for %s? ", line);
  318.         fflush(stdout);
  319.         fgets(p_archive, sizeof(p_archive), tty);
  320.         chop(p_archive);
  321.         prompted.archive = p_archive;
  322.         NewsGroup = &prompted;
  323.     }
  324.  
  325.     /*
  326.      *  If we find that we have a moderated newsgroup, we assume
  327.      *  that somewhere there will be a line in the header that tells
  328.      *  us about the current volume and the archive name of the 
  329.      *  program that's in this article.
  330.      */
  331.     if ( NewsGroup->moderated )
  332.     {
  333.         if ( mode != PROMPT )
  334.         mode = SUBJECT;
  335.  
  336.         do
  337.         {
  338.         fgets_buffer();
  339.         }
  340.         while ( strncmp(line, NewsGroup->volume_tag,
  341.                 strlen(NewsGroup->volume_tag)) != MATCH );
  342.  
  343.         vol = atoi(&line[strlen(NewsGroup->volume_tag) + 8 ]);
  344.     }
  345.     }
  346.  
  347.     /*
  348.      *  Everything that we need to know about output is determined.
  349.      */
  350.     if ( NewsGroup->moderated )
  351.     sprintf(arc_dir, "%s/%s/v%02d", arcdir, NewsGroup->archive, vol);
  352.     else
  353.     sprintf(arc_dir, "%s/%s", arcdir, NewsGroup->archive);
  354.  
  355.     if ( debug )
  356.     {
  357.     printf("Archive directory selected is: %s\n", arc_dir);
  358.     strcpy(arc_dir, ".");
  359.     }
  360.     else
  361.     {
  362.     if ( chdir(arc_dir) )
  363.     {
  364.         sprintf(arc_file, "Could not chdir to %s", arc_dir);
  365.         perror(arc_file);
  366.         exit(1);
  367.     }
  368.     }
  369.  
  370.     /*
  371.      *  The tmp file is created in the archive directory
  372.      */
  373.     mktemp(tmp_file);
  374.     if ( (out = fopen(tmp_file,"w")) == FNULL )
  375.     {
  376.     sprintf(arc_file, "Error opening %s" , tmp_file);
  377.     perror(arc_file);
  378.     exit(1);
  379.     };
  380.  
  381.     /*
  382.      *  This is the "scratch" index that gets the raw data from
  383.      *  each article subject.  The user will extract one line for
  384.      *  each submission for the "real" index.
  385.      */
  386.     if ( (aindex = fopen(INDEXR, "a")) == FNULL )
  387.     {
  388.     perror("Error opening index");
  389.     exit(1);
  390.     };
  391.  
  392.     /*
  393.      *  If we are not moderated and the user has not requested us
  394.      *  to prompt for names, then we need to generate a name.  No
  395.      *  advance preparation is required for this; if the .names file
  396.      *  does not exist, we will start naming at 000.
  397.      */
  398.     if ( !NewsGroup -> moderated && mode != PROMPT )
  399.     {
  400.     if ( (name_fd = open(".names", O_RDWR | O_CREAT, 0666)) == NULL )
  401.     {
  402.         perror("Error opening .names");
  403.         exit(1);
  404.     }
  405.  
  406.     read(name_fd, &article_name, sizeof(int));
  407.     lseek(name_fd, 0L, L_SET);
  408.     }
  409.  
  410.     /*
  411.      *  Now that we have somewhere to put the input, restart the input and
  412.      *  skip the first line which is (we hope) Path: ..
  413.      */
  414.     head_buf_ptr = head_buf;
  415.     fgets_unbuffer(line);
  416.     fputs(line, out);
  417.  
  418.     /*
  419.      *  Process stdin until EOF.  We expect to have a sequence of net news
  420.      *  articles, each of which has a subject line.
  421.      */
  422.     while ( fgets_unbuffer() != PNULL )
  423.     {
  424.     if ( strncmp(line, "Path: ", 6) == MATCH )
  425.         close_article();
  426.  
  427.     /*
  428.      *  Now that we have the new file (if that's what happened),
  429.      *  dispose of the current line
  430.      */
  431.     fputs(line, out);
  432.  
  433.     /*
  434.      *  Does this line define the archive name?
  435.      */
  436.     if ( !named && (strncmp(line, "Archive-name: ", 13) == MATCH) )
  437.     {
  438.         /*
  439.          *  Process only one archive name per article
  440.          */
  441.         named = TRUE;
  442.  
  443.         strcpy(arc_name, &line[14]);
  444.         *(strpbrk(arc_name, "/ \n")) = '\0';
  445.     }
  446.  
  447.     /*
  448.      *  Do we have a Subject?
  449.      */
  450.     if ( !subjected && (strncmp(line, "Subject: ", 9) == MATCH) )
  451.     {
  452.         /*
  453.          *  Process only one Subject: per article
  454.          */
  455.         selected  = TRUE;
  456.         subjected = TRUE;
  457.  
  458.         /*
  459.          *  Generate a name, using the subject line is possible
  460.          */
  461.  
  462.         /*
  463.          *  First, drop the "Subject: "
  464.          */
  465.         subj = &line[9];
  466.  
  467.         /*
  468.          *  Then, if this is a re-posting note the fact so that we
  469.          *  don't hassle the user if the first posting happens to
  470.          *  be in the archive and skip the "REPOST "
  471.          */
  472.         if ( strncmp(subj, "REPOST ", 7) == MATCH )
  473.         {
  474.         subj += 7;
  475.         repost = TRUE;
  476.         }
  477.  
  478.         /*
  479.          *  How we generate the name depends on what kind of newsgroup
  480.          */
  481.         switch ( mode )
  482.         {
  483.         case SUBJECT:        /* moderated groups */
  484.             /*
  485.              *  Tell the user what's happening
  486.              */
  487.             printf("%s", subj);
  488.  
  489.             /*
  490.              *  Name should begin with something like v02i023:
  491.              *  Assume this, and use the fist 7 characters for
  492.              *  the file name
  493.              */
  494.             strncpy(arc_file, subj, 7);
  495.             break;
  496.         case PROMPT:        /* user wants us to prompt for name */
  497.             /*
  498.              *  This is the article
  499.              */
  500.             printf("%s", subj);
  501.             fputs("Output file? ", stdout);
  502.             fflush(stdout);
  503.             fgets(arc_name, sizeof(arc_name), tty);
  504.             chop(arc_name);
  505.             if ( strlen(arc_name) == 0 )
  506.             {
  507.             /*
  508.              *  User declines to save this item
  509.              */
  510.             selected = FALSE;
  511.             continue;
  512.             }
  513.             strcpy(arc_file, arc_name);
  514.             break;
  515.         case INDEX:        /* generate name */
  516.             sprintf(arc_file, "%03d", article_name++);
  517.             strcpy(arc_name, arc_file);
  518.             /*
  519.              *  Tell the user what we did
  520.              */
  521.             printf("%s: %s", arc_file, subj);
  522.             break;
  523.         default:
  524.             break;
  525.         }
  526.         strcpy(descr, subj);
  527.     }
  528.     }
  529.  
  530.     close_article();
  531.     unlink(tmp_file);
  532.  
  533.     if ( name_fd )
  534.     {
  535.     write(name_fd, &article_name, sizeof(int));
  536.     close(name_fd);
  537.     }
  538. }
  539.