home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume7 / arcnews / arcnews.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-07-02  |  13.1 KB  |  553 lines

  1. /* ta=4 */
  2. /****************************************************************************
  3. *                    a r c n e w s . c                                        *
  4. *                                                                            *
  5. *    Archive news files.                                                        *
  6. *                                                                            *
  7. *    Tony Field     (tony@ajfcal)                                            *
  8. ****************************************************************************/
  9.  
  10. /*    Archive news articles (comp.sources.*).  Construct a file name 
  11.     based on the "Newsgroups:", "Subject:" and "Archive-name:" headers.
  12.  
  13.     usage        arcnews [ -v ] [ file file ...]  archive.path
  14.                           -v = verbose
  15.                           
  16.                 input could be from stdin
  17.  
  18.         eg:        arcnews /usr/spool/lbgm/[A-Z]* /u/archive
  19.  
  20.     MUST BE SETUID ROOT to work in news forward environment.
  21.                 (i.e.  chmod 4711 arcnews)
  22.                 
  23.     EXIT CODES:        0 = successful operation
  24.                    >0 = error of some form has been noted  (usually 2)
  25.  
  26. NOTE:    Modify the list of "subject_line_groups[]" for those moderated
  27.         groups that use an archive file name as the first word of the
  28.         Subject: line.
  29.  
  30. BUGS:    maybe arcnews should quit when it finds the first error.
  31. */
  32.  
  33. #include <stdio.h>
  34. #include <errno.h>
  35. #include <sys/types.h>
  36. #include <sys/stat.h>
  37.  
  38. #define        NLINES        20        /*    number of lines to search for
  39.                                     Newsgroups:, Subject: Archive-name:
  40.                                 */
  41.  
  42. #define IS_FILE        0            /*    file type from ftype()    */
  43. #define    IS_DIR        1
  44. #define    IS_SPECIAL    2
  45. #define    IS_ERROR    -1
  46.  
  47.  
  48. /*    construct the list of groups that use the first word of Subject:
  49.                                               ~~~~~~~~~~~~~~~~~~~~~~
  50. */
  51. char    *subject_line_groups[] =
  52.         {    "comp.doc",
  53.             "comp.doc.techreports",
  54.             (char *) NULL                        /*    <--- end of list    */
  55.         } ;
  56.  
  57. char *first_lines[NLINES];
  58. int        nlines;
  59. int        nfiles;
  60. char    pgm_name[200];
  61.  
  62. extern int errno;
  63. int        rc = 0;
  64.  
  65. main (argc, argv)
  66. int        argc;
  67. char    *argv[];
  68. {    int        i, j, c;
  69.     FILE    *fp;
  70.     int        verbose = 0;
  71.     int        nfiles = 0;
  72.     char    arc_dir[200];
  73.     extern char *optarg;
  74.     extern int    optind;
  75.     char     amon[10], alt_fname[50], t[50];
  76.     int        day, year, pid;
  77.     unsigned short    guid, geuid, getuid(), geteuid(), getegid();
  78.  
  79.  
  80.     strcpy (pgm_name, argv[0]);
  81.     fname_only (pgm_name);
  82.  
  83.     if (argc == 1)
  84.         usage ();
  85.  
  86.     /*    we really want to say:
  87.                 setuid(geteuid());
  88.                 setgid(getegid());
  89.         but Xenix get upset unless getuid() == root
  90.     */
  91.     guid = getuid();
  92.     geuid = geteuid();
  93.     if (guid != geuid)
  94.     {
  95.         setuid(geuid);
  96.         setgid(getegid());
  97.         if (geuid != getuid())
  98.         {    fprintf (stderr, "%s: must be setuid root\n", pgm_name);
  99.             exit (1);
  100.         }
  101.     }
  102.  
  103.     while ((c = getopt(argc, argv, "v")) != -1)
  104.     {    switch (c)
  105.         {
  106.         case 'v':
  107.             verbose = 1;
  108.             break;
  109.         
  110.         default:
  111.             usage ();
  112.         }
  113.     }
  114.  
  115.     strcpy (arc_dir, argv[--argc]);
  116.  
  117.     if (ftype (arc_dir) != IS_DIR)
  118.     {    fprintf (stderr, "%s: %s is not an valid directory\n", pgm_name, arc_dir);
  119.         exit (2);
  120.     }
  121.     if (optind >= argc)
  122.     {    /* make a file name as  YYMMMDD.PID.  eg.  89Jan01.1234 */
  123.         get_time (t);
  124.         sscanf (t, "%*s%s%d%*s%d", amon, &day, &year);
  125.         pid = getpid ();
  126.         sprintf (alt_fname, "%d%s%02d.%d",year % 100, amon, day, pid);
  127.         arc_file (arc_dir, alt_fname, verbose, "stdin");
  128.     }
  129.     else
  130.     {    for (i = optind;  i < argc;  i++)
  131.         {    arc_file (arc_dir, argv[i], verbose, NULL);
  132.         }
  133.     }
  134.     exit (rc);
  135. }
  136.  
  137.  
  138. /****************************************************************************
  139. *    arc_file()                                                                *
  140. *    archive the given file name                                                *
  141. ****************************************************************************/
  142.  
  143. arc_file (arc_dir, fn, verbose, alt_fname)
  144. char    *fn;            /*    name of file to archive                            */
  145. char    *arc_dir;        /*    name of archive directory                        */
  146. char    *alt_fname;        /*    NULL if file name specified or "stdin"            */
  147. int        verbose;        /*    1 = display operation,  0 = quiet mode            */
  148. {    int        i, j, c;
  149.     FILE    *fp;
  150.     char    line[1000], cline[1000];
  151.     char    *words[200];
  152.     int        nwords, trc;
  153.     char    arc_name[200], archive[200];
  154.     char    filename[200], dirname[200], subject[200], group[200];
  155.     int        nblanks;
  156.     int        have_group, have_subject, have_archive, sub_line;
  157.  
  158.  
  159.     trc = 0;
  160.  
  161.     if (alt_fname == NULL  &&  ftype (fn) != IS_FILE)
  162.         return (-1);
  163.  
  164.     strcpy (archive, fn);
  165.     fname_only (archive);
  166.     if (strcmp (archive, "save.log") == 0)        /*    ignore save.log    */
  167.         return (-1);
  168.     
  169.     if (alt_fname)
  170.         fp = stdin;
  171.     else if ((fp = fopen (fn, "r")) == NULL)
  172.     {    rc |= 2;
  173.         return (-1); 
  174.     }
  175.  
  176.     archive[0] = '\0';
  177.     *subject = '\0';
  178.     nlines = nblanks = 0;
  179.     have_group = have_subject = have_archive = 0;
  180.     while (nlines < NLINES  &&  fgets (line, 999, fp))            /*    get header lines    */
  181.     {
  182.         first_lines[nlines] = (char *) malloc (strlen (line) + 1);
  183.         strcpy (first_lines[nlines], line);
  184.         nlines++;
  185.  
  186.         strcpy (cline, line);
  187.         nwords = getwords (cline, words);
  188.         if (nwords)
  189.         {    if (strcmp (words[0], "Newsgroups:") == 0)
  190.             {    have_group = 1;
  191.                 strcpy (archive, arc_dir);
  192.                 strcat (archive, "/");
  193.                 strcat (archive, words[1]);
  194.                 strcpy (group,   words[1]);
  195.             }
  196.             else if (strcmp (words[0], "Subject:") == 0)
  197.             {    if (nwords >= 2)
  198.                 {    strcpy (subject, words[1]);
  199.                     trim (subject);                    /* remove ":" */
  200.                     have_subject = 1;
  201.                 }
  202.                 else *subject = '\0';
  203.             }
  204.             else if (strcmp (words[0], "Archive-name:") == 0)
  205.             {    have_archive = 1;
  206.                 strcpy (arc_name, words[1]);
  207.             }
  208.         }
  209.         else if (++nblanks == 2)            /*    quit if 2 blank lines    */
  210.             break;
  211.     }
  212.  
  213.     if (have_group == 0)                    /* invalid news file        */
  214.     {    if (fp != stdin)
  215.             fclose (fp);
  216.         return (-1);
  217.     }
  218.  
  219.     if (have_archive)
  220.     {    /*    have an Archive-name: header specified.
  221.             sometimes R$alz uses /dev/null for notes.
  222.             if so, use the Subject: line for the file name.
  223.          */
  224.         if (arc_name[0] ==  '/')
  225.         {    if (have_subject  &&  *subject)
  226.                 strcpy (arc_name, subject);    /*    use subject as file name */
  227.             else
  228.                 strcpy (arc_name, fn);    /*     no subject, use original file name */
  229.             fname_only (arc_name);
  230.         }
  231.     }
  232.     else
  233.     {    /*    subject_line_groups[] get file name from Subject:    */
  234.     
  235.         for (j = sub_line = 0;  subject_line_groups[j] != NULL;  j++)
  236.         {    if (sub_line = (strcmp (group, subject_line_groups[j]) == 0))
  237.                 break;
  238.         }
  239.         if (sub_line   &&  have_subject  &&  *subject)
  240.             strcpy (arc_name, subject);
  241.         else
  242.         {    /*    all other groups use original file name    */
  243.             strcpy (arc_name, fn);    
  244.             fname_only (arc_name);
  245.         }
  246.     }
  247.  
  248.     /*    now make the path to the file and actually do a copy */
  249.  
  250.     names (archive, arc_name, filename, dirname);
  251.     if (makepath (dirname) == 0)
  252.     {
  253.         trc = copyfile (fp, filename);
  254.         if (verbose  ||  trc)
  255.         {
  256.             fprintf (stderr, "%s %s -> %s\n", pgm_name, fn, filename);
  257.             if (trc)
  258.                 fprintf (stderr, "--------^^^-- copy failed\n");
  259.         }
  260.         rc |= trc;
  261.         nfiles++;
  262.     }
  263.     else
  264.     {    fprintf (stderr, "%s: cannot create directory %s\n", pgm_name, dirname);
  265.         rc |= 2;
  266.         trc = 1;
  267.     }
  268.     if (trc)
  269.     {    fputs ("---> header of offending file:\n", stderr);
  270.         for (j = 0;  j < nlines;  j++)
  271.             if (first_lines[j][0] > ' ')
  272.                 fprintf (stderr, "\t%s", first_lines[j]);
  273.             else
  274.                 break;
  275.     }
  276.     if (fp != stdin)
  277.         fclose (fp);
  278.     for (j = 0;  j < nlines;  j++)
  279.         free (first_lines[j]);
  280.     return (trc);
  281. }
  282.  
  283.  
  284. /****************************************************************************
  285. *    name()                                                                    *
  286. *    given a archive name and program id, return the full file name and        *
  287. *    the path only.                                                            *
  288. ****************************************************************************/
  289.  
  290. names (archive, arc_name, filename, dirname)
  291. char    *archive;        /*    in: such as /u/archive/comp.sources.unix        */
  292. char    *arc_name;        /*    in: such as program/part01                        */
  293. char    *filename;        /*    ret: /u/archive/comp/sources/unix/program/part01*/
  294. char    *dirname;        /*    ret: /u/archive/comp/sources/unix/program        */
  295. {    char    *c, *strrchr();
  296.  
  297.     strcpy (filename, archive);
  298.     dots (filename);                /*    change a.b.c to a/b/c  */
  299.     strcat (filename, "/");
  300.     strcat (filename, arc_name);    /*    arc_name may be a path */
  301.  
  302.     strcpy (dirname, filename);
  303.     c = strrchr (dirname, '/');
  304.     *c = '\0';
  305. }
  306.  
  307. /****************************************************************************
  308. *    dots()                                                                    *
  309. *    translate . to /                                                        *
  310. ****************************************************************************/
  311.  
  312. dots (c)
  313. char    *c;
  314. {
  315.     while (*c)
  316.     {    if (*c == '.')
  317.             *c = '/';
  318.         c++;
  319.     }
  320. }
  321.  
  322. /****************************************************************************
  323. *    fname_only ()                                                            *
  324. *    replace the path with just the file name (right end of the path)        *
  325. ****************************************************************************/
  326.  
  327. fname_only (fname)
  328. char     *fname;
  329. {    char    c[500], *strrchr(), *tc;
  330.  
  331.     if ((tc = strrchr (fname, '/')) != NULL)
  332.     {    strcpy (c, tc + 1);
  333.         strcpy (fname, c);
  334.     }
  335. }
  336.  
  337. /****************************************************************************
  338. *    getwords ()                                                                *
  339. *    given a string 's', return pointers in 'words' that point to the        *
  340. *    beginning of each word.  's' is modified with null word terminators.    *
  341. *    Function return is the number of words found                            *
  342. ****************************************************************************/
  343.  
  344. getwords(s, words)
  345. char    *s;                /*    string to scan                                    */
  346. char    *words[];        /*    vector of words found.                            */
  347. {    int    nwords = 0;
  348.  
  349.     while (*s)
  350.     {    if (*s > ' ')
  351.         {    words[nwords++] = s;
  352.             while (*s  &&  *s > ' '  &&  *s != ',')
  353.                 s++;
  354.             if (*s == '\0')
  355.                 return (nwords);
  356.             *s++ = '\0';
  357.         }
  358.         else
  359.             s++;
  360.     }
  361.     return (nwords);
  362. }
  363.  
  364. /****************************************************************************
  365. *    trim()                                                                    *
  366. *    remove trailing ':' marks.  (eg subject:)                                *
  367. ****************************************************************************/
  368.  
  369. trim (s)
  370. char    *s;
  371. {
  372.     int    n;
  373.     
  374.     n = strlen (s);
  375.     if (s[n-1] == ':')
  376.         s[n-1] = '\0';
  377. }
  378.  
  379. /****************************************************************************
  380. *    makepath ()                                                                *
  381. *    for a given path specification, make all directores needed to ensure    *
  382. *    that the path is valid.                                                    *
  383. *    If the path is valid, then return 0.  If the path cannot be made,        *
  384. *    then return 2.                                                            *
  385. ****************************************************************************/
  386.  
  387. makepath (path_name)
  388. char    *path_name;
  389. {    char    path[500], p[500], *w[50], cmd[500];
  390.     char    *c, *strrchr();
  391.     int        i, j, n;
  392.     
  393.     strcpy (path, path_name);
  394.     if ((c = strrchr (path, '/')) == NULL)
  395.         return (-1);
  396.     
  397.     /*    if path exists, the return now */
  398.  
  399.     if (access (path, 7) == 0)
  400.         return (0);
  401.  
  402.     /*    path does not exist.  Start from left side of path and
  403.         build towards the right as necessary
  404.     */
  405.     c = path;
  406.     if (*c != '/')                /* get each path directory */
  407.     {    n = 0;
  408.         w[n++] = path;
  409.     }
  410.     else
  411.         n = 0;
  412.     while (*c)
  413.     {    if (*c == '/')
  414.         {    *c++ = '\0';
  415.             w[n++] = c;
  416.         }
  417.         else
  418.             c++;
  419.     }
  420.  
  421.     for (i = 0;  i < n;  i++)        /* try /a, /a/b, /a/b/c, etc. in order */
  422.     {    p[0] = '\0';
  423.         if (*path_name == '/')
  424.             strcat (p, "/");
  425.         for (j = 0;  j <= i;  j++)
  426.         {    strcat (p, w[j]);
  427.             if (j == i)
  428.                 break;
  429.             strcat (p, "/");
  430.         }
  431.         switch (ftype (p))
  432.         {
  433.         case IS_ERROR:
  434.                    if (errno == ENOENT)                /*    no entry, build it    */
  435.                 {    sprintf (cmd, "mkdir %s", p);
  436.                     j = system (cmd);
  437.                     chmod (p, 0775);
  438.                     rc |= j;
  439.                     if (j)
  440.                         return (j);
  441.                 }
  442.                 else
  443.                 {    rc |= 2;
  444.                     return (2);
  445.                 }
  446.                 break;
  447.  
  448.         case IS_DIR:                     /*    directory already exists, continue*/
  449.                 break;
  450.         
  451.         default:                        /*    file with same name. bad.        */
  452.                 rc |= 2;
  453.                 return (2);
  454.                 break;
  455.         }
  456.     }
  457.     return (0);
  458. }
  459.  
  460. /****************************************************************************
  461. *    copyfile()                                                                *
  462. *    If a copy problem happens, return -1,  otherwise return 0                *
  463. ****************************************************************************/
  464.  
  465. copyfile (fp_in, out_file)
  466. FILE    *fp_in;
  467. char    *out_file;
  468. {
  469.     FILE    *fp_out;
  470.     int        r_many, w_many, rc, i;
  471.     char    buf[2000];
  472.  
  473.     if ((fp_out = fopen (out_file, "w")) == NULL)
  474.         return (2);
  475.  
  476.     for (i = 0;  i < nlines;  i++)
  477.         fputs (first_lines[i], fp_out);
  478.  
  479.     rc = 0;
  480.     while (r_many = fread (buf, 1, 2000, fp_in))
  481.     {    w_many = fwrite (buf, 1, r_many, fp_out);
  482.         if (w_many != r_many)
  483.         {    rc = 2;
  484.             break;
  485.         }
  486.         if (r_many < 2000)
  487.             break;
  488.     }
  489.     fclose (fp_out);
  490.     chmod (out_file, 0664);
  491.     
  492. #ifdef COMPRESSION
  493.     /*    compress the file just received.  should use zoo and 
  494.         create an library file
  495.     */
  496.     if (rc == 0)
  497.     {    sprintf (buf, "/local/bin/compress %s", out_file);
  498.         system (buf);
  499.     }
  500. #endif
  501.     return (rc);
  502. }
  503.  
  504.  
  505. /****************************************************************************
  506. *    ftype (name)                                                            *
  507. *                                                                            *
  508. *    What is "name"?                                                            *
  509. *        0 = file,  1 = directory,   2 = special file,   -1 = error            *
  510. ****************************************************************************/
  511.  
  512. ftype (name)
  513. char    *name;
  514. {
  515.     struct stat buf;
  516.  
  517.     if (stat (name, &buf) != 0)
  518.         return (IS_ERROR);
  519.     if ((buf.st_mode & S_IFMT) == S_IFDIR)
  520.         return (IS_DIR);
  521.     if ((buf.st_mode & S_IFMT) == S_IFREG)
  522.         return (IS_FILE);
  523.     return (IS_SPECIAL);
  524. }
  525.  
  526.  
  527. /****************************************************************************
  528. *    time_name()                                                                *
  529. ****************************************************************************/
  530.  
  531. get_time (t)
  532. char    *t;
  533. {
  534.     long    n_time, time ();
  535.     char    *x_time, *cc, *strchr(), *ctime();
  536.     
  537.     n_time = time ((long *) 0L);        /* get time         */
  538.     x_time = (char *) ctime (&n_time);    /* convert ascii     */
  539.     if ((cc = strchr (x_time, '\n')) != NULL)
  540.         *cc = '\0';
  541.     strcpy (t, x_time);
  542. }
  543.  
  544. /****************************************************************************
  545. *    usage()                                                                    *
  546. ****************************************************************************/
  547.  
  548. usage ()
  549. {    fprintf (stderr, "usage:   %s [ -v ] [ file file ...]  archive.path\n\n", pgm_name);
  550.     fprintf (stderr, "e.g.:    %s /usr/spool/lbgm/src/[A-Z]* /u/archive\n\n",pgm_name);
  551.     exit (2);
  552. }
  553.