home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / cnews.tar / readnews / readnews.c < prev    next >
C/C++ Source or Header  |  1995-04-27  |  22KB  |  1,107 lines

  1. /*
  2.  * readnews
  3.  *
  4.  *    Michael Rourke (UNSW) April 1984
  5.  */
  6.  
  7. #include "defs.h"
  8.  
  9. #define ARTSEP "/"
  10.  
  11. char admsub[BUFLEN]     = "general";
  12. char dfltsub[BUFLEN]     = "general";
  13. char mailvia[BUFLEN]     = "";
  14. char *mailpath     = MAIL;
  15.  
  16. #define    MAXARGV    10        /* used in building argv[]s below */
  17.  
  18. bool iflag;        /* -i ignore .newsrc */
  19. bool lflag;        /* -l print headers only */
  20. bool cflag;        /* -c check for news only */
  21. bool pflag;        /* -p print everything selected */
  22. bool Cflag;        /* -C verbose -c */
  23. bool sflag;        /* -s print newsgroup subscription list */
  24. bool splus;        /* -s+ */
  25. bool sminus;        /* -s- */
  26. char *sarg;        /* arg to -s[+-] */
  27. char *nflag;        /* -n newsgroups */
  28. extern char *rcgrps;    /* -n newsgroups from newsrc file */
  29. bool n_on_cline;    /* nflag set on command line */
  30. char *disable;        /* -d disabled commands */
  31.  
  32. extern newsrc    *rc;        /* internal .newsrc */
  33.  
  34. active *alist;        /* internal active list */
  35.  
  36. long now;        /* current time */
  37. bool interrupt;        /* if interrupt hit */
  38. char *newsdir;        /* %news */
  39. bool su;        /* if super user (not used) */
  40.  
  41. applycom list(), check(), commands();
  42. void onintr();
  43. bool ureject(), seen(), subs(), subsub();
  44.  
  45. char *progname;
  46.  
  47. main(argc, argv)
  48. int argc;
  49. char *argv[];
  50. {
  51.     char buf[BUFSIZ], *p;
  52.  
  53.     progname = argv[0];
  54.     setbuf(stdout, buf);
  55.     if (options(argc, argv, true) < 0) {
  56.         (void) fprintf(stderr,
  57.     "Usage: readnews [-n newsgroups] [-i] [-clpCL] [-Agroup] [-Dgroup]\n");
  58.         exit(1);
  59.     }
  60.     now = time(&now);
  61.  
  62.     newsdir = newstr(fullartfile((char *)NULL));
  63.     getctl();
  64.  
  65.     if (!iflag)
  66.         readnewsrc();
  67.  
  68.     if (nflag)
  69.         convgrps(nflag);
  70.     else
  71.         nflag = dfltsub;
  72.     if (rcgrps)
  73.         convgrps(rcgrps);
  74.     if (!n_on_cline && !ngmatch(admsub, nflag))
  75.         nflag = newstr3(admsub, NGSEPS, nflag);
  76.     if ((int) sflag + (int) lflag + (int) cflag + (int) pflag > 1)
  77.         error("-clpsC flags are mutually exclusive.");
  78.  
  79.     /* user has private mailer? */
  80.     if ((p = getenv("MAILER")) != NULL)
  81.         mailpath = newstr(p);
  82.  
  83.     alist = readactive();
  84.  
  85.     if (sflag) {
  86.         if (subs() && !iflag)
  87.             writenewsrc(alist);
  88.     } else if (lflag)
  89.         apply(alist, nflag, list, false);
  90.     else if (cflag)
  91.         apply(alist, nflag, check, false);
  92.     else {
  93.         if (!pflag) {
  94.             if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  95.                 (void) signal(SIGINT, onintr);
  96.             if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
  97.                 (void) signal(SIGQUIT, onintr);
  98.         }
  99.         apply(alist, nflag, commands, true);
  100.         if (!iflag)
  101.             writenewsrc(alist);
  102.     }
  103.     fflush(stdout);
  104.     exit(0);
  105. }
  106.  
  107. #if MANGRPS
  108. /*
  109.  * if newsgroup "ng" isn't subscribed to, add it to subscription list
  110.  */
  111. addsub(ng, slist)
  112. char *ng;
  113. char **slist;
  114. {
  115.     if (!ngmatch(ng, *slist))
  116.         *slist = newstr3(ng, NGSEPS, *slist);
  117. }
  118. #endif
  119.  
  120. /* ARGSUSED */
  121. void
  122. onintr(dummy)
  123. int dummy;
  124. {
  125.     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  126.         (void) signal(SIGINT, onintr);
  127.     if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
  128.         (void) signal(SIGQUIT, onintr);
  129.     interrupt = true;
  130. }
  131.  
  132. /*
  133.  * process options
  134.  * can be called from readnewsrc()
  135.  */
  136. int                /* < 0 failure, otherwise success */
  137. options(argc, argv, cline)
  138. int argc;
  139. char *argv[];
  140. bool cline;
  141. {
  142.     int c;
  143.     extern int optind;
  144.     extern char *optarg;
  145.  
  146.     optind = 1;            /* reset scan, if necessary */
  147.     while ((c = getopt(argc, argv, "n:d:iLA:D:plcC")) != EOF)
  148.         switch (c) {
  149.         case 'n':
  150.             if (cline)
  151.                 nflag = optarg, n_on_cline = true;
  152.             else {
  153.                 if (!n_on_cline)
  154.                     nflag = (nflag?
  155.                         catstr2(nflag, NGSEPS, optarg):
  156.                         newstr(optarg));
  157.                 rcgrps = (rcgrps?
  158.                     catstr2(rcgrps, NGSEPS, optarg):
  159.                     newstr(optarg));
  160.             }
  161.             break;
  162.         case 'd':
  163.             disable = (disable?
  164.                     catstr2(disable, "", optarg):
  165.                     newstr(optarg));
  166.             break;
  167.         case 'i':
  168.             iflag = true; 
  169.             break;
  170.         case 'L':
  171.             sflag = true;
  172.             break;
  173.         case 'A':
  174.             sflag = true;
  175.             splus = true;
  176.             sarg = optarg;
  177.             break;
  178.         case 'D':
  179.             sflag = true;
  180.             sminus = true;
  181.             sarg = optarg;
  182.             break;
  183.         case 'p':
  184.             pflag = true; 
  185.             break;
  186.         case 'l':
  187.             lflag = true; 
  188.             break;
  189.         case 'c':
  190.             cflag = true; 
  191.             break;
  192.         case 'C':
  193.             cflag = Cflag = true; 
  194.             break;
  195.         case '?':
  196.         default:
  197.             return(-1);
  198.             break;
  199.         }
  200.  
  201.     return(0);
  202. }
  203.  
  204. /*
  205.  * subscription list handling
  206.  * return true if newsrc is to be re-written
  207.  */
  208. bool
  209. subs()
  210. {
  211.     register newsrc    *np;
  212.     register active    *ap;
  213.     register char *tmp, *com;
  214.     register FILE *f;
  215.  
  216.     if (splus || sminus) {
  217.         if (strpbrk(sarg, BADGRPCHARS)) {
  218.             (void) printf("%s: Illegal char in newsgroup.\n", sarg);
  219.             return false;
  220.         }
  221.         if (ngmatch(sarg, nflag)) {
  222.             /*
  223.              * normally we subscribe, check for an exclusion
  224.              */
  225.             for (np = rc; np; np = np->n_next)
  226.                 if (CMP(sarg, np->n_name) == 0)
  227.                     break;
  228.             if (np) {
  229.                 /*
  230.                  * altering subscribe flag is all
  231.                  * we need to change
  232.                  */
  233.                 np->n_subscribe = splus;
  234.                 return true;
  235.             }
  236.             if (sminus) {
  237.                 /*
  238.                  * try deleting from sub list
  239.                  */
  240.                 if (subsub(sarg, rcgrps))
  241.                     return true;
  242.                 /*
  243.                  * add specific exclusion
  244.                  */
  245.                 rcgrps = newstr4(rcgrps, NGSEPS, NEGS, sarg);
  246.                 return true;
  247.             }
  248.         } else if (splus) {
  249.             /*
  250.              * we don't subscribe,
  251.              * try deleting !sarg first
  252.              */
  253.             tmp = newstr2(NEGS, sarg);
  254.             subsub(tmp, rcgrps);
  255.             if (!ngmatch(sarg, rcgrps))
  256.                 /*
  257.                  * didn't work, so add explicit subscription
  258.                  */
  259.                 rcgrps = rcgrps? newstr3(rcgrps, NGSEPS, sarg):
  260.                     newstr(sarg);
  261.             return true;
  262.         }
  263.     } else {
  264.         (void) printf("Subscription list: %s", nflag);
  265.         for (np = rc; np; np = np->n_next)
  266.             if (!np->n_subscribe && ngmatch(np->n_name, nflag))
  267.                 (void) printf(",!%s", np->n_name);
  268.         (void) printf("\n");
  269.     }
  270.     return false;
  271. }
  272.  
  273.  
  274. /*
  275.  * try and delete group from subscription list
  276.  * return true if successful
  277.  */
  278. bool
  279. subsub(grp, slist)
  280. char *grp;
  281. char *slist;
  282. {
  283.     register char *delim;
  284.  
  285.     while (*slist) {
  286.         if (delim = strchr(slist, NGSEPCHAR))
  287.             *delim = '\0';
  288.         if (CMP(grp, slist) == 0) {
  289.             if (delim)
  290.                 (void) strcpy(slist, delim + 1);
  291.             else if (slist[-1] == ',')
  292.                 slist[-1] = '\0';
  293.             else
  294.                 slist[0] = '\0';
  295.             return true;
  296.         }
  297.         if (delim)
  298.             *delim = NGSEPCHAR, slist = delim + 1;
  299.         else
  300.             break;
  301.     }
  302.     return false;
  303. }
  304.  
  305. char *
  306. ltoa(l)
  307. long l;
  308. {
  309.     static char buf[30];
  310.  
  311.     sprintf(buf, "%ld", l);
  312.     return buf;
  313. }
  314.  
  315. /*
  316.  * list titles command (-l)
  317.  */
  318. applycom
  319. list(ap, np)
  320. active *ap;
  321. newsrc *np;
  322. {
  323.     static active *lastap;
  324.     static bool first = true;
  325.     register char *fname;
  326.     register FILE *f;
  327.     header h;
  328.     ino_t ino;
  329.  
  330.     np->n_last++;
  331.     fname = convg(newstr5(newsdir, "/", ap->a_name, ARTSEP,
  332.         ltoa(np->n_last)));
  333.     ino = 0;
  334.     f = fopen(fname, "r");
  335.     free(fname);
  336.     if (!f || seen(f, &ino))
  337.         return next;
  338.     gethead(f, &h);
  339.     if (first) {
  340.         (void) printf("News articles:\n");
  341.         first = false;
  342.     }
  343.     if (lastap != ap)
  344.         (void) printf("  %s:\n", ap->a_name);
  345.     lastap = ap;
  346.     (void) printf("    %-4d %s\n", np->n_last, h.h_subject);
  347.     (void) fclose(f);
  348.     freehead(&h);
  349.     if (ino)
  350.         seen(NIL(FILE), &ino);
  351.     return next;
  352. }
  353.  
  354. /*
  355.  * check command (-c or -C)
  356.  */
  357. applycom
  358. check(ap, np)
  359. active *ap;
  360. newsrc *np;
  361. {
  362.     static bool done;
  363.  
  364.     np->n_last++;
  365.     if (Cflag) {
  366.         register long num;
  367.  
  368.         if (!done)
  369.             (void) printf("You have news:\n");
  370.         done = true;
  371.         num = ap->a_seq - np->n_last + 1;
  372.         (void) printf("\t%s at most %ld article%s\n",
  373.             ap->a_name, num, (num > 1? "s": ""));
  374.         return nextgroup;
  375.     } else {
  376.         (void) printf("You have news.\n");
  377.         fflush(stdout);
  378.         exit(0);
  379.         /* NOTREACHED */
  380.     }
  381. }
  382.  
  383. /*
  384.  * normal command handler (or pflag)
  385.  * commands:
  386.  *
  387.  * \n         print current article
  388.  * n         go to next article
  389.  * q        quit
  390.  * r        reply
  391.  * f         followup
  392.  * p         postnews
  393.  * N [newsgrp]    next newsgroup
  394.  * s [file]    save
  395.  * U        unsubscribe from group
  396.  * !stuff    shell escape
  397.  * number or .    go to number
  398.  * -         back to previous article (toggle)
  399.  * x        quick exit
  400.  * h        long header info
  401.  * H        full header
  402.  *
  403.  * inside r, f or p:
  404.  *    .e    edit
  405.  *    .i    interpolate
  406.  *    . or EOT terminate message
  407.  *    .!comd    shell escape
  408.  */
  409. applycom
  410. commands(ap, np, last, pushed)
  411. active *ap;
  412. newsrc *np;
  413. bool last;
  414. bool pushed;
  415. {
  416.     register char *com, *arg;
  417.     register int c, size;
  418.     register long i;
  419.     register FILE *f;
  420.     char *fname;
  421.     header        h;
  422.     newsrc        ntmp;
  423.     ino_t        ino;
  424.     bool printed, pheader, verbose, hadinterrupt;
  425.     applycom    nextact;
  426.     static char errmess[] = "Unknown command; type `?' for help.\n";
  427.     static char form[]    = "%s: %s\n";
  428.     static char savedsys[BUFSIZ / 2];
  429.     static active    *lastap, *rlastap;
  430.     static newsrc    lastn;
  431.     static char number[20];
  432.     static active    *wantap;
  433.     extern char t_from[], t_subject[], t_date[];
  434.     extern char t_newsgroups[], t_path[], t_sender[];
  435.     extern char t_replyto[], t_organization[];
  436.     extern active    *activep();
  437.  
  438.     if (last) {
  439.         /*
  440.          * give user one last chance to
  441.          * see this article
  442.          */
  443.         ap = rlastap;
  444.         np = &lastn;
  445.         wantap = NIL(active);
  446.         if (!ap || pflag)
  447.             return stop;
  448.     } else if (wantap)
  449.         /*
  450.          * doing an "n newsgroup" command
  451.          */
  452.         if (wantap != ap)
  453.             return nextgroup;
  454.         else
  455.             wantap = NULL;
  456.  
  457.     fname = convg(newstr5(newsdir, "/", ap->a_name, ARTSEP,
  458.         ltoa(np->n_last + 1)));
  459.     f = fopen(fname, "r");
  460.     ino = 0;
  461.     if (!f || !last && !pushed && seen(f, &ino)) {
  462.         if (pushed)
  463.             (void) printf("Article %ld (%s) no longer exists.\n",
  464.                 np->n_last + 1, ap->a_name);
  465.         else
  466.             np->n_last++;
  467.         if (f)
  468.             (void) fclose(f);
  469.         free(fname);
  470.         return next;
  471.     }
  472.  
  473.     gethead(f, &h);
  474.  
  475.     (void) printf("\n");
  476.     interrupt = hadinterrupt = verbose = false;
  477.     if (last) {
  478.         (void) printf("No more articles (press RETURN again to quit).\n");
  479.         printed = pheader = true;
  480.     } else
  481.         printed = pheader = false;
  482.  
  483.     while (1) {
  484.         if (lastap != ap) {
  485.             size = strlen(ap->a_name) + sizeof("Newsgroup");
  486.             for (i = 0; i < size; i++)
  487.                 (void) putc('-', stdout);
  488.             (void) printf("\nNewsgroup %s\n", ap->a_name);
  489.             for (i = 0; i < size; i++)
  490.                 (void) putc('-', stdout);
  491.             (void) printf("\n\n");
  492.         }
  493.         lastap = ap;
  494.         if (!pheader) {
  495.             time_t itsdate;
  496.             (void) printf("Article %ld of %ld (%s)",
  497.                 np->n_last + 1, ap->a_seq, ap->a_name);
  498.             if (h.h_lines != 0)
  499.                 (void) printf(" (%s lines)", h.h_lines);
  500.             if (h.h_date != NULL) {
  501.                 itsdate = atot(h.h_date);
  502.                 (void) printf(" %s", ctime(&itsdate));
  503.             } else
  504.                 (void) printf(" %s", "<no date!>\n");
  505.             (void) printf(form, t_from, h.h_from);
  506.             (void) printf(form, t_subject, h.h_subject);
  507.             if (verbose || pflag) {
  508.                 (void) printf(form, t_date, h.h_date);
  509.                 (void) printf(form, t_newsgroups, h.h_newsgroups);
  510.                 (void) printf(form, t_path, h.h_path);
  511.                 if (h.h_sender)
  512.                     (void) printf(form, t_sender, h.h_sender);
  513.                 if (h.h_replyto)
  514.                     (void) printf(form, t_replyto, h.h_replyto);
  515.                 if (h.h_organisation)
  516.                     (void) printf(form, t_organization, h.h_organisation);
  517.                 verbose = false;
  518.             }
  519.             pheader = true;
  520.         }
  521.         if (!pushed && number[0])
  522.             /*
  523.              * just returned from a number command
  524.              * and have another to do
  525.              */
  526.             com = number;
  527.         else if (pflag)
  528.             /*
  529.              * just print it
  530.              */
  531.             com = "";
  532.         else {
  533.             (void) printf("? ");
  534.             if (fflush(stdout) == EOF) {
  535.                 (void) printf("\n? ");
  536.                 (void) fflush(stdout);
  537.             }
  538.             interrupt = false;
  539.             if ((com = mgets()) == NIL(char)) {
  540.                 if (interrupt)
  541.                     if (!hadinterrupt) {
  542.                         clearerr(stdin);
  543.                         (void) printf("Interrupt\n");
  544.                         hadinterrupt = true;
  545.                         interrupt = false;
  546.                         continue;
  547.                     }
  548.                     else
  549.                         exit(1);
  550.                 nextact = stop;
  551.                 break;
  552.             }
  553.             hadinterrupt = false;
  554.         }
  555.         if (disable && *com && strchr(disable, *com) != NULL) {
  556.             (void) printf("Command `%c' disabled.\n", *com);
  557.             continue;
  558.         }
  559.         if (*com == '!') {
  560.             if (com[1] == '!') {
  561.                 (void) printf("!%s\n", savedsys);
  562.                 com = savedsys;
  563.             } else
  564.                 com++;
  565.             (void) fflush(stdout);
  566.             (void) fcntl(fileno(f), F_SETFD, 1);    /* close on exec */
  567.             (void) system(com);
  568.             if (com != savedsys)
  569.                 strncpy(savedsys, com, sizeof(savedsys) - 1);
  570.             (void) printf("!\n");
  571.             if (!printed)
  572.                 pheader = false;
  573.             continue;
  574.         }
  575.         /*
  576.          * check command syntax
  577.          */
  578.         if (*com && !isdigit(*com) && com[1] && (!isspace(com[1]) ||
  579.             strchr("Nsm", *com) == NULL)) {
  580.             (void) printf(errmess);
  581.             continue;
  582.         }
  583.         if (c = *com) {
  584.             arg = com;
  585.             while (isspace(*++arg))
  586.                 ;
  587.         } else
  588.             arg = NULL;
  589.         switch (c) {
  590.         case 0:
  591.         case '.':
  592.             if (!printed || c == '.') {
  593.                 if (pflag)
  594.                     (void) printf("\n");
  595.                 print(&h, f);
  596.                 if (pflag) {
  597.                     nextact = next;
  598.                     break;
  599.                 }
  600.                 printed = true;
  601.                 continue;
  602.             }
  603.         case 'n':            /* B compatible */
  604.         case '+':
  605.         case ';':
  606.             nextact = next;
  607.             break;
  608.         case '?':
  609.             help();
  610.             continue;
  611.         case 'r':
  612.             reply(&h, fname);
  613.             continue;
  614.         case 'f':
  615.             followup(&h, fname);
  616.             continue;
  617.         case 'p':
  618.             pnews(ap->a_name);
  619.             continue;
  620.         case 'U':
  621.             if (ngmatch(np->n_name, admsub)) {
  622.                 (void) printf(
  623.                     "Group \"%s\" can't be unsubscribed.\n",
  624.                     np->n_name);
  625.                 continue;
  626.             }
  627.             np->n_subscribe = false;
  628.             nextact = nextgroup;
  629.             break;
  630.         case 'N':            /* B compatible */
  631.             if (!*arg) {
  632.                 nextact = nextgroup;
  633.                 break;
  634.             }
  635.             if ((wantap = activep(arg)) == NIL(active)) {
  636.                 (void) printf("%s: non-existent newsgroup.\n", arg);
  637.                 continue;
  638.             }
  639.             if (!ngmatch(arg, nflag)) {
  640.                 (void) printf("%s: is not subscribed to!\n", arg);
  641.                 wantap = NULL;
  642.                 continue;
  643.             }
  644.             nextact = searchgroup;
  645.             break;
  646.         case 's':
  647.             save(&h, f, arg);
  648.             continue;
  649.         case 'q':
  650.             nextact = stop;
  651.             break;
  652.         case 'x':
  653.             fflush(stdout);
  654.             exit(0);
  655.         case 'h':
  656.             verbose = true;
  657.             pheader = false;
  658.             continue;
  659.         case 'H':
  660.             puthead(&h, stdout, printing);
  661.             continue;
  662.         case '-':
  663.             if (pushed) {
  664.                 nextact = next;
  665.                 break;
  666.             }
  667.             if (!rlastap || !lastn.n_name) {
  668.                 (void) printf("Can't go back!\n");
  669.                 continue;
  670.             }
  671.             nextact = commands(rlastap, &lastn, false, true);
  672.             /*
  673.              * number commands, after a "-" act on the
  674.              * group of the "-" command
  675.              */
  676.             while (number[0]) {
  677.                 ntmp = lastn;
  678.                 ntmp.n_last = atol(number) - 1;
  679.                 number[0] = '\0';
  680.                 nextact = commands(rlastap, &ntmp, false, true);
  681.             }
  682.             if (nextact != next)
  683.                 break;
  684.             (void) printf("\n");
  685.             pheader = false;
  686.             continue;
  687.         default:
  688.             if (isdigit(c)) {
  689. /*                i = atol(arg);        */
  690.                 i = c - '0';
  691.                 while (isdigit(*arg))
  692.                     i = i * 10 + *arg++ - '0';
  693.             }
  694.             if (!isdigit(c) || *arg != '\0') {
  695.                 (void) printf(errmess);
  696.                 continue;
  697.             }
  698.             number[0] = '\0';
  699.             if (i < ap->a_low || i > ap->a_seq) {
  700.                 (void) printf(
  701.                     "Articles in \"%s\" group range %ld to %ld.\n",
  702.                     np->n_name, ap->a_low, ap->a_seq);
  703.                 continue;
  704.             }
  705.             if (pushed) {
  706.                 sprintf(number, "%ld", i);
  707.                 nextact = next;
  708.                 break;
  709.             }
  710.             ntmp = *np;
  711.             ntmp.n_last = i - 1;
  712.             if ((nextact = commands(ap, &ntmp, false, true)) != next)
  713.                 break;
  714.             if (!number[0]) {
  715.                 (void) printf("\n");
  716.                 pheader = false;
  717.             }
  718.             continue;
  719.         }
  720.         break;
  721.     }
  722.     rlastap = ap;
  723.     lastn = *np;
  724.     if (!pushed && (nextact == next || printed)) {
  725.         np->n_last++;
  726.         if (ino)
  727.             seen(NIL(FILE), &ino);
  728.     }
  729.     freehead(&h);
  730.     (void) fclose(f);
  731.     free(fname);
  732.     return nextact;
  733. }
  734.  
  735.  
  736. /*
  737.  * see if the article has links, if so have we seen it?
  738.  * close file if we return true
  739.  *
  740.  * called twice,
  741.  *    first (with f set) to test (and possibly set *ino)
  742.  *    again to put *ino in memory
  743.  */
  744. bool
  745. seen(f, ino)
  746. FILE *f;
  747. ino_t *ino;
  748. {
  749.     static int num;
  750.     static ino_t    *ilist;
  751.     struct stat statb;
  752.     register int i;
  753.  
  754.     if (f) {
  755.         if (fstat(fileno(f), &statb) != 0 || statb.st_nlink <= 1)
  756.             return false;
  757.         for (i = 0; i < num; i++)
  758.             if (ilist[i] == statb.st_ino) {
  759.                 (void) fclose(f);
  760.                 return true;
  761.             }
  762.         *ino = statb.st_ino;
  763.         return false;
  764.     } else if (*ino) {
  765.         num++;
  766.         ilist = (ino_t * ) (ilist ? myrealloc((char *) ilist, (int) sizeof(ino_t) *
  767.             num) : myalloc((int) sizeof(ino_t)));
  768.         ilist[num - 1] = *ino;
  769.     }
  770.     return true;
  771. }
  772.  
  773.  
  774. /*
  775.  * print out help file
  776.  */
  777. help()
  778. {
  779.     register FILE    *f;
  780.     register int c;
  781.     register char *helppath;
  782.  
  783.     helppath = ctlfile("readnews.help");
  784.     if ((f = fopen(helppath, "r")) == NIL(FILE)) {
  785.         (void) printf("Can't open %s\n", helppath);
  786.         return;
  787.     }
  788.     while ((c = getc(f)) != EOF)
  789.         (void) putc(c, stdout);
  790.     (void) fclose(f);
  791. }
  792.  
  793. /*
  794.  * reply to sender by mail
  795.  */
  796. /* ARGSUSED fname */
  797. reply(hp, fname)
  798. header *hp;
  799. char *fname;
  800. {
  801.     char *argv[MAXARGV];
  802.     register int argc;
  803.  
  804.     argc = 0;
  805.     argv[argc++] = "mail";
  806. #ifdef UNSWMAIL
  807.     argv[argc++] = "-s";
  808.     if ((argv[argc++] = getsubject(hp)) == NIL(char))
  809.         return;
  810.     argv[argc++] = "-i";
  811.     argv[argc++] = fname;
  812. #endif
  813.  
  814.     if ((argv[argc++] = getretaddr(hp)) == NIL(char)) {
  815.         (void) printf("Can't work out an address!\n");
  816.         return;
  817.     }
  818.  
  819.     argv[argc++] = NIL(char);
  820.  
  821.     run(mailpath, argv, false);
  822.  
  823.     free(argv[argc - 2]);
  824. }
  825.  
  826.  
  827.  
  828. /*
  829.  * generate correct headers for a followup article
  830.  * then call postnews.
  831.  */
  832. followup(hp, fname)
  833. header *hp;
  834. char *fname;
  835. {
  836.     char tmpf[50];
  837.     register FILE *fo;
  838.     char *s = getsubject(hp);
  839.  
  840.     if (s == NULL)
  841.         return;
  842.     (void) strcpy(tmpf, "/tmp/rfXXXXXX");
  843.     (void) mktemp(tmpf);
  844.     fo = fopen(tmpf, "w");
  845.     if (fo == NULL)
  846.         error("can't create `%s'", tmpf);
  847.     fprintf(fo, "Newsgroups: %s\n", (hp->h_followupto) ? hp->h_followupto :
  848.                             hp->h_newsgroups);
  849.     fprintf(fo, "Subject: %s\n", s);
  850.     free(s);
  851.     if (hp->h_references && hp->h_messageid)
  852.         fprintf(fo, "References: %s %s\n", hp->h_references, hp->h_messageid);
  853.     else if (hp->h_messageid)
  854.         fprintf(fo, "References: %s\n", hp->h_messageid);
  855.     (void) fclose(fo);
  856.  
  857.     s = newstr3(binfile("inject/postnews"), " -h ", tmpf);
  858.     system(s);
  859.     free(s);
  860.  
  861.     (void) unlink(tmpf);
  862. }
  863.  
  864. /*
  865.  * get correct "Subject: Re: .." line
  866.  */
  867. char *
  868. getsubject(hp)
  869. register header *hp;
  870. {
  871.     register char *s;
  872.  
  873.     if (!hp->h_subject) {
  874.         (void) printf("Subject: Re: ");
  875.         (void) fflush(stdout);
  876.         if ((s = mgets()) == NIL(char) || !*s) {
  877.             (void) printf("The Subject field is mandatory.\n");
  878.             return NIL(char);
  879.         }
  880.         return newstr2("Re: ", s);
  881.     } else if (CMPN(hp->h_subject, "Re: ", 4) != 0 && CMPN(hp->h_subject,
  882.          "re: ", 4) != 0)
  883.         return newstr2("Re: ", hp->h_subject);
  884.     else
  885.         return newstr(hp->h_subject);
  886. }
  887.  
  888.  
  889. /*
  890.  * run a command, optionally closing stdin
  891.  */
  892. run(com, argv, closein)
  893. char *com;
  894. char *argv[];
  895. bool closein;
  896. {
  897.     int pid, status, r;
  898.  
  899.     switch (pid = fork()) {
  900.     default:
  901.         /* parent */
  902.         break;
  903.     case 0:
  904.         /* child */
  905.         if (closein)
  906.             close(fileno(stdin));
  907.         execvp(com, argv);
  908.         error("can't exec %s", com);
  909.         exit(1);
  910.  
  911.     case -1:
  912.         error("can't fork");
  913.     }
  914.  
  915.     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  916.         (void) signal(SIGINT, SIG_IGN);
  917.     if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
  918.         (void) signal(SIGQUIT, SIG_IGN);
  919.  
  920.     while ((r = wait(&status)) != pid && r != -1)
  921.         ;
  922.  
  923.     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  924.         (void) signal(SIGINT, onintr);
  925.     if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
  926.         (void) signal(SIGQUIT, onintr);
  927. }
  928.  
  929. /*
  930.  * call postnews
  931.  */
  932. pnews(group)
  933. char *group;
  934. {
  935.     register char *s = newstr3(binfile("inject/postnews"), " ", group);
  936.     system(s);
  937.     free(s);
  938. }
  939.  
  940. /*
  941.  * save an article
  942.  */
  943. save(hp, f, s)
  944. header *hp;
  945. FILE *f;
  946. char *s;
  947. {
  948.     register long pos;
  949.     register int c;
  950.     register char *cp;
  951.     register FILE    *sf;
  952.     register char *aname;
  953.     long then;
  954.     extern char *getenv();
  955.  
  956.     if (!*s) {
  957.         if ((aname = getenv("HOME")) == NIL(char)) {
  958.             (void) printf("No $HOME in environment.\n");
  959.             return;
  960.         }
  961.         s = aname = newstr3(aname, "/", ARTICLES);
  962.     } else
  963.         aname = NIL(char);
  964.     if ((sf = fopen(s, "a")) == NIL(FILE)) {
  965.         (void) fprintf(stderr, "readnews: can't open %s\n", s);
  966.         return;
  967.     }
  968.     if (aname)
  969.         free(aname);
  970.     pos = ftell(f);
  971.     rewind(f);
  972.     if (cp = strchr(hp->h_from, ' '))
  973.         *cp = '\0';
  974.     if (hp->h_date)
  975.         then = atot(hp->h_date);
  976.     else
  977.         then = 0L;
  978.     (void) fprintf(sf, "From %s %s", hp->h_from, ctime(then ? &then : &now));
  979.     if (cp)
  980.         *cp = ' ';
  981.     while ((c = getc(f)) != EOF)
  982.         (void) putc(c, sf);
  983.     (void) putc('\n', sf);
  984.     (void) fclose(sf);
  985.     fseek(f, pos, 0);
  986. }
  987.  
  988. /*
  989.  * put - like putchar, but filtering control characters
  990.  */
  991. int                /* like putchar */
  992. put(c)
  993. int c;
  994. {
  995.     register int trimc = c & 0177;        /* trim off top bit */
  996.     register int newc;
  997.  
  998.     if (iscntrl(trimc) && trimc != '\n' && trimc != '\b' && trimc != '\t')
  999.         newc = '#';
  1000.     else
  1001.         newc = c;
  1002.     return(putchar(newc));
  1003. }
  1004.  
  1005.  
  1006.  
  1007. /*
  1008.  * print an article, if it's long enough call page()
  1009.  */
  1010. /* ARGSUSED */
  1011. print(hp, f)
  1012. header *hp;
  1013. FILE *f;
  1014. {
  1015.     register int c;
  1016.     register long pos;
  1017.  
  1018.     pos = ftell(f);
  1019.     if (!pflag)
  1020.         page(f);
  1021.     else
  1022.         while ((c = getc(f)) != EOF)
  1023.             (void) put(c);
  1024.     (void) fseek(f, pos, 0);
  1025. }
  1026.  
  1027.  
  1028. /*
  1029.  * copy article text to stdout, and break into pages
  1030.  */
  1031. page(f)
  1032. FILE *f;
  1033. {
  1034.     register int c;
  1035.     register unsigned lineno;
  1036.     char lbuf[80];
  1037.  
  1038.     lineno = 1;
  1039.     while (!interrupt) {
  1040.         for (; lineno < PAGESIZE - 4 && !interrupt; lineno++) {
  1041.             while ((c = getc(f)) != EOF && c != '\n')
  1042.                 (void) put(c);
  1043.             if (c == EOF)
  1044.                 goto fastexit;
  1045.             if (lineno < PAGESIZE - 5)
  1046.                 (void) put('\n');
  1047.         }
  1048.         if (interrupt)
  1049.             break;
  1050.         if (fflush(stdout) == EOF)
  1051.             break;
  1052.         if (read(fileno(stdin), lbuf, sizeof(lbuf)) <= 0)
  1053.             break;
  1054.         lineno = 0;
  1055.     }
  1056.     if (lineno)
  1057.         (void) put('\n');
  1058.     interrupt = false;
  1059. fastexit:    ;
  1060. }
  1061.  
  1062. /* VARARGS1 */
  1063. error(s, a0, a1, a2, a3)
  1064. char *s;
  1065. {
  1066.     (void) fprintf(stderr, "readnews: ");
  1067.     (void) fprintf(stderr, s, a0, a1, a2, a3);
  1068.     (void) fprintf(stderr, "\n");
  1069.     fflush(stdout);        /* just on principle */
  1070.     exit(1);
  1071. }
  1072.  
  1073. /*
  1074.  - getctl - pick up control file for subscriptions etc.
  1075.  */
  1076. getctl()
  1077. {
  1078.     register FILE *f;
  1079.     register char *fname;
  1080.     char line[BUFLEN];
  1081.     register char *p;
  1082.  
  1083.     fname = ctlfile("readnews.ctl");
  1084.     f = fopen(fname, "r");
  1085.     if (f == NULL)
  1086.         return;
  1087.  
  1088.     while (fgets(line, sizeof(line), f) != NULL) {
  1089.         line[strlen(line)-1] = '\0';    /* dispose of newline */
  1090.         p = strchr(line, '\t');
  1091.         if (p == NULL)
  1092.             p = strchr(line, ' ');
  1093.         if (line[0] != '#' && p != NULL) {
  1094.             while (*p == ' ' || *p == '\t')
  1095.                 *p++ = '\0';
  1096.             if (strcmp(line, "defsub") == 0)
  1097.                 (void) strcpy(dfltsub, p);
  1098.             else if (strcmp(line, "mustsub") == 0)
  1099.                 (void) strcpy(admsub, p);
  1100.             else if (strcmp(line, "mailvia") == 0)
  1101.                 (void) strcpy(mailvia, p);
  1102.         }
  1103.     }
  1104.  
  1105.     (void) fclose(f);
  1106. }
  1107.