[net.sources] DMAIL REPOST -- PART II of III

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

	Sorry, it would seem that a couple problems cropped up.  I will just
repost the entire thing... this time, I also have the MAN page.  problems
that occured:

		Make had a problem with spaces in-front of shell commands
		some 4.2 systems don't have <fcntl.h>
		some files were not tabbed correctly (dmail.help)

	Please bear with me.... after all, this is the first time I've posted
something to this net.

	I've split the program up into Three sections.  Don't combine these
files with files from the first posting, just redo it with these.

			Thanks,

			-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:
#	execom.c
#	execom.h
#	globals.c
#	help.c
#	load_mail.c
# This archive created: Sat Dec  7 15:27:26 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
#	End of shell archive
exit 0