removed global uid variable
[unix-history] / usr / src / usr.bin / mail / collect.c
CommitLineData
9552e6b8
DF
1/*
2 * Copyright (c) 1980 Regents of the University of California.
0c5f72fb
KB
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
acfc7e9b
KB
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
9552e6b8
DF
16 */
17
acfc7e9b
KB
18#ifndef lint
19static char sccsid[] = "@(#)collect.c 5.10 (Berkeley) %G%";
20#endif /* not lint */
06300dd9
KS
21
22/*
23 * Mail -- a mail program
24 *
25 * Collect input from standard input, handling
26 * ~ escapes.
27 */
28
06300dd9
KS
29#include "rcv.h"
30#include <sys/stat.h>
828615a1 31#include <sys/wait.h>
06300dd9
KS
32
33/*
34 * Read a message from standard output and return a read file to it
35 * or NULL on error.
36 */
37
38/*
39 * The following hokiness with global variables is so that on
40 * receipt of an interrupt signal, the partial message can be salted
828615a1 41 * away on dead.letter.
06300dd9
KS
42 */
43
828615a1 44static int (*saveint)(); /* Previous SIGINT value */
7feecf47 45static int (*savehup)(); /* Previous SIGHUP value */
2d55c063 46static int (*savecont)(); /* Previous SIGCONT value */
828615a1 47static FILE *collf; /* File for saving away */
06300dd9
KS
48static int hadintr; /* Have seen one SIGINT so far */
49
50static jmp_buf coljmp; /* To get back to work */
51
52FILE *
3d6f01e5 53collect(hp, printheaders)
06300dd9
KS
54 struct header *hp;
55{
828615a1
EW
56 FILE *fp, *fbuf;
57 int lc, cc, escape, eof;
58 int collrub(), intack(), collcont();
06300dd9
KS
59 register int c, t;
60 char linebuf[LINESIZE], *cp;
61 extern char tempMail[];
97bff179 62 int notify();
4e161d3b 63 char getsub;
828615a1 64 int omask;
06300dd9
KS
65
66 noreset++;
828615a1
EW
67 fp = NULL;
68 collf = NULL;
69
70 /*
71 * Start catching signals from here, but we're still die on interrupts
72 * until we're in the main loop.
73 */
74 omask = sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
75 if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
76 signal(SIGINT, value("ignore") != NOSTR ? intack : collrub);
77 if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
78 signal(SIGHUP, collrub);
79 savecont = signal(SIGCONT, SIG_DFL);
80 if (setjmp(coljmp)) {
81 remove(tempMail);
06300dd9
KS
82 goto err;
83 }
828615a1
EW
84 sigsetmask(omask & ~(sigmask(SIGINT) | sigmask(SIGHUP)));
85
86 if ((fp = fopen(tempMail, "w+")) == NULL) {
06300dd9 87 perror(tempMail);
06300dd9
KS
88 goto err;
89 }
828615a1 90 collf = fp;
06300dd9
KS
91 remove(tempMail);
92
93 /*
94 * If we are going to prompt for a subject,
95 * refrain from printing a newline after
96 * the headers (since some people mind).
97 */
06300dd9 98 t = GTO|GSUBJECT|GCC|GNL;
4e161d3b 99 getsub = 0;
686f6134
EW
100 if (hp->h_subject == NOSTR && value("interactive") != NOSTR &&
101 value("ask"))
4e161d3b 102 t &= ~GNL, getsub++;
3d6f01e5 103 if (printheaders) {
06300dd9
KS
104 puthead(hp, stdout, t);
105 fflush(stdout);
106 }
06300dd9
KS
107 if ((cp = value("escape")) != NOSTR)
108 escape = *cp;
686f6134
EW
109 else
110 escape = ESCAPE;
0fa68535 111 eof = 0;
828615a1
EW
112 hadintr = 0;
113
114 /*
115 * We can put the setjmp here because register variable
116 * needs to be saved in the loop.
117 */
118 if (!setjmp(coljmp)) {
119 signal(SIGCONT, collcont);
120 if (getsub)
4e161d3b 121 grabh(hp, GSUBJECT);
828615a1
EW
122 } else {
123 /*
124 * Come here for printing the after-signal message.
125 * Duplicate messages won't be printed because
126 * the write is aborted if we get a SIGTTOU.
127 */
128cont:
129 if (hadintr) {
130 fflush(stdout);
131 fprintf(stderr,
132 "\n(Interrupt -- one more to kill letter)\n");
133 } else {
134 printf("(continue)\n");
135 fflush(stdout);
4e161d3b 136 }
828615a1
EW
137 }
138 for (;;) {
139 if (readline(stdin, linebuf) < 0) {
686f6134
EW
140 if (value("interactive") != NOSTR &&
141 value("ignoreeof") != NOSTR) {
0fa68535
KS
142 if (++eof > 35)
143 break;
828615a1 144 printf("Use \".\" to terminate letter\n");
0fa68535
KS
145 continue;
146 }
06300dd9 147 break;
0fa68535
KS
148 }
149 eof = 0;
06300dd9 150 hadintr = 0;
686f6134
EW
151 if (linebuf[0] == '.' && linebuf[1] == '\0' &&
152 value("interactive") != NOSTR &&
b068a67e 153 (value("dot") != NOSTR || value("ignoreeof") != NOSTR))
06300dd9 154 break;
686f6134 155 if (linebuf[0] != escape || value("interactive") == NOSTR) {
828615a1 156 if (putline(fp, linebuf) < 0)
06300dd9
KS
157 goto err;
158 continue;
159 }
160 c = linebuf[1];
06300dd9
KS
161 switch (c) {
162 default:
163 /*
164 * On double escape, just send the single one.
165 * Otherwise, it's an error.
166 */
06300dd9 167 if (c == escape) {
828615a1 168 if (putline(fp, &linebuf[1]) < 0)
06300dd9
KS
169 goto err;
170 else
171 break;
172 }
173 printf("Unknown tilde escape.\n");
174 break;
06300dd9
KS
175 case 'C':
176 /*
177 * Dump core.
178 */
06300dd9
KS
179 core();
180 break;
06300dd9
KS
181 case '!':
182 /*
183 * Shell escape, send the balance of the
184 * line to sh -c.
185 */
06300dd9
KS
186 shell(&linebuf[2]);
187 break;
06300dd9 188 case ':':
06300dd9
KS
189 /*
190 * Escape to command mode, but be nice!
191 */
343c874e 192 execute(&linebuf[2], 1);
828615a1 193 goto cont;
06300dd9
KS
194 case '.':
195 /*
196 * Simulate end of file on input.
197 */
828615a1 198 goto out;
06300dd9 199 case 'q':
06300dd9
KS
200 /*
201 * Force a quit of sending mail.
202 * Act like an interrupt happened.
203 */
06300dd9
KS
204 hadintr++;
205 collrub(SIGINT);
206 exit(1);
06300dd9
KS
207 case 'h':
208 /*
209 * Grab a bunch of headers.
210 */
06300dd9 211 grabh(hp, GTO|GSUBJECT|GCC|GBCC);
828615a1 212 goto cont;
06300dd9
KS
213 case 't':
214 /*
215 * Add to the To list.
216 */
3d6f01e5 217 hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
06300dd9 218 break;
06300dd9
KS
219 case 's':
220 /*
221 * Set the Subject list.
222 */
06300dd9 223 cp = &linebuf[2];
828615a1 224 while (isspace(*cp))
06300dd9
KS
225 cp++;
226 hp->h_subject = savestr(cp);
06300dd9 227 break;
06300dd9
KS
228 case 'c':
229 /*
230 * Add to the CC list.
231 */
3d6f01e5 232 hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
06300dd9 233 break;
06300dd9
KS
234 case 'b':
235 /*
236 * Add stuff to blind carbon copies list.
237 */
3d6f01e5 238 hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
06300dd9 239 break;
06300dd9 240 case 'd':
828615a1 241 strcpy(linebuf + 2, deadletter);
06300dd9 242 /* fall into . . . */
06300dd9
KS
243 case 'r':
244 /*
245 * Invoke a file:
246 * Search for the file name,
828615a1 247 * then open it and copy the contents to fp.
06300dd9 248 */
06300dd9 249 cp = &linebuf[2];
828615a1 250 while (isspace(*cp))
06300dd9
KS
251 cp++;
252 if (*cp == '\0') {
253 printf("Interpolate what file?\n");
254 break;
255 }
256 cp = expand(cp);
257 if (cp == NOSTR)
258 break;
259 if (isdir(cp)) {
828615a1 260 printf("%s: Directory\n", cp);
06300dd9
KS
261 break;
262 }
263 if ((fbuf = fopen(cp, "r")) == NULL) {
264 perror(cp);
265 break;
266 }
267 printf("\"%s\" ", cp);
80187484 268 fflush(stdout);
06300dd9
KS
269 lc = 0;
270 cc = 0;
828615a1 271 while (readline(fbuf, linebuf) >= 0) {
06300dd9 272 lc++;
828615a1 273 if ((t = putline(fp, linebuf)) < 0) {
06300dd9
KS
274 fclose(fbuf);
275 goto err;
276 }
277 cc += t;
278 }
279 fclose(fbuf);
280 printf("%d/%d\n", lc, cc);
281 break;
06300dd9
KS
282 case 'w':
283 /*
284 * Write the message on a file.
285 */
06300dd9
KS
286 cp = &linebuf[2];
287 while (any(*cp, " \t"))
288 cp++;
289 if (*cp == '\0') {
290 fprintf(stderr, "Write what file!?\n");
291 break;
292 }
293 if ((cp = expand(cp)) == NOSTR)
294 break;
828615a1
EW
295 rewind(fp);
296 exwrite(cp, fp, 1);
06300dd9 297 break;
06300dd9
KS
298 case 'm':
299 case 'f':
300 /*
301 * Interpolate the named messages, if we
302 * are in receiving mail mode. Does the
303 * standard list processing garbage.
304 * If ~f is given, we don't shift over.
305 */
06300dd9
KS
306 if (!rcvmode) {
307 printf("No messages to send from!?!\n");
308 break;
309 }
310 cp = &linebuf[2];
311 while (any(*cp, " \t"))
312 cp++;
828615a1 313 if (forward(cp, fp, c) < 0)
06300dd9 314 goto err;
828615a1 315 goto cont;
06300dd9 316 case '?':
06300dd9 317 if ((fbuf = fopen(THELPFILE, "r")) == NULL) {
e5cd0021 318 perror(THELPFILE);
06300dd9
KS
319 break;
320 }
4ee3e718 321 while ((t = getc(fbuf)) != EOF)
06300dd9 322 putchar(t);
06300dd9
KS
323 fclose(fbuf);
324 break;
06300dd9
KS
325 case 'p':
326 /*
327 * Print out the current state of the
328 * message without altering anything.
329 */
828615a1 330 rewind(fp);
06300dd9
KS
331 printf("-------\nMessage contains:\n");
332 puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
828615a1 333 while ((t = getc(fp)) != EOF)
06300dd9 334 putchar(t);
828615a1 335 goto cont;
06300dd9
KS
336 case '|':
337 /*
338 * Pipe message through command.
339 * Collect output as new message.
340 */
828615a1
EW
341 rewind(fp);
342 fp = mespipe(fp, &linebuf[2]);
343 goto cont;
06300dd9
KS
344 case 'v':
345 case 'e':
346 /*
347 * Edit the current message.
348 * 'e' means to use EDITOR
349 * 'v' means to use VISUAL
350 */
828615a1
EW
351 rewind(fp);
352 if ((fp = mesedit(fp, c)) == NULL)
06300dd9 353 goto err;
828615a1 354 goto cont;
06300dd9
KS
355 }
356 }
828615a1 357 goto out;
06300dd9 358err:
828615a1
EW
359 if (fp != NULL) {
360 fclose(fp);
361 fp = NULL;
362 }
363out:
364 if (fp != NULL)
365 rewind(fp);
366 signal(SIGINT, saveint);
367 signal(SIGHUP, savehup);
368 signal(SIGCONT, savecont);
369 sigsetmask(omask);
06300dd9 370 noreset = 0;
828615a1 371 return(fp);
06300dd9
KS
372}
373
374/*
375 * Write a file, ex-like if f set.
376 */
377
828615a1 378exwrite(name, fp, f)
06300dd9 379 char name[];
828615a1 380 FILE *fp;
06300dd9
KS
381{
382 register FILE *of;
383 register int c;
384 long cc;
385 int lc;
386 struct stat junk;
387
388 if (f) {
389 printf("\"%s\" ", name);
390 fflush(stdout);
391 }
6d1cdf8d 392 if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) {
06300dd9
KS
393 if (!f)
394 fprintf(stderr, "%s: ", name);
828615a1 395 fprintf(stderr, "File exists\n");
06300dd9
KS
396 return(-1);
397 }
398 if ((of = fopen(name, "w")) == NULL) {
399 perror(NOSTR);
400 return(-1);
401 }
402 lc = 0;
403 cc = 0;
828615a1 404 while ((c = getc(fp)) != EOF) {
06300dd9
KS
405 cc++;
406 if (c == '\n')
407 lc++;
408 putc(c, of);
409 if (ferror(of)) {
410 perror(name);
411 fclose(of);
412 return(-1);
413 }
414 }
415 fclose(of);
416 printf("%d/%ld\n", lc, cc);
417 fflush(stdout);
418 return(0);
419}
420
421/*
828615a1 422 * Edit the message being collected on fp.
06300dd9
KS
423 * Write the message out onto some poorly-named temp file
424 * and point an editor at it.
425 *
426 * On return, make the edit file the new temp file.
427 */
428
429FILE *
828615a1
EW
430mesedit(fp, c)
431 FILE *fp;
06300dd9 432{
828615a1
EW
433 int pid;
434 union wait s;
06300dd9
KS
435 FILE *fbuf;
436 register int t;
828615a1 437 int (*sigint)(), (*sigcont)();
06300dd9 438 struct stat sbuf;
001d60ad 439 extern char tempEdit[];
06300dd9
KS
440 register char *edit;
441
828615a1
EW
442 sigint = signal(SIGINT, SIG_IGN);
443 sigcont = signal(SIGCONT, SIG_DFL);
06300dd9
KS
444 if (stat(tempEdit, &sbuf) >= 0) {
445 printf("%s: file exists\n", tempEdit);
446 goto out;
447 }
448 close(creat(tempEdit, 0600));
449 if ((fbuf = fopen(tempEdit, "w")) == NULL) {
450 perror(tempEdit);
451 goto out;
452 }
828615a1 453 while ((t = getc(fp)) != EOF)
06300dd9 454 putc(t, fbuf);
06300dd9
KS
455 fflush(fbuf);
456 if (ferror(fbuf)) {
457 perror(tempEdit);
458 remove(tempEdit);
459 goto fix;
460 }
461 fclose(fbuf);
462 if ((edit = value(c == 'e' ? "EDITOR" : "VISUAL")) == NOSTR)
463 edit = c == 'e' ? EDITOR : VISUAL;
464 pid = vfork();
465 if (pid == 0) {
828615a1
EW
466 if (sigint != SIG_IGN)
467 signal(SIGINT, SIG_DFL);
06300dd9
KS
468 execl(edit, edit, tempEdit, 0);
469 perror(edit);
470 _exit(1);
471 }
472 if (pid == -1) {
473 perror("fork");
474 remove(tempEdit);
475 goto out;
476 }
477 while (wait(&s) != pid)
478 ;
828615a1 479 if (s.w_status != 0) {
06300dd9
KS
480 printf("Fatal error in \"%s\"\n", edit);
481 remove(tempEdit);
482 goto out;
483 }
484
485 /*
486 * Now switch to new file.
487 */
488
828615a1 489 if ((fbuf = fopen(tempEdit, "a+")) == NULL) {
06300dd9
KS
490 perror(tempEdit);
491 remove(tempEdit);
492 goto out;
493 }
06300dd9 494 remove(tempEdit);
828615a1
EW
495 collf = fbuf;
496 fclose(fp);
497 fp = fbuf;
06300dd9
KS
498 goto out;
499fix:
500 perror(tempEdit);
501out:
828615a1
EW
502 signal(SIGCONT, sigcont);
503 signal(SIGINT, sigint);
504 return(fp);
06300dd9
KS
505}
506
507/*
508 * Pipe the message through the command.
509 * Old message is on stdin of command;
510 * New message collected from stdout.
511 * Sh -c must return 0 to accept the new message.
512 */
513
514FILE *
828615a1
EW
515mespipe(fp, cmd)
516 FILE *fp;
06300dd9
KS
517 char cmd[];
518{
001d60ad 519 extern char tempEdit[];
828615a1
EW
520 register FILE *nf;
521 int pid;
522 union wait s;
523 int (*saveint)();
06300dd9
KS
524 char *Shell;
525
828615a1 526 if ((nf = fopen(tempEdit, "w+")) == NULL) {
06300dd9 527 perror(tempEdit);
828615a1 528 return(fp);
06300dd9
KS
529 }
530 remove(tempEdit);
828615a1 531 saveint = signal(SIGINT, SIG_IGN);
06300dd9
KS
532 if ((Shell = value("SHELL")) == NULL)
533 Shell = "/bin/sh";
534 if ((pid = vfork()) == -1) {
535 perror("fork");
536 goto err;
537 }
538 if (pid == 0) {
828615a1 539 int fd;
06300dd9
KS
540 /*
541 * stdin = current message.
542 * stdout = new message.
543 */
544
545 close(0);
828615a1 546 dup(fileno(fp));
06300dd9 547 close(1);
828615a1
EW
548 dup(fileno(nf));
549 for (fd = getdtablesize(); --fd > 2;)
550 close(fd);
06300dd9
KS
551 execl(Shell, Shell, "-c", cmd, 0);
552 perror(Shell);
553 _exit(1);
554 }
555 while (wait(&s) != pid)
556 ;
828615a1 557 if (s.w_status != 0 || pid == -1) {
06300dd9
KS
558 fprintf(stderr, "\"%s\" failed!?\n", cmd);
559 goto err;
560 }
828615a1 561 if (fsize(nf) == 0) {
06300dd9
KS
562 fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
563 goto err;
564 }
565
566 /*
567 * Take new files.
568 */
569
828615a1
EW
570 fseek(nf, 0L, 2);
571 collf = nf;
572 fclose(fp);
573 signal(SIGINT, saveint);
574 return(nf);
06300dd9
KS
575
576err:
828615a1
EW
577 fclose(nf);
578 signal(SIGINT, saveint);
579 return(fp);
06300dd9
KS
580}
581
582/*
583 * Interpolate the named messages into the current
584 * message, preceding each line with a tab.
585 * Return a count of the number of characters now in
586 * the message, or -1 if an error is encountered writing
587 * the message temporary. The flag argument is 'm' if we
588 * should shift over and 'f' if not.
589 */
828615a1 590forward(ms, fp, f)
06300dd9 591 char ms[];
828615a1 592 FILE *fp;
06300dd9
KS
593{
594 register int *msgvec, *ip;
595 extern char tempMail[];
596
597 msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec);
598 if (msgvec == (int *) NOSTR)
599 return(0);
600 if (getmsglist(ms, msgvec, 0) < 0)
601 return(0);
602 if (*msgvec == NULL) {
603 *msgvec = first(0, MMNORM);
604 if (*msgvec == NULL) {
605 printf("No appropriate messages\n");
606 return(0);
607 }
608 msgvec[1] = NULL;
609 }
610 printf("Interpolating:");
611 for (ip = msgvec; *ip != NULL; ip++) {
612 touch(*ip);
613 printf(" %d", *ip);
614 if (f == 'm') {
828615a1 615 if (transmit(&message[*ip-1], fp) < 0L) {
06300dd9
KS
616 perror(tempMail);
617 return(-1);
618 }
619 } else
828615a1 620 if (send(&message[*ip-1], fp, 0) < 0) {
06300dd9
KS
621 perror(tempMail);
622 return(-1);
623 }
624 }
625 printf("\n");
626 return(0);
627}
628
629/*
630 * Send message described by the passed pointer to the
631 * passed output buffer. Insert a tab in front of each
632 * line. Return a count of the characters sent, or -1
633 * on error.
634 */
5deb0e1e 635long
828615a1 636transmit(mailp, fp)
06300dd9 637 struct message *mailp;
828615a1 638 FILE *fp;
06300dd9
KS
639{
640 register struct message *mp;
5deb0e1e
CS
641 register int ch;
642 long c, n;
643 int bol;
06300dd9 644 FILE *ibuf;
1d6cdde7 645 char *tabst;
06300dd9
KS
646
647 mp = mailp;
648 ibuf = setinput(mp);
5deb0e1e 649 c = mp->m_size;
06300dd9
KS
650 n = c;
651 bol = 1;
1d6cdde7
EW
652 if ((tabst = value("tabstr")) == NOSTR)
653 tabst = "\t";
5deb0e1e 654 while (c-- > 0L) {
828615a1
EW
655 ch = getc(ibuf);
656 if (ch == '\n')
657 bol = 1;
658 else if (bol) {
06300dd9 659 bol = 0;
1d6cdde7 660 fputs(tabst, fp);
06300dd9 661 n++;
06300dd9 662 }
828615a1
EW
663 putc(ch, fp);
664 if (ferror(fp)) {
06300dd9 665 perror("/tmp");
5deb0e1e 666 return(-1L);
06300dd9
KS
667 }
668 }
669 return(n);
670}
671
2d55c063
KS
672/*
673 * Print (continue) when continued after ^Z.
674 */
828615a1 675/*ARGSUSED*/
2d55c063
KS
676collcont(s)
677{
678
828615a1
EW
679 hadintr = 0;
680 longjmp(coljmp, 1);
2d55c063
KS
681}
682
06300dd9
KS
683/*
684 * On interrupt, go here to save the partial
7feecf47 685 * message on ~/dead.letter.
06300dd9
KS
686 * Then restore signals and execute the normal
687 * signal routine. We only come here if signals
688 * were previously set anyway.
689 */
06300dd9
KS
690collrub(s)
691{
692 register FILE *dbuf;
693 register int c;
694
7feecf47 695 if (s == SIGINT && hadintr == 0) {
828615a1 696 hadintr = 1;
06300dd9
KS
697 longjmp(coljmp, 1);
698 }
828615a1
EW
699 rewind(collf);
700 if (s == SIGINT && value("nosave") != NOSTR || fsize(collf) == 0)
06300dd9
KS
701 goto done;
702 if ((dbuf = fopen(deadletter, "w")) == NULL)
703 goto done;
704 chmod(deadletter, 0600);
828615a1 705 while ((c = getc(collf)) != EOF)
06300dd9
KS
706 putc(c, dbuf);
707 fclose(dbuf);
06300dd9 708done:
828615a1
EW
709 fclose(collf);
710 signal(SIGINT, saveint);
711 signal(SIGHUP, savehup);
712 signal(SIGCONT, savecont);
7feecf47
KS
713 if (rcvmode) {
714 if (s == SIGHUP)
715 hangup(SIGHUP);
716 else
2d55c063 717 stop(s);
828615a1 718 } else
06300dd9
KS
719 exit(1);
720}
721
722/*
723 * Acknowledge an interrupt signal from the tty by typing an @
724 */
828615a1 725/*ARGSUSED*/
06300dd9
KS
726intack(s)
727{
828615a1 728
06300dd9
KS
729 puts("@");
730 fflush(stdout);
731 clearerr(stdin);
06300dd9 732}