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