home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / trn_12.zip / src / mthreads.c < prev    next >
C/C++ Source or Header  |  1993-12-04  |  40KB  |  1,557 lines

  1. /* $Id: mthreads.c,v 2.3 1992/12/14 00:14:06 davison Trn $
  2. */
  3.  
  4. /* mthreads.c -- for making and updating a discussion-thread database
  5. **
  6. ** We use the active file to read the high/low counts for each newsgroup
  7. ** and compare them with the corresponding values in a file called active2
  8. ** (which we created to keep the database high/lows in one place).  If they
  9. ** don't match, we read/update/write the group's thread file and move on.
  10. ** If the active2 file is missing or corrupted, it will be repaired in the
  11. ** normal course of operation.
  12. **
  13. ** Usage:  mthreads [-d[MM]] [-e[HHMM]] [-s[hsec]] [-aDfknv] [hierarchy_list]
  14. */
  15.  
  16. /*** OS2: we need OS2.H for the double-fopen-patch, where
  17.            we use a DosCopy call to doublicate the active2-file ***/
  18. #include <OS2.H>
  19.  
  20. #include "EXTERN.h"
  21. #include "common.h"
  22. #include "threads.h"
  23. #ifdef SERVER
  24. #include "server.h"
  25. #endif
  26. #include "INTERN.h"
  27. #include "mthreads.h"
  28.  
  29. #if !defined(FTRUNCATE) && !defined(CHSIZE)
  30. # ifdef F_FREESP
  31. #  define MYCHSIZE
  32. # else
  33. #  define MVTRUNC
  34. # endif
  35. #endif
  36.  
  37. #ifdef USESYSLOG
  38. #include <syslog.h>
  39. #else
  40. FILE *fp_log;
  41. #endif
  42.  
  43. FILE *fp_tmp, *fp_active, *fp_active2, *fp_active2w = Nullfp;
  44. bool eof_active = FALSE, eof_active2 = FALSE;
  45. long first, last, first2, last2;
  46. char ch, ch2;
  47.  
  48. struct stat filestat;
  49.  
  50. char buf[LBUFLEN+1];
  51.  
  52. char line[256];
  53. static char line2[256];
  54.  
  55. char fmt_active2[] = "%s %010ld %07ld %c\n";
  56.  
  57. /*** OS2: now we need two filenames, one for the old active
  58.           file, which is only read, and one for the new one,
  59.           which will be written. So we define a new variable
  60.           which is called "filenameold".   ***/
  61.  
  62. char *filename;
  63. char filenameold[254+1];
  64.  
  65. typedef struct _active_line {
  66.     struct _active_line *link;
  67.     char *name;
  68.     long last;
  69.     long first;
  70.     char type;
  71. } ACTIVE_LINE;
  72.  
  73. #define Nullact Null(ACTIVE_LINE*)
  74.  
  75. ACTIVE_LINE *line_root = Nullact, *last_line = Nullact, *pline = Nullact;
  76.  
  77. bool force_flag = FALSE, kill_mthreads = FALSE, no_processing = FALSE;
  78. bool add_new = FALSE, rebuild = FALSE, zap_thread = FALSE;
  79. bool acttimes_flag = FALSE, grevious_error;
  80. bool initializing = TRUE;
  81. int daemon_delay = 0, log_verbosity = 0, debug = 0, slow_down = 0;
  82. long expire_time = 0;
  83. char *hierarchy_list = NULL;
  84. long truncate_len = -1;
  85.  
  86. char nullstr[1] = "";
  87.  
  88. extern int locked, cron_locking;
  89.  
  90. #ifdef TZSET
  91. time_t tnow;
  92. #else
  93. struct timeb ftnow;
  94. #endif
  95.  
  96. #define TIMER_FIRST 1
  97. #define TIMER_DEFAULT (10 * 60)
  98.  
  99. int processed_groups = 0, added_groups = 0, removed_groups = 0;
  100. int action;
  101.  
  102. #define NG_DEFAULT    0
  103. #define NG_MATCH    1
  104. #define NG_SKIP        2
  105.  
  106. #ifdef SERVER
  107. char *server;
  108. #else
  109. time_t last_modified = 0;
  110. #endif
  111.  
  112. SIGRET alarm_handler(), int_handler();
  113. bool makethreads ANSI((void));
  114. void log_startup ANSI((void));
  115. void log_stats ANSI((void));
  116.  
  117. int
  118. main(argc, argv)
  119. int  argc;
  120. char *argv[];
  121. {
  122.     int fd;
  123.     long pid;
  124.  
  125. /*** OS2: Now we load the settings out of the uupc-rc-files.
  126.           This settings contain the information, which was
  127.           previously hardcoded in the config.h-file by
  128.           the Configure-shell-script.       ***/
  129.  
  130.     if (!load_uupc_rc())
  131.     {  fprintf(stderr,"An error occurred while reading the uupc-rc-files!\n\
  132. The following settings must be defined in the UUPCSYSRC-file:\n\
  133.        MailServ, NodeName, NewsDir, Domain, TempDir, MailDir, rmail\n");
  134.        fprintf(stderr,"These settings must be defined in the UUPCUSRRC-file:\n\
  135.        Mailbox, Signature, Name, Home, Organization, Editor\n");
  136.        fprintf(stderr,"And these settings must be defined in the UUPCTRNRC-file:\n\
  137.        Newsadmin, TrnLib, LocalDist, OrganizationDist, CityDist,\n\
  138.        StateDist, Countrydist, ContinentDist\n");
  139.        fprintf(stderr,"\nDon't forget, these settings are required, not optional!\n");
  140.        fflush(stderr);
  141.        exit(1);
  142.     }
  143.  
  144.     while (--argc) {
  145.     if (**++argv == '-') {
  146.         while (*++*argv) {
  147.         switch (**argv) {
  148.         case 'a':        /* automatically thread new groups */
  149.             add_new = TRUE;
  150.             break;
  151.         case 'c':        /* continue trying to lock */
  152.             cron_locking = TRUE;
  153.             break;
  154.         case 'D':        /* run in debug mode */
  155.             debug++;
  156.             break;
  157.         case 'd':        /* run in daemon mode */
  158.             if (*++*argv <= '9' && **argv >= '0') {
  159.             daemon_delay = atoi(*argv) * 60;
  160.             while (*++*argv <= '9' && **argv >= '0') {
  161.                 ;
  162.             }
  163.             } else {
  164.             daemon_delay = TIMER_DEFAULT;
  165.             }
  166.             --*argv;
  167.             break;
  168.         case 'e': {        /* enhanced expire processing */
  169.             struct tm *ts;
  170.             long desired;
  171.  
  172.             (void) time(&expire_time);
  173.             ts = localtime(&expire_time);
  174.  
  175.             if (*++*argv <= '9' && **argv >= '0') {
  176.             desired = atol(*argv);
  177.             if (desired/100 > 23 || desired%100 > 59) {
  178.                 fprintf(stderr, "Illegal expire time: '%04d'\n",
  179.                 desired);
  180.                 exit(1);
  181.             }
  182.             desired = (desired/100)*60 + desired%100;
  183.             while (*++*argv <= '9' && **argv >= '0') {
  184.                 ;
  185.             }
  186.             } else {
  187.             desired = 30;            /* 0030 = 12:30am */
  188.             }
  189.             --*argv;
  190.             desired -= ts->tm_hour * 60 + ts->tm_min;
  191.             if (desired < 0) {
  192.             desired += 24 * 60;
  193.             }
  194.             expire_time += desired * 60 - ts->tm_sec;
  195.             break;
  196.          }
  197.         case 'f':        /* force each group to process */
  198.             force_flag = TRUE;
  199.             break;
  200.         case 'k':        /* kill running mthreads */
  201.             kill_mthreads = TRUE;
  202.             break;
  203.         case 'n':        /* don't process anything */
  204.             no_processing = TRUE;
  205.             break;
  206.         case 's':        /* sleep between articles */
  207.             if (*++*argv <= '9' && **argv >= '0') {
  208.             slow_down = atoi(*argv);
  209.             while (*++*argv <= '9' && **argv >= '0') {
  210.                 ;
  211.             }
  212.             } else {
  213.             slow_down = 1L * 1000 * 1000;
  214.             }
  215.             --*argv;
  216.             break;
  217.         case 't':        /* maintain active.times file */
  218. #ifdef ACTIVE_TIMES
  219.             if (strEQ(ACTIVE_TIMES, "nntp")) {
  220.             fprintf(stderr, "Ignoring the -t option.\n");
  221.             } else {
  222.             acttimes_flag = TRUE;
  223.             }
  224. #else
  225.             fprintf(stderr, "Ignoring the -t option.\n");
  226. #endif
  227.             break;
  228.         case 'v':        /* get more verbose in the log file */
  229.             log_verbosity++;
  230.             break;
  231.         case 'z':        /* destroy .thread on severe signal */
  232.             zap_thread = TRUE;
  233.             break;
  234.         default:
  235.             fprintf(stderr, "Unknown option: '%c'\n", **argv);
  236.             exit(1);
  237.         }
  238.         }
  239.     } else {
  240.         if (hierarchy_list) {
  241.         fprintf(stderr, "Specify the newsgroups in one comma-separated list.\n");
  242.         exit(1);
  243.         }
  244.         hierarchy_list = *argv;
  245.     }
  246.     }
  247.  
  248.     /* Initialize umask(), file_exp(), etc. */
  249.     mt_init();
  250.  
  251.     /* If this is a kill request, look for the daemon lock file. */
  252.     if (kill_mthreads) {
  253.     if (!mt_lock(DAEMON_LOCK, SIGTERM)) {
  254.         fprintf(stderr, "No mthreads daemon is running.\n");
  255.         wrap_it_up(1);
  256.     }
  257.     fprintf(stderr, "Killed mthreads daemon.\n");
  258.     wrap_it_up(0);
  259.     }
  260.  
  261.     /* See if an mthreads pass is already going. */
  262.     if (mt_lock(PASS_LOCK, 0) != 0 && !daemon_delay) {
  263.     fprintf(stderr, "mthreads is already running.\n");
  264.     wrap_it_up(1);
  265.     }
  266.  
  267. #ifdef USESYSLOG
  268. # ifdef LOG_DAEMON
  269.     openlog("mthreads", LOG_PID, USESYSLOG);
  270. # else
  271.     openlog("mthreads", LOG_PID);
  272. # endif
  273. #else
  274.     /* Open our log file */
  275.     filename = file_exp(MTLOG);
  276.     change_bsl2sl(filename);
  277.     if ((fp_log = fos2open(filename, "a")) == Nullfp) {
  278.     fprintf(stderr, "Unable to open `%s'.\n", filename);
  279.     wrap_it_up(1);
  280.     }
  281. #endif
  282.  
  283. #ifdef SIGHUP
  284.     if (sigset(SIGHUP, SIG_IGN) != SIG_IGN) {
  285.     sigset(SIGHUP, int_handler);
  286.     }
  287. #endif
  288.     if (sigset(SIGINT, SIG_IGN) != SIG_IGN) {
  289.     sigset(SIGINT, int_handler);
  290.     }
  291. #ifdef SIGQUIT
  292.     if (sigset(SIGQUIT, SIG_IGN) != SIG_IGN) {
  293.     sigset(SIGQUIT, int_handler);
  294.     }
  295. #endif
  296.     sigset(SIGTERM, int_handler);
  297. #ifdef SIGBUS
  298.     sigset(SIGBUS, int_handler);
  299. #endif
  300.     sigset(SIGSEGV, int_handler);
  301. #ifdef SIGTTIN
  302.     sigset(SIGTTIN, SIG_IGN);
  303.     sigset(SIGTTOU, SIG_IGN);
  304. #endif
  305.     sigset(SIGALRM, SIG_IGN);
  306. #ifdef SERVER
  307.     sigset(SIGPIPE, int_handler);
  308. #endif
  309. #ifdef lint
  310.     alarm_handler();            /* foolishness for lint's sake */
  311.     int_handler(SIGINT);
  312. #endif
  313.  
  314.     /* Ensure this machine has the right byte-order for the database */
  315.     filename = file_exp(DBINIT);
  316.     change_bsl2sl(filename);
  317.     if ((fp_tmp = fos2open(filename, FOPEN_RB)) == Nullfp
  318.      || fread((char*)&mt_bmap,1,sizeof (BMAP), fp_tmp) < sizeof (BMAP)-1) {
  319.     if (fp_tmp != Nullfp) {
  320.         fclose(fp_tmp);
  321.     }
  322.      write_db_init:
  323.     mybytemap(&mt_bmap);
  324.     if ((fp_tmp = fos2open(filename, FOPEN_WB)) == Nullfp) {
  325.         log_entry("Unable to create file: `%s'.\n", filename);
  326.         wrap_it_up(1);
  327.     }
  328.     mt_bmap.version = DB_VERSION;
  329.     fwrite((char*)&mt_bmap, 1, sizeof (BMAP), fp_tmp);
  330.     fclose(fp_tmp);
  331.     } else {
  332.     int i;
  333.  
  334.     fclose(fp_tmp);
  335.     if (mt_bmap.version != DB_VERSION) {
  336.         if (mt_bmap.version == DB_VERSION-1) {
  337.         rebuild = TRUE;
  338.         log_entry("Upgrading database to version %d.\n", DB_VERSION);
  339.         goto write_db_init;
  340.         }
  341.         log_entry("** Database is not the right version (%d instead of %d) **\n",
  342.         mt_bmap.version, DB_VERSION);
  343.         wrap_it_up(1);
  344.     }
  345.     mybytemap(&my_bmap);
  346.     for (i = 0; i < sizeof (LONG); i++) {
  347.         if (my_bmap.l[i] != mt_bmap.l[i]
  348.          || (i < sizeof (WORD) && my_bmap.w[i] != mt_bmap.w[i])) {
  349.         log_entry("\
  350. ** Byte-order conflict -- re-run from a compatible machine **\n\
  351. \t\tor remove the current thread files, including db.init **\n");
  352.         wrap_it_up(1);
  353.         }
  354.     }
  355.     }
  356.  
  357. #ifdef SERVER
  358.     if ((server = get_server_name(0)) == NULL) {
  359.     log_entry("Couldn't find name of news server.\n");
  360.     wrap_it_up(1);
  361.     }
  362. #endif
  363.  
  364.     initializing = FALSE;
  365.  
  366. /*** OS2: Ok, let's disable daemon mode, this is for
  367.           now the easiest way to avoid the fork's which
  368.           are needed in daemon-mode.        ***/
  369.  
  370.     /* If we're not in daemon mode, run through once and quit. */
  371.     daemon_delay = 0;
  372.     if (!daemon_delay) {
  373.     log_startup();
  374.     setbuf(stdout, Nullch);
  375.     extra_expire = (expire_time != 0);
  376.     makethreads();
  377.     } else {
  378.     cron_locking = FALSE;
  379.     if (mt_lock(DAEMON_LOCK, 0) != 0) {
  380.         fprintf(stderr, "An mthreads daemon is already running.\n");
  381.         wrap_it_up(1);
  382.     }
  383.     /* For daemon mode, we cut ourself off from anything tty-related and
  384.     ** run in the background (involves forks, but no knives).
  385.     */
  386.     close(0);
  387.     if (open("/dev/null", 2) != 0) {
  388.         fprintf(stderr, "unable to open /dev/null!\n");
  389.         wrap_it_up(1);
  390.     }
  391.     close(1);
  392.     close(2);
  393.     dup(0);
  394.     dup(0);
  395.  
  396. /*** OS2: And now let's patch out this fork's.   ***/
  397.  
  398. /*    while ((pid = fork()) < 0) {  */
  399. /*        sleep(2);                 */
  400. /*    }                             */
  401. /*    if (pid) {                    */
  402. /*        exit(0);                  */
  403. /*    }                             */
  404. /*#ifdef TIOCNOTTY                              */
  405. /*    if ((fd = open("/dev/tty", 1)) >= 0) {    */
  406. /*        ioctl(fd, TIOCNOTTY, (int*)0);        */
  407. /*        close(fd);                            */
  408. /*    }                                         */
  409. /*#else                                         */
  410. /*    (void) setpgrp();                         */
  411. /*    while ((pid = fork()) < 0) {              */
  412. /*        sleep(2);                             */
  413. /*    }                                         */
  414. /*    if (pid) {                                */
  415. /*        exit(0);                              */
  416. /*    }                                         */
  417. /*#endif                                        */
  418.     /* Put our pid in the lock file for death detection */
  419.     if ((fp_tmp = fos2open(file_exp(MTDLOCK), "w")) != Nullfp) {
  420.         fprintf(fp_tmp, "%ld\n", (long)getpid());
  421.         fclose(fp_tmp);
  422.     }
  423.     log_startup();
  424.  
  425.     sigset(SIGALRM, alarm_handler);
  426.  
  427.     /* Start timer -- first interval is shorter than all others */
  428.     alarm(TIMER_FIRST);
  429.     for (;;) {
  430.  
  431. /*** OS2: This is the daemon mode, but it is disabled
  432.           in this version, so let's patch out the pause  ***/
  433.  
  434. /*        pause();        /* let alarm go off */
  435.         alarm(0);
  436.  
  437. #ifndef USESYSLOG
  438.         /* Re-open our log file, if needed */
  439.         if (!fp_log && !(fp_log = fos2open(file_exp(MTLOG), "a"))) {
  440.         wrap_it_up(1);
  441.         }
  442. #endif
  443. #ifndef SERVER
  444. /*** OS2: because we get settings out of uupc-rc-files,
  445.           we don't use file_exp to expand the filename ***/
  446. /*        if (stat(file_exp(ACTIVE), &filestat) < 0) {   */
  447.         if (stat(ACTIVE, &filestat) < 0) {
  448.             if (stat(ACTIVE_OPT, &filestat) < 0) {
  449.                 log_entry("Unable to stat active file -- quitting.\n");
  450.         wrap_it_up(1);
  451.         }
  452.     }
  453. #endif
  454.         if (expire_time && time(Null(time_t*)) > expire_time) {
  455.         expire_time += 24L * 60 * 60;
  456.         extra_expire = TRUE;
  457.         }
  458. #ifdef SERVER
  459.         makethreads();    /* NNTP version always compares files */
  460. #else
  461.         if (extra_expire || filestat.st_mtime != last_modified) {
  462.         last_modified = filestat.st_mtime;
  463.         if (!makethreads()) {
  464.             last_modified--;
  465.         }
  466.         }
  467. #endif
  468.         alarm(daemon_delay);
  469. #ifndef USESYSLOG
  470.         fclose(fp_log);    /* close the log file while we sleep */
  471.         fp_log = Nullfp;
  472. #endif
  473.     } /* for */
  474.     }/* if */
  475.  
  476.     wrap_it_up(0);
  477.     return 0;                /* NOTREACHED */
  478. }
  479.  
  480. SIGRET
  481. alarm_handler()
  482. {
  483.     sigset(SIGALRM, alarm_handler);
  484. }
  485.  
  486. SIGRET
  487. int_handler(sig)
  488. int sig;
  489. {
  490.     static int visits = 0;
  491.     int ret = 0;
  492.  
  493.     if (++visits > 4) {
  494.     wrap_it_up(1);
  495.     }
  496.  
  497. #ifndef USESYSLOG
  498.     /* Re-open our log file, if needed */
  499.     if (fp_log || (fp_log = fos2open(file_exp(MTLOG), "a")))
  500. #endif
  501.     {
  502.     switch (sig) {
  503.     case SIGTERM:
  504. #ifdef SIGHUP
  505.     case SIGHUP:
  506. #endif
  507. #ifdef SIGQUIT
  508.     case SIGQUIT:
  509. #endif
  510.         log_entry("halt requested.\n");
  511.         zap_thread = 0;
  512.         break;
  513. #ifdef SERVER
  514.     case SIGPIPE:
  515.         log_entry("broken pipe -- trying to continue.\n");
  516.         sigset(SIGPIPE, int_handler);
  517.         return;
  518. #endif
  519. #ifdef SIGBUS
  520.         case SIGBUS:
  521. #endif
  522.         case SIGSEGV:
  523.         log_error("** Severe signal: %d **\n", sig);
  524.         /* Destroy offending thread file if requested to do so. */
  525.         if (zap_thread) {
  526.             unlink(thread_name(line));
  527.             log_entry("Destroyed thread file for %s\n", line);
  528.         }
  529.         break;
  530.     default:
  531.         log_entry("interrupt %d received.\n", sig);
  532.         zap_thread = 0;
  533.         ret = 1;
  534.         break;
  535.     }
  536.     }
  537.     if (!daemon_delay) {
  538.     printf("Interrupt %d!\n", sig);
  539.     if (zap_thread) {
  540.         printf("Destroyed thread file for %s\n", line);
  541.     }
  542.     }
  543.  
  544.     /* If we're in the middle of writing the new active2 file, finish it. */
  545.     if (fp_active2w) {
  546.     if (*line2) {
  547.         if (index(line2, ' ')) {
  548.         fputs(line2, fp_active2w);
  549.         } else {
  550.         fprintf(fp_active2w, fmt_active2, line2, last2, first2, ch2);
  551.         }
  552.     }
  553.     for (pline = line_root; pline; pline = pline->link) {
  554.         fprintf(fp_active2w, fmt_active2,
  555.             pline->name, pline->last, pline->first, pline->type);
  556.     }
  557.     if (!eof_active2) {
  558.         while (fgets(line2, sizeof line2, fp_active2)) {
  559.         fputs(line2, fp_active2w);
  560.         }
  561.     }
  562.     log_stats();
  563.     }
  564.     wrap_it_up(ret);
  565. }
  566.  
  567. void
  568. wrap_it_up(ret)
  569. int ret;
  570. {
  571.     mt_unlock(locked);
  572.     (void) chdir(MTLIB);        /* for *mon.out files, etc. */
  573.     exit(ret);
  574. }
  575.  
  576. /* Process the active file, creating/modifying the active2 file and
  577. ** creating/modifying the thread data files.
  578. */
  579. bool
  580. makethreads()
  581. {
  582.     register char *cp, *cp2;
  583.     char data_file_open;
  584.     bool update_successful, old_groups, touch_thread;
  585. #ifdef SERVER
  586.     int server_failure = 0;
  587. #endif
  588.  
  589.     /* See if an mthreads pass is already going. */
  590.     if (!(locked & PASS_LOCK) && mt_lock(PASS_LOCK, 0) != 0) {
  591.     log_entry("unable to get a lock for this pass.\n");
  592.     return FALSE;
  593.     }
  594. #ifdef SERVER
  595.     if (!open_server()) {
  596.     return FALSE;
  597.     }
  598.     put_server("LIST");    /* ask server for the active file */
  599.     get_server(line, sizeof line);
  600.     if (*line != CHAR_OK) {
  601.     log_entry("Unable to get active file from server.\n");
  602.     close_server();
  603.     return FALSE;
  604.     }
  605.     if ((fp_active = fos2open(file_exp(ACTIVE1), "w+")) == Nullfp) {
  606.     log_entry("Unable to write the active1 file -- quitting.\n");
  607.     wrap_it_up(1);
  608.     }
  609.     while (1) {
  610.     if (get_server(line, sizeof line) < 0) {
  611.         log_entry("Server failed to send entire active file.\n");
  612.         fclose(fp_active);
  613.         close_server();
  614.         return FALSE;
  615.     }
  616.     if (*line == '.') {
  617.         break;
  618.     }
  619.     fputs(line, fp_active);
  620.     putc('\n', fp_active);
  621.     }
  622.     if (ferror(fp_active)) {
  623.     log_entry("Error writing to active1 file.\n");
  624.     fclose(fp_active);
  625.     close_server();
  626.     return FALSE;
  627.     }
  628.     fseek(fp_active, 0L, 0);        /* rewind for read */
  629. #else /* not SERVER */
  630. /*** OS2: because we get settings out of the uupc-rc-files,
  631.           we don't use the file_exp-function here ***/
  632. /*    if ((fp_active = fos2open(file_exp(ACTIVE), "r")) == Nullfp) {  */
  633.     if ((fp_active = fos2open(ACTIVE, "r")) == Nullfp) {
  634.         if ((fp_active = fos2open(ACTIVE_OPT, "r")) == Nullfp) {
  635.             log_entry("Unable to open the active file.\n");
  636.         wrap_it_up(1);
  637.     }
  638.     }
  639. #endif
  640.  
  641. /*** OS2: emx gcc cannot open a file twice, although that
  642.           would be possible under OS/2. So we try to copy the
  643.           file first, and then open the copied(old)-one only
  644.           for reading, and the other one for writing.
  645.  
  646.           We need to allocate the string filenameold, because
  647.           file_exp returns only a pointer to a string which
  648.           is statically defined in file_exp (side offect). ***/
  649.  
  650. /*OS2-patch:*/
  651.     strcpy(filenameold,file_exp(ACTIVE2OLD));
  652.  
  653.     filename = file_exp(ACTIVE2);
  654.  
  655.     if (DosCopy(filename,filenameold,DCPY_EXISTING))  {
  656.              /*** DosCopy not successful ? ***/
  657.        if ((fp_active2w = fos2open(filename, "r")) != Nullfp) {
  658.              /*** DosCopy not successful, although active2 exists?***/
  659.           log_entry("Cannot copy active2 file (OS/2 port).\n");
  660.           wrap_it_up(1);
  661.        }
  662.        else {
  663.           if ((fp_active2 = fos2open(filenameold, "w")) == Nullfp) {
  664.              log_entry("Unable to create the active2.old file (OS/2 port).\n");
  665.              wrap_it_up(1);
  666.           }
  667.           else {
  668.              fclose(fp_active2);
  669.           }
  670.        }
  671.     }
  672. /*OS2-patch-end*/
  673.     if ((fp_active2w = fos2open(filename, "r+")) == Nullfp) {
  674.     if ((fp_active2w = fos2open(filename, "w")) == Nullfp) {
  675.         log_entry("Unable to open the active2 file for update.\n");
  676.         wrap_it_up(1);
  677.     }
  678.     /* Add existing groups to active.times file with ancient date. */
  679.     old_groups = TRUE;
  680.     } else {
  681.     old_groups = FALSE;
  682.     }
  683. /*OS2-patch:*/
  684. /*    if ((fp_active2 = fos2open(filename, "r")) == Nullfp) {     */
  685.     if ((fp_active2 = fos2open(filenameold, "r")) == Nullfp) {
  686. /*OS2-patch-end*/
  687.     log_entry(filename);
  688.     log_entry(filenameold);
  689.     log_entry("Unable to open the active2 file.\n");
  690.     wrap_it_up(1);
  691.     }
  692.     if (extra_expire && log_verbosity) {
  693.     log_entry("Using enhanced expiration for this pass.\n");
  694.     }
  695.  
  696.     /* What time is it? */
  697. #ifdef TZSET
  698.     (void) time(&tnow);
  699.     (void) tzset();
  700. #else
  701.     (void) ftime(&ftnow);
  702. #endif
  703.  
  704.     eof_active = eof_active2 = FALSE;
  705.     fp_tmp = Nullfp;
  706.  
  707.     /* Loop through entire active file. */
  708.     for (;;) {
  709.     if (eof_active || !fgets(line, sizeof line, fp_active)) {
  710.         if (eof_active2 && !line_root) {
  711.         break;
  712.         }
  713.         eof_active = TRUE;
  714.         ch = 'x';
  715.     } else {
  716.         cp = line + strlen(line) - 1;
  717.         if (*cp == '\n') {
  718.         *cp = '\0';
  719.         }
  720.         if (!(cp = index(line, ' '))) {
  721.         log_entry("** line in 'active' has no space: %s **\n", line);
  722.         continue;
  723.         }
  724.         *cp = '\0';
  725.         if (sscanf(cp+1, "%ld %ld %c", &last, &first, &ch) != 3) {
  726.         log_entry("** digits corrupted in 'active': %s %s **\n",
  727.             line, cp+1);
  728.         continue;
  729.         }
  730.         if (last < first - 1) {
  731.         log_entry("** bogus group values in 'active': %s %s **\n",
  732.             line, cp+1);
  733.         continue;
  734.         }
  735.     }
  736.     if (debug > 1 || log_verbosity > 3) {
  737.         log_entry("Processing %s:\n", line);
  738.     }
  739.     data_file_open = 0;
  740.     /* If we've allocated some lines in memory while searching for
  741.     ** newsgroups (they've scrambled the active file on us), check
  742.     ** them first.
  743.     */
  744.     last_line = Nullact;
  745.     for (pline = line_root; pline; pline = pline->link) {
  746.         if (eof_active || strEQ(line, pline->name)) {
  747.         strcpy(line2, pline->name);
  748.         free(pline->name);
  749.         first2 = pline->first;
  750.         last2 = pline->last;
  751.         ch2 = pline->type;
  752.         if (last_line) {
  753.             last_line->link = pline->link;
  754.         } else {
  755.             line_root = pline->link;
  756.         }
  757.         free(pline);
  758.         break;
  759.         }
  760.         last_line = pline;
  761.     }/* for */
  762.     touch_thread = FALSE;
  763.  
  764.     /* If not found yet, check the active2 file. */
  765.     if (!pline) {
  766.         for (;;) {
  767.         if (eof_active2 || !fgets(line2, sizeof line2, fp_active2)) {
  768.             /* At end of file, check if the thread data file exists.
  769.             ** If so, use its high/low values.  Else, default to
  770.             ** some initial values.
  771.             */
  772.             eof_active2 = TRUE;
  773.             if (eof_active) {
  774.             break;
  775.             }
  776.             strcpy(line2, line);
  777.             if ((data_file_open = init_data(thread_name(line)))) {
  778.             last2 = total.last;
  779.             first2 = total.first;
  780.             ch2 = 'y';
  781.             } else {
  782.             total.first = first2 = first;
  783.             if (add_new && (!hierarchy_list
  784.               || ngmatch(hierarchy_list, line) == NG_MATCH)) {
  785.                 total.last = last2 = first - 1;
  786.                 ch2 = (ch == '=' ? 'x' : ch);
  787.                 touch_thread = TRUE;
  788.                 added_groups += (ch2 != 'x');
  789.             } else {
  790.                 total.last = last2 = last;
  791.                 ch2 = (ch == '=' ? 'X' : toupper(ch));
  792.             }
  793.             }
  794.             data_file_open++;        /* (1 == empty, 2 == open) */
  795. #ifdef ACTIVE_TIMES
  796.             /* Found a new group -- see if we need to log it. */
  797.             if (acttimes_flag) {
  798.             if (!fp_tmp && !(fp_tmp = fos2open(ACTIVE_TIMES, "a"))) {
  799.                 log_entry("unable to append to %s.\n",ACTIVE_TIMES);
  800.                 acttimes_flag = FALSE;
  801.             } else {
  802.                 fprintf(fp_tmp, "%s %ld mthreads@%s\n", line,
  803.                 old_groups ? 30010440L : time(Null(time_t)),
  804.                 OURDOMAIN);
  805.             }
  806.             }
  807. #endif
  808.             break;
  809.         }
  810.         if (!(cp2 = index(line2, ' '))) {
  811.             log_entry("active2 line has no space: %s\n", line2);
  812.             continue;
  813.         }
  814.         *cp2 = '\0';
  815.         if (sscanf(cp2+1,"%ld %ld %c",&last2,&first2,&ch2) != 3) {
  816.             log_entry("active2 digits corrupted: %s %s\n",
  817.             line2, cp2+1);
  818.             continue;
  819.         }
  820.         /* Check if we're still in-sync */
  821.         if (eof_active || strEQ(line, line2)) {
  822.             break;
  823.         }
  824.         /* Nope, we've got to go looking for this line somewhere
  825.         ** down in the file.  Save each non-matching line in memory
  826.         ** as we go.
  827.         */
  828.         pline = (ACTIVE_LINE*)safemalloc(sizeof (ACTIVE_LINE));
  829.         pline->name = savestr(line2);
  830.         pline->last = last2;
  831.         pline->first = first2;
  832.         pline->type = ch2;
  833.         pline->link = Nullact;
  834.         if (!last_line) {
  835.             line_root = pline;
  836.         } else {
  837.             last_line->link = pline;
  838.         }
  839.         *line2 = '\0';
  840.         last_line = pline;
  841.         }/* for */
  842.         if (eof_active && eof_active2) {
  843.         break;
  844.         }
  845.     }/* if !pline */
  846.     if (eof_active) {
  847.         strcpy(line, line2);
  848.         if (truncate_len < 0) {
  849.         truncate_len = ftell(fp_active2w);
  850.         }
  851.     }
  852.     if (rebuild) {
  853.         unlink(thread_name(line));
  854.     }
  855.     update_successful = FALSE;
  856.     if (hierarchy_list && !add_new) {
  857.         switch ((action = ngmatch(hierarchy_list, line))) {
  858.         case NG_MATCH:            /* if unthreaded, add it */
  859.         if (ch2 < 'a' && ch != 'x' && ch != '=') {
  860.             total.last = last2 = first2 - 1;
  861.             touch_thread = TRUE;
  862.             added_groups++;
  863.         }
  864.         break;
  865.         case NG_SKIP:            /* if threaded, remove it */
  866.         if (ch2 >= 'a') {
  867.             unlink(thread_name(line));
  868.             removed_groups++;
  869.         }
  870.         break;
  871.         }
  872.     } else {
  873.         action = (ch2 < 'a' ? NG_SKIP : NG_MATCH);
  874.     }
  875.     if (action == NG_DEFAULT || (debug && action == NG_SKIP)) {
  876.         dont_read_data(data_file_open);    /* skip silently */
  877.         if (touch_thread) {
  878.         (void) write_data(thread_name(line));
  879.         }
  880.     } else if (ch == 'x' || ch == '=') {
  881.         if (!daemon_delay) {        /* skip 'x'ed groups */
  882.         putchar('x');
  883.         }
  884.         ch = (action == NG_SKIP ? 'X' : 'x');
  885.         if ((ch2 >= 'a' && ch2 != 'x') || force_flag) {
  886.         /* Remove thread file if group is newly 'x'ed out */
  887.         unlink(thread_name(line));
  888.         }
  889.         update_successful = TRUE;
  890.         dont_read_data(data_file_open);
  891.     } else if (action == NG_SKIP) {    /* skip excluded groups */
  892.         if (!daemon_delay) {
  893.         putchar('X');
  894.         }
  895.         ch = toupper(ch);
  896.         if (force_flag) {
  897.         unlink(thread_name(line));
  898.         }
  899.         update_successful = TRUE;
  900.         dont_read_data(data_file_open);
  901.     } else if (no_processing) {
  902.         if (!daemon_delay) {
  903.         putchar(',');
  904.         }
  905.         ch2 = ch;
  906.         dont_read_data(data_file_open);
  907.         if (touch_thread) {
  908.         (void) write_data(thread_name(line));
  909.         }
  910.     } else if (!force_flag && !extra_expire && !rebuild
  911.      && first == first2 && last == last2) {
  912.         /* We're up-to-date here.  Skip it. */
  913.         if (!daemon_delay) {
  914.         putchar('.');
  915.         }
  916.         update_successful = TRUE;
  917.         dont_read_data(data_file_open);
  918.         if (touch_thread) {
  919.         (void) write_data(thread_name(line));
  920.         }
  921.     } else {
  922.         /* Looks like we need to process something. */
  923. #ifdef SERVER
  924.         if (server_failure == 1) {
  925.         if (!open_server()) {
  926.             log_entry("failed to re-open server for this pass.\n");
  927.             server_failure = 2;
  928.         } else {
  929.             server_failure = 0;
  930.         }
  931.         }
  932.         if (!server_failure) {
  933.         sprintf(buf, "GROUP %s", line);
  934.         put_server(buf);        /* go to next group */
  935.         if (get_server(buf, sizeof buf) < 0
  936.          || *buf != CHAR_OK) {
  937.             log_error("NNTP failure -- %s.\n", buf);
  938.             if (strnNE(buf, "400", 3)) {
  939.             close_server();
  940.             }
  941.             server_failure = 1;
  942.         }
  943.         }
  944.         if (server_failure) {
  945. #else
  946.         strcpy(cp = buf, line2);
  947.         while ((cp = index(cp, '.'))) {
  948.         *cp = '/';
  949.         }
  950.         filename = file_exp(buf);        /* relative to spool dir */
  951.         change_bsl2sl(filename);
  952.         change_bsl2sl(buf);
  953.         if (chdir(filename) < 0) {
  954.         if (errno != ENOENT) {
  955.             log_entry("Unable to chdir to `%s'.\n", filename);
  956.         }
  957. #endif
  958.         if (!daemon_delay) {
  959.             putchar('*');
  960.         }
  961.         dont_read_data(data_file_open);
  962.         } else {
  963.         filename = thread_name(line);
  964.         /* Try to open the data file only if we didn't try it
  965.         ** in the name matching code above.
  966.         */
  967.         if (!data_file_open--) {    /* (0 == haven't tried yet) */
  968.             if (!(data_file_open = init_data(filename))) {
  969.             total.last = first - 1;
  970.             total.first = first;
  971.             }
  972.         }
  973.  
  974.         if (data_file_open) {        /* (0 == empty, 1 == open) */
  975.             if (!read_data()) {    /* did read fail? */
  976.             if (debug) {
  977.                 strcpy(buf, filename);
  978.                 cp = rindex(buf, '/') + 1;
  979.                 strcpy(cp, "bad.read");
  980. /*** OS2: rename fails if destination already exists, so delete it before ***/
  981.                 unlink(buf);
  982.                 rename(filename, buf);
  983.             }
  984.             data_file_open = init_data(Nullch);
  985.             total.last = first - 1;
  986.             total.first = first;
  987.             }
  988.         }
  989.         grevious_error = FALSE;
  990.         process_articles(first, last);
  991.         processed_groups++;
  992.         if (!added_count && !expired_count && !touch_thread
  993.          && last == last2) {
  994.             (void) write_data(Nullch);
  995.             if (!daemon_delay) {
  996.             putchar(':');
  997.             }
  998.             update_successful = TRUE;
  999.         } else {
  1000.             strcpy(buf, filename);
  1001.             cp = rindex(buf, '/') + 1;
  1002.             strcpy(cp, NEW_THREAD);    /* write data as .new */
  1003.             if (write_data(buf) && !grevious_error) {
  1004. /*** OS2: rename fails if destination already exists, so delete it before ***/
  1005.             unlink(filename);
  1006.             rename(buf, filename);
  1007.             added_articles += added_count;
  1008.             expired_articles += expired_count;
  1009.             if (!daemon_delay) {
  1010.                 if (!total.root) {
  1011.                 putchar('-');
  1012.                 } else {
  1013.                 putchar('#');
  1014.                 }
  1015.             }
  1016.             update_successful = TRUE;
  1017.             } else {
  1018.             if (debug) {
  1019.                 cp = rindex(filename, '/') + 1;
  1020.                 strcpy(cp, "bad.write");
  1021. /*** OS2: rename fails if destination already exists, so delete it before ***/
  1022.                 unlink(filename);
  1023.                 rename(buf, filename);
  1024.             } else {
  1025.                 unlink(buf);    /* blow away bad write */
  1026.                 if (grevious_error) {
  1027.                 unlink(filename); /* blow away the .thread, */
  1028.                 (void) init_data(Nullch); /* set totals */
  1029.                 total.last = first-1;
  1030.                 total.first = first;
  1031.                 (void) write_data(filename); /* write it null */
  1032.                 }
  1033.             }
  1034.             if (!daemon_delay) {
  1035.                 putchar('!');
  1036.             }
  1037.             }/* if */
  1038.         }/* if */
  1039.         }/* if */
  1040.     }/* if */
  1041.     /* Finally, update the active2 entry for this newsgroup. */
  1042.     if (!eof_active) {
  1043.         if (update_successful) {
  1044.         fprintf(fp_active2w, fmt_active2, line, last, first, ch);
  1045.         } else {
  1046.         fprintf(fp_active2w, fmt_active2, line, last2, first2, ch2);
  1047.         }
  1048.     }
  1049.     *line2 = '\0';
  1050.     /* If we're not out of sync, keep active2 file flushed. */
  1051.     if (!line_root) {
  1052.         fflush(fp_active2w);
  1053.     }
  1054. #ifdef CHECKLOAD
  1055.     checkload();
  1056. #endif
  1057.     }/* for */
  1058.  
  1059. #ifdef SERVER
  1060.     if (!server_failure) {
  1061.     close_server();
  1062.     }
  1063. #endif
  1064.     fclose(fp_active);
  1065.     fclose(fp_active2);
  1066.  
  1067.     if (truncate_len >= 0) {
  1068. #ifdef FTRUNCATE
  1069.     if (ftruncate(fileno(fp_active2w), truncate_len) == -1)
  1070. #else
  1071. #ifdef MVTRUNC
  1072.     if (mvtrunc(file_exp(ACTIVE2), truncate_len) == -1)
  1073. #else
  1074.     if (chsize(fileno(fp_active2w), truncate_len) == -1)
  1075. #endif
  1076. #endif
  1077.     {
  1078.         log_entry("Unable to truncate the active2 file.\n");
  1079.     }
  1080.     truncate_len = -1;
  1081.     }
  1082.     fclose(fp_active2w);
  1083.     fp_active2w = Nullfp;
  1084.  
  1085.     if (fp_tmp) {
  1086.     fclose(fp_tmp);
  1087.     }
  1088.     log_stats();
  1089.     processed_groups = added_groups = removed_groups = 0;
  1090.     added_articles = expired_articles = 0;
  1091.  
  1092.     extra_expire = FALSE;
  1093.     rebuild = FALSE;
  1094.  
  1095.     mt_unlock(PASS_LOCK);        /* remove single-pass lock */
  1096.  
  1097.     return TRUE;
  1098. }
  1099.  
  1100. #ifdef SERVER
  1101. int
  1102. open_server()
  1103. {
  1104.     switch (server_init(server)) {
  1105.     case OK_NOPOST:
  1106.     case OK_CANPOST:
  1107.     return 1;
  1108.     case ERR_ACCESS:
  1109.     log_entry("Server %s rejected connection -- quitting.\n", server);
  1110.     wrap_it_up(1);
  1111.     default:
  1112.     log_entry("Couldn't connect with server %s.\n", server);
  1113.     return 0;
  1114.     }
  1115. }
  1116. #endif
  1117.  
  1118. /*
  1119. ** ngmatch - newsgroup name matching
  1120. **
  1121. ** returns NG_MATCH for a positive patch, NG_SKIP for a negative match,
  1122. ** and NG_DEFAULT if the group doesn't match at all.
  1123. **
  1124. ** "all" in a pattern is a wildcard that matches exactly one word;
  1125. ** it does not cross "." (NGDELIM) delimiters.
  1126. **
  1127. ** This matching code was borrowed from C news.
  1128. */
  1129.  
  1130. #define ALL "all"            /* word wildcard */
  1131.  
  1132. #define NGNEG '!'
  1133. #define NGSEP ','
  1134. #define NGDELIM '.'
  1135.  
  1136. int
  1137. ngmatch(ngpat, grp)
  1138. char *ngpat, *grp;
  1139. {
  1140.     register char *patp;        /* point at current pattern */
  1141.     register char *patcomma;
  1142.     register int depth;
  1143.     register int faildeepest = 0, hitdeepest = 0;    /* in case no match */
  1144.     register bool negation;
  1145.  
  1146.     for (patp = ngpat; patp != Nullch; patp = patcomma) {
  1147.     negation = FALSE;
  1148.     patcomma = index(patp, NGSEP);
  1149.     if (patcomma != Nullch) {
  1150.         *patcomma = '\0';        /* will be restored below */
  1151.     }
  1152.     if (*patp == NGNEG) {
  1153.         ++patp;
  1154.         negation = TRUE;
  1155.     }
  1156.     depth = onepatmatch(patp, grp); /* try 1 pattern, 1 group */
  1157.     if (patcomma != Nullch) {
  1158.         *patcomma++ = NGSEP;    /* point after the comma */
  1159.     }
  1160.     if (depth == 0) {        /* mis-match */
  1161.         ;                /* ignore it */
  1162.     } else if (negation) {
  1163.         /* record depth of deepest negated matched word */
  1164.         if (depth > faildeepest) {
  1165.         faildeepest = depth;
  1166.         }
  1167.     } else {
  1168.         /* record depth of deepest plain matched word */
  1169.         if (depth > hitdeepest) {
  1170.         hitdeepest = depth;
  1171.         }
  1172.     }
  1173.     }
  1174.     if (hitdeepest > faildeepest) {
  1175.     return NG_MATCH;
  1176.     } else if (faildeepest) {
  1177.     return NG_SKIP;
  1178.     } else {
  1179.     return NG_DEFAULT;
  1180.     }
  1181. }
  1182.  
  1183. /*
  1184. ** Match a pattern against a group by looking at each word of pattern in turn.
  1185. **
  1186. ** On a match, return the depth (roughly, ordinal number * k) of the rightmost
  1187. ** word that matches.  If group runs out first, the match fails; if pattern
  1188. ** runs out first, it succeeds.  On a failure, return zero.
  1189. */
  1190. int
  1191. onepatmatch(patp, grp)
  1192. char *patp, *grp;
  1193. {
  1194.     register char *rpatwd;        /* used by word match (inner loop) */
  1195.     register char *patdot, *grdot;    /* point at dots after words */
  1196.     register char *patwd, *grwd;    /* point at current words */
  1197.     register int depth = 0;
  1198.  
  1199.     for (patwd = patp, grwd = grp;
  1200.      patwd != Nullch && grwd != Nullch;
  1201.      patwd = patdot, grwd = grdot
  1202.     ) {
  1203.     register bool match = FALSE;
  1204.     register int incr = 20;
  1205.  
  1206.     /* null-terminate words */
  1207.     patdot = index(patwd, NGDELIM);
  1208.     if (patdot != Nullch) {
  1209.         *patdot = '\0';        /* will be restored below */
  1210.     }
  1211.     grdot = index(grwd, NGDELIM);
  1212.     if (grdot != Nullch) {
  1213.         *grdot = '\0';        /* will be restored below */
  1214.     }
  1215.     /*
  1216.      * Match one word of pattern with one word of group.
  1217.      * A pattern word of "all" matches any group word,
  1218.      * but isn't worth as much.
  1219.      */
  1220. #ifdef FAST_STRCMP
  1221.     match = STREQ(patwd, grwd);
  1222.     if (!match && STREQ(patwd, ALL)) {
  1223.         match = TRUE;
  1224.         --incr;
  1225.     }
  1226. #else
  1227.     for (rpatwd = patwd; *rpatwd == *grwd++;) {
  1228.         if (*rpatwd++ == '\0') {
  1229.         match = TRUE;        /* literal match */
  1230.         break;
  1231.         }
  1232.     }
  1233.     if (!match) {
  1234.         /* ugly special case match for "all" */
  1235.         rpatwd = patwd;
  1236.         if (*rpatwd++ == 'a' && *rpatwd++ == 'l'
  1237.          && *rpatwd++ == 'l' && *rpatwd   == '\0') {
  1238.         match = TRUE;
  1239.          --incr;
  1240.         }
  1241.     }
  1242. #endif                /* FAST_STRCMP */
  1243.  
  1244.     if (patdot != Nullch) {
  1245.         *patdot++ = NGDELIM;    /* point after the dot */
  1246.     }
  1247.     if (grdot != Nullch) {
  1248.         *grdot++ = NGDELIM;
  1249.     }
  1250.     if (!match) {
  1251.         depth = 0;        /* words differed - mismatch */
  1252.         break;
  1253.     }
  1254.     depth += incr;
  1255.     }
  1256.     /* if group name ran out before pattern, then match fails */
  1257.     if (grwd == Nullch && patwd != Nullch) {
  1258.     depth = 0;
  1259.     }
  1260.     return depth;
  1261. }
  1262.  
  1263. /* Put our startup options into the log file.
  1264. */
  1265. void
  1266. log_startup()
  1267. {
  1268.     char tmpbuf[256];
  1269.  
  1270.     strcpy(tmpbuf, "Started mthreads");
  1271.     if (cron_locking) {
  1272.     strcat(tmpbuf, " -c");
  1273.     }
  1274.     if (debug) {
  1275.     strcat(tmpbuf, " -D");
  1276.     }
  1277.     if (force_flag) {
  1278.     strcat(tmpbuf, " -f");
  1279.     }
  1280.     if (daemon_delay) {
  1281.     sprintf(tmpbuf + strlen(tmpbuf), " -d%d", daemon_delay / 60);
  1282.     if (expire_time) {
  1283.         struct tm *ts;
  1284.  
  1285.         ts = localtime(&expire_time);
  1286.         sprintf(tmpbuf + strlen(tmpbuf), " -e%02d%02d",ts->tm_hour,ts->tm_min);
  1287.     }
  1288.     } else if (expire_time) {
  1289.     strcat(tmpbuf, " -e");
  1290.     }
  1291.     if (slow_down) {
  1292.     sprintf(tmpbuf + strlen(tmpbuf), " -s%d", slow_down);
  1293.     }
  1294.     if (no_processing) {
  1295.     strcat(tmpbuf, " -n");
  1296.     }
  1297.     if (log_verbosity) {
  1298.     sprintf(tmpbuf + strlen(tmpbuf), " -v%d", log_verbosity);
  1299.     }
  1300.     if (zap_thread) {
  1301.     strcat(tmpbuf, " -z");
  1302.     }
  1303.     if (add_new) {
  1304.     if (hierarchy_list) {
  1305.         sprintf(tmpbuf + strlen(tmpbuf), " -a %s", hierarchy_list);
  1306.     } else {
  1307.         strcat(tmpbuf, " -a all");
  1308.     }
  1309.     } else if (hierarchy_list) {
  1310.     sprintf(tmpbuf + strlen(tmpbuf), " %s (only)", hierarchy_list);
  1311.     }
  1312.     log_entry("%s\n", tmpbuf);
  1313. }
  1314.  
  1315. /* Put our statistics into the log file.
  1316. */
  1317. void
  1318. log_stats()
  1319. {
  1320.     sprintf(line, "Processed %d group%s:  added %d article%s, expired %d.\n",
  1321.     processed_groups, processed_groups == 1 ? nullstr : "s",
  1322.     added_articles, added_articles == 1 ? nullstr : "s",
  1323.     expired_articles);
  1324.  
  1325.     log_entry(line);
  1326.  
  1327.     if (!daemon_delay) {
  1328.     putchar('\n');
  1329.     fputs(line, stdout);
  1330.     }
  1331.     if (added_groups) {
  1332.     sprintf(line, "Turned %d group%s on.\n", added_groups,
  1333.         added_groups == 1 ? nullstr : "s");
  1334.     log_entry(line);
  1335.     if (!daemon_delay) {
  1336.         fputs(line, stdout);
  1337.     }
  1338.     }
  1339.     if (removed_groups) {
  1340.     sprintf(line, "Turned %d group%s off.\n", removed_groups,
  1341.         removed_groups == 1 ? nullstr : "s");
  1342.     log_entry(line);
  1343.     if (!daemon_delay) {
  1344.         fputs(line, stdout);
  1345.     }
  1346.     }
  1347. }
  1348. /* Generate a log entry with timestamp.
  1349. */
  1350. /*VARARGS1*/
  1351. void
  1352. log_entry(fmt, arg1, arg2, arg3)
  1353. char *fmt;
  1354. long arg1, arg2, arg3;
  1355. {
  1356. #ifndef USESYSLOG
  1357.     time_t now;
  1358.     char *ctime();
  1359. #endif
  1360.  
  1361.     if (initializing) {
  1362.     fprintf(stderr, fmt, arg1, arg2, arg3);
  1363.     return;
  1364.     }
  1365.  
  1366. #ifndef USESYSLOG
  1367.     (void) time(&now);
  1368.     fprintf(fp_log, "%.12s%c", ctime(&now)+4, daemon_delay ? ' ' : '+');
  1369.     fprintf(fp_log, fmt, arg1, arg2, arg3);
  1370.     fflush(fp_log);
  1371. #else
  1372.     syslog(LOG_INFO, fmt, arg1, arg2, arg3);
  1373. #endif
  1374. }
  1375.  
  1376. /* Generate a log entry, with 'E'rror flagging (non-daemon mode), time-stamp,
  1377. ** and newsgroup name.
  1378. */
  1379. /*VARARGS1*/
  1380. void
  1381. log_error(fmt, arg1, arg2, arg3)
  1382. char *fmt;
  1383. long arg1;
  1384. long arg2;
  1385. long arg3;
  1386. {
  1387.     char fmtbuf[256];
  1388.  
  1389.     sprintf(fmtbuf, "%s: %s", line, fmt);
  1390. #ifndef USESYSLOG
  1391.     log_entry(fmtbuf, arg1, arg2, arg3);
  1392. #else
  1393.     syslog(LOG_NOTICE, fmtbuf, arg1, arg2, arg3);
  1394. #endif
  1395.     if (*fmt == '*') {
  1396.     grevious_error = TRUE;
  1397.     if (!daemon_delay) {
  1398.         putchar('E');
  1399.     }
  1400.     }
  1401.     else {
  1402.     if (!daemon_delay) {
  1403.         putchar('e');
  1404.     }
  1405.     }
  1406. }
  1407.  
  1408. #ifdef MYCHSIZE
  1409.     /* code courtesy of William Kucharski */
  1410.  
  1411. int
  1412. chsize(fd, length)
  1413. int fd;            /* file descriptor */
  1414. off_t length;        /* length to set file to */
  1415. {
  1416.     extern long lseek();
  1417.     struct flock fl;
  1418.  
  1419.     if (fstat(fd, &filestat) < 0) {
  1420.     return -1;
  1421.     }
  1422.     if (filestat.st_size < length) {    /* extend file length */
  1423.     /* Write a 0 byte at the end. */
  1424.     if (lseek(fd, length - 1, 0) < 0
  1425.      || write(fd, "", 1) != 1) {
  1426.         return -1;
  1427.     }
  1428.     } else {
  1429.     /* Truncate file at length. */
  1430.     fl.l_whence = 0;
  1431.     fl.l_len = 0;
  1432.     fl.l_start = length;
  1433.     fl.l_type = F_WRLCK;        /* write lock on file space */
  1434.  
  1435.     /*
  1436.     ** This relies on the UNDOCUMENTED F_FREESP argument to
  1437.     ** fcntl(2), which truncates the file so that it ends at the
  1438.     ** position indicated by fl.l_start.
  1439.     **
  1440.     ** Will minor miracles never cease?
  1441.     */
  1442.     if (fcntl(fd, F_FREESP, &fl) < 0) {
  1443.         return -1;
  1444.     }
  1445.     }
  1446.     return 0;
  1447. }
  1448. #endif
  1449.  
  1450. #ifdef MVTRUNC
  1451. int
  1452. mvtrunc(filename, truncate_len)
  1453. char *filename;
  1454. long truncate_len;
  1455. {
  1456.     FILE *fp_in, *fp_out;
  1457.  
  1458.     sprintf(line, "%s.new", filename);
  1459.     if ((fp_out = fos2open(line, "w")) == Nullfp) {
  1460.     log_entry("Tried to create active2.new.\n");
  1461.     return -1;
  1462.     }
  1463.     if ((fp_in = fos2open(filename, "r")) == Nullfp) {
  1464.     fclose(fp_out);
  1465.     unlink(line);
  1466.     log_entry("Tried to re-open the active2 file.\n");
  1467.     return -1;
  1468.     }
  1469.     while (ftell(fp_out) < truncate_len) {
  1470.     if (!fgets(buf, sizeof buf, fp_in)) {
  1471.         break;
  1472.     }
  1473.     fputs(buf, fp_out);
  1474.     }
  1475.     sprintf(buf, "%s.old", filename);
  1476. /*** OS2: rename fails if destination already exists, so delete it before ***/
  1477.     unlink(buf);
  1478.     rename(filename, buf);
  1479. /*** OS2: rename fails if destination already exists, so delete it before ***/
  1480.     unlink(filename);
  1481.     rename(line, filename);
  1482.     fclose(fp_in);
  1483.     fclose(fp_out);
  1484.  
  1485.     return 0;
  1486. }
  1487. #endif
  1488.  
  1489. #ifndef RENAME
  1490. int
  1491. rename(old, new)
  1492. char    *old, *new;
  1493. {
  1494.     struct stat st;
  1495.  
  1496.     if (stat(old, &st) == -1) {
  1497.     return -1;
  1498.     }
  1499.     if (unlink(new) == -1 && errno != ENOENT) {
  1500.     return -1;
  1501.     }
  1502.     if (link(old, new) == -1) {
  1503.     return -1;
  1504.     }
  1505.     if (unlink(old) == -1) {
  1506.     int e = errno;
  1507.     (void) unlink(new);
  1508.     errno = e;
  1509.     return -1;
  1510.     }
  1511.     return 0;
  1512. }
  1513. #endif /*RENAME*/
  1514.  
  1515. #ifdef CHECKLOAD
  1516.  
  1517. #define FREQCHECK    300
  1518. #define SLPLOAD        300
  1519. #define UPTIME        "/usr/ucb/uptime"
  1520. #define TOOHIGH        5
  1521.  
  1522. checkload()
  1523. {
  1524.     static long lastcheck = 0;
  1525.     long time(), i;
  1526.     FILE *pp;
  1527.     char buf[BUFSIZ];
  1528.     register char *cp;
  1529.     char *strrchr();
  1530.  
  1531.     i = time(Null(time_t*));
  1532.     if ((i - lastcheck) < FREQCHECK) {
  1533.     return;
  1534.     }
  1535.     lastcheck = i;
  1536.  
  1537. again:
  1538.     if ((pp = popen(UPTIME, "r")) == NULL) {
  1539.     return;
  1540.     }
  1541.     if (fgets(buf, BUFSIZ, pp) == NULL) {
  1542.     pclose(pp);
  1543.     return;
  1544.     }
  1545.     pclose(pp);
  1546.     if ((cp = strrchr(buf, ':')) == NULL) {
  1547.     return;
  1548.     }
  1549.     if (atoi(cp + 2) >= TOOHIGH) {
  1550.     sleep(SLPLOAD);
  1551.     goto again;
  1552.     } else {
  1553.     return;
  1554.     }
  1555. }
  1556. #endif
  1557.