[net.sources] DMAIL REPOST -- PART III 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:
#	main.c
#	nroff.out
#	range.c
#	sendmail.c
#	set.c
#	sub.c
# This archive created: Sat Dec  7 15:29:06 1985
export PATH; PATH=/bin:$PATH
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 "'nroff.out'" '(9692 characters)'
if test -f 'nroff.out'
then
	echo shar: will not over-write existing file "'nroff.out'"
else
cat << \!Funky!Stuff! > 'nroff.out'



DMAIL(1)            UNIX Programmer's Manual             DMAIL(1)



NAME
     dmail - an original mail like interface for mail

SYNOPSIS
     dmail -v -O -f [fromfile] -o [tofile] -l rcfile -L -F
     [field] -F [field]...

     dmail user user user user...

GENERAL
     _D_m_a_i_l is a program which hopefully simplifies reading and
     organizing your mail.  It was designed to overcome several
     gripes people had over other mailers.  _D_m_a_i_l uses the
     /usr/spool format for any folder's you create.  To simply
     matters even more, it was partially modeled on standard Mail
     in terms of writing to your 'mbox' and getting items from
     your 'spool', at least as default parameters.  About the
     only other similarity between the two programs is in the
     'type', 'quit', and 'exit' commands of _d_m_a_i_l.  _D_m_a_i_l _k_e_e_p_s
     _m_u_l_t_i_p_l_e _m_e_s_s_a_g_e_s _p_e_r _f_i_l_e, mail (thus folders are files
     rather than directories).

     _D_m_a_i_l is not a glorified _M_a_i_l, however.  Though most other
     mailers have folders, you are usually required to read your
     mail in the order it comes in.  For somebody who is on three
     high-volume mailing lists, it can be rather difficult. _D_m_a_i_l
     provides a mechanism in which you can separate your mail by
     any partial string in any Field without having to create a
     folder.  The _s_e_l_e_c_t command is usually instantanious, and
     with aliases, you can switch between mailing lists, personal
     mail, or anything you wish at a keystroke.  Whenever you
     _s_e_l_e_c_t on something, you get a reduced list (as if the other
     messages didn't exist), and may execute such things as
     'delete all' on it without effecting other mail.  It serves
     to allow you to read each mailing list (or whatever) one at
     a time.

     Mail-headers are a problem for many people, especially those
     logging in over 300 or 1200 baud lines.  In many cases, the
     header is larger than the message; in any case, they can be
     a nuisances.  _D_m_a_i_l will not print any fields you do not
     want printed.  This seems to be a better mechanism than
     Mail, which allows you to exclude certain headers.  In
     _d_m_a_i_l, you specify what to include.  To see the entire
     header, you use the _h_e_a_d_e_r command from _d_m_a_i_l.

     Apart from that, _d_m_a_i_l gives you set variables, aliases,
     folder's, and is generally faster than other mail programs.

OPTIONS
     _D_m_a_i_l may be executed with several options, putting it in
     one of two modes.  Firstly, when executed with a user-name



Printed 12/7/85          6 December 1985                        1






DMAIL(1)            UNIX Programmer's Manual             DMAIL(1)



     list, _d_m_a_i_l will put you into the editor setting up the To:
     field properly and allowing you to enter a letter which
     would then be sent off.  Secondly, if no user-list is given,
     dmail will attempt to read mail from your spool file (or
     from file), and put you into command mode.  If the mail file
     is empty or does not exist, you will be told that you have
     no mail, and control will be returned to your shell.  The
     editor is specified by the enviroment variable VISUAL
     (Default vi).

     dmail -O -v -f [fromfile] -o [tofile] -l rcfile -L -F
     [field] -F [field]...

     -O   places _d_m_a_i_l in interactive mode, even if there is no
          mail to read.

     -v   Places sendmail in verbose mode when you mail, reply,
          or forward a message. (sets the verbose variable)

     -f   Specify where mail should be taken from.  The default
          is /usr/spool/mail/$USER.  This option without a
          filename specifies that mail should be taken from
          $HOME/mbox.  Otherwise, the specified filename becomes
          the fromfile.  This means that by specifying just the
          -f option and not the -o option will cause both the
          infile and outfile to be $HOME/mbox

     -o   Specify where 'read' mail will be written out to.  The
          default is $HOME/mbox.  This option without a filename
          specifies that the mail should be placed in $HOME/.mbox
          .  Otherwise, the specified filename becomes the out-
          file.  Additionally, if both -f and -o are specified
          without filenames, both the fromfile and the outfile
          will be set to $HOME/.mbox

     -l   Specify the RC file (default is $HOME/.dmailrc).  The
          RC file consists of a list of _d_m_a_i_l commands, as if
          they were typed in.

     -L   Disables sourcing of any RC files

     -F   This option tells _d_m_a_i_l to include the given field on
          initial mail load.  Normally, only the From:, To:, and
          Subject: fields are initially loaded.  When you specify
          other fields, _d_m_a_i_l must scan your fromfile again.
          When you have large amounts of mail (+50000 bytes),
          this can take a couple of seconds.  The -F option
          allows you to specify a field to be loaded in addition
          to the defaults listed above.  You may specify multiple
          -F field  options.  This is only a convenience feature,
          normally you don't have to worry about it.  Some peo-
          ple, however, have spool files of several hundred



Printed 12/7/85          6 December 1985                        2






DMAIL(1)            UNIX Programmer's Manual             DMAIL(1)



          thousand bytes and would use this option to prevent
          delays later on.

INTERRUPTS
     _d_m_a_i_l handles the INTR character.  You may use it to break
     out of listings, types, or anything else without worry of
     destroying something.

COMMAND OVERVIEW
     The basic commands for _d_m_a_i_l are as follows (There are, of
     course, many commands, these are just a few): _l_i_s_t _t_y_p_e
     _h_e_a_d_e_r _d_e_l_e_t_e _n_e_x_t _s_e_l_e_c_t _q_u_i_t _e_x_i_t _a_l_i_a_s _u_n_a_l_i_a_s _s_e_t _u_n_s_e_t
     and _m_a_i_l

     You may abbrieviate any command you wish.  It is suggested
     that you do not abbreviate commands in your rc file (default
     .dmailrc), as any new commands added in later versions may
     change something. Using full command names in your rc file
     will keep things compatible.

     For many commands (e.g. delete), you may specify a message
     list.  You can give numerical ranges (1-4 6-9), number lists
     (1 2 3 6), or any of several reserved words, such as 'all'.
     See 'help keywords' from dmail for a complete list.  All
     lists apply only to the currently selected messages.

     _l_i_s_t displays parts of the headers of messages, one message
     per line, for all messages currently selected.  You can set
     the output format with the _s_e_t_l_i_s_t command.

     _t_y_p_e prints the message text of a message.  By the way, only
     header fields specified by the _s_e_t_l_i_s_t command will be
     printed (default- From: To: Subject:), which is useful.

     _h_e_a_d_e_r prints out the entire header of a message.

     _d_e_l_e_t_e deletes a message or message list (you can say
     'delete all' to delete all messages currently selected).

     _n_e_x_t goes to the next message and type's or header's it,
     depending on what last was done (type or header).

     _s_e_l_e_c_t selects what you want to look at.  You can say
     'select all', to select all the messages, or selects of the
     form:

     select Field match match match , Field match match ....

     where Field is From:, To:, or any field you wish (you can
     abbreviate the fields.... Fro To, etc...).  match is a
     string which you are attempting to match in the Field.  If a
     match is found, that message will become selected.  Thus,



Printed 12/7/85          6 December 1985                        3






DMAIL(1)            UNIX Programmer's Manual             DMAIL(1)



     you can select all your personal mail with:

     select Cc myname , To myname

     Use the on-line help for more information on the _s_e_l_e_c_t or
     any other command.

     _q_u_i_t from _d_m_a_i_l please.  Any unread messages stay in your
     spool (or fromfile), any read messages are placed in your
     mail-box, any deleted messages are deleted.

     _e_x_i_t from _d_m_a_i_l please.  Nothing is written or altered, exit
     without changing anything.  _a_l_i_a_s is used to alias commands
     to other commands, or command sequences.  For instance, you
     can alias 'me' to select all personal mail:

     alias me "select Cc myname , To myname"

     you can alias a sequence of commands:

     alias q  "select all ; pre all ; quit"

     The above would unread any read mail and quit (therefore,
     any mail not deleted will end up staying in your spool
     file).  You can also _u_n_a_l_i_a_s aliases.

     _s_e_t a variable to a string.  You can also _u_n_s_e_t a variable.
     Several variables have special meaning, use _h_e_l_p _s_e_t from
     _d_m_a_i_l for a full explanation.

     _m_a_i_l and _r_e_p_l_y may be used to mail out or reply to messages.

BUGS
     Please send bug reports to:           ...!ucbvax!dillon
               dillon@ucb-vax.berkeley.edu




















Printed 12/7/85          6 December 1985                        4



!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'" '(7315 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 <sys/file.h>
#include <sysexits.h>
#include <signal.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