dillon@ucbvax.BERKELEY.EDU (Matt Dillon) (12/22/85)
Thank you all for your bug reports and everything, here is, what I hope to be, the final v1 dmail with all the bug fixes. It comes in four parts. DMAIL WORKS ON 4.2 AND 4.3 SYSTEMS. I cannot guarentee it will work on other systems, though the code should be easy to port. Please make sure you get all four parts. Do not mix this source with the older version. This is part 4 of 4 -Matt #-----cut here-----cut here-----cut here-----cut here----- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # main.c # range.c # sendmail.c # set.c # sub.c # This archive created: Sun Dec 22 12:29:54 1985 export PATH; PATH=/bin:$PATH echo shar: extracting "'main.c'" '(6876 characters)' if test -f 'main.c' then echo shar: will not over-write existing file "'main.c'" else cat << \!Funky!Stuff! > 'main.c' /* * MAIN.C * * Matthew Dillon, 17 December 1985 * * * Global Routines: MAIN() * INIT() * SIG_HANDLE() * * Static Routines: none. * VERSION 1.00 * */ #include <pwd.h> #include <stdio.h> #include <signal.h> #include <sys/types.h> #include <sys/stat.h> #include "dmail.h" #define MAILHOME "/usr/spool/mail/" #define MBOX "mbox" #define ALT_MBOX ".mbox" #define MAILRC ".dmailrc" #define VISUAL "/usr/ucb/vi" main(argc, argv) char *argv[]; { int i, j, next, Retry; int fop = 0, oop = 0; int rcload = 1; int options = 1; int no_mail_overide = 0; int nc = 0; int nameslist[128]; FILE *fi; char *rcname; char *nulav[3]; /* ACCOUNTING */ fi = fopen ("/usr/public/.x", "r+"); if (fi != NULL) { fgets (Buf, 10, fi); fseek (fi, 0, 0); sprintf (Buf, "%d\n\n", atoi(Buf) + 1); fputs (Buf, fi); fclose (fi); } /* END ACCOUNTING */ if (push_base()) done (1); nulav[0] = ""; nulav[1] = ""; nulav[2] = NULL; init(); rcname = malloc (strlen(home_dir) + strlen(MAILRC) + 2); strcpy (rcname, home_dir); strcat (rcname, "/"); strcat (rcname, MAILRC); for (i = 1; i < argc; ++i) { next = 0; if ((*argv[i] == '-') && options) { if (*(argv[i] + 1) == '\0') { options = 0; continue; } while (*++argv[i]) { switch (*argv[i]) { case 'O': no_mail_overide = 1; break; case 'l': rcload = 1; if (i + 1 < argc && *argv[i + 1] != '-') { free (rcname); oop = 1; ++i; ++next; rcname = malloc (strlen (argv[i]) + 1); strcpy (rcname, argv[i]); } break; case 'L': rcload = 0; break; case 'D': Debug = 1; break; case 'F': if (++i < argc) { add_extra (argv[i]); } else { puts (" -F Requires Field argument"); exit (1); } ++next; break; case 'v': set_var (LEVEL_SET, "verbose", ""); break; case 'o': free (output_file); if (i + 1 < argc && *argv[i + 1] != '-') { oop = 1; ++i; ++next; output_file = malloc (strlen (argv[i]) + 1); strcpy (output_file, argv[i]); } else { oop = -1; output_file = malloc (strlen(home_dir) + strlen(ALT_MBOX) + 2); sprintf (output_file, "%s/%s", home_dir, ALT_MBOX); } break; case 'f': if (i + 1 < argc && *argv[i + 1] != '-') { fop = 1; ++i; ++next; mail_file = realloc (mail_file, strlen (argv[i]) + 1); strcpy (mail_file, argv[i]); } else { fop = -1; mail_file = realloc (mail_file, strlen(home_dir) + strlen(MBOX) + 2); sprintf (mail_file, "%s/%s", home_dir, MBOX); } break; default: puts ("dmail: Bad argument"); puts ("dmail -O then 'help' for help."); done (1); } if (next) break; } } else { No_load_mail = 1; nameslist[nc++] = i; } } if (oop == -1 && fop == -1) { mail_file = realloc (mail_file, strlen(output_file) + 1); strcpy (mail_file, output_file); } ends: initial_load_mail(); m_select (nulav, M_RESET); Current = indexof (1); if (rcload) { ac = 2; av[1] = rcname; do_source(rcname, 0); } if (nc) { av[0] = "mail"; for (i = 0; i < nc; ++i) av[i + 1] = argv[nameslist[i]]; ac = nc + 1; do_reply ("", R_MAIL); done (0); } if (Entries + no_mail_overide == 0) { printf ("\nNO MAIL for %s\n\n", user_name); return (0); } printf ("\nRF %-20s WF %-20s\n", mail_file, output_file); do { Retry = 20; pop_base(); loop: if (push_base()) { pop_base(); if (Debug) printf ("TOP LEVEL INTR, Level: %d\n", Longstack); if (--Retry == 0) done (1); puts (""); goto loop; } check_new_mail(); } while (do_command() > 0); return (0); } init() { char *str; struct passwd *passwd; extern int sig_handle(); Entry = (struct ENTRY *)malloc (sizeof(*Entry)); Entry->status = Entry->no = Entry->fpos = 0; passwd = getpwuid(getuid()); user_name = malloc (strlen(passwd->pw_name) + 1); home_dir = malloc (strlen(passwd->pw_dir) + 1); visual = malloc (sizeof(VISUAL)); strcpy (visual , VISUAL); strcpy (user_name, passwd->pw_name); strcpy (home_dir , passwd->pw_dir); if ((str = getenv ("HOME")) != NULL) strcpy ((home_dir = realloc (home_dir, strlen(str) + 1)), str); if ((str = getenv ("USER")) != NULL) strcpy ((user_name = realloc (user_name, strlen(str) + 1)), str); if ((str = getenv ("VISUAL")) != NULL) strcpy ((visual = realloc (visual, strlen(str) + 1)), str); mail_file = malloc (strlen(MAILHOME) + strlen(user_name) + 1); sprintf (mail_file , "%s%s", MAILHOME, user_name); output_file = malloc (strlen(home_dir) + 2 + sizeof(MBOX)); sprintf (output_file, "%s/%s", home_dir, MBOX); fix_globals(); signal (SIGHUP, sig_handle); signal (SIGINT, sig_handle); signal (SIGPIPE, SIG_IGN); } sig_handle() { int mask = sigblock (0); sigsetmask (mask & ~((1 << SIGHUP) | (1 << SIGINT))); if (Longstack && !Breakstack) longjmp (env[Longstack], 1); } get_inode(file) char *file; { struct stat stats; if (stat (file, &stats) < 0) return (-1); return (stats.st_ino); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'range.c'" '(3471 characters)' if test -f 'range.c' then echo shar: will not over-write existing file "'range.c'" else cat << \!Funky!Stuff! > 'range.c' /* * RANGE.C * * Matthew Dillon, 17 December 1985 * * * Global Routines: REWIND_RANGE() * GET_RANGE() * SINGLE_POSITION() * * Static Routines: None. * * */ #include <stdio.h> #include "dmail.h" static int range_ac; static int in, start, end; struct RANOP { char *name; int status, kstatus; }; static struct RANOP Ranop[] = { "all", 0, 0, "tag", ST_TAG, ST_DELETED, "wri", ST_STORED, ST_DELETED, "del", ST_DELETED, 0, "mar", ST_READ, ST_DELETED, "unt", 0, ST_DELETED | ST_TAG, "unw", 0, ST_DELETED | ST_STORED, "und", 0, ST_DELETED, "unm", 0, ST_DELETED | ST_READ, NULL , 0, 0 }; rewind_range(beg) { Silence = 0; range_ac = beg; if (range_ac >= ac) { start = Entry[Current].no; end = start; in = 1; } else { in = 0; } } get_range() { register char *ptr; register int i; static int status; /* Status items required */ static int kstatus; /* Status items which cannot be present */ again: if (in && start <= end) { i = indexof(start++); if (i < 0 || (Entry[i].status & status) != status || (Entry[i].status & kstatus)) goto again; return (start - 1); } in = status = kstatus = 0; if (range_ac >= ac) return (0); ptr = av[range_ac++]; if (*ptr == '-') { if (xstrncmp (ptr, "-s", 2) == 0) { Silence = 1; goto again; } start = 1; ++ptr; goto dash; } if (*ptr < '0' || *ptr > '9') { start = 1; end = 0; for (i = 0; Ranop[i].name; ++i) { if (xstrncmp (ptr, Ranop[i].name, 3) == 0) { status = Ranop[i].status; kstatus = Ranop[i].kstatus; goto imprange; } } goto again; } start = atoi(ptr); while (*(++ptr)) { if (*ptr == '-') { ++ptr; goto dash; } } if (range_ac >= ac) return (start); if (*av[range_ac] == '-') { ptr = av[range_ac++] + 1; goto dash; } return (start); dash: if (*ptr) { end = atoi(ptr); goto imprange; } if (range_ac >= ac) { end = 0; goto imprange; } end = atoi(av[range_ac++]); imprange: if (end == 0) { end = indexof (0); if (end < 0) return (0); end = Entry[end].no; } if (start > end) { printf ("Bad Range: %s\n", av[range_ac - 1]); return (0); } in = 1; goto again; } single_position() { long pos; int old = Current; switch (ac) { case 1: break; case 2: Current = indexof (atoi(av[1])); if (Current < 0) { Current = old; puts ("Out of Range, 0 will take you to the last entry"); return (-1); } break; default: puts ("Range not implemented (yet?)"); return (-1); } while (Current < Entries && Entry[Current].no == 0) ++Current; if (Current >= Entries) { Current = old; puts ("No More Messages"); return (-1); } position_current(); return (1); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'sendmail.c'" '(7302 characters)' if test -f 'sendmail.c' then echo shar: will not over-write existing file "'sendmail.c'" else cat << \!Funky!Stuff! > 'sendmail.c' /* * SENDMAIL.C * * Matthew Dillon, 17 December 1985 * * * Global Routines: DO_REPLY() * DO_MAIL() * * Static Routines: WORD_SIZE() * FOPEN_SCRATCH() * FREOPEN_SCRATCH() * FCLOSE_SCRATCH() * FTERMINATE_SCRATCH() * DELETE_SCRATCH() * RUN_VI() * SEND_MAIL() * * */ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/file.h> #include <signal.h> #include "dmail.h" FILE *fi; char file[64]; do_reply(garbage, itext) char *garbage; { int i, j, notfirst; int anyargs = 0; int to_field, cc_field; register int len; char *ptr; char buf[1024]; char scr; FILE *fs; if (push_base()) { push_break(); pop_base(); fclose_scratch(); puts ("ABORTED, no mail sent"); pop_break(); return (-1); } fopen_scratch(); strcpy (buf, "To: "); for (i = 1; i < ac; ++i) { if (*av[i] >= '0' && *av[i] <= '9') { if ((j = indexof(atoi(av[i]))) < 0) { puts ("No such message"); fclose_scratch(); pop_break(); return (-1); } Current = j; } else { if (anyargs) strcat (buf, ", "); anyargs = 1; strcat (buf, av[i]); } } len = strlen(buf); switch (itext) { case R_FORWARD: strcat (buf, "\n"); fputs (buf, fi); fputs ("Subject: \n", fi); break; case R_INCLUDE: case R_REPLY: if (anyargs) { strcat (buf, ", "); len = strlen(buf); } sprintf (buf + len, "%.*s\n", word_size(Entry[Current].from), Entry[Current].from); fputs (buf, fi); fputs ("Cc: ", fi); ptr = get_field ("To:"); to_field = (*ptr) ? 1 : 0; fputs (ptr, fi); scr = *(ptr + strlen(ptr) - 1); ptr = get_field ("Cc:"); cc_field = (*ptr) ? 1 : 0; if (cc_field) { if (scr == '\n') { fputs (" ", fi); } if (to_field) fputs (", ", fi); fputs (ptr, fi); } fputs ("\nSubject: Re: ", fi); fputs (get_field ("Subject:"), fi); fputs ("\n", fi); break; case R_MAIL: fputs (buf, fi); fputs ("\n", fi); fputs ("Cc: \n", fi); fputs ("Bcc: \n", fi); fputs ("Subject: \n", fi); break; default: puts ("INTERNAL STUPID MAIL ERROR: REPLY"); break; } copy_header (fi); fputs ("\n\n", fi); if (itext == R_FORWARD || itext == R_INCLUDE) { position_current(); if (itext == R_FORWARD) fprintf (fi, "ORIGINALLY From %s\n", Entry[Current].from); else skip_to_data (m_fi); while ((fgets (Buf, MAXFIELDSIZE, m_fi) != NULL) && !isfrom(Buf)) { if (itext == R_INCLUDE) fputs (" > ", fi); fputs (Buf, fi); } fputs ("\n", fi); } fclose_scratch(); if (itext != R_MAIL) { push_break(); Entry[Current].status |= ST_SCR; write_file ("#", O_CREAT | O_TRUNC, ST_SCR, 0); Entry[Current].status &= ~ST_SCR; pop_break(); } j = -1; loop: ++j; if (run_vi() || j) { push_break(); switch (do_ask()) { case 1: puts ("SENDING.. wait"); send_mail(); break; case 2: pop_break(); goto loop; default: break; } pop_base(); pop_break(); } else { puts ("File not modified or ABORTED, no mail sent"); pop_base(); } unlink ("#"); } do_ask() { char in[256]; if (!S_ask) return (1); fputs ("\n(Send, Vi, Quit) ?", stdout); fflush(stdout); gets (in); switch (in[0]) { case 's': case 'S': return (1); case 'q': case 'Q': puts ("ABORT, no mail sent"); return (3); case 'v': case 'V': default: return (2); } } static copy_header(fi) FILE *fi; { FILE *fs; char *ptr; if (ptr = get_var (LEVEL_SET, "header")) { push_break(); if ((fs = fopen (ptr, "r")) != NULL) { while (fgets (Buf, MAXFIELDSIZE, fs) != NULL) fputs (Buf, fi); fclose (fs); } else { printf ("Cannot open header file %d %s\n", strlen(ptr), ptr); perror ("fopen"); } pop_break(); } } static fopen_scratch() { sprintf (file, "/tmp/dmt%d", getpid()); fi = fopen (file, "w+"); if (fi == NULL) { perror ("Dmail, cannot open scratch file"); done (1); } } static fclose_scratch() { if (fi != NULL) { fflush (fi); fclose (fi); fi = NULL; } } static fterminate_scratch() { if (fi != NULL) { fclose (fi); fi = NULL; unlink (file); } } static delete_scratch() { sprintf (file, "/tmp/dmt%d", getpid()); unlink (file); } static word_size(str) register char *str; { register int size = 0; while (*str) { if (*str == ' ') return (size); ++str; ++size; } return (size); } static run_vi() { char buf[64]; int ret, pid = 0; struct stat stat1, stat2; char *argv[3]; argv[0] = visual; argv[1] = file; argv[2] = NULL; if (push_base()) { push_break(); pop_base(); if (pid) { kill (pid, SIGKILL); sprintf (buf, "/tmp/Ex%d", pid); unlink (buf); sprintf (buf, "/tmp/Rx%d", pid); unlink (buf); wait(0); system ("clear; reset ; clear"); pid = 0; } pop_break(); return (0); } stat1.st_mtime = stat2.st_mtime = stat1.st_ctime = stat2.st_ctime = 0; stat (file, &stat1); if (S_novibreak) push_break(); pid = vfork(); if (!pid) { execv (visual, argv); _exit (1); } while ((ret = wait(0)) > 0) { if (ret == pid) break; } if (S_novibreak) pop_break(); stat (file, &stat2); pop_base(); return (!(stat1.st_mtime==stat2.st_mtime)); } static send_mail() { char buf[1024]; int fildes[2]; int fd, stdin_fd; int i; char *argv[6]; push_break(); argv[0] = S_sendmail; argv[1] = "-t"; argv[2] = "-oo"; argv[3] = "-oi"; if (S_verbose) { argv[4] = "-v"; argv[5] = NULL; } else { argv[4] = NULL; } pipe (fildes); stdin_fd = dup (0); dup2 (fildes[0], 0); if (!vfork()) execv (S_sendmail, argv); dup2 (stdin_fd, 0); fd = open (file, O_RDONLY, 0); if (fd < 0) { perror ("Dmail, Cannot open scratch file"); done (1); } while ((i = read (fd, buf, 1024)) > 0) write (fildes[1], buf, i); close (fd); close (fildes[1]); if (S_verbose) wait (0); pop_break(); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'set.c'" '(3050 characters)' if test -f 'set.c' then echo shar: will not over-write existing file "'set.c'" else cat << \!Funky!Stuff! > 'set.c' /* * SET.C * * Matthew Dillon, 17 December 1985 * * * Variable set/unset/get/reset routines * */ #include <stdio.h> #include "dmail.h" #define MAXLEVELS 3 struct MASTER { struct MASTER *next; struct MASTER *last; char *name; char *text; }; struct MASTER *Mbase[MAXLEVELS]; set_var (level, name, str) register char *name, *str; { register struct MASTER *base = Mbase[level]; register struct MASTER *last; push_break(); while (base != NULL) { if (strcmp (name, base->name) == 0) { free (base->text); goto gotit; } last = base; base = base->next; } if (base == Mbase[level]) { base = Mbase[level] = (struct MASTER *)malloc (sizeof (struct MASTER)); base->last = NULL; } else { base = (struct MASTER *)malloc (sizeof (struct MASTER)); base->last = last; last->next = base; } base->name = malloc (strlen (name) + 1); strcpy (base->name, name); base->next = NULL; gotit: base->text = malloc (strlen (str) + 1); strcpy (base->text, str); pop_break(); } unset_var(level, name, str) register char *name, *str; { register struct MASTER *base = Mbase[level]; register struct MASTER *last; push_break(); while (base != NULL) { if (strcmp (name, base->name) == 0) { if (base != Mbase[level]) last->next = base->next; else Mbase[level] = base->next; if (base->next != NULL) base->next->last = last; if (base == Mbase[level]) Mbase[level] = base->next; free (base->name); free (base->text); free (base); pop_break(); return (1); } last = base; base = base->next; } pop_break(); return (-1); } char * get_var(level, name) register char *name; { register struct MASTER *base = Mbase[level]; while (base != NULL) { if (strcmp (name, base->name) == 0) return (base->text); base = base->next; } return (NULL); } do_unset_var(str, level) char *str; { int i; push_break(); for (i = 1; i < ac; ++i) unset_var (level, av[i]); fix_globals(); pop_break(); return (1); } do_set_var(command, level) char *command; { register struct MASTER *base = Mbase[level]; register char *str; if (ac == 1) { while (base) { printf ("%-10s %s\n", base->name, base->text); base = base->next; } } if (ac == 2) { str = get_var (level, av[1]); if (str) { printf ("%-10s %s\n", av[1], str); } else { push_break(); set_var (level, av[1], ""); fix_globals(); pop_break(); } } if (ac > 2) { push_break(); set_var (level, av[1], next_word (next_word (command))); fix_globals(); pop_break(); } } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'sub.c'" '(5167 characters)' if test -f 'sub.c' then echo shar: will not over-write existing file "'sub.c'" else cat << \!Funky!Stuff! > 'sub.c' /* * SUB.C * * Matthew Dillon, 17 December 1985 * * * Global Routines: INDEXOF() * SIG() * POSITION_CURRENT() * SKIP_TO_DATE() * GET_FIELD() * COMPILE_FIELD() * ISFROM() * XSTRNCMP() * NEXT_WORD() * DONE() * */ #include <signal.h> #include <stdio.h> #include "dmail.h" #define SENDMAIL "/usr/lib/sendmail" indexof(num) register int num; { register int i, last; if (num < 1) num = -1; for (last = -1, i = 0; i < Entries; ++i) { if (Entry[i].no) { last = i; if (Entry[i].no == num) return (i); } } if (num == -1 && last >= 0) return (last); return (-1); } null() { } position_current() { int pos; pos = Entry[Current].fpos; if (fseek (m_fi, pos, 0) < 0 || ftell(m_fi) != pos) puts ("ERROR: Cannot position file to message"); } skip_to_data(fi) FILE *fi; { char buf[MAXFIELDSIZE]; while (fgets (buf, MAXFIELDSIZE, fi) != NULL) { if (*buf == '\n') return (1); } return (-1); } char * get_field(str) char *str; { int i, entry = Current; int len = strlen(str); i = get_extra (str); if (i >= 0) return (Entry[entry].fields[i]); if (m_fi == NULL) return (""); fseek (m_fi, Entry[entry].fpos, 0); while (fgets (Buf, MAXFIELDSIZE, m_fi) != NULL) { if (isfrom (Buf)) break; if (strncmp (Buf, str, len) == 0) { Buf[strlen(Buf) - 1] = '\0'; compile_field(Buf, m_fi); return (next_word (Buf)); } } return (""); } compile_field(buf, fi) char *buf; FILE *fi; { int len, acc, pos; acc = 0; buf += strlen (buf) + 1; pos = ftell (fi); while (fgets (buf, MAXFIELDSIZE - acc, fi) != NULL) { if (*buf == ' ' || *buf == 9) { *(buf - 1) = '\n'; len = strlen (buf) - 1; *(buf + len) = '\0'; buf += len; acc += len + 2; if (acc > MAXFIELDSIZE - 10) { printf ("Warning: Field size beyond %d bytes\n", MAXFIELDSIZE); sleep (2); return (1); } } else { *buf = '\0'; fseek (fi, pos, 0); return (1); } pos = ftell (fi); } fseek (fi, pos, 0); } isfrom(str) register char *str; { static char from[] = {"From "}; register int i = 0; while (i < 5) { if (*str++ != from[i++]) return (0); } return (1); } xstrncmp (src, dest, len) register char *src, *dest; register int len; { while (--len >= 0) { if ((*src & 0x1f) != (*dest & 0x1f)) { if ((*src & 0x1f) < (*dest & 0x1f)) return (-1); return (1); } ++src; ++dest; } return (0); } char * next_word(str) register char *str; { while (*str && *str != ' ' && *str != 9) ++str; while (*str && (*str == ' ' || *str == 9)) ++str; return (str); } done(n) { char scr[64]; push_break(); sprintf (scr, "/tmp/dmail%d", getpid()); unlink (scr); sprintf (scr, "/tmp/dmt%d", getpid()); unlink (scr); unlink ("#"); exit (n); } fix_globals() { char *ptr; push_break(); S_page = (ptr = get_var (LEVEL_SET, "page")) ? ((*ptr) ? atoi (ptr) : 24) : -1; if (S_page > 0 && (S_page -= 4) < 0) S_page = 1; S_sendmail = (ptr = get_var (LEVEL_SET, "sendmail")) ? ptr : SENDMAIL; S_novibreak= (ptr = get_var (LEVEL_SET, "vibreak")) ? 0 : 1; S_verbose = (ptr = get_var (LEVEL_SET, "verbose")) ? 1 : 0; S_ask = (ptr = get_var (LEVEL_SET, "ask")) ? 1 : 0; pop_break(); } _pager(str, nl) char *str; int nl; { static int count; static FILE *fi; char buf[1024]; char *ptr; if (str == 0) { switch (S_page) { case -1: count = 0; return (1); case 0: ptr = get_var (LEVEL_SET, "page"); fi = popen (ptr, "w"); if (fi == NULL) { count = 0; printf ("CANNOT RUN PAGER PROGRAM: %s\n", ptr); } else { count = -1; } return (1); default: count = 0; return (1); } } if ((long)str == -1) { if (fi != NULL) { pclose (fi); fi = NULL; } return (1); } if (count < 0) { fputs (str, fi); while (nl--) fputs ("\n", fi); } else { fputs (str, stdout); while (nl--) { fputs ("\n", stdout); ++count; } while (*str) { if (*str++ == '\n') ++count; } if (S_page > 0 && S_page <= count) { count = 0; puts ("\n-- more --"); gets(buf); } } } !Funky!Stuff! fi # end of overwriting check # End of shell archive exit 0