first cut at new makefile; file reorg, new depend
[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 18#ifndef lint
2ee3bce2 19static char sccsid[] = "@(#)collect.c 5.17 (Berkeley) %G%";
acfc7e9b 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>
31
32/*
33 * Read a message from standard output and return a read file to it
34 * or NULL on error.
35 */
36
37/*
38 * The following hokiness with global variables is so that on
39 * receipt of an interrupt signal, the partial message can be salted
828615a1 40 * away on dead.letter.
06300dd9
KS
41 */
42
828615a1 43static int (*saveint)(); /* Previous SIGINT value */
7feecf47 44static int (*savehup)(); /* Previous SIGHUP value */
2ee3bce2
EW
45static int (*savetstp)(); /* Previous SIGTSTP value */
46static int (*savettou)(); /* Previous SIGTTOU value */
47static int (*savettin)(); /* Previous SIGTTIN value */
828615a1 48static FILE *collf; /* File for saving away */
06300dd9
KS
49static int hadintr; /* Have seen one SIGINT so far */
50
2ee3bce2
EW
51static jmp_buf colljmp; /* To get back to work */
52static int colljmp_p; /* whether to long jump */
53static jmp_buf collabort; /* To end collection with error */
06300dd9
KS
54
55FILE *
3d6f01e5 56collect(hp, printheaders)
06300dd9
KS
57 struct header *hp;
58{
d33aa50d 59 FILE *fbuf;
2ee3bce2
EW
60 int lc, cc, escape, eofcount;
61 int collint(), collhup(), collstop();
06300dd9
KS
62 register int c, t;
63 char linebuf[LINESIZE], *cp;
64 extern char tempMail[];
4e161d3b 65 char getsub;
828615a1 66 int omask;
06300dd9 67
828615a1 68 collf = NULL;
828615a1
EW
69 /*
70 * Start catching signals from here, but we're still die on interrupts
71 * until we're in the main loop.
72 */
73 omask = sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
74 if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
2ee3bce2 75 signal(SIGINT, collint);
828615a1 76 if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
2ee3bce2
EW
77 signal(SIGHUP, collhup);
78 savetstp = signal(SIGTSTP, collstop);
79 savettou = signal(SIGTTOU, collstop);
80 savettin = signal(SIGTTIN, collstop);
81 if (setjmp(collabort) || setjmp(colljmp)) {
828615a1 82 remove(tempMail);
06300dd9
KS
83 goto err;
84 }
828615a1
EW
85 sigsetmask(omask & ~(sigmask(SIGINT) | sigmask(SIGHUP)));
86
2ee3bce2 87 noreset++;
d33aa50d 88 if ((collf = fopen(tempMail, "w+")) == NULL) {
06300dd9 89 perror(tempMail);
06300dd9
KS
90 goto err;
91 }
d33aa50d 92 unlink(tempMail);
06300dd9
KS
93
94 /*
95 * If we are going to prompt for a subject,
96 * refrain from printing a newline after
97 * the headers (since some people mind).
98 */
06300dd9 99 t = GTO|GSUBJECT|GCC|GNL;
4e161d3b 100 getsub = 0;
686f6134 101 if (hp->h_subject == NOSTR && value("interactive") != NOSTR &&
638cc92d 102 (value("ask") != NOSTR || value("asksub") != NOSTR))
4e161d3b 103 t &= ~GNL, getsub++;
3d6f01e5 104 if (printheaders) {
06300dd9
KS
105 puthead(hp, stdout, t);
106 fflush(stdout);
107 }
06300dd9
KS
108 if ((cp = value("escape")) != NOSTR)
109 escape = *cp;
686f6134
EW
110 else
111 escape = ESCAPE;
2ee3bce2 112 eofcount = 0;
828615a1
EW
113 hadintr = 0;
114
2ee3bce2 115 if (!setjmp(colljmp)) {
828615a1 116 if (getsub)
4e161d3b 117 grabh(hp, GSUBJECT);
828615a1
EW
118 } else {
119 /*
120 * Come here for printing the after-signal message.
121 * Duplicate messages won't be printed because
122 * the write is aborted if we get a SIGTTOU.
123 */
124cont:
125 if (hadintr) {
126 fflush(stdout);
127 fprintf(stderr,
128 "\n(Interrupt -- one more to kill letter)\n");
129 } else {
130 printf("(continue)\n");
131 fflush(stdout);
4e161d3b 132 }
828615a1
EW
133 }
134 for (;;) {
2ee3bce2
EW
135 colljmp_p = 1;
136 c = readline(stdin, linebuf, LINESIZE);
137 colljmp_p = 0;
138 if (c < 0) {
686f6134 139 if (value("interactive") != NOSTR &&
2ee3bce2 140 value("ignoreeof") != NOSTR && ++eofcount < 25) {
828615a1 141 printf("Use \".\" to terminate letter\n");
0fa68535
KS
142 continue;
143 }
06300dd9 144 break;
0fa68535 145 }
2ee3bce2 146 eofcount = 0;
06300dd9 147 hadintr = 0;
686f6134
EW
148 if (linebuf[0] == '.' && linebuf[1] == '\0' &&
149 value("interactive") != NOSTR &&
b068a67e 150 (value("dot") != NOSTR || value("ignoreeof") != NOSTR))
06300dd9 151 break;
686f6134 152 if (linebuf[0] != escape || value("interactive") == NOSTR) {
d33aa50d 153 if (putline(collf, linebuf) < 0)
06300dd9
KS
154 goto err;
155 continue;
156 }
157 c = linebuf[1];
06300dd9
KS
158 switch (c) {
159 default:
160 /*
161 * On double escape, just send the single one.
162 * Otherwise, it's an error.
163 */
06300dd9 164 if (c == escape) {
d33aa50d 165 if (putline(collf, &linebuf[1]) < 0)
06300dd9
KS
166 goto err;
167 else
168 break;
169 }
170 printf("Unknown tilde escape.\n");
171 break;
06300dd9
KS
172 case 'C':
173 /*
174 * Dump core.
175 */
06300dd9
KS
176 core();
177 break;
06300dd9
KS
178 case '!':
179 /*
180 * Shell escape, send the balance of the
181 * line to sh -c.
182 */
06300dd9
KS
183 shell(&linebuf[2]);
184 break;
06300dd9 185 case ':':
06300dd9
KS
186 /*
187 * Escape to command mode, but be nice!
188 */
343c874e 189 execute(&linebuf[2], 1);
828615a1 190 goto cont;
06300dd9
KS
191 case '.':
192 /*
193 * Simulate end of file on input.
194 */
828615a1 195 goto out;
06300dd9 196 case 'q':
06300dd9
KS
197 /*
198 * Force a quit of sending mail.
199 * Act like an interrupt happened.
200 */
06300dd9 201 hadintr++;
2ee3bce2 202 collint(SIGINT);
06300dd9 203 exit(1);
06300dd9
KS
204 case 'h':
205 /*
206 * Grab a bunch of headers.
207 */
06300dd9 208 grabh(hp, GTO|GSUBJECT|GCC|GBCC);
828615a1 209 goto cont;
06300dd9
KS
210 case 't':
211 /*
212 * Add to the To list.
213 */
3d6f01e5 214 hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
06300dd9 215 break;
06300dd9
KS
216 case 's':
217 /*
218 * Set the Subject list.
219 */
06300dd9 220 cp = &linebuf[2];
828615a1 221 while (isspace(*cp))
06300dd9
KS
222 cp++;
223 hp->h_subject = savestr(cp);
06300dd9 224 break;
06300dd9
KS
225 case 'c':
226 /*
227 * Add to the CC list.
228 */
3d6f01e5 229 hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
06300dd9 230 break;
06300dd9
KS
231 case 'b':
232 /*
233 * Add stuff to blind carbon copies list.
234 */
3d6f01e5 235 hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
06300dd9 236 break;
06300dd9 237 case 'd':
62e28b79 238 strcpy(linebuf + 2, getdeadletter());
06300dd9 239 /* fall into . . . */
06300dd9
KS
240 case 'r':
241 /*
242 * Invoke a file:
243 * Search for the file name,
d33aa50d 244 * then open it and copy the contents to collf.
06300dd9 245 */
06300dd9 246 cp = &linebuf[2];
828615a1 247 while (isspace(*cp))
06300dd9
KS
248 cp++;
249 if (*cp == '\0') {
250 printf("Interpolate what file?\n");
251 break;
252 }
253 cp = expand(cp);
254 if (cp == NOSTR)
255 break;
256 if (isdir(cp)) {
828615a1 257 printf("%s: Directory\n", cp);
06300dd9
KS
258 break;
259 }
260 if ((fbuf = fopen(cp, "r")) == NULL) {
261 perror(cp);
262 break;
263 }
264 printf("\"%s\" ", cp);
80187484 265 fflush(stdout);
06300dd9
KS
266 lc = 0;
267 cc = 0;
2ee3bce2 268 while (readline(fbuf, linebuf, LINESIZE) >= 0) {
06300dd9 269 lc++;
d33aa50d 270 if ((t = putline(collf, linebuf)) < 0) {
06300dd9
KS
271 fclose(fbuf);
272 goto err;
273 }
274 cc += t;
275 }
276 fclose(fbuf);
277 printf("%d/%d\n", lc, cc);
278 break;
06300dd9
KS
279 case 'w':
280 /*
281 * Write the message on a file.
282 */
06300dd9 283 cp = &linebuf[2];
470c33f3 284 while (*cp == ' ' || *cp == '\t')
06300dd9
KS
285 cp++;
286 if (*cp == '\0') {
287 fprintf(stderr, "Write what file!?\n");
288 break;
289 }
290 if ((cp = expand(cp)) == NOSTR)
291 break;
d33aa50d
EW
292 rewind(collf);
293 exwrite(cp, collf, 1);
06300dd9 294 break;
06300dd9 295 case 'm':
2de8fc95 296 case 'M':
06300dd9 297 case 'f':
2de8fc95 298 case 'F':
06300dd9
KS
299 /*
300 * Interpolate the named messages, if we
301 * are in receiving mail mode. Does the
302 * standard list processing garbage.
303 * If ~f is given, we don't shift over.
304 */
470c33f3 305 if (forward(linebuf + 2, collf, c) < 0)
06300dd9 306 goto err;
828615a1 307 goto cont;
06300dd9 308 case '?':
06300dd9 309 if ((fbuf = fopen(THELPFILE, "r")) == NULL) {
e5cd0021 310 perror(THELPFILE);
06300dd9
KS
311 break;
312 }
4ee3e718 313 while ((t = getc(fbuf)) != EOF)
2ee3bce2 314 (void) putchar(t);
06300dd9
KS
315 fclose(fbuf);
316 break;
06300dd9
KS
317 case 'p':
318 /*
319 * Print out the current state of the
320 * message without altering anything.
321 */
d33aa50d 322 rewind(collf);
06300dd9
KS
323 printf("-------\nMessage contains:\n");
324 puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
d33aa50d 325 while ((t = getc(collf)) != EOF)
2ee3bce2 326 (void) putchar(t);
828615a1 327 goto cont;
06300dd9
KS
328 case '|':
329 /*
330 * Pipe message through command.
331 * Collect output as new message.
332 */
d33aa50d
EW
333 rewind(collf);
334 mespipe(collf, &linebuf[2]);
828615a1 335 goto cont;
06300dd9
KS
336 case 'v':
337 case 'e':
338 /*
339 * Edit the current message.
340 * 'e' means to use EDITOR
341 * 'v' means to use VISUAL
342 */
d33aa50d
EW
343 rewind(collf);
344 mesedit(collf, c);
828615a1 345 goto cont;
06300dd9
KS
346 }
347 }
828615a1 348 goto out;
06300dd9 349err:
d33aa50d
EW
350 if (collf != NULL) {
351 fclose(collf);
352 collf = NULL;
828615a1
EW
353 }
354out:
d33aa50d
EW
355 if (collf != NULL)
356 rewind(collf);
2ee3bce2
EW
357 noreset--;
358 sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
828615a1
EW
359 signal(SIGINT, saveint);
360 signal(SIGHUP, savehup);
2ee3bce2
EW
361 signal(SIGTSTP, savetstp);
362 signal(SIGTTOU, savettou);
363 signal(SIGTTIN, savettin);
828615a1 364 sigsetmask(omask);
d33aa50d 365 return collf;
06300dd9
KS
366}
367
368/*
369 * Write a file, ex-like if f set.
370 */
371
828615a1 372exwrite(name, fp, f)
06300dd9 373 char name[];
828615a1 374 FILE *fp;
06300dd9
KS
375{
376 register FILE *of;
377 register int c;
378 long cc;
379 int lc;
380 struct stat junk;
381
382 if (f) {
383 printf("\"%s\" ", name);
384 fflush(stdout);
385 }
6d1cdf8d 386 if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) {
06300dd9
KS
387 if (!f)
388 fprintf(stderr, "%s: ", name);
828615a1 389 fprintf(stderr, "File exists\n");
06300dd9
KS
390 return(-1);
391 }
392 if ((of = fopen(name, "w")) == NULL) {
393 perror(NOSTR);
394 return(-1);
395 }
396 lc = 0;
397 cc = 0;
828615a1 398 while ((c = getc(fp)) != EOF) {
06300dd9
KS
399 cc++;
400 if (c == '\n')
401 lc++;
2ee3bce2 402 (void) putc(c, of);
06300dd9
KS
403 if (ferror(of)) {
404 perror(name);
405 fclose(of);
406 return(-1);
407 }
408 }
409 fclose(of);
410 printf("%d/%ld\n", lc, cc);
411 fflush(stdout);
412 return(0);
413}
414
415/*
828615a1 416 * Edit the message being collected on fp.
06300dd9
KS
417 * On return, make the edit file the new temp file.
418 */
828615a1
EW
419mesedit(fp, c)
420 FILE *fp;
06300dd9 421{
d33aa50d 422 int (*sigint)() = signal(SIGINT, SIG_IGN);
d33aa50d 423 FILE *nf = run_editor(fp, (off_t)-1, c, 0);
06300dd9 424
d33aa50d
EW
425 if (nf != NULL) {
426 fseek(nf, (off_t)0, 2);
427 collf = nf;
428 fclose(fp);
06300dd9 429 }
d33aa50d 430 (void) signal(SIGINT, sigint);
06300dd9
KS
431}
432
433/*
434 * Pipe the message through the command.
435 * Old message is on stdin of command;
436 * New message collected from stdout.
437 * Sh -c must return 0 to accept the new message.
438 */
828615a1
EW
439mespipe(fp, cmd)
440 FILE *fp;
06300dd9
KS
441 char cmd[];
442{
d33aa50d
EW
443 FILE *nf;
444 int (*sigint)() = signal(SIGINT, SIG_IGN);
001d60ad 445 extern char tempEdit[];
06300dd9 446
828615a1 447 if ((nf = fopen(tempEdit, "w+")) == NULL) {
06300dd9 448 perror(tempEdit);
d33aa50d 449 goto out;
06300dd9 450 }
d33aa50d
EW
451 (void) unlink(tempEdit);
452 /*
453 * stdin = current message.
454 * stdout = new message.
455 */
456 if (run_command(cmd, 0, fileno(fp), fileno(nf), NOSTR) < 0) {
457 (void) fclose(nf);
458 goto out;
06300dd9 459 }
828615a1 460 if (fsize(nf) == 0) {
06300dd9 461 fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
d33aa50d
EW
462 (void) fclose(nf);
463 goto out;
06300dd9 464 }
06300dd9
KS
465 /*
466 * Take new files.
467 */
d33aa50d 468 (void) fseek(nf, 0L, 2);
828615a1 469 collf = nf;
d33aa50d
EW
470 (void) fclose(fp);
471out:
472 (void) signal(SIGINT, sigint);
06300dd9
KS
473}
474
475/*
476 * Interpolate the named messages into the current
477 * message, preceding each line with a tab.
478 * Return a count of the number of characters now in
479 * the message, or -1 if an error is encountered writing
480 * the message temporary. The flag argument is 'm' if we
481 * should shift over and 'f' if not.
482 */
828615a1 483forward(ms, fp, f)
06300dd9 484 char ms[];
828615a1 485 FILE *fp;
06300dd9 486{
470c33f3 487 register int *msgvec;
06300dd9 488 extern char tempMail[];
2de8fc95
EW
489 struct ignoretab *ig;
490 char *tabst;
06300dd9
KS
491
492 msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec);
493 if (msgvec == (int *) NOSTR)
494 return(0);
495 if (getmsglist(ms, msgvec, 0) < 0)
496 return(0);
470c33f3 497 if (*msgvec == 0) {
06300dd9
KS
498 *msgvec = first(0, MMNORM);
499 if (*msgvec == NULL) {
500 printf("No appropriate messages\n");
501 return(0);
502 }
503 msgvec[1] = NULL;
504 }
2de8fc95
EW
505 if (f == 'f' || f == 'F')
506 tabst = NOSTR;
507 else if ((tabst = value("tabstr")) == NOSTR)
508 tabst = "\t";
509 ig = isupper(f) ? NULL : ignore;
06300dd9 510 printf("Interpolating:");
470c33f3
EW
511 for (; *msgvec != 0; msgvec++) {
512 struct message *mp = message + *msgvec - 1;
513
514 touch(mp);
515 printf(" %d", *msgvec);
516 if (send(mp, fp, ig, tabst) < 0) {
2de8fc95
EW
517 perror(tempMail);
518 return(-1);
519 }
06300dd9
KS
520 }
521 printf("\n");
522 return(0);
523}
524
2d55c063
KS
525/*
526 * Print (continue) when continued after ^Z.
527 */
828615a1 528/*ARGSUSED*/
2ee3bce2 529collstop(s)
2d55c063 530{
2ee3bce2 531 int (*old_action)() = signal(s, SIG_DFL);
2d55c063 532
2ee3bce2
EW
533 sigsetmask(sigblock(0) & ~sigmask(s));
534 kill(0, s);
535 sigblock(sigmask(s));
536 signal(s, old_action);
537 if (colljmp_p) {
538 colljmp_p = 0;
539 hadintr = 0;
540 longjmp(colljmp, 1);
541 }
2d55c063
KS
542}
543
06300dd9 544/*
2ee3bce2
EW
545 * On interrupt, come here to save the partial message in ~/dead.letter.
546 * Then jump out of the collection loop.
06300dd9 547 */
2ee3bce2
EW
548/*ARGSUSED*/
549collint(s)
06300dd9 550{
2ee3bce2
EW
551 /*
552 * the control flow is subtle, because we can be called from ~q.
553 */
554 if (!hadintr) {
555 if (value("ignore") != NOSTR) {
556 puts("@");
557 fflush(stdout);
558 clearerr(stdin);
559 return;
560 }
828615a1 561 hadintr = 1;
2ee3bce2 562 longjmp(colljmp, 1);
06300dd9 563 }
828615a1 564 rewind(collf);
2ee3bce2 565 if (value("nosave") == NOSTR)
62e28b79 566 savedeadletter(collf);
2ee3bce2
EW
567 longjmp(collabort, 1);
568}
569
570/*ARGSUSED*/
571collhup(s)
572{
573 rewind(collf);
574 savedeadletter(collf);
575 /*
576 * Let's pretend nobody else wants to clean up,
577 * a true statement at this time.
578 */
579 exit(1);
06300dd9
KS
580}
581
62e28b79
EW
582savedeadletter(fp)
583 register FILE *fp;
584{
585 register FILE *dbuf;
586 register int c;
587 char *cp;
588
589 if (fsize(fp) == 0)
590 return;
591 cp = getdeadletter();
592 c = umask(077);
593 dbuf = fopen(cp, "a");
2ee3bce2 594 (void) umask(c);
62e28b79
EW
595 if (dbuf == NULL)
596 return;
597 while ((c = getc(fp)) != EOF)
2ee3bce2 598 (void) putc(c, dbuf);
62e28b79
EW
599 fclose(dbuf);
600 rewind(fp);
601}