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