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