home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume8 / multi-feed.c++ < prev    next >
Text File  |  1987-02-08  |  36KB  |  1,254 lines

  1. Subject:  v08i049:  Simultaneous multi-site news feeder in C++
  2. Newsgroups: mod.sources
  3. Approved: mirror!rs
  4.  
  5. Submitted by: "Stephen J. Muir" <stephen%computing.lancaster.ac.uk@Cs.Ucl.AC.UK>
  6. Mod.sources: Volume 8, Issue 49
  7. Archive-name: multi-feed.c++
  8.  
  9. [  I do not have C++.  --r$  ]
  10.  
  11. #!/bin/sh
  12. echo 'Start of pack.out, part 01 of 01:'
  13. echo 'x - README'
  14. sed 's/^X//' > README << '/'
  15. XSimultaneous Multi-Site News Feeder (version 1.0)
  16. X--------------------------------------------------
  17. X
  18. XThis software may be freely copied provided that no charge is made and you
  19. Xdon't pretend you wrote it.
  20. X
  21. XThis is the simultaneous multi-site news feeder.  It was written to solve the
  22. Xfollowing problems with existing news batchers:
  23. X
  24. X    o When feeding several sites, each news article was the subject of
  25. X      several batch runs, thus consuming a lot of processor power.
  26. X
  27. X    o When feeding several sites, disk space would be gobbled up very
  28. X      quickly.
  29. X
  30. X    o If, for some reason, two batchers for the same site tried to run
  31. X      simultaneously, chaos would ensue.
  32. X
  33. X    o If the disk filled during batching, the batcher wouldn't notice and
  34. X      carry on regardless.
  35. X
  36. X    o If news was being unbatched during news batching, you would end up
  37. X      with lots of tiny batches.
  38. X
  39. X    o The log file(s) had to be trimmed from time to time.
  40. X
  41. X    o The "maximum batch size" was, in fact, a minimum batch size!
  42. X
  43. XNow for the bad news!  It is not very portable (yet!).  These are the minimum
  44. Xrequirements:
  45. X
  46. X    o 4.2 BSD UNIX
  47. X
  48. X    o C++ version 1.1
  49. X
  50. X    o 2.10.3 news (if you want the multi-site facility)
  51. X
  52. XIf you have a different environment, then I'm afraid you'll have to hack it.
  53. X
  54. XInstallation:
  55. X
  56. X    1)  Edit the following files to your requirements:
  57. X        Makefile
  58. X        config.h
  59. X        newsfeedlist.sh
  60. X
  61. X    2)  There are several bugs in the include files in C++ version 1.1.
  62. X        All but one of these will show up as compilation errors.  The
  63. X        remaining one, which will cause a memory fault if not fixed, is
  64. X        that the "stat" structure is different from that used by 4.2 BSD
  65. X        UNIX.  If you have done everything properly, there should be no
  66. X        errors or warnings during compilation.  You should be able to
  67. X        achieve this by editing the C++ include files alone.
  68. X
  69. X    3a) Edit your "sys" file, in order to make full use of the multi-site
  70. X        capabilities of this news batcher, to use the MULTICAST facility.
  71. X        This also has to be defined in inews.  (It will still accept old
  72. X        style batches.)  Here is a rather contrived example:
  73. X
  74. X        site1:M:world,comp,sci,rec,news:uucp-feed
  75. X        site2:M:world,comp,sci,rec,news:uucp-feed
  76. X        site3:M:world,comp,news:uucp-feed
  77. X        uucp-feed:OF:all:
  78. X
  79. X        All these sites will now be fed by "newsfeed uucp-feed"
  80. X        (but see below).
  81. X
  82. X    3b) You now need a command file which will take a news batch and queue
  83. X        it for several sites simultaneously (if you use the above feature).
  84. X        To save disk space problems when batching, it should ideally make
  85. X        one copy and use UNIX links for the remaining sites.  Here is an
  86. X        example, for an 8-bit compressed multi-feed, to put in file
  87. X        "uucp-feed.cmd":
  88. X
  89. X        #!/bin/sh
  90. X        (echo "#! cunbatch"; /usr/lib/news/compress -q) | \
  91. X        /usr/lib/news/uucp-multifeed $*
  92. X
  93. X        You have to write "uucp-multifeed" yourself!  It takes the sites to
  94. X        queue for as arguments.  (I only use UUCP for one of the sites I
  95. X        feed, so I haven't bothered to write it.)  If the command file
  96. X        exits abnormally, batching will be abandoned and the various files
  97. X        will be reset to their previous state.  Thus, for example, if you
  98. X        wish to stop batching when the disk is too full, you could add the
  99. X        following to "uucp-feed.cmd" (after the first line):
  100. X
  101. X        if /usr/lib/news/disktoofull
  102. X        then
  103. X            echo $0: DISK TOO FULL >> /usr/lib/news/errlog
  104. X            exit 1
  105. X        fi
  106. X
  107. X        where "disktoofull" returns normally if your disk is too full.
  108. X
  109. XSend any bugs/improvements to me:
  110. X
  111. XName:    Stephen J. Muir            | Post: University of Lancaster,
  112. XEMAIL:    stephen@comp.lancs.ac.uk    |    Department of Computing,
  113. XUUCP:    ...!mcvax!ukc!dcl-cs!stephen    |    Bailrigg, Lancaster, UK.
  114. XPhone:    +44 524 65201 Ext. 4120        |    LA1 4YR
  115. /
  116. echo 'x - Makefile'
  117. sed 's/^X//' > Makefile << '/'
  118. XCC=CC
  119. XCFLAGS=-O
  120. XCONFIGOBJS=main.o log.o misc.o
  121. XOBJS=${CONFIGOBJS} feed.o batch.o io.o
  122. XINSTALLDIR=/usr/lib/news/
  123. X
  124. Xnewsfeed:    ${OBJS}
  125. X        ${CC} -o $@ ${OBJS}
  126. X
  127. X${OBJS}:    defs.h
  128. X
  129. X${CONFIGOBJS}:    config.h
  130. X
  131. Xinstall:    newsfeed newsfeedlist.sh newsfeed.man newsfeedlist.man
  132. X        install -o news -g news -s newsfeed ${INSTALLDIR}
  133. X        cp newsfeedlist.sh ${INSTALLDIR}newsfeedlist
  134. X        chmod 755 ${INSTALLDIR}newsfeedlist
  135. X        chown news ${INSTALLDIR}newsfeedlist
  136. X        chgrp news ${INSTALLDIR}newsfeedlist
  137. X        cp newsfeed.man /usr/man/mann/newsfeed.n
  138. X        cp newsfeedlist.man /usr/man/mann/newsfeedlist.n
  139. X
  140. Xclean:
  141. X        rm -f newsfeed *.o *..o *..c core
  142. /
  143. echo 'x - config.h'
  144. sed 's/^X//' > config.h << '/'
  145. X/* Written by Stephen J. Muir, Lancaster University, England, UK.
  146. X *
  147. X * EMAIL: stephen@comp.lancs.ac.uk
  148. X * UUCP:  ...!mcvax!ukc!dcl-cs!stephen
  149. X *
  150. X * Version 1.0
  151. X */
  152. X
  153. X// Any of the lines below may be commented out
  154. X
  155. X//# define DEBUG
  156. X
  157. X// News uid and gid.
  158. X# define NEWSUID        2
  159. X# define NEWSGID        4
  160. X
  161. X// Priority to run at.
  162. X//# define NICENESS        1
  163. X
  164. X// Working directory.
  165. X# define BATCH_OUT_DIR        "/usr/spool/news/batchout/"
  166. X
  167. X// Default maximum size of batch.
  168. X# define DEFAULT_MAX_SIZE    200000
  169. X
  170. X// Stamp log even if there is nothing to batch.
  171. X//# define ALWAYS_STAMP_LOG
  172. X
  173. X// Number of seconds after which to delete log entries.
  174. X# define LOG_EXPIRE_SECONDS    (14*24*60*60)
  175. X
  176. X// Error log.
  177. X# define ERROR_LOG        "/usr/lib/news/errlog"
  178. /
  179. echo 'x - defs.h'
  180. sed 's/^X//' > defs.h << '/'
  181. X/* Written by Stephen J. Muir, Lancaster University, England, UK.
  182. X *
  183. X * EMAIL: stephen@comp.lancs.ac.uk
  184. X * UUCP:  ...!mcvax!ukc!dcl-cs!stephen
  185. X *
  186. X * Version 1.0
  187. X */
  188. X
  189. X# include <sys/errno.h>
  190. X# include <sys/types.h>
  191. X# include <sys/file.h>
  192. X# include <sys/stat.h>
  193. X# include <sys/time.h>
  194. X# include <sys/resource.h>
  195. X# include <signal.h>
  196. X# include <stream.h>
  197. X# include <string.h>
  198. X# include <osfcn.h>
  199. X
  200. X# define BUFFER_SIZE    4096
  201. X# define WARNING(x)    error(x,0,0)
  202. X# define SYS_WARNING(x)    error(x,1,0)
  203. X# define FATAL(x)    error(x,0,1)
  204. X# define SYS_FATAL(x)    error(x,1,1)
  205. X
  206. Xstruct sitelist
  207. X{   const char    *site_name;
  208. X    short    site_seen;
  209. X    sitelist    *site_next;
  210. X};
  211. X
  212. X// to free memory automagically
  213. Xclass auto_delete
  214. X{   void    *stored_ptr;
  215. Xpublic:
  216. X    auto_delete (void *ptr)    { stored_ptr = ptr; }
  217. X    ~auto_delete ()        { delete stored_ptr; }
  218. X};
  219. X
  220. X// to close files automagically
  221. Xclass auto_close
  222. X{   int    stored_fd;
  223. Xpublic:
  224. X    auto_close (int fd)    { stored_fd = fd; }
  225. X    ~auto_close ()    { int e = errno; close (stored_fd); errno = e; }
  226. X};
  227. X
  228. X// feed.o
  229. Xextern void        feed_news (const char *, const char **, off_t);
  230. X// log.o
  231. Xextern void        open_log (const char *);
  232. Xextern void        add_to_log (const char *, const sitelist *);
  233. Xextern void        close_log ();
  234. Xextern void        sync_log ();
  235. Xextern void        reset_log ();
  236. X// batch.o
  237. Xextern int        open_batch (const char **, off_t);
  238. Xextern int        send_article (const char *);
  239. Xextern int        close_batch ();
  240. Xextern void        kill_batch ();
  241. X// io.o
  242. Xextern void        fatal_input_stream_error (const char *, int);
  243. Xextern void        fatal_output_stream_error (const char *);
  244. Xextern int        getline (const istream&, char *, int);
  245. Xextern int        append_file (const char *, const char *);
  246. X// misc.o
  247. Xextern const char    *rename_error;
  248. Xextern char        *new_string (const char *);
  249. Xextern char        *join_string (const char *, const char *);
  250. Xextern void        error (const char *, int, int);
  251. X// libC.a
  252. Xextern void        (*_new_handler) ();
  253. /
  254. echo 'x - batch.c'
  255. sed 's/^X//' > batch.c << '/'
  256. X/* Written by Stephen J. Muir, Lancaster University, England, UK.
  257. X *
  258. X * EMAIL: stephen@comp.lancs.ac.uk
  259. X * UUCP:  ...!mcvax!ukc!dcl-cs!stephen
  260. X *
  261. X * Version 1.0
  262. X */
  263. X
  264. X# include "defs.h"
  265. X
  266. Xstatic short        batch_error;
  267. Xstatic int        pid, output_channel = -1, (*old_pipe_signal) ();
  268. Xstatic off_t        current_size, max_size;
  269. Xstatic struct stat    statbuf;
  270. X
  271. X// This routine sets up a pipe to the enqueueing command.
  272. X// Parameters:
  273. X//    command -> command to queue the batch for transfer
  274. X//    new_max_size -> maximum batch size
  275. X// Return value:
  276. X//    -2: couldn't create pipe
  277. X//    -1: invalid invocation (pipe already open)
  278. X//     0: ok (as far as the parent process can tell)
  279. Xopen_batch (const char **command, off_t new_max_size)
  280. X{   if (output_channel != -1)
  281. X    return -1;
  282. X    max_size = new_max_size;
  283. X    int        pipes [2];
  284. X    if (pipe (pipes) == -1)
  285. X    return -2;
  286. X    // process table could be full, in which case we may be in trouble anyway,
  287. X    // but at least it's not our fault for not reporting it
  288. X    while ((pid = fork ()) == -1)
  289. X    sleep (1);
  290. X    if (pid == 0)
  291. X    {    // child process ...
  292. X    close (pipes [1]);
  293. X    if (dup2 (pipes [0], 0) == -1)
  294. X        SYS_FATAL ("dup2()");
  295. X    close (pipes [0]);
  296. X    execv (*command, command);
  297. X    // I hope this works!
  298. X    SYS_FATAL (*command);
  299. X    }
  300. X    close (pipes [0]);
  301. X    old_pipe_signal = signal (SIGPIPE, SIG_IGN);
  302. X    output_channel = pipes [1];
  303. X    batch_error = 0;
  304. X    current_size = 0;
  305. X    return 0;
  306. X}
  307. X
  308. X// This routine closes the pipe and (hopefully) reports whether or not it
  309. X// worked.
  310. X// Return value:
  311. X//    -2: something went wrong!
  312. X//    -1: invalid invocation (pipe not open)
  313. X//     0: ok (as far as we can tell)
  314. Xclose_batch ()
  315. X{   if (output_channel == -1)
  316. X    return -1;
  317. X    close (output_channel);
  318. X    output_channel = -1;
  319. X    signal (SIGPIPE, old_pipe_signal);
  320. X    int        status, wait_pid;
  321. X    while ((wait_pid = wait (&status)) != pid && wait_pid != -1)
  322. X    ;
  323. X    return (wait_pid == -1 || status) ? -2 : 0;
  324. X}
  325. X
  326. X// This routine adds a file to the batch.  If this would cause batch overflow to
  327. X// occur, the article is not added, assuming the caller will try again.
  328. X// Parameters:
  329. X//    pathname -> article to be included
  330. X// Return value:
  331. X//    -3: error (batch write error)
  332. X//    -2: error (article read error)
  333. X//    -1: invalid invocation (pipe not open or current batch in error)
  334. X//     0: ok (article added to batch)
  335. X//     1: ok (article didn't exist -- maybe it was cancelled/expired)
  336. X//     2: ok (article would overflow batch -- article not included)
  337. Xsend_article (const char *pathname)
  338. X{   if (output_channel == -1 || batch_error)
  339. X    return -1;
  340. X    register int    article_fd = open (pathname, O_RDONLY, 0);
  341. X    if (article_fd == -1)
  342. X    return 1;
  343. X    auto_close    auto_article (article_fd);
  344. X    if (fstat (article_fd, &statbuf) == -1)
  345. X    return 1;
  346. X    char    *rnews_header = form ("#! rnews %d\n", statbuf.st_size);
  347. X    int        rnews_header_size = strlen (rnews_header);
  348. X    if (current_size > 0 &&
  349. X    max_size > 0 &&
  350. X    (current_size + rnews_header_size + statbuf.st_size) > max_size
  351. X       )
  352. X    return 2;
  353. X    char        buffer [BUFFER_SIZE];
  354. X    register int    byte_count;
  355. X    current_size += rnews_header_size + statbuf.st_size;
  356. X    if (write (output_channel, rnews_header, rnews_header_size) !=
  357. X    rnews_header_size
  358. X       )
  359. X    {    batch_error = 1;
  360. X    return -3;
  361. X    }
  362. X    while ((byte_count = read (article_fd, buffer, BUFFER_SIZE)) > 0)
  363. X    if (write (output_channel, buffer, byte_count) != byte_count)
  364. X    {   batch_error = 1;
  365. X        return -3;
  366. X    }
  367. X    if (byte_count)
  368. X    {    batch_error = 1;
  369. X    return -2;
  370. X    }
  371. X    return 0;
  372. X}
  373. X
  374. X// This routine tries to kill the current batch.
  375. Xvoid    kill_batch ()
  376. X{   if (output_channel != -1)
  377. X    kill (pid, SIGTERM);
  378. X}
  379. /
  380. echo 'x - feed.c'
  381. sed 's/^X//' > feed.c << '/'
  382. X/* Written by Stephen J. Muir, Lancaster University, England, UK.
  383. X *
  384. X * EMAIL: stephen@comp.lancs.ac.uk
  385. X * UUCP:  ...!mcvax!ukc!dcl-cs!stephen
  386. X *
  387. X * Version 1.0
  388. X */
  389. X
  390. X# include "defs.h"
  391. X
  392. Xstatic char    *base_again, *base_hold, *base_cmd, *default_site;
  393. Xstatic short    first_article;
  394. Xstatic int    fd_again = -1, fd_hold = -1;
  395. Xstatic off_t    max_batch_size;
  396. Xstatic sitelist    *hold_list;
  397. Xstatic ostream    *stream_again, *stream_hold;
  398. X
  399. X// This routine writes the given line to the "again" file for reprocessing.
  400. X// Parameters:
  401. X//    article_line -> line to write
  402. X// Return value:
  403. X//    exits on error
  404. Xstatic void    write_again (const char *article_line)
  405. X{   if (fd_again == -1)
  406. X    {    if ((fd_again = open (base_again,
  407. X                  O_CREAT | O_TRUNC | O_WRONLY | O_APPEND,
  408. X                  0666
  409. X                 )
  410. X         ) == -1
  411. X       )
  412. X        SYS_FATAL (base_again);
  413. X    fcntl (fd_again, F_SETFD, 1);
  414. X    stream_again = new ostream (fd_again);
  415. X    }
  416. X    *stream_again << article_line << "\n";
  417. X}
  418. X
  419. X// This routine writes the given article to the "hold" file.
  420. X// Parameters:
  421. X//    article -> article to write
  422. X// Return value:
  423. X//    exits on error
  424. Xstatic void    write_hold (const char *article)
  425. X{   if (fd_hold == -1)
  426. X    {    if ((fd_hold = open (base_hold, O_CREAT | O_WRONLY | O_APPEND, 0666))
  427. X        == -1
  428. X       )
  429. X        SYS_FATAL (base_hold);
  430. X    fcntl (fd_hold, F_SETFD, 1);
  431. X    stream_hold = new ostream (fd_hold);
  432. X    }
  433. X    *stream_hold << article;
  434. X    for (sitelist *site_ptr = hold_list;
  435. X     site_ptr;
  436. X     site_ptr = site_ptr->site_next
  437. X    )
  438. X    if (site_ptr->site_seen)
  439. X        *stream_hold << " " << site_ptr->site_name;
  440. X    *stream_hold << "\n";
  441. X}
  442. X
  443. X// This routine tries to write the given article to the current batch, starting
  444. X// one if none is current.  If the article doesn't belong to the current batch,
  445. X// it will save it for further processing.  Finally, it may add the article to
  446. X// the "hold" file.
  447. X// Parameters:
  448. X//    article_line -> a line from the batch file
  449. X// Return value:
  450. X//    exits on error
  451. Xstatic void    feed_article (const char *article_line)
  452. X{   static short    batch_finished;
  453. X    static sitelist    *feed_list;
  454. X    register char    *c_ptr;
  455. X    register int    ret, holding = 0;
  456. X    register sitelist    *site_ptr, **site_ptr_ptr;
  457. X
  458. X    // wait until the whole input is reprocessed
  459. X    if (first_article)
  460. X    batch_finished = 0;
  461. X    if (batch_finished)
  462. X    {    write_again (article_line);
  463. X    return;
  464. X    }
  465. X
  466. X    // reset the "hold" list
  467. X    for (site_ptr = hold_list; site_ptr; site_ptr = site_ptr->site_next)
  468. X    site_ptr->site_seen = 0;
  469. X
  470. X    register char    *new_article_line = new_string (article_line);
  471. X    auto_delete    auto_new_article_line (new_article_line);
  472. X
  473. X    // split line into (article, sitelist)
  474. X    for (c_ptr = new_article_line; *c_ptr != ' ' && *c_ptr != '\0'; ++c_ptr)
  475. X    ;
  476. X    while (*c_ptr == ' ')
  477. X    *c_ptr++ = '\0';
  478. X    if (*c_ptr == '\0')
  479. X    c_ptr = default_site;
  480. X
  481. X    // if first article, make sure we can read it and set up the "feed" list
  482. X    if (first_article)
  483. X    {    int    site_count = 0;
  484. X    // delete any previous "feed" list
  485. X    for (site_ptr = feed_list; site_ptr; )
  486. X    {   sitelist    *delete_ptr = site_ptr;
  487. X        site_ptr = site_ptr->site_next;
  488. X        delete delete_ptr->site_name;
  489. X        delete delete_ptr;
  490. X    }
  491. X    feed_list = 0;
  492. X
  493. X    // the first article *must* exist
  494. X    if ((ret = access (new_article_line, R_OK)) == -1)
  495. X    {   if (errno == ENOENT)
  496. X        return;
  497. X        SYS_WARNING (new_article_line);
  498. X    }
  499. X
  500. X    // now set up the "feed" list
  501. X    for (site_ptr_ptr = &feed_list; *c_ptr; )
  502. X    {   register char    *new_c_ptr = c_ptr;
  503. X
  504. X        while (*new_c_ptr != ' ' && *new_c_ptr != '\0')
  505. X        ++new_c_ptr;
  506. X        while (*new_c_ptr == ' ')
  507. X        *new_c_ptr++ = '\0';
  508. X
  509. X        // make sure it's not in the hold list
  510. X        for (site_ptr = hold_list; site_ptr; site_ptr = site_ptr->site_next)
  511. X        if ( ! strcmp (c_ptr, site_ptr->site_name))
  512. X        {   site_ptr->site_seen = holding = 1;
  513. X            break;
  514. X        }
  515. X
  516. X        // if it's not in the hold list, add it to the feed list
  517. X        if (site_ptr == 0)
  518. X        {    sitelist    *new_entry = new sitelist;
  519. X        new_entry->site_name = new_string (c_ptr);
  520. X        //new_entry->site_seen = 1;
  521. X        *site_ptr_ptr = new_entry;
  522. X        site_ptr_ptr = &new_entry->site_next;
  523. X        ++site_count;
  524. X        }
  525. X
  526. X        c_ptr = new_c_ptr;
  527. X    }
  528. X    *site_ptr_ptr = 0;
  529. X
  530. X    // write the article to the hold list (if necessary)
  531. X    if (holding)
  532. X        write_hold (new_article_line);
  533. X
  534. X    // we *must* have some sites to feed
  535. X    if ( ! site_count)
  536. X        return;
  537. X
  538. X    // start a batch
  539. X    const char    **command_args = new char *[site_count + 2],
  540. X            **args_ptr_ptr = command_args;
  541. X    *args_ptr_ptr++ = base_cmd;
  542. X    for (site_ptr = feed_list; site_ptr; site_ptr = site_ptr->site_next)
  543. X        *args_ptr_ptr++ = site_ptr->site_name;
  544. X    *args_ptr_ptr = 0;
  545. X    if ((ret = open_batch (command_args, max_batch_size)) < 0)
  546. X        FATAL (form ("open_batch() returns %d", ret));
  547. X    delete command_args;
  548. X    first_article = 0;
  549. X    }    else    // ! first_article
  550. X    {    // reset feed list
  551. X    for (site_ptr = feed_list; site_ptr; site_ptr = site_ptr->site_next)
  552. X        site_ptr->site_seen = 0;
  553. X
  554. X    // check each site
  555. X    while (*c_ptr)
  556. X    {   register char    *new_c_ptr = c_ptr;
  557. X        while (*new_c_ptr != ' ' && *new_c_ptr != '\0')
  558. X        ++new_c_ptr;
  559. X        while (*new_c_ptr == ' ')
  560. X        *new_c_ptr++ = '\0';
  561. X
  562. X        // check hold list
  563. X        for (site_ptr = hold_list; site_ptr; site_ptr = site_ptr->site_next)
  564. X        if ( ! strcmp (c_ptr, site_ptr->site_name))
  565. X        {   site_ptr->site_seen = holding = 1;
  566. X            break;
  567. X        }
  568. X
  569. X        // if not in hold list, try the feed list
  570. X        if (site_ptr == 0)
  571. X        for (site_ptr = feed_list;
  572. X             site_ptr;
  573. X             site_ptr = site_ptr->site_next
  574. X            )
  575. X            if ( ! strcmp (c_ptr, site_ptr->site_name))
  576. X            {    site_ptr->site_seen = 1;
  577. X            break;
  578. X            }
  579. X
  580. X        // if not in either list, just push it out for later
  581. X        if (site_ptr == 0)
  582. X        {    write_again (article_line);
  583. X        return;
  584. X        }
  585. X
  586. X        c_ptr = new_c_ptr;
  587. X    }
  588. X
  589. X    // if feed list is not complete, just push it out for later
  590. X    for (site_ptr = feed_list; site_ptr; site_ptr = site_ptr->site_next)
  591. X        if ( ! site_ptr->site_seen)
  592. X        {    write_again (article_line);
  593. X        return;
  594. X        }
  595. X
  596. X    // write the article to the hold list (if necessary)
  597. X    if (holding)
  598. X        write_hold (new_article_line);
  599. X    }
  600. X
  601. X    // try to add the article to the current batch
  602. X    if ((ret = send_article (new_article_line)) < 0)
  603. X    FATAL (form ("send_article() returns %d", ret));
  604. X
  605. X    if (ret == 0)
  606. X    add_to_log (new_article_line, feed_list);
  607. X    else if (ret == 2)    // batch would've overflowed
  608. X    {    ++batch_finished;
  609. X    if (holding)
  610. X    {   for (site_ptr = feed_list;
  611. X         site_ptr;
  612. X         site_ptr = site_ptr -> site_next
  613. X        )
  614. X        // yuck ...
  615. X        strcat (new_article_line, form (" %s", site_ptr->site_name));
  616. X        write_again (new_article_line);
  617. X    } else
  618. X        write_again (article_line);
  619. X    }
  620. X}
  621. X
  622. X// This routine does everything necessary to feed news batches from a file.
  623. X// The format of each line of the input file looks like:
  624. X//    article_file
  625. X// or:
  626. X//    article_file site1 site2 ... siteN
  627. X// In the first case, the site to be fed that article is assumed to be that
  628. X// given by the name of the base_file (old batch format).
  629. X// Parameters:
  630. X//    base_file -> file containing articles to be batched
  631. X//    hold_array -> list of sites NOT to be fed
  632. X//    max_batch_size -> maximum size of batch (0 => unlimited)
  633. X// Return value:
  634. X//    exits on error
  635. Xvoid    feed_news (const char *base_file,
  636. X           const char **hold_array,
  637. X           off_t max_batch_size
  638. X          )
  639. X{   char    *base_input, *base_proc, *cp = rindex (base_file, '/'),
  640. X        buffer [BUFFER_SIZE];
  641. X    int        proc_fd;
  642. X
  643. X    // make this available to the routine that needs to know
  644. X    ::max_batch_size = max_batch_size;
  645. X
  646. X    // in case it's not a MULTIHOST batch ...
  647. X    default_site = cp ? cp + 1 : base_file;
  648. X
  649. X    // make our filenames
  650. X    base_input = join_string (base_file, ".input");
  651. X    base_proc = join_string (base_file, ".proc");
  652. X    base_again = join_string (base_file, ".again");
  653. X    base_hold = join_string (base_file, ".hold");
  654. X    base_cmd = join_string (base_file, ".cmd");
  655. X
  656. X    // open the log file
  657. X    open_log (base_file);
  658. X
  659. X    // error recovery from previous run ...
  660. X    int            ret;
  661. X    if ((ret = append_file (base_input, base_proc)) < 0)
  662. X    SYS_FATAL (ret != -2 ? base_input : base_proc);
  663. X
  664. X    // prepare "hold" list
  665. X    sitelist    **site_ptr_ptr = &hold_list;
  666. X    for (const char **cpp = hold_array; *cpp; ++cpp)
  667. X    {    sitelist    *new_entry = new sitelist;
  668. X    new_entry->site_name = *cpp;
  669. X    *site_ptr_ptr = new_entry;
  670. X    site_ptr_ptr = &new_entry->site_next;
  671. X    }
  672. X    *site_ptr_ptr = 0;
  673. X
  674. X    // OK, time's up!  Any more articles arriving will have to wait until the
  675. X    // next time ...
  676. X    if (rename (base_file, base_input) == -1 && errno != ENOENT)
  677. X    SYS_FATAL (form (rename_error, base_file, base_input));
  678. X    if ((ret = append_file (base_input, base_proc)) < 0)
  679. X    SYS_FATAL (ret != -2 ? base_input : base_proc);
  680. X
  681. X    // Main processing loop.
  682. X    while ((proc_fd = open (base_proc, O_RDONLY, 0)) != -1)
  683. X    {    auto_close auto_proc_fd (proc_fd);
  684. X    fcntl (proc_fd, F_SETFD, 1);
  685. X    istream    stream_proc (proc_fd, 0);
  686. X    first_article = 1;
  687. X
  688. X    // give each line of the file to the article feeder
  689. X    while ((ret = getline (stream_proc, buffer, BUFFER_SIZE)) == 0)
  690. X        feed_article (buffer);
  691. X    if (ret != 1)
  692. X        fatal_input_stream_error (base_proc, ret);
  693. X
  694. X    // end of file ... finish current batch (if any)
  695. X    if ( ! first_article && (ret = close_batch ()))
  696. X        FATAL (form ("close_batch() returns %d", ret));
  697. X    sync_log ();
  698. X
  699. X    // tidy up the "again" file
  700. X    if (fd_again != -1)
  701. X    {   (*stream_again).flush ();
  702. X        if ( ! (*stream_again).good ())
  703. X        fatal_output_stream_error (base_again);
  704. X        delete stream_again;
  705. X        close (fd_again);
  706. X        fd_again = -1;
  707. X    }
  708. X
  709. X    // rename the "again" file to the "proc" file
  710. X    if (rename (base_again, base_proc) == -1)
  711. X    {   if (errno == ENOENT)
  712. X        {    if (unlink (base_proc) != -1)
  713. X            continue;
  714. X        SYS_FATAL (base_proc);
  715. X        }
  716. X        else
  717. X        SYS_FATAL (form (rename_error, base_again, base_proc));
  718. X    }
  719. X    }
  720. X
  721. X    if (errno != ENOENT)
  722. X    SYS_FATAL (base_proc);
  723. X
  724. X    // tidy up the "hold" file
  725. X    if (fd_hold != -1)
  726. X    {    (*stream_hold).flush ();
  727. X    if ( ! (*stream_hold).good ())
  728. X        fatal_output_stream_error (base_hold);
  729. X    delete stream_hold;
  730. X    }
  731. X
  732. X    // close the log file
  733. X    close_log ();
  734. X}
  735. /
  736. echo 'x - io.c'
  737. sed 's/^X//' > io.c << '/'
  738. X/* Written by Stephen J. Muir, Lancaster University, England, UK.
  739. X *
  740. X * EMAIL: stephen@comp.lancs.ac.uk
  741. X * UUCP:  ...!mcvax!ukc!dcl-cs!stephen
  742. X *
  743. X * Version 1.0
  744. X */
  745. X
  746. X# include "defs.h"
  747. X
  748. X// This routine prints an error message and exits.
  749. X// Parameters:
  750. X//    filename -> file on which the error occurred
  751. X//    code -> cause of error
  752. Xvoid    fatal_input_stream_error (const char *filename, int code)
  753. X{   register char    *cp;
  754. X    switch (code)
  755. X    {    case -3:
  756. X        cp = "stream error";
  757. X        break;
  758. X    case -2:
  759. X        cp = "incomplete last line";
  760. X        break;
  761. X    case -1:
  762. X        cp = "line too long";
  763. X        break;
  764. X    case 1:
  765. X        cp = "unexpected end of file";
  766. X        break;
  767. X    default:
  768. X        cp = form ("something(code %d) is badly wrong", code);
  769. X        break;
  770. X    }
  771. X    FATAL (form ("%s: %s", filename, cp));
  772. X}
  773. X
  774. X// This routine prints an error message and exits.
  775. X// Parameters:
  776. X//    filename -> file on which the error occurred
  777. Xvoid    fatal_output_stream_error (const char *filename)
  778. X{   FATAL (form ("%s: output stream error", filename));    }
  779. X
  780. X// This routine attempts to read the next line from the given input stream.
  781. X// Parameters:
  782. X//    infile -> input stream
  783. X//    buffer -> where to put the line
  784. X//    buffer_size -> size of the above
  785. X// Return value:
  786. X//    -3: stream failure (irrecoverable)
  787. X//    -2: last line not terminated by a newline
  788. X//    -1: line too long (irrecoverable)
  789. X//     0: ok
  790. X//     1: end of file
  791. Xgetline (const istream& infile, char *buffer, int buffer_size)
  792. X{   register char    c = '\0';
  793. X    if (infile.get (buffer, buffer_size))
  794. X    {    if (infile.get (c))
  795. X        return (c == '\n') ? 0 : -1;
  796. X    return infile.eof () ? -2 : -3;
  797. X    }
  798. X    return infile.eof () ? 1 : -3;
  799. X}
  800. X
  801. X// This routine attempts to copy the first file (if it exists) to the end of
  802. X// the second (which is created if it doesn't exist).  Then, the first file is
  803. X// removed.
  804. X// Parameters:
  805. X//    from -> name of input file
  806. X//    to -> name of output file
  807. X// Return Value:
  808. X//    -3: error in unlinking input file
  809. X//    -2: error writing output file
  810. X//    -1: error reading input file
  811. X//     0: ok
  812. X//     1: no input file
  813. X//     2: output file was created
  814. Xappend_file (const char *from, const char *to)
  815. X{   register int    ret = 0, count, from_fd, to_fd;
  816. X    if ((from_fd = open (from, O_RDONLY, 0)) == -1)
  817. X    return (errno == ENOENT) ? 1 : -1;
  818. X    auto_close auto_from_fd (from_fd);
  819. X    if (access (to, F_OK) == -1)
  820. X    ret = 2;
  821. X    if ((to_fd = open (to, O_CREAT | O_WRONLY | O_APPEND, 0666)) == -1)
  822. X    return -2;
  823. X    auto_close auto_to_fd (to_fd);
  824. X    char    buffer [BUFFER_SIZE];
  825. X    while ((count = read (from_fd, buffer, BUFFER_SIZE)) > 0)
  826. X    if (write (to_fd, buffer, count) != count)
  827. X        return -2;
  828. X    if (count < 0 || close (from_fd) == -1)
  829. X    return -1;
  830. X    if (close (to_fd) == -1)
  831. X    return -2;
  832. X    if (unlink (from) == -1)
  833. X    return -3;
  834. X    return ret;
  835. X}
  836. /
  837. echo 'x - log.c'
  838. sed 's/^X//' > log.c << '/'
  839. X/* Written by Stephen J. Muir, Lancaster University, England, UK.
  840. X *
  841. X * EMAIL: stephen@comp.lancs.ac.uk
  842. X * UUCP:  ...!mcvax!ukc!dcl-cs!stephen
  843. X *
  844. X * Version 1.0
  845. X */
  846. X
  847. X# include "defs.h"
  848. X# include "config.h"
  849. X
  850. Xstatic char    *base_log;
  851. Xstatic int    log_fd, log_size = -1;
  852. Xstatic ostream    *stream_log;
  853. X
  854. X// This routine puts a date stamp on the log file, if this hasn't been done
  855. X// yet.  It also creates the output stream for it (if necessary).
  856. Xstatic void    start_log ()
  857. X{   static short    log_started;
  858. X    if (log_started)
  859. X    return;
  860. X    ++log_started;
  861. X    if ( ! stream_log)
  862. X    stream_log = new ostream (log_fd);
  863. X    struct timeval    tv;
  864. X    struct timezone    tz;
  865. X    gettimeofday (&tv, &tz);
  866. X    struct tm        *tp = localtime (&tv.tv_sec);
  867. X    char        *ap = asctime (tp),
  868. X            *tzn = timezone (tz.tz_minuteswest, tp->tm_isdst);
  869. X    *stream_log << form (":%lu:%.20s%s%s",
  870. X             tv.tv_sec,
  871. X             ap,
  872. X             tzn ? tzn : "",
  873. X             ap + 19
  874. X            );
  875. X}
  876. X
  877. X// This routine removes those articles expected to be expired from the log file
  878. X// and returns with the log file opened (for appending) and locked.
  879. X// Parameters:
  880. X//    base_file -> file containing articles to be batched
  881. X// Return value:
  882. X//    exits on error
  883. Xvoid    open_log (const char *base_file)
  884. X{   if ((log_fd = open (base_log = join_string (base_file, ".log"),
  885. X            O_CREAT | O_RDWR | O_APPEND,
  886. X            0666
  887. X               )
  888. X    ) == -1
  889. X       )
  890. X    SYS_FATAL (base_log);
  891. X    fcntl (log_fd, F_SETFD, 1);
  892. X    if (flock (log_fd, LOCK_EX | LOCK_NB) == -1)
  893. X    FATAL (form ("previous batching for \"%s\" is still running", base_file)
  894. X          );
  895. X# ifdef LOG_EXPIRE_SECONDS
  896. X    istream        stream_old_log (log_fd);
  897. X    char        buffer [BUFFER_SIZE];
  898. X    register int    ret;
  899. X    if (ret = getline (stream_old_log, buffer, BUFFER_SIZE))
  900. X    {    if (ret == 1)    // empty log file
  901. X        return;
  902. X    fatal_input_stream_error (base_log, ret);
  903. X    }
  904. X    if (buffer [0] != ':')
  905. X    {    WARNING ("bad log format -- not truncated");
  906. X    return;
  907. X    }
  908. X    long    expire_time = time (0) - LOG_EXPIRE_SECONDS;
  909. X    if ((atol (&buffer [1]) - expire_time) > 0)
  910. X    return;
  911. X
  912. X    // we don't want the old log anymore
  913. X    auto_close auto_log_fd (log_fd);
  914. X    char    *base_newlog = join_string (base_file, ".newlog");
  915. X    if ((log_fd = open (base_newlog,
  916. X            O_CREAT | O_TRUNC | O_WRONLY | O_APPEND,
  917. X            0666
  918. X               )
  919. X    ) == -1
  920. X       )
  921. X    SYS_FATAL (base_newlog);
  922. X    fcntl (log_fd, F_SETFD, 1);
  923. X    if (flock (log_fd, LOCK_EX | LOCK_NB) == -1)
  924. X    SYS_FATAL (base_newlog);
  925. X    stream_log = new ostream (log_fd);
  926. X    short    transferring = 0;
  927. X    while ((ret = getline (stream_old_log, buffer, BUFFER_SIZE)) == 0)
  928. X    {    if ( ! transferring &&
  929. X        buffer [0] == ':' &&
  930. X        (atol (&buffer [1]) - expire_time) > 0
  931. X       )
  932. X        ++transferring;
  933. X    if (transferring)
  934. X        *stream_log << buffer << "\n";
  935. X    }
  936. X    if (ret != 1)
  937. X    fatal_input_stream_error (base_log, ret);
  938. X
  939. X    (*stream_log).flush ();
  940. X    if ( ! (*stream_log).good ())
  941. X    fatal_output_stream_error (base_newlog);
  942. X    if (rename (base_newlog, base_log) == -1)
  943. X    SYS_FATAL (form (rename_error, base_newlog, base_log));
  944. X# endif LOG_EXPIRE_SECONDS
  945. X}
  946. X
  947. X// This routine adds an article, together with the list of sites it was sent
  948. X// to, to the log file.
  949. Xvoid    add_to_log (const char *article_line, const sitelist *fed_sites)
  950. X{   register const sitelist    *site_ptr;
  951. X    start_log ();
  952. X    *stream_log << article_line;
  953. X    for (site_ptr = fed_sites; site_ptr; site_ptr = site_ptr->site_next)
  954. X    *stream_log << " " << site_ptr->site_name;
  955. X    *stream_log << "\n";
  956. X}
  957. X
  958. X// This routine closes the log file.
  959. Xvoid    close_log ()
  960. X{
  961. X# ifdef ALWAYS_STAMP_LOG
  962. X    start_log ();
  963. X# endif ALWAYS_STAMP_LOG
  964. X    if (stream_log)
  965. X    {    (*stream_log).flush ();
  966. X    if ( ! (*stream_log).good ())
  967. X        fatal_output_stream_error (base_log);
  968. X    delete stream_log;
  969. X    }
  970. X}
  971. X
  972. X// This routine attempts to flush all data to the log file and remember its size
  973. X// so that the next routine can truncate it if necessary.
  974. Xvoid    sync_log ()
  975. X{   if ( ! stream_log)
  976. X    FATAL ("sync_log(): log not open");
  977. X    (*stream_log).flush ();
  978. X    if ( ! (*stream_log).good ())
  979. X    fatal_output_stream_error (base_log);
  980. X    if ((log_size = lseek (log_fd, 0, L_INCR)) == -1)
  981. X    SYS_WARNING ("sync_log()");
  982. X}
  983. X
  984. X// This routine attempts to truncate the log to the size determined by the
  985. X// previous routine.
  986. Xvoid    reset_log ()
  987. X{   if (stream_log && log_size != -1)
  988. X    if (ftruncate (log_fd, log_size) == -1)
  989. X        SYS_WARNING ("reset_log()");
  990. X}
  991. /
  992. echo 'x - main.c'
  993. sed 's/^X//' > main.c << '/'
  994. X/* Written by Stephen J. Muir, Lancaster University, England, UK.
  995. X *
  996. X * EMAIL: stephen@comp.lancs.ac.uk
  997. X * UUCP:  ...!mcvax!ukc!dcl-cs!stephen
  998. X *
  999. X * Version 1.0
  1000. X */
  1001. X
  1002. X# include "defs.h"
  1003. X# include "config.h"
  1004. X
  1005. Xstatic void    new_handler ()
  1006. X{   FATAL ("out of memory");
  1007. X}
  1008. X
  1009. X// This program does a batched feed of several sites simultaneously.  After
  1010. X// changing directory to BATCH_OUT_DIR (in feed_news()), the sites in base_file
  1011. X// are fed.  If any (optional) hold sites are given, they are not fed but are
  1012. X// saved for a future run.  This could be done if, say, those sites are down
  1013. X// for an extended period or perhaps ...
  1014. Xmain (int argc, char **argv)
  1015. X{   register int    fd;
  1016. X    register off_t    max_size = (off_t)DEFAULT_MAX_SIZE;
  1017. X    _new_handler = &new_handler;
  1018. X
  1019. X    // just in case we've been given a duff environment
  1020. X    while ((fd = open ("/dev/null", O_RDWR, 0)) != -1 && fd < 3)
  1021. X    ;
  1022. X    if (fd != -1)
  1023. X    close (fd);
  1024. X    if (argc < 2 || (**++argv == '-' && argc < 3))
  1025. X    {    cerr << "usage: newsfeed [-max_size] base_file [list_of_hold_sites]\n";
  1026. X    exit (1);
  1027. X    }
  1028. X# ifndef DEBUG
  1029. X# ifdef NICENESS
  1030. X    if (setpriority (PRIO_PROCESS, getpid (), NICENESS) == -1)
  1031. X    SYS_WARNING ("setpriority()");
  1032. X# endif NICENESS
  1033. X# ifdef NEWSGID
  1034. X    if (setgid (NEWSGID) == -1)
  1035. X    SYS_WARNING ("setgid()");
  1036. X# endif NEWSGID
  1037. X# ifdef NEWSUID
  1038. X    if (setuid (NEWSUID) == -1)
  1039. X    SYS_WARNING ("setuid()");
  1040. X# endif NEWSUID
  1041. X    umask (022);    // We are (usually) not invoked by a user.
  1042. X# ifdef BATCH_OUT_DIR
  1043. X    char    *batch_out_dir = BATCH_OUT_DIR;    // saving space !!!
  1044. X    // go to where it's at!
  1045. X    if (chdir (batch_out_dir) == -1)
  1046. X    SYS_FATAL (batch_out_dir);
  1047. X# endif BATCH_OUT_DIR
  1048. X# endif DEBUG
  1049. X
  1050. X    if (**argv == '-')
  1051. X    max_size = (off_t)atol (*argv++ + 1);
  1052. X    feed_news (*argv, argv + 1, max_size);
  1053. X    exit (0);
  1054. X}
  1055. /
  1056. echo 'x - misc.c'
  1057. sed 's/^X//' > misc.c << '/'
  1058. X/* Written by Stephen J. Muir, Lancaster University, England, UK.
  1059. X *
  1060. X * EMAIL: stephen@comp.lancs.ac.uk
  1061. X * UUCP:  ...!mcvax!ukc!dcl-cs!stephen
  1062. X *
  1063. X * Version 1.0
  1064. X */
  1065. X
  1066. X# include "defs.h"
  1067. X# include "config.h"
  1068. X
  1069. X# if !defined(DEBUG) && defined(ERROR_LOG)
  1070. Xstatic int    error_fd;
  1071. X# endif
  1072. Xstatic ostream    *error_stream;
  1073. X
  1074. Xconst char    *rename_error = "rename(%s,%s)";
  1075. X
  1076. X// This routine prints an error message (and may exit).
  1077. X// Parameters:
  1078. X//    string -> error message
  1079. X//    perror_style -> adds a system error message by looking up "errno"
  1080. X//    fatal -> exits at end
  1081. X// Return value:
  1082. X//    exits if "fatal" is set or on error
  1083. Xvoid    error (const char *string, int perror_style, int fatal)
  1084. X{
  1085. X# if !defined(DEBUG) && defined(ERROR_LOG)
  1086. X    char    *error_log = ERROR_LOG;    // saving space !!!
  1087. X    if ( ! error_stream)
  1088. X    {    if ((error_fd = open (error_log, O_WRONLY | O_APPEND, 0)) != -1)
  1089. X    {   fcntl (error_fd, F_SETFD, 1);
  1090. X        error_stream = new ostream (error_fd);
  1091. X    } else
  1092. X        perror (error_log);
  1093. X    }
  1094. X# else
  1095. X    error_stream = &cerr;
  1096. X# endif
  1097. X    if (error_stream)
  1098. X    {    *error_stream << "newsfeed: "
  1099. X              << (fatal ? "fatal: " : "warning: ")
  1100. X              << string
  1101. X              << (perror_style ? ": " : "")
  1102. X              << (perror_style ? (errno < sys_nerr ? sys_errlist [errno]
  1103. X                               : "Unknown error"
  1104. X                     )
  1105. X                       : ""
  1106. X             )
  1107. X              << "\n";
  1108. X    (*error_stream).flush ();
  1109. X    }
  1110. X    if (fatal)
  1111. X    {    kill_batch ();
  1112. X    reset_log ();
  1113. X    exit (1);
  1114. X    }
  1115. X}
  1116. X
  1117. X// This routine returns a copy of its parameter.
  1118. Xchar    *new_string (const char *old_string)
  1119. X{   char    *string_ptr = new char [strlen (old_string) + 1];
  1120. X    strcpy (string_ptr, old_string);
  1121. X    return string_ptr;
  1122. X}
  1123. X
  1124. X// This routine returns the concatenation of its first and second parameter.
  1125. Xchar    *join_string (const char *first_string, const char *second_string)
  1126. X{   return new_string (form ("%s%s", first_string, second_string));    }
  1127. /
  1128. echo 'x - newsfeedlist.sh'
  1129. sed 's/^X//' > newsfeedlist.sh << '/'
  1130. X#! /bin/sh
  1131. Xfor i in $*
  1132. Xdo
  1133. X    case $i in
  1134. X    -h)    hold=;
  1135. X        continue;;
  1136. X    -h*)    hold="$hold `echo x$i|sed -e s/...//`"
  1137. X        continue;;
  1138. X    -)    size=;
  1139. X        continue;;
  1140. X    -*)    size=$i;
  1141. X        continue;;
  1142. X    //*)    continue;;
  1143. X    esac
  1144. X    /usr/lib/news/newsfeed $size $i $hold
  1145. Xdone
  1146. /
  1147. echo 'x - newsfeed.man'
  1148. sed 's/^X//' > newsfeed.man << '/'
  1149. X.TH NEWSFEED 1 "12 November 1986"
  1150. X.SH NAME
  1151. Xnewsfeed \- simultaneous multi-site news feeder
  1152. X.SH SYNOPSIS
  1153. X.B newsfeed
  1154. X[-max_size] base_file [list_of_hold_sites]
  1155. X.SH DESCRIPTION
  1156. X.I newsfeed
  1157. Xtakes a list of news articles (with sitenames) in file
  1158. X.I base_file
  1159. Xand batches them simultaneously for the given sites after changing to
  1160. Xthe batching directory (whatever was compiled in).
  1161. XEach line is a set of space\-separated strings,
  1162. Xthe first string being the full pathname of a news article.
  1163. XThe remaining strings are sites to which that news article is being fed.
  1164. XIf there are no sites, the last component of
  1165. X.I base_file
  1166. Xis used (old batch format).
  1167. X.PP
  1168. XIf the
  1169. X.I max_size
  1170. Xparameter is given, each batch is limited to that maximum size
  1171. X(unless a single article is greater than that size).
  1172. XThus, a maximum batch size of 1 causes each article to be contained in its own
  1173. Xbatch.
  1174. XA maximum batch size of 0 means there is no upper limit.
  1175. XThe default is whatever is compiled in.
  1176. X.PP
  1177. XAny
  1178. X.I hold_sites
  1179. Xgiven are saved to a file for later processing.
  1180. XThis may be used, for example, when those sites are down for extended periods.
  1181. X.SH FILES
  1182. X.ta \w'base_file.newlog  'u
  1183. Xbase_file    input file
  1184. X.br
  1185. Xbase_file.input    temporary file for error recovery
  1186. X.br
  1187. Xbase_file.proc    batch being processed
  1188. X.br
  1189. Xbase_file.again    batch to be reprocessed
  1190. X.br
  1191. Xbase_file.cmd    command to queue the batch
  1192. X.br
  1193. X    (takes a list of sites as parameters)
  1194. X.br
  1195. Xbase_file.log    log file
  1196. X.br
  1197. Xbase_file.newlog    temporary log file
  1198. X.br
  1199. Xbase_file.hold    list of held articles and their sites
  1200. X.SH "SEE ALSO"
  1201. Xnewsfeedlist(1)
  1202. X.SH AUTHOR
  1203. XStephen J. Muir, Lancaster University, England, UK
  1204. /
  1205. echo 'x - newsfeedlist.man'
  1206. sed 's/^X//' > newsfeedlist.man << '/'
  1207. X.TH NEWSFEEDLIST 1 "12 November 1986"
  1208. X.SH NAME
  1209. Xnewsfeedlist \- invoke news feeder repeatedly
  1210. X.SH SYNOPSIS
  1211. X.B newsfeedlist
  1212. X[arguments]
  1213. X.SH DESCRIPTION
  1214. X.I newsfeedlist
  1215. Xtakes a list of arguments
  1216. Xand calls the simultaneous multi-site news feeder with each one in turn.
  1217. X.PP
  1218. XInterspersed with the arguments may be any of the following options:
  1219. X.TP 8
  1220. X.B \-num
  1221. Xset the maximum batch size
  1222. X.TP
  1223. X.B \-
  1224. Xrestore the maximum batch size to the default
  1225. X.TP
  1226. X.B \-hsite
  1227. Xadd
  1228. X.B site
  1229. Xto the list of
  1230. X.I hold
  1231. Xsites
  1232. X.TP
  1233. X.B -h
  1234. Xdelete the list of
  1235. X.I hold
  1236. Xsites
  1237. X.TP
  1238. X.B //
  1239. Xany argument beginning with this string is ignored
  1240. X.SH "SEE ALSO"
  1241. Xnewsfeed(1)
  1242. X.SH AUTHOR
  1243. XStephen J. Muir, Lancaster University, England, UK
  1244. /
  1245. echo 'Part 01 of pack.out complete.'
  1246. exit
  1247. ----------------------------------- Cut Here ----------------------------------
  1248. -- 
  1249. EMAIL:    stephen@comp.lancs.ac.uk    | Post: University of Lancaster,
  1250. UUCP:    ...!mcvax!ukc!dcl-cs!stephen    |    Department of Computing,
  1251. Phone:    +44 524 65201 Ext. 4120        |    Bailrigg, Lancaster, UK.
  1252. Project:Alvey ECLIPSE Distribution    |    LA1 4YR
  1253.  
  1254.