xref: /original-bsd/usr.bin/mail/collect.c (revision a0a7d8f4)
1 #ifndef lint
2 static char sccsid[] = "@(#)collect.c	2.18 (Berkeley) 01/09/85";
3 #endif
4 
5 /*
6  * Mail -- a mail program
7  *
8  * Collect input from standard input, handling
9  * ~ escapes.
10  */
11 
12 #include "rcv.h"
13 #include <sys/stat.h>
14 
15 /*
16  * Read a message from standard output and return a read file to it
17  * or NULL on error.
18  */
19 
20 /*
21  * The following hokiness with global variables is so that on
22  * receipt of an interrupt signal, the partial message can be salted
23  * away on dead.letter.  The output file must be available to flush,
24  * and the input to read.  Several open files could be saved all through
25  * Mail if stdio allowed simultaneous read/write access.
26  */
27 
28 static	int	(*savesig)();		/* Previous SIGINT value */
29 static	int	(*savehup)();		/* Previous SIGHUP value */
30 # ifdef VMUNIX
31 static	int	(*savecont)();		/* Previous SIGCONT value */
32 # endif VMUNIX
33 static	FILE	*newi;			/* File for saving away */
34 static	FILE	*newo;			/* Output side of same */
35 static	int	hf;			/* Ignore interrups */
36 static	int	hadintr;		/* Have seen one SIGINT so far */
37 
38 static	jmp_buf	coljmp;			/* To get back to work */
39 
40 FILE *
41 collect(hp)
42 	struct header *hp;
43 {
44 	FILE *ibuf, *fbuf, *obuf;
45 	int lc, cc, escape, collrub(), intack(), collhup, collcont(), eof;
46 	register int c, t;
47 	char linebuf[LINESIZE], *cp;
48 	extern char tempMail[];
49 	int notify();
50 	extern collintsig(), collhupsig();
51 	char getsub;
52 
53 	noreset++;
54 	ibuf = obuf = NULL;
55 	if (value("ignore") != NOSTR)
56 		hf = 1;
57 	else
58 		hf = 0;
59 	hadintr = 0;
60 # ifdef VMUNIX
61 	if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN)
62 		sigset(SIGINT, hf ? intack : collrub), sigblock(sigmask(SIGINT));
63 	if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN)
64 		sigset(SIGHUP, collrub), sigblock(sigmask(SIGHUP));
65 	savecont = sigset(SIGCONT, collcont);
66 # else VMUNIX
67 	savesig = signal(SIGINT, SIG_IGN);
68 	savehup = signal(SIGHUP, SIG_IGN);
69 # endif VMUNIX
70 	newi = NULL;
71 	newo = NULL;
72 	if ((obuf = fopen(tempMail, "w")) == NULL) {
73 		perror(tempMail);
74 		goto err;
75 	}
76 	newo = obuf;
77 	if ((ibuf = fopen(tempMail, "r")) == NULL) {
78 		perror(tempMail);
79 		newo = NULL;
80 		fclose(obuf);
81 		goto err;
82 	}
83 	newi = ibuf;
84 	remove(tempMail);
85 
86 	/*
87 	 * If we are going to prompt for a subject,
88 	 * refrain from printing a newline after
89 	 * the headers (since some people mind).
90 	 */
91 
92 	t = GTO|GSUBJECT|GCC|GNL;
93 	getsub = 0;
94 	if (intty && sflag == NOSTR && hp->h_subject == NOSTR && value("ask"))
95 		t &= ~GNL, getsub++;
96 	if (hp->h_seq != 0) {
97 		puthead(hp, stdout, t);
98 		fflush(stdout);
99 	}
100 	escape = ESCAPE;
101 	if ((cp = value("escape")) != NOSTR)
102 		escape = *cp;
103 	eof = 0;
104 	for (;;) {
105 		int omask = sigblock(0) &~ (sigmask(SIGINT)|sigmask(SIGHUP));
106 
107 		setjmp(coljmp);
108 # ifdef VMUNIX
109 		sigsetmask(omask);
110 # else VMUNIX
111 		if (savesig != SIG_IGN)
112 			signal(SIGINT, hf ? intack : collintsig);
113 		if (savehup != SIG_IGN)
114 			signal(SIGHUP, collhupsig);
115 # endif VMUNIX
116 		fflush(stdout);
117 		if (getsub) {
118 			grabh(hp, GSUBJECT);
119 			getsub = 0;
120 			continue;
121 		}
122 		if (readline(stdin, linebuf) <= 0) {
123 			if (intty && value("ignoreeof") != NOSTR) {
124 				if (++eof > 35)
125 					break;
126 				printf("Use \".\" to terminate letter\n",
127 				    escape);
128 				continue;
129 			}
130 			break;
131 		}
132 		eof = 0;
133 		hadintr = 0;
134 		if (intty && equal(".", linebuf) &&
135 		    (value("dot") != NOSTR || value("ignoreeof") != NOSTR))
136 			break;
137 		if (linebuf[0] != escape || rflag != NOSTR) {
138 			if ((t = putline(obuf, linebuf)) < 0)
139 				goto err;
140 			continue;
141 		}
142 		c = linebuf[1];
143 		switch (c) {
144 		default:
145 			/*
146 			 * On double escape, just send the single one.
147 			 * Otherwise, it's an error.
148 			 */
149 
150 			if (c == escape) {
151 				if (putline(obuf, &linebuf[1]) < 0)
152 					goto err;
153 				else
154 					break;
155 			}
156 			printf("Unknown tilde escape.\n");
157 			break;
158 
159 		case 'C':
160 			/*
161 			 * Dump core.
162 			 */
163 
164 			core();
165 			break;
166 
167 		case '!':
168 			/*
169 			 * Shell escape, send the balance of the
170 			 * line to sh -c.
171 			 */
172 
173 			shell(&linebuf[2]);
174 			break;
175 
176 		case ':':
177 		case '_':
178 			/*
179 			 * Escape to command mode, but be nice!
180 			 */
181 
182 			execute(&linebuf[2], 1);
183 			printf("(continue)\n");
184 			break;
185 
186 		case '.':
187 			/*
188 			 * Simulate end of file on input.
189 			 */
190 			goto eofl;
191 
192 		case 'q':
193 		case 'Q':
194 			/*
195 			 * Force a quit of sending mail.
196 			 * Act like an interrupt happened.
197 			 */
198 
199 			hadintr++;
200 			collrub(SIGINT);
201 			exit(1);
202 
203 		case 'h':
204 			/*
205 			 * Grab a bunch of headers.
206 			 */
207 			if (!intty || !outtty) {
208 				printf("~h: no can do!?\n");
209 				break;
210 			}
211 			grabh(hp, GTO|GSUBJECT|GCC|GBCC);
212 			printf("(continue)\n");
213 			break;
214 
215 		case 't':
216 			/*
217 			 * Add to the To list.
218 			 */
219 
220 			hp->h_to = addto(hp->h_to, &linebuf[2]);
221 			hp->h_seq++;
222 			break;
223 
224 		case 's':
225 			/*
226 			 * Set the Subject list.
227 			 */
228 
229 			cp = &linebuf[2];
230 			while (any(*cp, " \t"))
231 				cp++;
232 			hp->h_subject = savestr(cp);
233 			hp->h_seq++;
234 			break;
235 
236 		case 'c':
237 			/*
238 			 * Add to the CC list.
239 			 */
240 
241 			hp->h_cc = addto(hp->h_cc, &linebuf[2]);
242 			hp->h_seq++;
243 			break;
244 
245 		case 'b':
246 			/*
247 			 * Add stuff to blind carbon copies list.
248 			 */
249 			hp->h_bcc = addto(hp->h_bcc, &linebuf[2]);
250 			hp->h_seq++;
251 			break;
252 
253 		case 'd':
254 			copy(deadletter, &linebuf[2]);
255 			/* fall into . . . */
256 
257 		case 'r':
258 			/*
259 			 * Invoke a file:
260 			 * Search for the file name,
261 			 * then open it and copy the contents to obuf.
262 			 */
263 
264 			cp = &linebuf[2];
265 			while (any(*cp, " \t"))
266 				cp++;
267 			if (*cp == '\0') {
268 				printf("Interpolate what file?\n");
269 				break;
270 			}
271 			cp = expand(cp);
272 			if (cp == NOSTR)
273 				break;
274 			if (isdir(cp)) {
275 				printf("%s: directory\n");
276 				break;
277 			}
278 			if ((fbuf = fopen(cp, "r")) == NULL) {
279 				perror(cp);
280 				break;
281 			}
282 			printf("\"%s\" ", cp);
283 			fflush(stdout);
284 			lc = 0;
285 			cc = 0;
286 			while (readline(fbuf, linebuf) > 0) {
287 				lc++;
288 				if ((t = putline(obuf, linebuf)) < 0) {
289 					fclose(fbuf);
290 					goto err;
291 				}
292 				cc += t;
293 			}
294 			fclose(fbuf);
295 			printf("%d/%d\n", lc, cc);
296 			break;
297 
298 		case 'w':
299 			/*
300 			 * Write the message on a file.
301 			 */
302 
303 			cp = &linebuf[2];
304 			while (any(*cp, " \t"))
305 				cp++;
306 			if (*cp == '\0') {
307 				fprintf(stderr, "Write what file!?\n");
308 				break;
309 			}
310 			if ((cp = expand(cp)) == NOSTR)
311 				break;
312 			fflush(obuf);
313 			rewind(ibuf);
314 			exwrite(cp, ibuf, 1);
315 			break;
316 
317 		case 'm':
318 		case 'f':
319 			/*
320 			 * Interpolate the named messages, if we
321 			 * are in receiving mail mode.  Does the
322 			 * standard list processing garbage.
323 			 * If ~f is given, we don't shift over.
324 			 */
325 
326 			if (!rcvmode) {
327 				printf("No messages to send from!?!\n");
328 				break;
329 			}
330 			cp = &linebuf[2];
331 			while (any(*cp, " \t"))
332 				cp++;
333 			if (forward(cp, obuf, c) < 0)
334 				goto err;
335 			printf("(continue)\n");
336 			break;
337 
338 		case '?':
339 			if ((fbuf = fopen(THELPFILE, "r")) == NULL) {
340 				perror(THELPFILE);
341 				break;
342 			}
343 			t = getc(fbuf);
344 			while (t != -1) {
345 				putchar(t);
346 				t = getc(fbuf);
347 			}
348 			fclose(fbuf);
349 			break;
350 
351 		case 'p':
352 			/*
353 			 * Print out the current state of the
354 			 * message without altering anything.
355 			 */
356 
357 			fflush(obuf);
358 			rewind(ibuf);
359 			printf("-------\nMessage contains:\n");
360 			puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
361 			t = getc(ibuf);
362 			while (t != EOF) {
363 				putchar(t);
364 				t = getc(ibuf);
365 			}
366 			printf("(continue)\n");
367 			break;
368 
369 		case '^':
370 		case '|':
371 			/*
372 			 * Pipe message through command.
373 			 * Collect output as new message.
374 			 */
375 
376 			obuf = mespipe(ibuf, obuf, &linebuf[2]);
377 			newo = obuf;
378 			ibuf = newi;
379 			newi = ibuf;
380 			printf("(continue)\n");
381 			break;
382 
383 		case 'v':
384 		case 'e':
385 			/*
386 			 * Edit the current message.
387 			 * 'e' means to use EDITOR
388 			 * 'v' means to use VISUAL
389 			 */
390 
391 			if ((obuf = mesedit(ibuf, obuf, c)) == NULL)
392 				goto err;
393 			newo = obuf;
394 			ibuf = newi;
395 			printf("(continue)\n");
396 			break;
397 		}
398 	}
399 eofl:
400 	fclose(obuf);
401 	rewind(ibuf);
402 	sigset(SIGINT, savesig);
403 	sigset(SIGHUP, savehup);
404 # ifdef VMUNIX
405 	sigset(SIGCONT, savecont);
406 	sigsetmask(0);
407 # endif VMUNIX
408 	noreset = 0;
409 	return(ibuf);
410 
411 err:
412 	if (ibuf != NULL)
413 		fclose(ibuf);
414 	if (obuf != NULL)
415 		fclose(obuf);
416 	sigset(SIGINT, savesig);
417 	sigset(SIGHUP, savehup);
418 # ifdef VMUNIX
419 	sigset(SIGCONT, savecont);
420 	sigsetmask(0);
421 # endif VMUNIX
422 	noreset = 0;
423 	return(NULL);
424 }
425 
426 /*
427  * Non destructively interrogate the value of the given signal.
428  */
429 
430 psig(n)
431 {
432 	register (*wassig)();
433 
434 	wassig = sigset(n, SIG_IGN);
435 	sigset(n, wassig);
436 	return((int) wassig);
437 }
438 
439 /*
440  * Write a file, ex-like if f set.
441  */
442 
443 exwrite(name, ibuf, f)
444 	char name[];
445 	FILE *ibuf;
446 {
447 	register FILE *of;
448 	register int c;
449 	long cc;
450 	int lc;
451 	struct stat junk;
452 
453 	if (f) {
454 		printf("\"%s\" ", name);
455 		fflush(stdout);
456 	}
457 	if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) {
458 		if (!f)
459 			fprintf(stderr, "%s: ", name);
460 		fprintf(stderr, "File exists\n", name);
461 		return(-1);
462 	}
463 	if ((of = fopen(name, "w")) == NULL) {
464 		perror(NOSTR);
465 		return(-1);
466 	}
467 	lc = 0;
468 	cc = 0;
469 	while ((c = getc(ibuf)) != EOF) {
470 		cc++;
471 		if (c == '\n')
472 			lc++;
473 		putc(c, of);
474 		if (ferror(of)) {
475 			perror(name);
476 			fclose(of);
477 			return(-1);
478 		}
479 	}
480 	fclose(of);
481 	printf("%d/%ld\n", lc, cc);
482 	fflush(stdout);
483 	return(0);
484 }
485 
486 /*
487  * Edit the message being collected on ibuf and obuf.
488  * Write the message out onto some poorly-named temp file
489  * and point an editor at it.
490  *
491  * On return, make the edit file the new temp file.
492  */
493 
494 FILE *
495 mesedit(ibuf, obuf, c)
496 	FILE *ibuf, *obuf;
497 {
498 	int pid, s;
499 	FILE *fbuf;
500 	register int t;
501 	int (*sig)(), (*scont)(), signull();
502 	struct stat sbuf;
503 	extern char tempMail[], tempEdit[];
504 	register char *edit;
505 
506 	sig = sigset(SIGINT, SIG_IGN);
507 # ifdef VMUNIX
508 	scont = sigset(SIGCONT, signull);
509 # endif VMUNIX
510 	if (stat(tempEdit, &sbuf) >= 0) {
511 		printf("%s: file exists\n", tempEdit);
512 		goto out;
513 	}
514 	close(creat(tempEdit, 0600));
515 	if ((fbuf = fopen(tempEdit, "w")) == NULL) {
516 		perror(tempEdit);
517 		goto out;
518 	}
519 	fflush(obuf);
520 	rewind(ibuf);
521 	t = getc(ibuf);
522 	while (t != EOF) {
523 		putc(t, fbuf);
524 		t = getc(ibuf);
525 	}
526 	fflush(fbuf);
527 	if (ferror(fbuf)) {
528 		perror(tempEdit);
529 		remove(tempEdit);
530 		goto fix;
531 	}
532 	fclose(fbuf);
533 	if ((edit = value(c == 'e' ? "EDITOR" : "VISUAL")) == NOSTR)
534 		edit = c == 'e' ? EDITOR : VISUAL;
535 	pid = vfork();
536 	if (pid == 0) {
537 		sigchild();
538 		if (sig != SIG_IGN)
539 			sigsys(SIGINT, SIG_DFL);
540 		execl(edit, edit, tempEdit, 0);
541 		perror(edit);
542 		_exit(1);
543 	}
544 	if (pid == -1) {
545 		perror("fork");
546 		remove(tempEdit);
547 		goto out;
548 	}
549 	while (wait(&s) != pid)
550 		;
551 	if ((s & 0377) != 0) {
552 		printf("Fatal error in \"%s\"\n", edit);
553 		remove(tempEdit);
554 		goto out;
555 	}
556 
557 	/*
558 	 * Now switch to new file.
559 	 */
560 
561 	if ((fbuf = fopen(tempEdit, "a")) == NULL) {
562 		perror(tempEdit);
563 		remove(tempEdit);
564 		goto out;
565 	}
566 	if ((ibuf = fopen(tempEdit, "r")) == NULL) {
567 		perror(tempEdit);
568 		fclose(fbuf);
569 		remove(tempEdit);
570 		goto out;
571 	}
572 	remove(tempEdit);
573 	fclose(obuf);
574 	fclose(newi);
575 	obuf = fbuf;
576 	goto out;
577 fix:
578 	perror(tempEdit);
579 out:
580 # ifdef VMUNIX
581 	sigset(SIGCONT, scont);
582 # endif VMUNIX
583 	sigset(SIGINT, sig);
584 	newi = ibuf;
585 	return(obuf);
586 }
587 
588 /*
589  * Pipe the message through the command.
590  * Old message is on stdin of command;
591  * New message collected from stdout.
592  * Sh -c must return 0 to accept the new message.
593  */
594 
595 FILE *
596 mespipe(ibuf, obuf, cmd)
597 	FILE *ibuf, *obuf;
598 	char cmd[];
599 {
600 	register FILE *ni, *no;
601 	int pid, s;
602 	int (*savesig)();
603 	char *Shell;
604 
605 	newi = ibuf;
606 	if ((no = fopen(tempEdit, "w")) == NULL) {
607 		perror(tempEdit);
608 		return(obuf);
609 	}
610 	if ((ni = fopen(tempEdit, "r")) == NULL) {
611 		perror(tempEdit);
612 		fclose(no);
613 		remove(tempEdit);
614 		return(obuf);
615 	}
616 	remove(tempEdit);
617 	savesig = sigset(SIGINT, SIG_IGN);
618 	fflush(obuf);
619 	rewind(ibuf);
620 	if ((Shell = value("SHELL")) == NULL)
621 		Shell = "/bin/sh";
622 	if ((pid = vfork()) == -1) {
623 		perror("fork");
624 		goto err;
625 	}
626 	if (pid == 0) {
627 		/*
628 		 * stdin = current message.
629 		 * stdout = new message.
630 		 */
631 
632 		sigchild();
633 		close(0);
634 		dup(fileno(ibuf));
635 		close(1);
636 		dup(fileno(no));
637 		for (s = 4; s < 15; s++)
638 			close(s);
639 		execl(Shell, Shell, "-c", cmd, 0);
640 		perror(Shell);
641 		_exit(1);
642 	}
643 	while (wait(&s) != pid)
644 		;
645 	if (s != 0 || pid == -1) {
646 		fprintf(stderr, "\"%s\" failed!?\n", cmd);
647 		goto err;
648 	}
649 	if (fsize(ni) == 0) {
650 		fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
651 		goto err;
652 	}
653 
654 	/*
655 	 * Take new files.
656 	 */
657 
658 	newi = ni;
659 	fclose(ibuf);
660 	fclose(obuf);
661 	sigset(SIGINT, savesig);
662 	return(no);
663 
664 err:
665 	fclose(no);
666 	fclose(ni);
667 	sigset(SIGINT, savesig);
668 	return(obuf);
669 }
670 
671 /*
672  * Interpolate the named messages into the current
673  * message, preceding each line with a tab.
674  * Return a count of the number of characters now in
675  * the message, or -1 if an error is encountered writing
676  * the message temporary.  The flag argument is 'm' if we
677  * should shift over and 'f' if not.
678  */
679 forward(ms, obuf, f)
680 	char ms[];
681 	FILE *obuf;
682 {
683 	register int *msgvec, *ip;
684 	extern char tempMail[];
685 
686 	msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec);
687 	if (msgvec == (int *) NOSTR)
688 		return(0);
689 	if (getmsglist(ms, msgvec, 0) < 0)
690 		return(0);
691 	if (*msgvec == NULL) {
692 		*msgvec = first(0, MMNORM);
693 		if (*msgvec == NULL) {
694 			printf("No appropriate messages\n");
695 			return(0);
696 		}
697 		msgvec[1] = NULL;
698 	}
699 	printf("Interpolating:");
700 	for (ip = msgvec; *ip != NULL; ip++) {
701 		touch(*ip);
702 		printf(" %d", *ip);
703 		if (f == 'm') {
704 			if (transmit(&message[*ip-1], obuf) < 0L) {
705 				perror(tempMail);
706 				return(-1);
707 			}
708 		} else
709 			if (send(&message[*ip-1], obuf, 0) < 0) {
710 				perror(tempMail);
711 				return(-1);
712 			}
713 	}
714 	printf("\n");
715 	return(0);
716 }
717 
718 /*
719  * Send message described by the passed pointer to the
720  * passed output buffer.  Insert a tab in front of each
721  * line.  Return a count of the characters sent, or -1
722  * on error.
723  */
724 
725 long
726 transmit(mailp, obuf)
727 	struct message *mailp;
728 	FILE *obuf;
729 {
730 	register struct message *mp;
731 	register int ch;
732 	long c, n;
733 	int bol;
734 	FILE *ibuf;
735 
736 	mp = mailp;
737 	ibuf = setinput(mp);
738 	c = mp->m_size;
739 	n = c;
740 	bol = 1;
741 	while (c-- > 0L) {
742 		if (bol) {
743 			bol = 0;
744 			putc('\t', obuf);
745 			n++;
746 			if (ferror(obuf)) {
747 				perror("/tmp");
748 				return(-1L);
749 			}
750 		}
751 		ch = getc(ibuf);
752 		if (ch == '\n')
753 			bol++;
754 		putc(ch, obuf);
755 		if (ferror(obuf)) {
756 			perror("/tmp");
757 			return(-1L);
758 		}
759 	}
760 	return(n);
761 }
762 
763 /*
764  * Print (continue) when continued after ^Z.
765  */
766 collcont(s)
767 {
768 
769 	printf("(continue)\n");
770 	fflush(stdout);
771 }
772 
773 /*
774  * On interrupt, go here to save the partial
775  * message on ~/dead.letter.
776  * Then restore signals and execute the normal
777  * signal routine.  We only come here if signals
778  * were previously set anyway.
779  */
780 
781 # ifndef VMUNIX
782 collintsig()
783 {
784 	signal(SIGINT, SIG_IGN);
785 	collrub(SIGINT);
786 }
787 
788 collhupsig()
789 {
790 	signal(SIGHUP, SIG_IGN);
791 	collrub(SIGHUP);
792 }
793 # endif VMUNIX
794 
795 collrub(s)
796 {
797 	register FILE *dbuf;
798 	register int c;
799 
800 	if (s == SIGINT && hadintr == 0) {
801 		hadintr++;
802 		fflush(stdout);
803 		fprintf(stderr, "\n(Interrupt -- one more to kill letter)\n");
804 		longjmp(coljmp, 1);
805 	}
806 	fclose(newo);
807 	rewind(newi);
808 	if (s == SIGINT && value("nosave") != NOSTR || fsize(newi) == 0)
809 		goto done;
810 	if ((dbuf = fopen(deadletter, "w")) == NULL)
811 		goto done;
812 	chmod(deadletter, 0600);
813 	while ((c = getc(newi)) != EOF)
814 		putc(c, dbuf);
815 	fclose(dbuf);
816 
817 done:
818 	fclose(newi);
819 	sigset(SIGINT, savesig);
820 	sigset(SIGHUP, savehup);
821 # ifdef VMUNIX
822 	sigset(SIGCONT, savecont);
823 # endif VMUNIX
824 	if (rcvmode) {
825 		if (s == SIGHUP)
826 			hangup(SIGHUP);
827 		else
828 			stop(s);
829 	}
830 	else
831 		exit(1);
832 }
833 
834 /*
835  * Acknowledge an interrupt signal from the tty by typing an @
836  */
837 
838 intack(s)
839 {
840 
841 	puts("@");
842 	fflush(stdout);
843 	clearerr(stdin);
844 }
845 
846 /*
847  * Add a string to the end of a header entry field.
848  */
849 
850 char *
851 addto(hf, news)
852 	char hf[], news[];
853 {
854 	register char *cp, *cp2, *linebuf;
855 
856 	if (hf == NOSTR)
857 		hf = "";
858 	if (*news == '\0')
859 		return(hf);
860 	linebuf = salloc(strlen(hf) + strlen(news) + 2);
861 	for (cp = hf; any(*cp, " \t"); cp++)
862 		;
863 	for (cp2 = linebuf; *cp;)
864 		*cp2++ = *cp++;
865 	*cp2++ = ' ';
866 	for (cp = news; any(*cp, " \t"); cp++)
867 		;
868 	while (*cp != '\0')
869 		*cp2++ = *cp++;
870 	*cp2 = '\0';
871 	return(linebuf);
872 }
873