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