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