/* * mail: simple mail-reader interface */ #include #include #include #include #include #include #if GEMDOS #include #endif char *user; /* who's reading mail? */ char mailfile[200]; /* what the mailfile is called */ struct anart { long aa_begin; long aa_from; long aa_text; long aa_size; short aa_flag; #define AA_READ 0x01 #define AA_KILL 0x02 } ; struct anart *letters = (struct anart*)0; int letcount=0; char program[] = "mail"; main(argc, argv) char **argv; { FILE *mail; if (!getcfg()) exit(1); if ((user=getenv("LOGNAME")) == (char *)0) { fprintf(stderr, "mail: who are you?\n"); exit(2); } makepath(mailfile, MAIL, user); ++argv,--argc; while (argc > 0 && **argv == '-') { switch (argv[0][1]) { case 's': /* return status of mail */ case 'S': if (mail=fopen(mailfile, "rb")) exit(0); exit(1); case 'n': case 'N': if (mail=fopen(mailfile, "rb")) { printf("you have mail\n"); exit(0); } exit(1); } ++argv,--argc; } if (argc > 0) sendmail(argc, argv); else if (isatty(0)) readmail(); } /* main */ /* * sendmail() sends a mail message to some unsuspecting users */ sendmail(argc, argv) char **argv; { FILE *spool; char *tmp; char line[200]; char spoolfile[80]; if (tmp = getenv("TMP")) makepath(spoolfile, tmp, "%d.mai", getpid()); else sprintf(spoolfile, "%d.mai", getpid()); if (spool=fopen(spoolfile, "wb")) { while (specgets(line, 1)) { if (*line == '~') { switch (line[1]) { case 'v': if (tmp=getenv("EDITOR")) { fclose(spool); sprintf(line, "%s %s", EDITOR, spoolfile); system(line); if (spool=fopen(spoolfile, "ab")) puts("[continue]"); else fprintf(stderr, "mail: lost spoolfile!!\n"); } else fprintf(stderr, "mail: no EDITOR defined\n"); break; case 'p':{ FILE *review; fflush(spool); if (review=fopen(spoolfile, "rb")) { while (fgets(line, 200, review)) fputs(line, stdout); fclose(review); } else fprintf(stderr, "mail: can't reopen spoolfile!!\n"); puts("[continue]"); break; } case 'x': fclose(spool); remove(spoolfile); return 0; default: puts("illegal ~ escape; the following are legal:"); case '?': puts("~ escapes:\n\t~v - invoke external editor"); puts("\t~p - print the letter so far\n\t~? - get help"); puts("\t~x - cancel this letter\n[continue]\n"); break; case '~': fprintf(spool, "%s\n", 1+line); } } else fprintf(spool, "%s\n", line); } puts("EOT"); fclose(spool); while (argc > 0) { sprintf(line, "rmail %s < %s", *argv, spoolfile); system(line); ++argv, --argc; } remove(spoolfile); } else fprintf(stderr, "mail: error creating spoolfile <%s>!\n", spoolfile); } /* sendmail */ /* * specgets() gets a string from standard input, doing editing and handling * ^D and ^C */ specgets(line, intr_flag) char *line; { register c; register char *rp; if (isatty(0)) { rp=line; fflush(stdout); #if ATARIST while ((c=Crawcin()) != '\r') { #elif OS2 while ((c=getch()) != '\r') { #elif MSDOS while ((c=crawcin()) != '\r') { #endif switch (c) { case 'Z'-'@': if (rp == line) return 0; putchar(7); fflush(stdout); break; case 'H'-'@': if (rp > line) { printf("\b \b"); fflush(stdout); --rp; } break; case 'X'-'@': while (rp > line) { printf("\b \b"); --rp; } fflush(stdout); break; case 'C'-'@': if (intr_flag) { fprintf(stderr, "\nInterrupt-type another to cancel letter\n"); #if ATARIST c=Crawcin(); #elif OS2 c=getch(); #elif MSDOS c=crawcin(); #endif if (c == 'C'-'@') exit(0); rp=line; break; } return 0; default: *rp++ = c; putchar(c); fflush(stdout); } } putchar('\n'); *rp = 0; return 1; } else return gets(line); } /* * plural() pluralises a string. */ char * plural(count, string) long count; char *string; { static char buff[100]; sprintf(buff, "%ld %s%s", count, string, (count!=1)?"s":""); return buff; } /* plural */ /* * getnext() gets the next unread message; returns 1 if there is one, * otherwise 0 */ getnext(np) int *np; { register n; n = ++(*np); while (n < letcount && (letters[n].aa_flag & (AA_KILL|AA_READ))) ++n; if (n < letcount) { *np = n; return 1; } return 0; } /* getnext */ /* * getnumber() fabricates a number */ getnumber(firstc, rest) char firstc; char *rest; { register tmp; register i; tmp = firstc-'0'; for (i=0; isdigit(rest[i]); i++) tmp = (tmp*10) + (rest[i]-'0'); return tmp; } /* getnumber */ /* * readmail() reads your mail (not too surprisingly..) */ readmail() { FILE *mail; long msize; char text[100]; char cmdc, rest[99]; int cur=0, lastread=-1; long icount; int tmp; if ((mail=fopen(mailfile, "r+b")) && (icount=indexmail(mail)) > 0) { printindex(mail, cur); putchar('>'); while (specgets(text,0)) { memset(rest, 0, 99); if (sscanf(text, "%c %s", &cmdc, rest) > 0) { switch (toupper(cmdc)) { case 'd': case 'D': deletemail(lastread, rest); break; case '=': if (rest[0]) { tmp = getnumber('0', rest)-1; if (tmp >= 0 && tmp <= letcount) cur = tmp; else puts("?"); } else printf("%d\n", 1+cur); break; case 'X': case 'x': printf("held %s.\n", plural(icount, "letter")); exit(0); case 'H': case 'h': printindex(mail, cur); break; case 'S': case 's': if (lastread >= 0) savemail(mail, lastread, rest); else puts("?"); break; case '.': if (lastread >= 0) printletter(mail, lastread); else puts("?"); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': tmp = getnumber(cmdc, rest)-1; if (tmp >= 0 && tmp <= letcount) printletter(mail, lastread=tmp); else puts("?"); break; case '+': if (cur < letcount-1) { ++cur; printletter(mail, lastread=cur); } else puts("?"); break; case '-': if (cur > 0) { --cur; printletter(mail, lastread=cur); } else puts("?"); break; case 'Q': case 'q': icount=neaten(mail); printf("held %ld unread.\n", icount); exit(0); case '!': if (rest[0]) system(rest); else puts("incomplete shell escape"); break; case '?': putchar('\n'); puts("\t.\treprint last letter"); puts("\td#,#\tdelete letters from your mailbox"); puts("\tx\texit, leaving all mail intact"); puts("\tq\tquit, updating mailbox"); puts("\tsfile\tsave last-read letter to file"); puts("\tr#\treply to letter"); puts("\th\tprint headers"); puts("\t=#\tset current to msg#"); puts("\t-\tmove back 1 letter"); puts("\t+\tmove ahead one letter"); puts("\t#\tprints letter #"); puts("\t!\tshell escape"); putchar('\n'); break; case 'r': case 'R': replymail(mail, lastread, rest); break; default: puts("type ? for a menu"); break; } } else { printletter(mail, lastread=cur); getnext(&cur); } putchar('>'); } neaten(mail); exit(0); } else fprintf(stderr, "no mail for %s\n", user); } /* readmail */ /* * pullnumber() is a little utility to pull numbers from a stream, leaving * a pointer to the next character. */ pullnumber(cpp) char **cpp; { char *cp = (*cpp); int tmp; tmp=0; if (!isdigit(*cp)) return 0; while (isdigit(*cp)) { tmp = (tmp*10) + (*cp-'0'); ++cp; } *cpp = cp; return tmp; } /* pullnumber */ /* * deletemail() marks mail deleted, for cleanup at the end of the session */ deletemail(dot, rest) char *rest; { int tmp; int tmp2; if (rest[0]) { while (tmp = pullnumber(&rest)) { if (*rest == '-') { ++rest; if (tmp2 = pullnumber(&rest)) while (tmp <= tmp2 && tmp <= letcount) { letters[tmp-1].aa_flag |= AA_KILL; tmp++; } } else if (tmp <= letcount) letters[tmp-1].aa_flag |= AA_KILL; if (*rest == ',') ++rest; else break; } } else if (dot >= 0) letters[dot].aa_flag |= AA_KILL; else puts("?"); } /* deletemail */ /* * neaten() cleans up your mailbox. Iccky on a multitasking system. */ neaten(mail) FILE *mail; { register int x; FILE *fb; char tmpb[100]; char text[200]; register long size; int kept; for (kept=1, x=0; x 0 && fgets(text, 200, mail)) { fputs(text, fb); size -= strlen(text); } } fclose(fb); fclose(mail); remove(mailfile); rename(tmpb, mailfile); return kept; } /* neaten */ /* * printindex() shows an index of mail messages */ printindex(mail, cur) FILE *mail; { register int first; register int index; register int count=0; char from[200]; int aaf; if (cur < 20) first=0; else first = cur; for (index=first; count<20 && index < letcount; index++) { ++count; aaf = letters[index].aa_flag; printf("%c%c%-2d:%s, ", (index == cur) ? '>' : ' ', (aaf & AA_KILL) ? 'D': ((aaf & AA_READ) ? 'R' : ' '), 1+index, plural(letters[index].aa_size, "char")); if (letters[index].aa_from) { fseek(mail, letters[index].aa_from, SEEK_SET); fgets(from, 200, mail); fputs(from, stdout); } else puts("unknown sender"); } } /* printindex */ /* * indexmail() generates a table of contents, for later use */ indexmail(mail) FILE *mail; { long msize; struct anart *letp; char text[200]; register long size; register long loc; letcount=0; while (fgets(text, 200, mail)) if (sscanf(text, "! %ld", &msize) == 1) { ++letcount; letters = realloc(letters, letcount * sizeof letters[0]); if (letters == (struct anart*)0) { fprintf(stderr, "mail: out of memory!\n"); exit(5); } letp = &letters[letcount-1]; letp->aa_from = 0; letp->aa_text = letp->aa_begin = ftell(mail); letp->aa_size = msize; letp->aa_flag = 0; size = msize; loc = ftell(mail); while (size > 0 && fgets(text, 200, mail)) { if (strncmp(text, "From ", 5) == 0) { letp->aa_from = loc; break; } if (strcmp(text, "\n") == 0) break; loc = ftell(mail); } fseek(mail, letp->aa_begin+msize, SEEK_SET); } /* mail article beginning with !size */ return letcount; } /* indexmail */ /* * printletter() reads a single mail message and prompts for what to do with * it. */ printletter(mail, which) FILE *mail; { char text[200]; register long size; fseek(mail, letters[which].aa_begin, SEEK_SET); size = letters[which].aa_size; while (size > 0 && fgets(text, 200, mail)) { size -= strlen(text); fputs(text, stdout); fflush(stdout); } letters[which].aa_flag |= AA_READ; } /* * savemail() writes a letter into a file */ savemail(mail, what, dest) FILE *mail; char *dest; { FILE *save; register long lc=0; register long count; char text[200]; if (dest[0]) { if (save=fopen(dest, "ab")) { printf("\"%s\"", dest); fflush(stdout); fseek(mail, letters[what].aa_begin, SEEK_SET); count = letters[what].aa_size; while (count > 0 && fgets(text, 200, mail)) { lc++; fputs(text, save); count -= strlen(text); } fclose(save); printf("; %s, ", plural(lc, "line")); printf("%s.\n", plural(letters[what].aa_size, "char")); } else printf("can't open %s\n", dest); } else puts("?"); } /* savemail */ replymail(mail, dot, rest) FILE *mail; char *rest; { char reply[80]; char text[200]; char user[20], site[60]; int ac; char *avp; if (rest[0]) dot = pullnumber(&rest)-1; if (dot < 0) { puts("?"); return 0; } if (letters[dot].aa_from == 0) { fputs("reply to?", stdout); if (specgets(reply, 0) == 0) return 0; } else { fseek(mail, letters[dot].aa_from, SEEK_SET); fgets(text, 100, mail); ac = sscanf(text, "From %s %*s %*s %*s %*s %*s remote from %s", user, site); switch (ac) { case 1: strcpy(reply, user); break; case 2: sprintf(reply, "%s!%s", site, user); break; default:puts("can't reply to this letter!"); return; } } avp = reply; sendmail(1, &avp); } /* replymail */