home *** CD-ROM | disk | FTP | other *** search
/ back2roots/padua / padua.7z / padua / uucp / duucp-1.17 / AU-117b4-src.lha / src / news / rnews.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-27  |  24.6 KB  |  1,204 lines

  1. /*#define PROCDEBUG*/
  2. /*
  3. **  RNEWS.C
  4. **
  5. **  RNEWS [file]    (if not specified, stdin is used)
  6. **
  7. **  Copyright 1988 by William Loftus.  All rights reserved.
  8. **  Extensive Changes Copyright 1990 by Matthew Dillon. All Rights Reserved.
  9. **  Extensive Changes Copyright 1993 by Michael B. Smith. All Rights Reserved.
  10. **
  11. **  MUST be compiled with 32-bit integers.
  12. **
  13. **  This is RNEWS. This is equivalent to the C-News (and B-News) RELAYNEWS
  14. **  program, but for AmigaUUCP.
  15. **
  16. **  Most current news systems will UUCP a file to this system containing
  17. **  multiple articles. That file will be compressed, using the standard
  18. **  compress (1) algorithm. *Sometimes* this file will have "#! cunbatch\n"
  19. **  as the first 12 characters in plain text.
  20. **
  21. **  RNEWS will decompress the input file, when needed, and unbatch the
  22. **  individual articles into their appropriate newsgroup locations.
  23. **
  24. **  Each newsgroup directory has a .next file which indicates the next
  25. **  article number to write.  This number *used to* roll over at 32767.
  26. **  No longer.
  27. **
  28. **  The '-1' switch causes only the first valid newsgroup in a newsgroups
  29. **  header to get a copy of the article.
  30. **
  31. **  The '-D <number>' switch specifies the debug level.
  32. **
  33. **  The '-h' switch suppresses the use of hardlinks on 2.04 and above.
  34. **
  35. **  The '-n' switch means to "NOT" actually store articles (useful in
  36. **  debugging).
  37. **
  38. **  The '-o' switch causes the generation of '.overview' files for
  39. **  each newsgroup.
  40. **
  41. **  The '-p' switch indicates that PostNews is calling us -- that we don't
  42. **  have a compressed batch, regardless of the setting of '-u'.
  43. **
  44. **  The '-t <dir>' switch specifies the directory where all temporary files
  45. **  should go (NORMALLY UUNews: is used by this program). When set to use
  46. **  ram disk, RNEWS will work much faster....
  47. **
  48. **  The '-u <uncomp>' switch specifies the decompresser to use.
  49. **
  50. **  The '-x' switch sets the LogLevel used by ulog ().
  51. */
  52.  
  53. #ifdef PROCDEBUG
  54. #define PROC(x)     const char *_proc = x; printf ("%s: enter\n", _proc)
  55. #define D(x)        printf ("%s: ", _proc); printf x
  56. #else
  57. #define PROC(x)
  58. #define D(x)
  59. #endif
  60.  
  61. #include <exec/types.h>
  62. #include <exec/ports.h>
  63. #include <dos/dosextens.h>
  64. #include <dos/dostags.h>
  65. #include <clib/exec_protos.h>
  66. #include <clib/dos_protos.h>
  67. #include <stdio.h>
  68. #include <string.h>
  69. #include <stdlib.h>
  70. #include <errno.h>
  71. #include <fcntl.h>
  72. #include <ctype.h>
  73.  
  74. #include <log.h>
  75. #include <ForwardNews.h>    /* MRR, 09-23-90. */
  76. #include <owndevunit.h>
  77.  
  78. #include "version.h"
  79. #include "config.h"
  80.  
  81. IDENT (".28");
  82.  
  83. typedef struct group {
  84.     struct group *next;
  85.     long actual;    /*  actual bytes    */
  86.     long bytes;     /*  uncompressed bytes    */
  87.     long articles;    /*  articles        */
  88.     char name [1];
  89. } group;
  90.  
  91. void    myexit (void);
  92. int    dofile (FILE *, int);
  93. int    unbatch (FILE *, int);
  94. int    CopyToGroup (char *, int, char *, char **);
  95. int    LinkToGroup (char *, char *);
  96. void    initgroups (void);
  97. group    *ValidGroup (const char *);
  98. void    HandleSequenceNumber (char *, int *);
  99. int    rm (const char *);
  100.  
  101. static group
  102.     groups;
  103.  
  104. static char
  105.     Buf [256],
  106.     FBuf [256],
  107.     TmpBuf [4096];         /*  at least 1K */
  108.  
  109. char
  110.     HomeDir [256],
  111.     *NewsDir = NULL,
  112.     *TempDir = NULL,
  113.     *UnComp  = NULL;
  114. int
  115.     PostNews     = 0,        /* call is from PostNews */
  116.     JunkFlag_g   = 0,
  117.     _bufsiz      = 16384,        /* for DICE's stdio */
  118.     DDebug         = 0,
  119.     UHaveNews    = 0,
  120.     DummyOpt     = 0,
  121.     LinksOk      = 0,
  122.     OnlyOne      = 0,
  123.     NewsOverview = 0;
  124.  
  125. struct Library
  126.     *OwnDevUnitBase = NULL;
  127.  
  128. static const char
  129.     news_rdy [] = { "T:NewsRdy" };
  130.  
  131. #define PROG_NAME "RNews"
  132.  
  133. /*
  134. **  main ()
  135. **
  136. **  The main loop.  Note that OwnDevUnit must be valid for ulog() to work!
  137. */
  138.  
  139. int
  140. main (int ac, char **av)
  141. {
  142.     int
  143.         err = 0,
  144.         siz = 0,
  145.         ret = 0,
  146.         i,
  147.         delFile = 0;
  148.     FILE
  149.         *fi;
  150.     static const char
  151.         odu_name [] = { ODU_NAME };
  152.     char
  153.         *newsFile = NULL; /* may, or may not, be malloc'ed -- do not free */
  154.                   /* (compiler exit routine will take care of it) */
  155.  
  156.     PROC ("main");
  157.  
  158.     LogProgram = PROG_NAME;
  159.  
  160.     memset (HomeDir, '\0', sizeof HomeDir);
  161.     atexit (myexit);
  162.  
  163.     /*
  164.     **    File Locking (ulog, LockFile, etc...)
  165.     */
  166.  
  167.     if ((OwnDevUnitBase = OpenLibrary (odu_name, 0)) == NULL) {
  168.         fprintf (stderr, "Unable to open %s\n", odu_name);
  169.         D (("No %s\n", odu_name));
  170.         exit (30);
  171.     }
  172.  
  173.     /*
  174.     **    Hardlinks are possible for 2.04 or later.  NOTE!  Do not use
  175.     **    hardlinks on anything less than V37 !!
  176.     */
  177.  
  178.     if (DOSBase->dl_lib.lib_Version >= 37)
  179.         LinksOk = 1;
  180.  
  181.     getcwd (HomeDir, sizeof (HomeDir));
  182.     NewsDir = GetConfigDir (UUNEWS);
  183.  
  184.     if (ac == 0) {
  185.         ulog (-1, "cannot call RNews from workbench!");
  186.         D (("called from WB?\n"));
  187.         exit (30);
  188.     }
  189.  
  190.     for (i = 1; i < ac; ++i) {
  191.         char
  192.             *ptr = av [i];
  193.  
  194.         if (*ptr != '-') {
  195.             if (newsFile)
  196.                 ulog (-1, "Extraneous news file %s ignored", ptr);
  197.             else
  198.                 newsFile = strdup (ptr);
  199.             continue;
  200.         }
  201.  
  202.         switch (ptr[1]) {
  203.             case 'd': /* FIXME - use same as 'u' */
  204.             case 'D':
  205.                 DDebug = (ptr[2]) ? atoi(ptr + 2) : 1;
  206.                 break;
  207.             case 'h':
  208.                 LinksOk = 0;
  209.                 break;
  210.             case 'n':
  211.                 DummyOpt = 1;
  212.                 break;
  213.             case 'o':
  214.                 NewsOverview = 1;
  215.                 break;
  216.             case 'p':
  217.                 PostNews = 1;
  218.                 break;
  219.             case 't':
  220.                 if (ptr [2])
  221.                     TempDir = strdup (&ptr[2]);
  222.                 else
  223.                     TempDir = strdup (av [++i]);
  224.                 break;
  225.             case 'u':
  226.                 if (DOSBase->dl_lib.lib_Version < 37) {
  227.                     fprintf (stderr, "Can't use -u/-d before 2.04\n");
  228.                     ulog (-1, "Can't use -u/-d before 2.04\n");
  229.                     exit (30);
  230.                 }
  231.                 if (ptr [2])
  232.                     UnComp = strdup (&ptr[2]);
  233.                 else
  234.                     UnComp = strdup (av [++i]);
  235.                 break;
  236.             case 'x':
  237.                 LogLevel = atoi (ptr + 2);
  238.                 break;
  239.             case '1':
  240.                 OnlyOne = 1;
  241.                 break;
  242.             default:
  243.                 fprintf (stderr, "unknown flag '%s' ignored\n", ptr);
  244.                 break;
  245.         }
  246.     }
  247.  
  248.     initgroups ();
  249.     if (GetForwardingInfo ()) {     /* MRR, 09-23-90. */
  250.         ulog (-1, "Failed to get news forwarding information - abort.");
  251.         D (("exit, can't GetForwardingInfo\n"));
  252.         exit (30);
  253.     }
  254.  
  255.     /*
  256.     **    uncompress file, if told to do so..
  257.     */
  258.  
  259.     if (UnComp && PostNews == 0) {
  260.         BPTR
  261.             outfh,
  262.             fh;
  263.         int
  264.             tot = 0,
  265.             i;
  266.         char
  267.             *ptr;
  268.  
  269.         if (newsFile) {
  270.             fh = Open (newsFile, MODE_OLDFILE);
  271.         }
  272.         else {
  273.             fh = Input ();
  274.         }
  275.  
  276.         sprintf (FBuf, "%s%s",
  277.             TempDir ? TempDir : "",
  278.             TmpFileName ("news-comp"));
  279.         D (("temporary compressed file %s\n", FBuf));
  280.  
  281.         outfh = Open (FBuf, MODE_READWRITE);
  282.         if (!outfh) {
  283.             err = IoErr ();
  284.             D (("couldn't open compressed %s (%ld)\n", FBuf, err));
  285.             ulog (-1, "couldn't open for decompression %s (%ld)\n", FBuf, err);
  286.             if (newsFile)
  287.                 Close (fh);
  288.             exit (30);
  289.         }
  290.  
  291.         if ((i = Read (fh, TmpBuf, sizeof (TmpBuf))) > 0) {
  292.             if (TmpBuf [0] == '#' && TmpBuf [1] == '!') {
  293.                 if (LogLevel >= 1) {
  294.                     ptr = strchr (TmpBuf, '\n');
  295.                     memset (Buf, '\0', sizeof Buf);
  296.                     strncpy (Buf, TmpBuf, (int) (ptr - TmpBuf));
  297.                     ulog (1, "compression keyword: %s", Buf);
  298.                 }
  299.                 ptr = TmpBuf;
  300.                 while (*ptr != '\n') {
  301.                     ptr++;
  302.                     i--;
  303.                 }
  304.                 ptr++;
  305.                 i--;
  306.             }
  307.             else
  308.                 ptr = TmpBuf;
  309.  
  310.             Write (outfh, ptr, i);
  311.             tot = i;
  312.         }
  313.  
  314.         while ((i = Read (fh, TmpBuf, sizeof (TmpBuf))) > 0) {
  315.             Write (outfh, TmpBuf, i);
  316.             tot += i;
  317.         }
  318.  
  319.         ulog (5, "Copied batch of %ld bytes", tot);
  320.  
  321.         if (!Flush (outfh)) {  /* this Flush() is CRITICAL */
  322.             ulog (0, "Flush failure %ld", IoErr ());
  323.         }
  324.  
  325.         if (Seek (outfh, 0, OFFSET_BEGINNING) < 0) {
  326.             ulog (0, "Seek failure %ld", IoErr ());
  327.         }
  328.  
  329.         if (newsFile) {
  330.             Close (fh);
  331.         }
  332.  
  333.         sprintf (TmpBuf, "%s%s",
  334.             TempDir ? TempDir : "",
  335.             TmpFileName ("news-uncomp"));
  336.         D (("temporary uncompressed file %s\n", TmpBuf));
  337.  
  338.         fh = Open (TmpBuf, MODE_NEWFILE);
  339.         if (!fh) {
  340.             err = IoErr ();
  341.             D (("couldn't open decompress %s (IoErr %ld)\n", TmpBuf, err));
  342.             ulog (-1, "couldn't open decompress %s (IoErr %ld)", TmpBuf, err);
  343.             Close (outfh);
  344.             DeleteFile (FBuf);
  345.             exit (30);
  346.         }
  347.  
  348.         i = SystemTags (UnComp,        /* only in 2.04+ */
  349.                 SYS_Asynch,     FALSE,
  350.                 SYS_Input,        outfh,
  351.                 SYS_Output,     fh,
  352.                 SYS_UserShell,  TRUE,
  353.                 NP_Cli,        TRUE,
  354.                 NP_StackSize,   16384,
  355.                 NP_CloseInput,  FALSE,
  356.                 NP_CloseOutput, FALSE,
  357.                 TAG_DONE);
  358.  
  359.         /* do cleanup */
  360.         err = IoErr ();
  361.         Close (outfh);
  362.         Close (fh);
  363.         DeleteFile (FBuf);
  364.         strcpy (FBuf, TmpBuf);
  365.  
  366.         /* check failure */
  367.         if (i) {
  368.             D (("couldn't run %s (rslt %ld, IoErr %ld), assuming local\n", UnComp, i, err));
  369.             ulog (1, "couldn't run %s (rslt %ld, IoErr %ld), assuming local", UnComp, i, err);
  370.             DeleteFile (FBuf);
  371.         }
  372.         else {
  373.             ulog (1, "Decompression by '%s' successful", UnComp);
  374.             newsFile = FBuf; /* so it's deleted later */
  375.             delFile = 1;
  376.         }
  377.     }
  378.  
  379.     /*
  380.     **    open input file
  381.     */
  382.  
  383.     if (newsFile) {
  384.         __aligned struct FileInfoBlock
  385.             fib;
  386.         BPTR
  387.             lock;
  388.  
  389.         /*    we were given the name of a news file */
  390.         if ((fi = fopen (newsFile, "r")) == NULL) {
  391.             ulog (-1, "Unable to open %s", newsFile);
  392.             exit (30);
  393.         }
  394.         D (("input file %s\n", newsFile));
  395.  
  396.         if (lock = Lock (newsFile, SHARED_LOCK))
  397.             if (Examine (lock, &fib)) {
  398.                 siz = fib.fib_Size;
  399.                 UnLock (lock);
  400.             }
  401.             else {
  402.                 err = IoErr ();
  403.                 UnLock (lock);
  404.                 ulog (-1, "Can't Examine() %s (%ld)", newsFile, err);
  405.                 fclose (fi);
  406.                 exit (30);
  407.             }
  408.         else {
  409.             err = IoErr ();
  410.             ulog (-1, "Can't Lock() %s (%ld)", newsFile, err);
  411.             fclose (fi);
  412.             exit (30);
  413.         }
  414.     }
  415.     else {
  416.         /*    the input file is stdin   */
  417.         /*    ensure it is seek()-able */
  418.         if (fseek (stdin, 0L, 2) == 0) {
  419.             /* seekable */
  420.             fi = stdin;
  421.             siz = ftell (fi);
  422.             fseek (fi, 0L, 0);
  423.         }
  424.         else {
  425.             /* not seekable */
  426.             newsFile = malloc (255);
  427.             if (newsFile == NULL) {
  428.                 ulog (-1, "malloc() : out of memory");
  429.                 exit (30);
  430.             }
  431.  
  432.             sprintf (newsFile, "%s%s",
  433.             TempDir ? TempDir : "",
  434.             TmpFileName ("news-work"));
  435.             D (("temporary file %s\n", newsFile));
  436.  
  437.             if (fi = fopen (newsFile, "w+")) {
  438.                 int n;
  439.                 int err = 0;
  440.  
  441.                 while ((n = fread (TmpBuf, 1, sizeof (TmpBuf), stdin)) > 0) {
  442.                     int rslt = fwrite (TmpBuf, 1, n, fi);
  443.  
  444.                     siz += n;
  445.                     if (rslt != n) {
  446.                         ulog (-1, "Write failed file %s, errno %ld", newsFile, errno);
  447.                         err = 1;
  448.                         break;
  449.                     }
  450.                 }
  451.  
  452.                 if (err) {
  453.                     fclose (fi);
  454.                     rm (newsFile);
  455.                     exit (30);
  456.                 }
  457.  
  458.                 fseek (fi, 0L, 0);
  459.                 delFile = 1;
  460.             }
  461.             else {
  462.                 ulog (-1, "Unable to create scratch file %s", newsFile);
  463.                 exit (30);
  464.             }
  465.         }
  466.     }
  467.  
  468.     /*
  469.     **    If any news group in a "Newsgroups:" list exists here the
  470.     **    article is not stored to junk.    If no news groups in the list
  471.     **    exists the article is stored to junk.
  472.     **
  473.     **    The JunkSave configuration entry can be used to disable the
  474.     **    saving of articles to junk.
  475.     */
  476.  
  477.     {
  478.     char
  479.         *ptr = GetConfig (JUNKSAVE, "Y");
  480.  
  481.     if (ptr)
  482.         if (ptr[0] == 'y' || ptr[0] == 'Y')
  483.             JunkFlag_g = 1;
  484.         else
  485.             JunkFlag_g = -1;
  486.     else
  487.         JunkFlag_g = -1;
  488.     }
  489.  
  490.     D (("chdir (%s)\n", NewsDir));
  491.     if (chdir (NewsDir) != 0) {
  492.         ulog (-1, "NewsDir %s doesn't exist; quitting", NewsDir);
  493.         exit (30);
  494.     }
  495.  
  496.     ret = dofile (fi, siz);
  497.     fclose (fi);
  498.  
  499.     D (("chdir (%s)\n", HomeDir));
  500.     if (chdir (HomeDir)) {
  501.         ulog (-1, "Can't chdir %s", HomeDir);
  502.         D (("chdir (%s) failed\n", HomeDir));
  503.     }
  504.     HomeDir [0] = '\0';
  505.  
  506.     if (delFile && ret == 0)
  507.         rm (newsFile);
  508.  
  509.     if (UHaveNews) {
  510.         struct MsgPort
  511.             *port;
  512.  
  513.         Forbid ();
  514.         if ((port = FindPort (news_rdy)) && port->mp_SigTask) {
  515.             Signal (port->mp_SigTask, 1 << port->mp_SigBit);
  516.             UHaveNews = 0;
  517.         }
  518.         Permit ();
  519.     }
  520.     if (UHaveNews) {
  521.         int
  522.             fd = open (news_rdy, O_RDONLY);
  523.  
  524.         if (fd < 0) {
  525.             char
  526.                 *cmd;
  527.  
  528.             if (cmd = FindConfig (NEWSREADYCMD)) {
  529.                 fd = open (news_rdy, O_CREAT|O_TRUNC, 0666);
  530.                 if (fd >= 0)
  531.                     close (fd);
  532.                 sprintf (TmpBuf, "Run %s -x %s", cmd, news_rdy);
  533.                 Execute (TmpBuf, 0, 0);
  534.             }
  535.         }
  536.         else {
  537.             close (fd);
  538.         }
  539.     }
  540.  
  541.     D (("exit\n"));
  542.     exit (ret);
  543. }
  544.  
  545. /*
  546. **  processfile
  547. */
  548. int
  549. processfile (FILE *fi, char buf [], int bufsize)
  550. {
  551.     int
  552.         artlen = 0,
  553.         r      = 0,
  554.         res    = 0;
  555.  
  556.     PROC ("processfile");
  557.  
  558.     do {
  559.         D (("-->:%s\n", buf));
  560.  
  561.         if (strncmp (buf, "#! rnews ", 9) == 0) {
  562.             /*
  563.             **  uncompressed batch
  564.             */
  565.  
  566.             artlen = atoi (buf + 9);
  567.             D (("article is current style, len %ld\n", artlen));
  568.             r = unbatch (fi, artlen);
  569.             if (res < r)
  570.                 res = r;
  571.         }
  572.         else {
  573.             /*
  574.             **  an unknown batch header is a fatal error.
  575.             */
  576.             char
  577.                 *p;
  578.  
  579.             D (("unknown batch header '%-20s'\n", buf));
  580.             if (p = strchr (buf, '\n'))
  581.                 *p = '\0';
  582.  
  583.             ulog (-1, "unknown batch header (offset %ld, last artlen %ld) '%-20s'\n", ftell (fi), artlen, buf);
  584.             res = 30;
  585.         }
  586.  
  587.         D (("result %ld\n", res));
  588.         if (res >= 30) {
  589.             /* a fatal error has occured */
  590.             break;
  591.         }
  592.     } while (fgets (buf, bufsize, fi));
  593.  
  594.     return res;
  595. }
  596.  
  597. /*
  598. **  dofile ()
  599. **
  600. **  handle a news file
  601. */
  602.  
  603. int
  604. dofile (FILE *fi, int siz)
  605. {
  606.     FILE
  607.         *f2 = NULL;        /* the file we would switch to */
  608.     unsigned char
  609.         buf [255];           /* input buffer from 'fi' */
  610.     int
  611.         res = 0;           /* our final procedure result */
  612.  
  613.     PROC ("dofile");
  614.  
  615.     if (!fi) {
  616.         ulog (-1, "No file!");
  617.         D (("exit, called without a file\n"));
  618.         return 30;
  619.     }
  620.  
  621.     D (("file size %ld\n", siz));
  622.  
  623.     groups.actual += siz;
  624.  
  625.     if (PostNews) {
  626.         /* local posting */
  627.         res = unbatch (fi, siz);
  628.         goto leave;
  629.     }
  630.  
  631.     /* start out by finding out what kind of file it is... */
  632.     if (!fgets (buf, sizeof (buf), fi)) {
  633.         ulog (0, "Cannot read input file");
  634.         D (("exit, can't get first line\n"));
  635.         return 20;
  636.     }
  637.  
  638.     if (strncmp ("#! rnews ", buf, 9) == 0) {
  639.         D (("batch is uncompressed\n"));
  640.         res = processfile (fi, buf, sizeof (buf));
  641.     }
  642.     else {
  643.         int
  644.             uncompressit = 0;
  645.         static char
  646.             filename [255];
  647.  
  648.         if (strncmp ("#! cunbatch", buf, 11) == 0) {
  649.             /* Definitely a compressed batch of news... */
  650.             uncompressit = 1;
  651.             D (("compressed batch\n"));
  652.         }
  653.         else {
  654.             /* It *may* be a compressed batch of news, without a  */
  655.             /* cunbatch header. Figure it out.              */
  656.  
  657.             if (buf [0] == 0x1f && buf [1] == 0x9d) {
  658.                 /* it has the magic numbers... */
  659.                 uncompressit = 1;
  660.                 D (("compress batch without header\n"));
  661.             }
  662.             else {
  663.                 /* probably a new local post from POSTNEWS */
  664.                 uncompressit = 0;
  665.                 D (("not batch, local post assumed (0x%2x%2x)\n",
  666.                     (buf [0] & 0xff), (buf [1] & 0xff)));
  667.             }
  668.  
  669.             fseek (fi, 0L, 0);
  670.         }
  671.  
  672.         if (uncompressit == 0) {
  673.             res = unbatch (fi, siz);
  674.             goto leave;
  675.         }
  676.  
  677.         sprintf (filename, "%s%s",
  678.             TempDir ? TempDir : "",
  679.             TmpFileName ("news-work"));
  680.         D (("file to decompress to: %s\n", filename));
  681.  
  682.         if (uncompress_to_file (fi, filename) < 0) {
  683.         ulog (-1, "Newsfile Corrupt (%s), file not processed or deleted", filename);
  684.         return 30;
  685.         }
  686.  
  687.         f2 = fopen (filename, "r");
  688.         if (!f2) {
  689.             ulog (-1, "Cannot decompress news (IoErr %ld, errno %ld)\n",
  690.                 IoErr (), errno);
  691.             if (DDebug < 10)
  692.                 rm (filename);
  693. #ifdef PROCDEBUG
  694.             else {
  695.                 D (("should've removed '%s'\n", filename));
  696.             }
  697. #endif
  698.             D (("exit, couldn't open uncompressed file (%s)\n", filename));
  699.             return 20;
  700.         }
  701.  
  702.         if (!fgets (buf, sizeof (buf), f2)) {
  703.             D (("Oops! EOF on f2"));
  704.             res = 99;
  705.         }
  706.         else {
  707.             res = processfile (f2, buf, sizeof (buf));
  708.         }
  709.         fclose (f2);
  710.  
  711.         if (DDebug < 10)
  712.             rm (filename);
  713. #ifdef PROCDEBUG
  714.         else {
  715.             D (("should've removed '%s'\n", filename));
  716.         }
  717. #endif
  718.     }
  719.  
  720. leave:
  721.     if (res)
  722.         ulog (-1, "dofile() error %d", res);
  723.  
  724.     D (("exit, result %ld\n", res));
  725.     return res;
  726. }
  727.  
  728. /*
  729. **  storearticle
  730. **
  731. **    Responsible for actually placing the article into the news tree.
  732. **    Sets hardlinks where appropriate, and stops after the first when
  733. **    requested by the user.
  734. */
  735.  
  736. void
  737. storearticle (char *base, int bytes, char **realFileName)
  738. {
  739.     char
  740.         *grp;
  741.     int
  742.         didFwd = 0; /*    at least one copy has been written to disk  */
  743.  
  744.     ScanHeader (base, "Newsgroups:");
  745.     while (grp = ScanNext ()) {
  746.         group
  747.             *gp;
  748.  
  749.         if (gp = ValidGroup (grp)) {
  750.             ++gp->articles;
  751.             gp->bytes += bytes;
  752.  
  753.             if (didFwd == 0 || LinkToGroup (grp, *realFileName) < 0) {
  754.                 if (CopyToGroup (base, bytes, grp, realFileName) < 0)
  755.                     ulog (-1, "Unable to copy article to group %s", grp);
  756.                 else {
  757.                     didFwd = 1;
  758.                     if (OnlyOne) {
  759.                         ++UHaveNews;
  760.                         break;
  761.                     }
  762.                 }
  763.             }
  764.             ++UHaveNews;
  765.         }
  766.     }
  767.  
  768.     return;
  769. }
  770.  
  771. /*
  772. **  unbatch ()
  773. **
  774. **  Unbatches a news article starting at the current seek position and
  775. **  running for the specified number of bytes.
  776. **
  777. **  The article is stored into the appropriate newsgroup(s) as specified
  778. **  in UULib:NewsGroups, or in the 'junk' directory.  Individual article
  779. **  sequence numbers are kept in a ".next" file in each newsgroup directory.
  780. **
  781. **  Note that realFileName is actually a dot-formatted newsgroup/artno,
  782. **  not a directory hierarchy.
  783. */
  784.  
  785. int
  786. unbatch (FILE *fi, int bytes)
  787. {
  788.     char
  789.         *grp,
  790.         *base = malloc (bytes + 1),
  791.         *realFileName = NULL;
  792.     int
  793.         len = bytes,
  794.         junkFlag;
  795.     static const char
  796.         ctrl_hdr [] = { "Control:" },
  797.         control  [] = { "control" };
  798.  
  799.  
  800.     PROC ("unbatch");
  801.  
  802.     if (bytes <= 0) {
  803.         D (("exit, bytes %ld\n", bytes));
  804.         ulog (-1, "Internal error unbatch (), bytes %ld\n", bytes);
  805.         return 30;
  806.     }
  807.     if (!base) {
  808.         D (("exit, no mem\n"));
  809.         ulog (-1, "Unable to malloc %ld bytes", bytes + 1);
  810.         return 30;
  811.     }
  812.  
  813.     D (("position before fread() %ld\n", ftell (fi)));
  814.     bytes = fread (base, 1, bytes, fi);
  815.     if (bytes <= 0) {
  816.         D (("fread error %ld, errno %ld\n", bytes, errno));
  817.         ulog (-1, "unbatch: read failed %ld", bytes);
  818.         free (base);
  819.         return 30;
  820.     }
  821.     base [bytes] = 0;   /* NULL terminate buffer */
  822.     D (("got %ld bytes, wanted %ld bytes, pos %ld (old pos s/b %ld)\n",
  823.         bytes, len, ftell (fi), ftell (fi) - bytes));
  824.  
  825.     /*
  826.     **  Global statistics
  827.     */
  828.  
  829.     groups.bytes += bytes;
  830.     ++groups.articles;
  831.  
  832.     /*
  833.     **  See if this is a Control message
  834.     **  If so, and it's a valid newsgroup, then file it in UUNews:Control
  835.     */
  836.  
  837.     if (ScanHeader (base, ctrl_hdr) == 0) {
  838.         if (ValidGroup (control)) {
  839.             if (CopyToGroup (base, bytes, control, &realFileName) < 0)
  840.                 ulog (-1, "Unable to file %s message", control);
  841.             else {
  842.                 char
  843.                     *p = strstr (base, ctrl_hdr),
  844.                     *s = strchr (p, '\n'),
  845.                     c  = *s;
  846.  
  847.                 *s = '\0';
  848.                 ulog (-1, "Control message: %s", p);
  849.                 *s = c;
  850.                 ++UHaveNews;
  851.                 /* FIXME : PROCESS control messages here! */
  852.             }
  853.             goto do_other_stuff;
  854.         }
  855.     }
  856.  
  857.     /*
  858.     **  See if this article goes in junk
  859.     */
  860.  
  861.     junkFlag = JunkFlag_g;
  862.  
  863.     ScanHeader (base, "Newsgroups:");
  864.     while (grp = ScanNext ()) {
  865.         if (ValidGroup (grp)) {
  866.             junkFlag = 0;
  867.             break;
  868.         }
  869.     }
  870.  
  871.     D (("junkFlag %ld\n", junkFlag));
  872.  
  873.     if (junkFlag) {
  874.         if (junkFlag > 0) {
  875.             CopyToGroup (base, bytes, "junk", &realFileName);
  876.             ++UHaveNews;
  877.         }
  878.     }
  879.     else {
  880.         storearticle (base, bytes, &realFileName);
  881.     }
  882.  
  883. do_other_stuff:
  884.     /*
  885.     **  forward article to downstream sites if it was stored.
  886.     */
  887.  
  888.     if (realFileName) {
  889.         if (DummyOpt == 0)
  890.             ForwardArticle (base, bytes, realFileName);
  891.         free (realFileName);
  892.     }
  893.  
  894.     free (base);
  895.  
  896.     D (("exit\n"));
  897.     return 0;
  898. }
  899.  
  900. /*
  901. **  LinkToGroup ()
  902. **
  903. **  If we are allowed to hard link and we have already written out
  904. **  one file (realFileName != NULL), then create a hard link instead
  905. **  of copying the file.
  906. **
  907. **  note that realFileName is actually a newsgroup/artno (dot format)
  908. **
  909. **  If this call fails RNews will attempt to make a copy of the file
  910. **  instead.
  911. */
  912.  
  913. int
  914. LinkToGroup (char *grp, char *realFileName)
  915. {
  916.     BPTR
  917.         lock;
  918.     int
  919.         r = -1;
  920.     char
  921.         *real,
  922.         *path;
  923.  
  924.     PROC ("LinkToGroup");
  925.  
  926.     if (LinksOk && realFileName) {
  927.         /*
  928.         **    ok to link, already made at least one copy of the file
  929.         **    (need something to link to!)
  930.         */
  931.  
  932.         if (real = HandleHeirarchy (NewsDir, realFileName, 0)) {
  933.             if (lock = Lock (real, SHARED_LOCK)) {
  934.                 /*
  935.                 **    got a handle on the original file, convert
  936.                 **    group name to path and
  937.                 */
  938.  
  939.                 if (path = HandleHeirarchy (NewsDir, grp, 0)) {
  940.                     int
  941.                         seqNo;
  942.                     /*
  943.                     **    attempt to link.
  944.                     */
  945.  
  946.                     HandleSequenceNumber (path, &seqNo);
  947.                     sprintf (TmpBuf, "%s/%ld", path, seqNo);
  948.  
  949.                     if (MakeLink (TmpBuf, (long) lock, 0L) == 0) {
  950.                         ulog (-1, "Couldn't create hardlink to %s (%ld), copying instead", TmpBuf, IoErr ());
  951.                     }
  952.                     else {
  953.                         r = 0;
  954.                     }
  955.                     free (path);
  956.                 }
  957.                 UnLock (lock);
  958.             }
  959.             free (real);
  960.         }
  961.     }
  962.  
  963.     D (("exit\n"));
  964.     return r;
  965. }
  966.  
  967. /*
  968. **  CopyToGroup ()
  969. **
  970. **  In the event that a hardlink is disallowed or not possible (due to
  971. **  this being the first instance of a news file), write out the file.
  972. */
  973.  
  974. int
  975. CopyToGroup (char *buf, int bytes, char *grp, char **rfn)
  976. {
  977.     int
  978.         r = 0,
  979.         seqNo;
  980.     char
  981.         *path;
  982.     BPTR
  983.         out;
  984.  
  985.     PROC ("CopyToGroup");
  986.  
  987.     if ((path = HandleHeirarchy (NewsDir, grp, DummyOpt)) == NULL) {
  988.         D (("exit, HandleHeirarchy failed\n"));
  989.         return -1;
  990.     }
  991.  
  992.     /*
  993.     **  Obtain a sequence number (and bump .next file)
  994.     */
  995.  
  996.     HandleSequenceNumber (path, &seqNo);
  997.     D (("sequence number %ld\n", seqNo));
  998.  
  999.     /*
  1000.     **  write the file.  When we write a real file, we set *rfn to point
  1001.     **  to the dotted file name for ForwardArticle().  This allows us
  1002.     **  to implement hard links for multiple references
  1003.     */
  1004.  
  1005.     if (*rfn == NULL) {
  1006.         sprintf (TmpBuf, "%s/%d", grp, seqNo);
  1007.         *rfn = strdup (TmpBuf);
  1008.         D (("rfn = '%s'\n", TmpBuf));
  1009.     }
  1010.     sprintf (TmpBuf, "%s/%d", path, seqNo);
  1011.     D (("file = '%s'\n", TmpBuf));
  1012.  
  1013.     free (path); /* we are done with it... */
  1014.  
  1015.     if (DummyOpt) {
  1016.         D (("exit, DummyOpt\n"));
  1017.         return 0;
  1018.     }
  1019.  
  1020.     if (out = Open (TmpBuf, MODE_NEWFILE)) {
  1021.         int
  1022.             n = Write (out, buf, bytes);
  1023.  
  1024.         Close (out);
  1025.         if (n != bytes)
  1026.             r = -1;
  1027.     }
  1028.  
  1029.     D (("exit, result %ld\n", r));
  1030.     return r;
  1031. }
  1032.  
  1033. void
  1034. HandleSequenceNumber (char *path, int *pseq)
  1035. {
  1036.     int
  1037.         len;
  1038.     BPTR
  1039.         file;
  1040.  
  1041.     /*
  1042.     ** do not use update I/O on the .next file, so that XFH will work...
  1043.     */
  1044.  
  1045.     PROC ("HandleSequenceNumber");
  1046.  
  1047.     sprintf (TmpBuf, "%s/.next", path);
  1048.  
  1049.     if (file = Open (TmpBuf, MODE_OLDFILE)) {
  1050.         len = Read (file, Buf, sizeof Buf);
  1051.  
  1052.         if (len > 0) {
  1053.             Buf [len] = '\0';
  1054.             D (("'%s' contains '%s'\n", TmpBuf, Buf));
  1055.             *pseq = atoi (Buf);
  1056.         }
  1057.         else {
  1058.             len = IoErr ();
  1059.             D (("couldn't read '%s', IoErr %ld\n", TmpBuf, len));
  1060.             *pseq = 1;
  1061.         }
  1062.         Close (file);
  1063.     }
  1064.     else {
  1065.         len = IoErr ();
  1066.         D (("couldn't open '%s', IoErr = %ld, set sequence to 1\n",
  1067.             TmpBuf, len));
  1068.         *pseq = 1;
  1069.     }
  1070.  
  1071.     sprintf (Buf, "%ld", (*pseq + 1));
  1072.     D (("new sequence number %ld, Buf contains '%s'\n", (*pseq + 1), Buf));
  1073.  
  1074.     if (DummyOpt)
  1075.         return;
  1076.  
  1077.     if (file = Open (TmpBuf, MODE_NEWFILE)) {
  1078.         Write (file, Buf, strlen (Buf));
  1079.         Close (file);
  1080.     }
  1081.  
  1082.     D (("exit\n"));
  1083.     return;
  1084. }
  1085.  
  1086. void
  1087. myexit (void)
  1088. {
  1089.     group
  1090.         *gp;
  1091.  
  1092.     PROC ("myexit");
  1093.  
  1094.     if (HomeDir [0]) {
  1095.         D (("chdir (%s)\n", HomeDir));
  1096.         if (chdir (HomeDir)) {
  1097.             D (("chdir (%s) failed\n", HomeDir));
  1098.             ulog (-1, "chdir (%s) failed\n", HomeDir);
  1099.         }
  1100.     }
  1101.  
  1102.     OpenLog ();
  1103.     for (gp = groups.next; gp; gp = gp->next) {
  1104.         if (gp->articles) {
  1105.             ulog(-1, "%s%-24s %4ld ARTS %7ld UNCOMP",
  1106.                 DummyOpt ? "(d) " : "",
  1107.                 gp->name, gp->articles, gp->bytes);
  1108.         }
  1109.     }
  1110.  
  1111.     ulog (-1, "%-24s %4ld ARTS %7ld/%-7ld BYTES/UNCOMPRESSED",
  1112.         "TOTAL", groups.articles, groups.actual, groups.bytes);
  1113.     CloseLog ();
  1114.  
  1115.     UnLockFiles ();
  1116.  
  1117.     if (OwnDevUnitBase) {
  1118.         CloseLibrary (OwnDevUnitBase);
  1119.         OwnDevUnitBase = NULL;
  1120.     }
  1121.  
  1122.     D (("exit\n"));
  1123.     return;
  1124. }
  1125.  
  1126. /*
  1127. **  Newsgroups
  1128. */
  1129.  
  1130. void
  1131. initgroups (void)
  1132. {
  1133.     struct group
  1134.         *gp;
  1135.     int
  1136.         c,
  1137.         len;
  1138.     FILE
  1139.         *fp;
  1140.  
  1141.     if (groups.next != NULL)
  1142.         return;
  1143.  
  1144.     if ((fp = openlib ("newsgroups")) == NULL) {
  1145.         ulog (-1, "Couldn't open UULib:Newsgroups file");
  1146.         exit (30);
  1147.     }
  1148.  
  1149.     gp = &groups;
  1150.     while (fgets (Buf, sizeof (Buf), fp)) {
  1151.  
  1152.         for (len = 0;
  1153.             (c = Buf [len]) != ' ' && c != 9 && c != '\n';
  1154.             ++len)
  1155.             ;
  1156.         Buf [len] = 0;
  1157.  
  1158.         if ((gp->next = malloc (sizeof (group) + len + 1)) == NULL) {
  1159.             ulog (-1, "Malloc failed!");
  1160.             exit (30);
  1161.         }
  1162.         gp = gp->next;
  1163.         clrmem (gp, sizeof (group));
  1164.         strcpy (gp->name, Buf);
  1165.     }
  1166.     fclose (fp);
  1167.  
  1168.     return;
  1169. }
  1170.  
  1171. group *
  1172. ValidGroup (const char *grp)
  1173. {
  1174.     struct group
  1175.         *gp;
  1176.  
  1177.     for (gp = groups.next; gp; gp = gp->next) {
  1178.         if (stricmp (gp->name, grp) == 0)
  1179.             break;
  1180.     }
  1181.  
  1182.     return gp;
  1183. }
  1184.  
  1185. int
  1186. rm (const char *file)
  1187. {
  1188.     int
  1189.         rslt;
  1190.  
  1191.     PROC ("rm");
  1192.     D (("removing %s\n", file));
  1193.  
  1194.     rslt = remove (file);
  1195.  
  1196. #ifdef PROCDEBUG
  1197.     if (rslt) {
  1198.         D (("error removing file %ld\n", errno));
  1199.     }
  1200. #endif
  1201.  
  1202.     return rslt;
  1203. }
  1204.