/* * print mail */ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <setjmp.h> #include <ctype.h> #include "mail.h" #include "letter.h" #include "string.h" /* globals */ static int flgp; static int manual; static int forward; static int nlet; static int error; static int otherfile; static jmp_buf sjbuf; #define seq(i) (forward ? i-1 : nlet-i) static int changed; /* true if mail file has been changed */ /* exported */ char *thissys; /* imported */ extern void lock(); extern void unlock(); extern char *getlogin(); extern char *getenv(); extern char *getarg(); extern FILE *popen(); extern char *mailpath(); extern long getunix(); extern char *getsysname(); extern char *P(); extern void V(); /* predefined */ static char *doargs(); static long copymt(); static void domanual(); static void doauto(); static int docommand(); static int copyback(); static int done(); static int special_mailfile(); static int doprint(); static void dohelp(); static void print_from(); static int reply(); static int remail(); static int squirrel_away(); main(argc, argv) char **argv; { struct stat stbuf; char *mp, *tp; long mailsize; FILE *malf; mp = doargs(argc, argv); if (stat(mp, &stbuf) < 0 || stbuf.st_size == 0L) { printf("No mail.\n"); return 0; } thissys = getsysname(); (void)signal(SIGINT, done); (void)signal(SIGHUP, done); lock(mp); tp = otherfile ? NULL : P(); if (tp != NULL) fprintf(stderr, "You are already reading mail on %s!\n\n", tp); malf = fopen(mp, "r"); if (malf == NULL) { fprintf(stderr, "mail: cannot read %s.\n", mp); error++; (void)done(); } /* see if we're piping or forwarding */ if(special_mailfile(mp)) (void)done(); /* make temporary */ inittmp(); /* read mbox */ mailsize = copymt(malf); fclose(malf); unlock(); /* print the mail */ if (flgp) printall(); else { if (manual) domanual(); else doauto(); } if (changed) copyback(mailsize, mp); (void)done(); return 0; } /* Output all messages */ printall() { int i, current; for (i = 0; i < nlet; i++) { current = forward ? i : nlet - i - 1; doprint(current); } } /* remember an interrupt happened */ rememberint() { signal(SIGINT, rememberint); clearerr(stdin); clearerr(stdout); longjmp(sjbuf, 1); } #define eatwhite(p) while(*p==' '||*p=='\t') p++ /* * Return a letter number */ static int lnumber(pp, dot, def) char **pp; { eatwhite(*pp); switch(**pp) { case '$': (*pp)++; dot = nlet; break; case 'n': case '+': (*pp)++; dot = dot + 1; break; case '^': case '-': (*pp)++; dot = dot - 1; break; case '.': (*pp)++; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': dot = 0; while (isdigit(**pp)) dot = dot*10 + *(*pp)++ - '0'; break; /* first letter is 1 */ default: dot = def; break; } if (dot <= 0) dot = 1; return dot; } /* * Read and parse a command line. A command consists of an optional * range, a single letter command, and optional arguments. */ static char * parse(cmdp, firstp, lastp, dot) char *cmdp; /* the 1 letter command */ int *firstp; /* first command in a range */ int *lastp; /* last command in a range */ int dot; /* current letter */ { static char resp[CMDSIZE]; char *p = resp; /* get command and dump trailing newline */ printf("? "); fflush(stdout); if (fgets(resp, CMDSIZE, stdin) == NULL) return NULL; p[strlen(p) - 1] = '\0'; /* get range */ eatwhite(p); if (*p != '\0') { if (*p == ',') *firstp = 1; else *firstp = lnumber(&p, dot, dot); eatwhite(p); if (*p != ',') *lastp = *firstp; else { p++; *lastp = lnumber(&p, dot, nlet); } } else *firstp = *lastp = dot+1; /* get command */ eatwhite(p); if (*p != '\0') *cmdp = *p++; else *cmdp = 'p'; /* compound command */ if (*cmdp == 'd' && (*p == 'q' || *p == 'p')) *(cmdp+1) = *p++; else *(cmdp+1) = '\0'; eatwhite(p); return p; } /* Process user commands in the new style. * All actions are prompted for. */ static void domanual() { int dot=1, print, i; int first, last; char cmd[2], *args; printf("%d letters\n", nlet); while (1) { /* in case of interrupt, come back here but don't print */ if (setjmp(sjbuf)) /* after long jump */ printf("\n"); signal(SIGINT, rememberint); args = parse(cmd, &first, &last, dot); if (args == NULL) break; for (i = first; i <= last && i <= nlet; i++) { dot = i; /* must be here in case of interrupt */ if (docommand(cmd[0], args, i, &print) < 0) return; } switch (cmd[1]) { case 'q': return; case 'p': doprint(seq(++dot)); break; } } } /* Process user commands in the old style. * This implies printing the next message * without prompting for it */ static void doauto() { int print=1, dot=1, i; int first, last; char cmd[2], *args; while (dot <= nlet && dot >= 0) { /* in case of interrupt, come back here but don't print */ if (setjmp(sjbuf)) /* after long jump */ printf("\n"); else { /* here normally */ signal(SIGINT, rememberint); if (print) doprint(seq(dot)); } print = 0; args = parse(cmd, &first, &last, dot); if (args == NULL) break; for (i = first; i <= last && i <= nlet; i++) { dot = i; /* in case of interrupt while printing */ dot = docommand(cmd[0], args, i, &print); } if (dot > 0 && dot < last) dot = last; switch (cmd[1]) { case 'q': return; case 'p': print++; break; } } } /* * Perform a command. */ static int docommand(cmd, args, dot, pflag) char cmd; /* command to perform */ char *args; /* arguments to the command */ int dot; /* the message to apply it to */ int *pflag; /* (returned) true if next message to be printed */ { switch (cmd) { default: printf("usage\n"); case '?': dohelp(); break; case 'h': print_from(seq(dot)); break; case '=': printf("at letter %d\n", dot); break; case 'p': doprint(seq(dot)); break; case 'x': changed = 0; dot = -1; break; case 'y': case 'w': case 's': if (squirrel_away(seq(dot), args) == 0) { (void)ldelete(seq(dot)); changed++; (*pflag)++; dot++; } break; case 'm': if (remail(seq(dot), args) == 0) { (void)ldelete(seq(dot)); changed++; (*pflag)++; dot++; } break; case 'r': if (*args != '\0') { fprintf(stderr, "invalid: r takes no arguments\n"); break; } (void)reply(seq(dot)); printf("!\n"); break; case '!': system(args); printf("!\n"); break; case 'd': if (*args != '\0') { fprintf(stderr,"invalid: d takes no arguments\n"); break; } (void)ldelete(seq(dot)); changed++; (*pflag)++; dot++; break; case 'u': if (*args != '\0') { fprintf(stderr,"invalid: u takes no arguments\n"); break; } (void)lundelete(seq(dot)); (*pflag)++; dot++; break; case 'q': dot = -1; break; } return dot; } /* copy mail to temp file, returns size of mail file */ static long copymt(mfp) FILE *mfp; { letter *lp=NULL; char line[FROMLINESIZE]; /* read in letters */ while (fgets(line, FROMLINESIZE, mfp) != NULL) { if (strncmp(line, FROM, FSIZE)==0 || lp==NULL) { lp = lopen(nlet++, "w"); } lputs(line, lp); } /* remember where end of file is */ return ftell(mfp); } /* copy temp or whatever back to /usr/spool/mail */ static int copyback(oldsize, mp) long oldsize; char *mp; { register i; register int new = 0; struct stat stbuf; letter *lp; FILE *malf; lock(mp); /* read new mail that arrived while we were printing */ stat(mp, &stbuf); if (stbuf.st_size != oldsize) { /* new mail has arrived */ malf = fopen(mp, "r"); if (malf == NULL) { fprintf(stderr, "mail: can't re-read %s\n", mp); (void)done(); } fseek(malf, oldsize, 0); (void)copymt(malf); fclose(malf); new++; } /* turn off interrupts while writing back */ signal(SIGHUP, SIG_IGN); signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGTERM, SIG_IGN); /* write the sucker back */ malf = fopen(mp, "w"); if (malf == NULL) { fprintf(stderr, "mail: can't rewrite %s\n", mp); (void)done(); } for (i = 0; i < nlet; i++) if ((lp=lopen(i, "ru")) != NULL) { copyfrom(lp, malf); } fclose(malf); unlock(); if (new) printf("new mail arrived\n"); } /* come here to finish up */ static int done() { unlock(); V(); releasetmp(); exit(error); return 0; } static char * doargs(argc, argv) int argc; char *argv[]; { static char mailfile[256]; (void)strcpy(mailfile, mailpath(getlogin())); for (; argc>1; argv++, argc--) { if (argv[1][0]=='-') { if (argv[1][1]=='p') { flgp++; } else if (argv[1][1]=='f') { if (argc>=3) { strcpy(mailfile, argv[2]); argv++; argc--; otherfile = 1; } } else if (argv[1][1]=='r') { forward = 1; } else if (argv[1][1]=='m') { manual = 1; } else { fprintf(stderr, "mail: unknown option %c\n", argv[1][1]); done(); } } else break; } return mailfile; } /* return non-zero if this is not a normal mail file */ static int special_mailfile(mp) char *mp; /* mail file */ { char fbuf[256]; char *sp1, *sp2; int stat; sp2 = ""; /* if something is being done to the mail, go away */ stat = delivery_status(mp, fbuf); switch(stat & MFTYPE) { case MF_FORWARD: sp1 = "Your mail is being forwarded to"; break; case MF_PIPE: sp1 = "Your mail is being piped to"; break; default: return 0; } if (stat & MFEXTRA) sp2 = "and your mailbox contains extra stuff\n"; printf("%s %s %s\n", sp1, fbuf, sp2); return stat & MFTYPE; } static void dohelp() { printf("Commands are of the form '[range] command [args]'.\n"); printf("The command can be:\n"); printf("d\tdelete\n"); printf("u\tundelete\n"); printf("h\tprint from lines of all messages\n"); printf("m user\tmail to user\n"); printf("p\tprint\n"); printf("r\treply to last message\n"); printf("q\tquit\n"); printf("s[file]\tsave (default mbox)\n"); printf("x\texit without changing mail\n"); printf("=\tprint current message number\n"); printf("! cmd\texecute cmd\n"); } /* print the from line (and status) of each message */ static void print_from(let) int let; { int deleted; char from[FROMLINESIZE]; letter *lp; lp = lopen(let, "ru"); if (lp == NULL) { deleted = 1; lp = lopen(let, "r"); } else deleted = 0; lgets(from, FROMLINESIZE, lp); printf("%3d %c %4d %s", seq(let), deleted?'d':' ', lsize(lp), from+FSIZE); } /* save a letter in a file, return 0 if successful */ static int squirrel_away(let, resp) int let; char *resp; { char *p; letter *lp; char path[PATHSIZE]; int problems=0; FILE *malf; if (*resp == '\0') { p = getenv("HOME"); if(p != 0) { strcpy(resp, p); strcat(resp, "/mbox"); } else strcpy(resp, "mbox"); } for (p = resp; (p = getarg(path, p)) != NULL; ) { malf = fopen(path, "a"); if (malf == NULL) { fprintf(stderr, "mail: cannot append to %s\n", path); problems++; continue; } lp = lopen(let, "r"); copyfrom(lp, malf); fclose(malf); } return problems; } /* reply to mail, return 0 if no problems */ static char replyline[FROMLINESIZE]; static char replycmd[CMDSIZE]; static int reply(let) int let; { char *sp; letter *lp; lp = lopen(let, "r"); lgets(replyline, FROMLINESIZE, lp); sp = strchr(replyline+FSIZE, ' '); if (sp != NULL) *sp = '\0'; strcpy(replycmd, "/bin/mail "); strcat(replycmd, replyline+FSIZE); printf("!%s\n", replycmd); system(replycmd); return 0; } /* pass mail onto someone else, return 0 if no problems */ static int remail(let, resp) int let; char *resp; { char cmd[CMDSIZE]; FILE *pipf; letter *lp; if (*resp == '\0') { fprintf(stderr, "invalid: m requires a destination\n"); return -1; } strcpy(cmd, "mail "); strcat(cmd, resp); /* fork so that mail becomes super-user again */ pipf = popen(cmd, "w"); if(pipf == NULL) { fprintf(stderr, "mail: can't fork to remail\n"); return -1; } lp = lopen(let, "r"); copyfrom(lp, pipf); return pclose(pipf); } /* output the letter */ static int doprint(let) int let; { letter *lp; lp = lopen(let, "r"); if (lp == NULL) return -1; copyfrom(lp, stdout); return 0; }