[net.sources] dmail part II or II

dillon@ucbvax.BERKELEY.EDU (Matt Dillon) (12/07/85)

	This is the second and last part to DMAIL.. enjoy

			...!ucbvax!dillon
			dillon@ucb-vax.berkeley.edu



#-----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. place the shar file into an empty directory
# 4. Execute the file with /bin/sh (not csh) to create the files:
#	execom.c
#	execom.h
#	globals.c
#	help.c
#	load_mail.c
#	main.c
#	range.c
#	sendmail.c
#	set.c
#	sub.c
# This archive created: Fri Dec  6 23:25:06 1985
export PATH; PATH=/bin:$PATH
echo shar: extracting "'execom.c'" '(14695 characters)'
if test -f 'execom.c'
then
	echo shar: will not over-write existing file "'execom.c'"
else
cat << \!Funky!Stuff! > 'execom.c'

/*
 *  EXECOM.C
 *
 *  Matthew Dillon, 6 December 1985
 *
 *
 *  (C) 1985  Matthew Dillon
 *
 *  Routines to parse and execute command lines.
 *
 *  Global Routines:    DO_COMMAND()
 *                      EXEC_COMMAND()
 *                      FIX()
 *
 *  Static Routines:    E_COMMAND()
 *                      BREAKOUT()
 *                      FIND_COMMAND()
 */


#include <pwd.h>
#include <stdio.h>
#include <strings.h>
#include "dmail.h"
#include "execom.h"

#define C_NO        1
#define C_NOEXPN    2
#define F_EXACT     0
#define F_ABBR      1
#define SCRBUF      1024

extern char *breakout();

extern int do_quit(), do_exit(), do_help(), do_list(), do_setlist();
extern int do_select(), do_type(), do_header(), do_next(), do_mark();
extern int do_unmark(), do_reply(), do_delnext();
extern int do_write(), do_shell(), do_set_var(), do_unset_var();
extern int do_number(), do_cd(), do_source(), do_defer(), do_echo();
extern int do_go();


struct COMMAND Command[] = {
        do_number   , 0,        0,                  "",
        do_mark     , 0,        ST_DELETED,         "delete",
        do_unmark   , 0,        ST_DELETED,         "undelete",
        do_header   , 0,        0,                  "header",
        do_type     , 0,        0,                  "type",
        do_echo     , 0,        0,                  "echo",
        do_go       , 0,        0,                  "go",
        do_reply    , 0,        R_REPLY,            "reply",
        do_reply    , 0,        R_INCLUDE,          "Reply", 
        do_reply    , 0,        R_MAIL,             "mail",
        do_reply    , 0,        R_FORWARD,          "forward",  
        do_select   , 0,        0,                  "select",
        do_select   , 0,        1,                  "reselect",
        do_defer    , 0,        1,                  "defer",
        do_list     , 0,        0,                  "list",
        do_next     , 0,        1,                  "next",
        do_next     , 0,        -1,                 "back",
        do_next     , 0,        2,                  "_next",
        do_next     , 0,        -2,                 "_back",
        do_delnext  , 0,        0,                  "dt",
        do_set_var  , 0,        0,                  "set",
        do_unset_var, 0,        0,                  "unset",    
        do_set_var  , C_NOEXPN, 1,                  "alias",
        do_unset_var, C_NOEXPN, 1,                  "unalias",  
        do_set_var  , C_NO,     2,                  "malias",
        do_unset_var, C_NO,     2,                  "munalias", 
        do_setlist  , 0,        0,                  "setlist",
        do_cd       , 0,        0,                  "cd",
        do_source   , 0,        0,                  "source",
        do_unmark   , 0,        ST_READ | ST_STORED,"preserve",
        do_mark     , 0,        ST_READ,            "mark",
        do_mark     , 0,        ST_TAG,             "tag",
        do_unmark   , 0,        ST_TAG,             "untag",
        do_unmark   , 0,        ST_STORED,          "unwrite",
        do_write    , 0,        0,                  "write",
        do_shell    , 0,        0,                  "!",
        do_exit     , 0,        0,                  "x",
        do_quit     , 0,        0,                  "quit",
        do_exit     , 0,        1,                  "xswitch",
        do_quit     , 0,        1,                  "qswitch",
        do_help     , 0,        0,                  "help",
        do_help     , 0,        0,                  "?",
        NULL        , 0,        0,                  NULL };

char *Desc[] = {
        "",
        "<list>                   mark messages for deletion",
        "<list>                   UNDELETE & UNMARK messages",
        "[msg]                    Display header of a message",
        "[msg]                    type a message",
        "args....                 Echo to the screen",
        "#                        Go to a message, don't print out",
        "                         reply to mail",
        "                         reply to mail, include recv'd text",
        "user user ...            send mail to users",
        "user user ...            forward mail to users",
        "Field [!]match [match][ , Field match.]  SELECT from entire message list",
        "Field [!]match [match][ , Field match.]  SELECT from current message list",
        "                         De-select any read messages",
        "<list>                   list mail as specified by SETLIST",
        "[msg]                    type/header next or message #",
        "[msg]                    type/header previous or message #",
        "[msg]                    go to next or message #",
        "[msg]                    go to previous or message #",
        "                         delete current, type next",
        "[var [string]]           set a variable",
        "var var var ...          unset a variable",
        "[var [string]]           set an alias",
        "var var var ...          unset an alias",
        "[var [string]]           set a mail alias",
        "var var var ...          unset a mail alias",
        "[-s] [cols] Field [cols] Field...    SET LIST format for LIST",
        "path                     CD to a directory",
        "file                     Source a file",
        "<list>                   UNREAD & UNMARK messages",
        "<list>                   mark messages as 'read'",
        "<list>                   tag messages for whatever",
        "<list>                   untag messages",
        "<list>                   unwrite messages",
        "file <list>              append messages to a file, delete on quit",
        "[command]                execute a shell [command]",
        "                         EXIT, do not save changes",
        "                         QUIT, update files",
        "from to                  Exit and switch to a new from/to file",
        "from to                  Quit and switch to a new from/to file",
        "[topic]                  help on a topic",
        "[topic]                  alternate form of HELP",
        NULL };


do_command()
{
    int i;
    char *str;
    static char comline[1024];

    printf ("%3d:", Entry[Current].no);
    fflush (stdout);
    if (gets (comline) == NULL)
        done (1);
    exec_command(comline);
    return (1);
}



/*
 * EXEC_COMMAND()
 *
 *
 */


struct MLIST {
    struct MLIST *next;
};

static struct MLIST *Mlist;

char *
mpush(amount)
int amount;
{
    struct MLIST *ml;

    push_break();
    ml = (struct MLIST *)malloc (amount + sizeof(Mlist));
    ml->next = Mlist;
    Mlist = ml;
    pop_break();
    return ((char *)Mlist + sizeof(Mlist));
}


char *
mpop()
{
    char *old = NULL;

    push_break();
    if (Mlist == NULL) {
        puts ("MLIST INTERNAL ERROR");
    } else {
        old = (char *)Mlist + sizeof(Mlist);
        free (Mlist);
        Mlist = Mlist->next;
    }
    pop_break();
    return (old);
}

mrm()
{
    push_break();
    while (Mlist) {
        free (Mlist);
        Mlist = Mlist->next;
    }
    pop_break();
}


exec_command(base)
char *base;
{
    char *str;
    int i;

    if (push_base()) {
        push_break();
        pop_base();
        mrm();
        pop_break();
        return (-1);
    }
    strcpy (str = mpush(strlen(base) + 1), base);
    i = e_command(str);
    if (mpop() != str) 
        puts ("POP ERROR");
    pop_base();
    return (i);
}


static
e_command(base)
char *base;
{
    char *com, *start, *Scr, *avline, *alias;
    int flag = 0;
    int i, pcount, len, ccno;

loop:
    com = breakout (&base, &flag);
    if (*com == '\0') {
        if (flag > 1)
            return (1);
        goto loop;
    }
    if ((ccno = find_command(com, F_EXACT)) < 0) {
        if (*com == '$') 
            alias = get_var (LEVEL_SET, com + 1);
        else
            alias = get_var (LEVEL_ALIAS, com);
        if (alias == NULL) {
            if ((ccno = find_command (com, F_ABBR)) < 0) {
                printf ("%s Command Not found\n", com);
                return (-1);
            } else {
                goto good_command;
            }
        }

        /* At this point, base points to arguments */

        start = (flag == 0) ? base : "";
        while (flag == 0) {             /* find ';' or end of string        */
            flag = -1;                  /* disable breakout's "" terminator */
            breakout (&base, &flag);
        }

        /*
         * At this point, start points to all arguments, base set up for next
         * string
         */

        if (*alias == '%') {
            int xx = 0;
            char *select;

            alias = strcpy (mpush (strlen(alias) + 1), alias);
            select = breakout (&alias, &xx);
            set_var (LEVEL_SET, select + 1, start);
            i = e_command (alias);
            unset_var (LEVEL_SET, select + 1);
            mpop();
        } else {
            com = mpush (strlen(alias) + strlen(start) + 2);
            strcpy (com, alias);
            strcat (com, (flag == 1) ? ";" : " ");
            strcat (com, start);
            i = e_command (com);
            if (mpop() != com)
                puts ("ME BAE ERROR");
        }
        if (i < 0)
            return (-1);
        if (flag > 1)
            return (1);
        goto loop;
    }
good_command:
    pcount = 0;
    if (Command[ccno].stat & C_NO  &&  Debug == 0) {
        printf ("%s  Is currently being developed\n", Command[ccno].name);
        return (-1);
    }
    if (Debug)
        printf ("Good command, Raw: %s\n", com);
    i = pcount = 0;
    av[i] = mpush (strlen(com) + 1);
    ++pcount;
    strcpy (av[i++], com);
    while (flag < 1) {
        com = breakout (&base, &flag);
        if (Debug)
            printf ("BREAKOUT %d %s\n", strlen(com), com);
        if (*com == '\0')
            continue;
        switch (*com) {
        case '~':
            if (com[1] == '/'  ||  com[1] == '\0') {
                av[i] = mpush (strlen(home_dir) + strlen(com + 1) + 1);
                ++pcount;
                strcpy (av[i], home_dir);
                strcat (av[i], com + 1);
            } else {
                struct passwd *passwd;
                char *user = com;

                while (*com) {
                    if (*com == '/') {
                        *com = '\0';
                        ++com;
                        break;
                    }
                    ++com;
                }
                if ((passwd = getpwnam(user)) == NULL) {
                    printf ("USER %s Not found\n", user);
                    while (pcount--)
                        mpop();
                    return (-1);
                }
                av[i] = mpush (strlen(passwd->pw_dir) + strlen(com) + 2);
                ++pcount;
                strcpy (av[i], passwd->pw_dir);
                if (*com) {
                    strcat (av[i], "/");
                    strcat (av[i], com);
                }
            }
            break;
        case '\"':
            av[i] = com + 1;
            while (*++com && *com != '\"');
            *com = '\0';
            break;
        case '$':
            av[i] = get_var (LEVEL_SET, com + 1);
            if (av[i] == NULL) {
                printf ("Variable: %s Not found\n", com + 1);
                while (pcount--)
                    mpop();
                return (-1);
            }
            av[i] = strcpy (mpush(strlen(av[i]) + 1), av[i]);
            ++pcount;
            break;
        default:
            av[i] = com;
            break;
        }
        ++i;
    }
    av[i] = NULL;
    ac = i;
    for (len = 0, i = 0; i < ac; ++i) 
        len += strlen (av[i]) + 1;
    avline = mpush (len + 1);
    *avline = '\0';
    for (i = 0; i < ac; ++i) {
        strcat (avline, av[i]);
        if (i + 1 < ac)
            strcat (avline, " ");
    }
    if (Debug)
        printf ("DEST: %s\n", avline);
    i = (*Command[ccno].func)(avline, Command[ccno].val);
    if (mpop() != avline)
        puts ("AVLINE ERROR");
    while (pcount--)
        mpop();
    fix();
    if (i < 0)
        return (i);
    if (flag < 2)
        goto loop;
    return (1);
}


/*
 * BREAKOUT
 *
 * Breakout next argument.  If FLAG is set to 1 on return, the argument 
 * returned is the last in the command.  If FLAG is set to 2 on return, the
 * argument returned is the last, period.
 *
 */

static char *
breakout(base, flag)
int *flag;
char **base;
{
    register char *str, *scr;

loop:
    str = *base;                        /* next start           */
    while (*str == ' ' || *str == 9)    /* skip spaces and such */
        ++str;
    switch (*str) {
    case '\0':                          /* no more arguments    */
        *flag = 2;
        *base = str;
        return (str);
    case ';':                           /* no more args in this command */
        *flag = 1;
        *str = '\0';
        *base = str + 1;
        return (str);
    }
    scr = str;
    for (;;) {                          /* valid argument of somesort   */
        switch (*scr) {
        case ' ':
        case 9:
            if (*flag >= 0)
                *scr = '\0';
            *base = scr + 1;
            *flag = 0;
            return (str);
        case '\"':
            ++scr;
            while (*scr && (*scr++ != '\"'));   /* place to end of quote */
            break;
        case '\0':
            *flag = 2;
            *base = scr;
            return (str);
        case ';':
            *flag = 1;
            *base = scr + 1;
            *scr = '\0';
            return (str);
        default:
            ++scr;
        }
    }
}



fix()
{
    register int i;

    for (i = Current; i < Entries; ++i) {
        if (Entry[i].no  &&  !(Entry[i].status & ST_DELETED)) {
            Current = i;
            return (1);
        }
    }
    if (Current >= Entries) {
        Current = Entries - 1;
        if (Current < 0)
            Current = 0;
    }
    for (i = Current; i >= 0; --i) {
        if (Entry[i].no  &&  !(Entry[i].status & ST_DELETED)) {
            Current = i;
            return (-1);
        }
    }
    Current = 0;
    return (-1);
}


static
find_command(str, arg)
char *str;
int arg;
{
    int i;
    int len = strlen (str);

    if (*str >= '0'  &&  *str <= '9')
        return (0);
    for (i = 0; Command[i].func; ++i) {
        if (strncmp (str, Command[i].name, len) == 0) {
            if (arg == F_ABBR)
                return (i);
            if (strcmp (str, Command[i].name) == 0)
                return (i);
            return (-1);
        }
    }
    return (-1);
}

!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'execom.h'" '(142 characters)'
if test -f 'execom.h'
then
	echo shar: will not over-write existing file "'execom.h'"
else
cat << \!Funky!Stuff! > 'execom.h'

struct COMMAND {
    int  (*func)();
    int  stat;
    int  val;
    char *name;
};

extern struct COMMAND Command[];
extern char *Desc[];

!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'globals.c'" '(1900 characters)'
if test -f 'globals.c'
then
	echo shar: will not over-write existing file "'globals.c'"
else
cat << \!Funky!Stuff! > 'globals.c'

/*
 * GLOBALS.C
 *
 *  Matthew Dillon, 6 December 1985
 *
 *
 *  (C) 1985  Matthew Dillon
 *
 *  Declarations for most global variables.
 *
 */

#include <stdio.h>
#include "dmail.h"

FILE *m_fi;                         /* open file ptr to spool file  */
char *mail_file;                    /* name of from (spool) file    */
char *output_file;                  /* name of out file (i.e. mbox) */
char *user_name;                    /* user name from password entry*/
char *home_dir;                     /* home directory of user       */
char *visual;                       /* visual editor path           */
char Buf[MAXFIELDSIZE];             /* Scratch Buffer               */
char Puf[MAXFIELDSIZE];             /* Another Scratch Buffer       */
jmp_buf env[LONGSTACK];             /* Holds longjump stack         */
int  Debug;                         /* Debug mode                   */
int  _ls, Longstack, Breakstack;    /* longjump/break level stack   */
int  Entries, Current;              /* #Entries and Current entry   */
int  ac;                            /* internal argc, from/to stat  */
int  No_load_mail;                  /* disable loading of mail      */
int  Silence;                       /* -s command option status     */
struct ENTRY *Entry;                /* Base of Entry list           */
char *av[128];                      /* internal argv[]              */

int width[MAXLIST]  = { 18, 38, 10 };   /* Default setlist params       */
int header[MAXLIST] = {  0,  2,  1 };
int Listsize = 3;

/* The following are globals variables definable from the 'set' command */

char  *S_sendmail;                  /* sendmail program path        */
int   S_page;                       /* Paging status                */
int   S_novibreak;                  /* vi-break status              */
int   S_verbose;                    /* sendmail verbose status      */
int   S_ask;


!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'help.c'" '(2873 characters)'
if test -f 'help.c'
then
	echo shar: will not over-write existing file "'help.c'"
else
cat << \!Funky!Stuff! > 'help.c'

/*
 * HELP.C
 *
 *  Matthew Dillon, 6 December 1985
 *
 *
 *  (C) 1985  Matthew Dillon
 *
 *  Global Routines:    DO_HELP()
 *
 */

#include <stdio.h>
#include "dmail.h"
#include "execom.h"

#ifndef HELPFILE
static char *help[] = {
#include ".dmkout"
};

do_help()
{
    int i, j;
    char *ptr;

    if (push_base()) {
        push_break();
        pop_base();
        PAGER (-1);
        pop_break();
        return;
    }
    PAGER (0);
    if (ac == 1) {
        for (j = 0; help[j] && *help[j] != '.'; ++j)
            PAGER (help[j]);
        for (i = 0; Command[i].name != NULL; ++i) {
            if (*Command[i].name) {
                sprintf (Puf, "%-10s %s", Command[i].name, Desc[i]);
                PAGER (Puf);
            }
        }
    }
    PAGER ("");
    for (i = 1; i < ac; ++i) {
        j = 0;
again:
        while (help[j]  &&  *help[j] != '.')
            ++j;
        if (help[j]) {
            if (strncmp (av[i], help[j] + 1, strlen(av[i]))) {
                ++j;
                goto again;
            }
            while (help[j]  &&  *help[j] == '.')
                ++j;
            while (help[j]  &&  *help[j] != '.')
                PAGER (help[j++]);
            PAGER ("");
            goto again;
        }
    }
    PAGER (-1);
    pop_base();
}

#else

do_help()
{
    int i, j;
    FILE *fi = NULL;
    char *ptr;
    char *eof;

    if (push_base()) {
        push_break();
        pop_base();
        PAGER (-1);
        if (fi != NULL) {
            fclose (fi);
            fi = NULL;
        }
        pop_break();
        return (-1);
    }
    fi = fopen (HELPFILE, "r");
    if (fi == NULL) {
        printf ("Cannot open help file: %s\n", HELPFILE);
        PAGER (-1);
        pop_base();
        return (-1);
    }
    PAGER (0);
    if (ac == 1) {
        while (fgets (Puf, MAXFIELDSIZE, fi)  &&  *Puf != '.')
            FPAGER (Puf);
        fclose (fi);
        fi = NULL;
        for (i = 0; Command[i].name != NULL; ++i) {
            if (*Command[i].name) {
                sprintf (Puf, "%-10s %s", Command[i].name, Desc[i]);
                PAGER (Puf);
            }
        }
        PAGER (-1);
        pop_base();
        return (1);
    }
    PAGER ("");
    for (i = 1; i < ac; ++i) {
        fseek (fi, 0, 0);
again:
        while ((eof = fgets (Puf, MAXFIELDSIZE, fi))  &&  *Puf != '.');
        if (!eof)
            continue;
        if (strncmp (av[i], Puf + 1, strlen(av[i]))) 
            goto again;
        while ((eof = fgets (Puf, MAXFIELDSIZE, fi))  &&  *Puf == '.');
        if (!eof)
            continue;
        FPAGER (Puf);
        while ((eof = fgets (Puf, MAXFIELDSIZE, fi))  &&  *Puf != '.')
            FPAGER (Puf);
        PAGER ("");
        if (!eof)
            continue;
        goto again;
    }
    fclose (fi);
    fi = NULL;
    PAGER (-1);
    pop_base();
}

#endif

!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'load_mail.c'" '(13693 characters)'
if test -f 'load_mail.c'
then
	echo shar: will not over-write existing file "'load_mail.c'"
else
cat << \!Funky!Stuff! > 'load_mail.c'

/*
 *  LOAD_MAIL.C
 *
 *  Matthew Dillon, 6 December 1985
 *
 *
 *  file-io routines to scan the mail file and load required information.
 *
 *
 *  Global Routines:    HOLD_LOAD()         hold on loading mail after change
 *                      NOHOLD_LOAD()       hold off.. load if changes
 *                      LOAD_CHANGES()      reload mail if changed 
 *                      LOAD_MAIL()         load/reload mail
 *                      SAVE_FILE()         save mail items back to spool
 *                      CHECK_NEW_MAIL()    check for new mail
 *                      WRITE_FILE()        append mail items to a file
 *                      GET_EXTRA_OVR()     ret index of Field (create if not)
 *                      ADD_EXTRA()         add another field (reloads mail)
 *                      DELETE_EXTRA()      delete a field
 *                      GET_EXTRA()         ret index of Field, or error
 *                      M_SELECT()          select on current message list
 *
 *
 *  Static Routines:    LOAD_HASH()         load hash table from fields list
 *                      FREE_ENTRY()        unload EVERYTHING
 *                      FREE_TABLE()        unload all Fields table
 *                      LOAD_FILE()         raw file loading/counting
 *                      
 *
 */

#include <stdio.h>
#include <sys/file.h>
#include "dmail.h"

#define NOHOLD  0
#define HOLD    1

#define NO_BASE     0
#define NO_FIELDS   1
#define ENTRY_OK    2

struct FIND Find[MAXTYPE + 1] = {
        "From:"   , 5, 1,
        "To:"     , 3, 1,
        "Subject:", 8, 1 };

static int  File_size;
static int  changed, load_hold;
static int  Hash[256];


hold_load()
{
    load_hold = 1;
}


nohold_load()
{
    load_hold = 0;
    load_changes();
}


load_changes()
{
    if (changed  &&  !load_hold)
        load_mail(Entries, 1);
}

initial_load_mail()
{
    if (load_mail (0, 0) < 0)
        return (-1);
    return ((Entries) ? 1 : -1);
}


static
load_mail(at, from0)
{
    FILE *fi;
    int i, count, file_size;

    if (No_load_mail)
        return (-1);
    load_hash();
    if (from0)
        free_table (0, HOLD);
    else
        free_table (at, NOHOLD);
    fi = fopen (mail_file, "r");
    if (m_fi != NULL)
        fclose (m_fi);
    m_fi = fopen (mail_file, "r");
    if (fi == NULL  ||  m_fi == NULL)
        return (-1);
    flock (m_fi->_file, LOCK_EX);
    if (at)
        fseek (fi, Entry[at].fpos, 0);
    else
        fseek (fi, 0, 0);
    count = Entries;
    while (search_from(fi))
        ++count;
    if (Entries != count) {
        printf ("%d New Items loaded\n", count - Entries);
        Entry = (struct ENTRY *)realloc (Entry, sizeof(*Entry) * (count + 1));
    }
    Entries = count;
    for (i = at; i < Entries; ++i)
        Entry[i].no  =  Entry[i].status = 0;
    Entry[i].fpos = File_size = file_size = ftell (fi);
    fclose (fi);
    load_file ((from0) ? 0 : at);
    if (file_size != File_size) {       /* Last entry incomplete?       */
        free_table (Entries - 1, NOHOLD);
    }
    changed = 0;
    flock (m_fi->_file, LOCK_UN);
    return (1);
}


static
load_file(at)
int at;
{
    FILE *fi;
    char *next, *ptr;
    int i, bit, maxbit, len, count, havefrom;

    maxbit = 0;
    for (i = 0; Find[i].search != NULL; ++i) 
        maxbit = (maxbit << 1) | 1;
    fi = fopen (mail_file, "r");
    count = -1;
    havefrom = 0;
    while (havefrom  ||  search_from (fi)) {
        havefrom = 0;
        if (++count >= Entries)
            break;
        len = strlen(Buf) - 1;
        Buf[len] = '\0';
        next = next_word(Buf);
        len -= next - Buf;
        Entry[count].fpos = ftell (fi);
        Entry[count].from = malloc (len + 1);
        bcopy (next, Entry[count].from, len + 1);

        /* SEARCH FIELD LIST */

        bit = 0;
        if (Debug)
            printf ("No %d  ---------------------\n", count + 1);
        while (fgets (Buf, MAXFIELDSIZE, fi) != NULL) {
            if (Buf[0] == '\n')
                break;
            if (isfrom(Buf)) {
                havefrom = 1;
                break;
            }
            len = strlen(Buf) - 1;
            Buf[len] = '\0';
            if (Debug)
                printf ("CHECK: %s\n", Buf);
            next = next_word(Buf);
            len -= next - Buf;
            if (Hash[*Buf] == 0)
                continue;
            if (Hash[*Buf] > 0) {
                i = Hash[*Buf] & 0xff;
                if (strncmp (Find[i].search, Buf, Find[i].len) == 0) 
                    goto found;
                continue;
            }
            for (i = -Hash[*Buf] & 0xff; Find[i].search; ++i) {
                if (*Find[i].search != *Buf)
                    break;
                if (strncmp (Find[i].search, Buf, Find[i].len) == 0)
                    goto found;
            }
            continue;
found:
            if (Debug)
                printf ("Found: %d %s\n", i, Buf);
            if (Find[i].notnew == 0) {
                Find[i].notnew = 1;
                ptr = Buf;
                while (*ptr  &&  *ptr != ':')
                    ++ptr;
                ++ptr;
                Find[i].search =
                        realloc (Find[i].search, ptr - Buf + 1);
                strncpy (Find[i].search, Buf, ptr - Buf);
                *(Find[i].search + (ptr - Buf)) = '\0';
                Find[i].len = strlen(Find[i].search);
            }
            compile_field (Buf, fi);
            Entry[count].fields[i] = 
                    malloc (strlen(next) + 1);
            strcpy (Entry[count].fields[i], next);
            if ((bit |= (1 << i)) == maxbit) 
                break;
        }
        if (bit != maxbit) {
            for (i = 0; Find[i].search != NULL; ++i) {
                if (((1 << i) & bit) == 0) {
                    Entry[count].fields[i] = malloc (1);
                    *(Entry[count].fields[i]) = '\0';
                }
            }
        }
    } 
    File_size = ftell (fi);
    fclose (fi);
    return (1);
}


static
load_hash()
{
    register int i, c;

    bzero (Hash, sizeof(Hash));
    for (i = 0; Find[i].search; ++i) {
        c = *Find[i].search;
        if (Hash[c] > 0)
            Hash[c] = -Hash[c];
        if (Hash[c] == 0)
            Hash[c] = i | 0x100;
    }
}


free_entry()
{
    free_table(0, NOHOLD);
    Entry = (struct ENTRY *)realloc (Entry, sizeof(*Entry));
    File_size = Entries = 0;
    Entry->status = Entry->no = Entry->fpos = Current = 0;
    if (m_fi)
        fclose (m_fi);
}


static
free_table(at, hold)
{
    int i, j;

    for (i = at; i < Entries; ++i) {
        free (Entry[i].from);
        for (j = 0; Find[j].search != NULL; ++j) 
            free (Entry[i].fields[j]);
    }
    Entries = (hold == HOLD) ? Entries : at;
    File_size = (at) ? Entry[Entries].fpos : 0;
}

static
search_from(fi)
FILE *fi;
{
    while (fgets (Buf, MAXFIELDSIZE, fi) != NULL) {
        if (isfrom (Buf))
            return (1);
    }
    return (0);
}


save_file(reload, mark, notmark)
{
    FILE *fi, *fiscr;
    int fd, fdscr;
    int new_size, i;
    char scratch[64];
    char buf[MAXFIELDSIZE];
    char *avnul[1];

    avnul[0] = NULL;

    if (m_fi)
        fclose (m_fi);
    for (i = 0; i < Entries; ++i) {
        if ((Entry[i].status & mark) != mark  ||
                (~Entry[i].status & notmark) != notmark) 
            break;
    }
    if (i == Entries) {
        m_select (avnul, M_RESET);
        puts ("No Changes Made");
        return (Entries);
    }
    sprintf (scratch, "/tmp/dmail%d", getpid());
    fd = open (mail_file, O_RDWR, 0);
    if (fd < 0)
        return (-1);
    flock (fd, LOCK_EX);
    fdscr = open (scratch, O_RDWR | O_CREAT | O_TRUNC, MAILMODE);
    fi    = fdopen (fd, "r+");
    fiscr = fdopen (fdscr, "a+");
    for (i = 0; i < Entries; ++i) {
        if ((Entry[i].status & mark) == mark  &&
                (~Entry[i].status & notmark) == notmark) {
            fputs ("From ", fiscr);
            fputs (Entry[i].from, fiscr);
            fputc ('\n', fiscr);
            fseek (fi, Entry[i].fpos, 0);
            while (fgets (buf, MAXFIELDSIZE, fi) != NULL) {
                if (isfrom(buf))
                    break;
                fputs (buf, fiscr);
            }
        }
    }

    /* If NEW MAIL has come in, append that to the scratch file also */

    new_size = fseek (fi, 0, 2);
    if (File_size != new_size) {
        fseek (fi, File_size, 0);
        while (fgets (buf, MAXFIELDSIZE, fi) != NULL)
            fputs (buf, fiscr);
    }

    /* Write scratch file back to mail file, or try to */

    fflush (fi);
    fflush (fiscr);
    fseek (fi   , 0, 0);
    fseek (fiscr, 0, 0);
    lseek (fd   , 0, 0);
    lseek (fdscr, 0, 0);
    while ((i = read (fdscr, buf, MAXFIELDSIZE)) > 0) 
        write (fd, buf, i);
    ftruncate (fd, lseek (fd, 0, 1));
    if (lseek (fd, 0, 2) == 0  &&  !reload) {
        printf ("%s  Removed\n", mail_file);
        unlink (mail_file);
    }
    fclose (fi);
    fclose (fiscr);
    unlink (scratch);
    if (reload) {
        free_entry();
        load_mail(0, 0);
    }
    m_select (avnul, M_RESET);
    return (0);
}


check_new_mail()
{
    push_break();
    if (m_fi == NULL) {
        m_fi = fopen (mail_file, "r");
        if (m_fi == NULL) {
            pop_break();
            return;
        }
    }
    if (fseek (m_fi, 0, 2) != File_size)
        load_mail(Entries, 1);
    pop_break();
}


write_file(file, modes, mark, notmark)
char *file;
{
    int i, fd, notopen = 1;
    FILE *fi;
    char buf[MAXFIELDSIZE];

    for (i = 0; i < Entries; ++i) {
        if ((Entry[i].status & mark) == mark  &&
                (~Entry[i].status & notmark) == notmark) {
            if (notopen) {
                notopen = 0;
                fd = open (file, O_APPEND | O_WRONLY | modes, MAILMODE);
                if (fd < 0)
                    return (-1);
                flock (fd, LOCK_EX);
                fi = fdopen (fd, "a");
            }
            fputs ("From ", fi);
            fputs (Entry[i].from, fi);
            fputc ('\n', fi);
            if (m_fi) {
                fseek (m_fi, Entry[i].fpos, 0);
                while (fgets (buf, MAXFIELDSIZE, m_fi) != NULL) {
                    if (isfrom(buf))
                        break;
                    fputs (buf, fi);
                }
            }
        }
    }
    if (!notopen)
        fclose (fi);
    return (1);
}


get_extra_ovr(str)
char *str;
{
    register int i;

    i = get_extra (str);
    if (i < 0) {
        i = add_extra (str);
        load_changes();
    }
    return (i);
}


add_extra(str)
char *str;
{
    int i, j;

    for (i = 0; i < MAXTYPE; ++i) {
        if (Find[i].search == NULL)
            break;
    }
    if (i == MAXTYPE)
        i = EXSTART;
    for (; i < MAXTYPE; ++i) {
        for (j = 0; j < Listsize; ++j) {
            if (i == header[j])
                break;
        }
        if (j == Listsize)
            break;
    }
    if (i >= MAXTYPE)
        return (-1);
    if (Find[i].search != NULL)
        free (Find[i].search);
    Find[i].len = strlen(str);
    Find[i].search = malloc (Find[i].len + 1);
    Find[i].notnew = 0;
    strcpy (Find[i].search, str);
    changed = 1;
    return (i);
}


delete_extra(str)
char *str;
{
    int i;

    for (i = EXSTART; Find[i].search; ++i) {
        if (strncmp (Find[i].search, str, strlen(str)) == 0) {
            free (Find[i].search);
            do {
                Find[i].search = Find[i + 1].search;
            } while (Find[++i].search);
            changed = 1;
            return (i);
        }
    }
    return (-1);
}


delete_all()
{
    int i;

    for (i = EXSTART; Find[i].search; ++i) {
        free (Find[i].search);
        changed = 1;
    }
    return (1);
}


get_extra(str)
char *str;
{
    int i;

    for (i = 0; Find[i].search; ++i) {
        if (strncmp (str, Find[i].search, strlen(str)) == 0)
            return (i);
    }
    return (-1);
}


m_select(sav, mode)
register char *sav[];
{
    char *ptr, *dest;
    char l_map[256];
    int idx[MAXLIST], ix = 0;
    int ok, not, len, scr;
    register int i, j, avi;
        
    for (i = 0;i < 256; ++i)
        l_map[i] = i;
    for (i = 'A'; i <= 'Z'; ++i)
        l_map[i] += 'a' - 'A';
    hold_load();
    i = 0;
    idx[ix++] = get_extra_ovr (sav[i++]);
    for (; sav[i]; ++i) {
        if (strcmp (sav[i], ",") == 0  &&  sav[i + 1])
            idx[ix++] = get_extra_ovr (sav[++i]);
    }
    idx[ix] = -1;
    nohold_load();
    j = 1;
    for (i = 0; i < Entries; ++i) {
        if (mode == M_CONT  &&  Entry[i].no == 0)
            continue;
        ix = ok = 0;
        avi = 1;
        while ((ptr = sav[avi]) != NULL) {
            if (ptr[0] == ','  &&  ptr[1] == '\0' && sav[avi+1]) {
                ++ix;
                avi += 2;
                continue;
            }
            if (not = (*ptr == '!'))
                ++ptr;
            len = strlen (ptr);
            dest = Entry[i].fields[idx[ix]];
            if (*ptr == '\0') {
                ok = 1;
                goto gotit;
            }
            while (*dest) {
                scr = 0;
                while (l_map[dest[scr]] == l_map[ptr[scr]] && ptr[scr])
                    ++scr;
                if (ptr[scr] == '\0') {
                    ok = 1;
                    goto gotit;
                }
                ++dest;
            }
            ++avi;
        }
gotit:
        Entry[i].no = (ok ^ not) ? j++ : 0;
    }
    if (Entry[Current].no == 0) {
        Current = indexof (1);
        if (Current < 0) {
            Current = 0;
            return (-1);
        }   
    }
    return (1);
}


!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'main.c'" '(6875 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, 6 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'" '(3470 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, 6 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'" '(7312 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, 6 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 <sysexits.h>
#include <signal.h>
#include <fcntl.h>
#include "dmail.h"

extern FILE *popen();

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 'v':
    case 'V':
        return (2);
    }
    puts ("ABORT, no mail sent");
    return (0);
}



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'" '(3049 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, 6 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'" '(5147 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, 6 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) != 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