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 thils 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: # README # Makefile # commands.c # dmail.1 # dmail.h # dmail.help # dmkhelp.c # do_lists.c # This archive created: Sat Dec 7 15:25:03 1985 export PATH; PATH=/bin:$PATH echo shar: extracting "'README'" '(2253 characters)' if test -f 'README' then echo shar: will not over-write existing file "'README'" else cat << \!Funky!Stuff! > 'README' README FILE FOR DMAIL v1.00 Read Makefile for compiling and installation procedures. Dmail compiles fine on UNIX BSD 4.2/4.3. A man page is coming soon, though every command as a full help page (online from Dmail). AN EXAMPLE OF A .DMAILRC FILE: (happens to be mine) --------------------------------------------------------------------------- alias normal "setlist -s 18 From 38 Subject 10 To 0 Cc 0 Date" alias from "setlist -s 66 From; list; normal" alias me "select To dillon , Cc dillon" alias bugs "select To root staff manag , Cc staff manag root" alias trek "select To trek , Cc trek" alias notme "select -s To !dillon; resel -s Cc !dillon; resel From !dillon" alias hack "select To hacker , Cc hacker" alias page set page more alias nopage unset page alias k tag alias kn "tag ; next" alias spool "g /usr/spool/mail/dillon ~/Dmail/mbox" alias keep "g ~/Dmail/keep" alias mbox "g ~/Dmail/mbox" alias q "select -s all; write ~/Dmail/keep -s tag; delete -s tag; quit" alias g "select -s all; write ~/Dmail/keep -s tag; delete -s tag; qswi" set amiga "decwrl!pyramid!amiga!support" set header ~/.mailheader set ask normal cd ~/Dmail --------------------------------------------------------------------------- In the above example, I have created a Dmail directory to hold all my folders. Each folder will be a file containing multiple messages, fully compatible with /usr/spool/ and mbox. my dmail alias is this: alias dmail '\dmail -O -l ~/Dmail/.dmailrc -o ~/Dmail/mbox -F Cc -F Date' NOTE: you don't need to alias dmail to anything. without any arguments, it acts like /bin/Mail getting your mail from your spool, and placing read mail on quit to mbox in your home directory. I use -O so dmail gives me a command prompt even if there is no mail, and the -F options tell dmail to load those subjects into memory automatically (because I'm going to select on them immediately anyway). If a field which you select on is not in memory, dmail must go to the mail file to find the field. This is transparent. GOOD LUCK! -Matt dillon@ucb-vax.berkeley.edu ...!ucbvax!dillon !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'Makefile'" '(1758 characters)' if test -f 'Makefile' then echo shar: will not over-write existing file "'Makefile'" else cat << \!Funky!Stuff! > 'Makefile' # V1.00 (first distribution) # # Written by Matthew Dillon, distribution date 6 December 1985 # # (C)1985 by Matthew Dillon # # This code is completely original. I declare this code to be public # domain. You may have this code as long as any redistributions # contain this and all other files in their entirety, especially these # comments. # # Send bug reports and other gripes # # dillon@ucb-vax.berkeley.edu ARPA NET # ...!ucbvax!dillon USENET # # INSTRUCTIONS FOR COMPILING: # # (1) Set DESTDIR & HELP_DIR to some global directory accessable to # everybody (or whatever). Also set HELP_CHMOD and PROG_CHMOD # to the right thing if you don't like the defaults. # # (2) Do ONE of the following: # make -defaults to internal help file # make external -external help file (executable is smaller) # # (3) make install # CC = cc CFLAGS = DEST_DIR = ../../bin HELP_DIR = /usr/public HELP_FILE = ${HELP_DIR}/dmail.help HELP_CHMOD = 644 PROG_CHMOD = 751 OBJECTS = globals.o main.o do_lists.o sub.o execom.o commands.o range.o \ load_mail.o sendmail.o set.o help.o HEADERS = dmail.h execom.h internal: dmkhelp ${OBJECTS} ${CC} ${CFLAGS} -o dmail ${OBJECTS} external: ${OBJECTS} ${CC} ${CFLAGS} -c -DHELPFILE=\"${HELP_FILE}\" help.c ${CC} ${CFLAGS} -o dmail ${OBJECTS} ${OBJECTS}: ${HEADERS} help.o: dmkhelp ./dmkhelp > .dmkout ${CC} ${CFLAGS} -c help.c dmkhelp: dmail.help dmkhelp.o ${CC} ${CFLAGS} -o dmkhelp dmkhelp.o clean: rm -f *.o make.out a.out core dmail dmkhelp .dmkout install: cp dmail ${DEST_DIR} cp dmail.help ${HELP_DIR} chmod ${PROG_CHMOD} ${DEST_DIR}/dmail chmod ${HELP_CHMOD} ${HELP_DIR}/dmail.help !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'commands.c'" '(11206 characters)' if test -f 'commands.c' then echo shar: will not over-write existing file "'commands.c'" else cat << \!Funky!Stuff! > 'commands.c' /* * COMMANDS.C * * Matthew Dillon, 6 December 1985 * * DMAIL (C) 1985 Matthew Dillon * * Global Routines: DO_QUIT() * DO_EXIT() * DO_CD() * DO_ECHO() * DO_GO() * DO_SOURCE() * DO_SHELL() * DO_WRITE() * DO_DELNEXT() * DO_NUMBER() * DO_NEXT() * DO_HEADER() * DO_TYPE() * DO_DELETE() * DO_UNDELETE() * DO_MARK() * * Static Routines: None. * */ #include <stdio.h> #include <sys/file.h> #include "dmail.h" #define LAST_TYPE 0 #define LAST_HEADER 1 static int Last_operation; static int Last_deleted = -1; do_quit(garbage, com) char *garbage; { int fd, r, back; char *str; char *nulav[3]; nulav[0] = ""; nulav[1] = ""; nulav[2] = NULL; push_break(); if (get_inode (mail_file) == get_inode (output_file)) { back = save_file (0, 0, ST_DELETED | ST_STORED); } else { r = write_file (output_file, O_CREAT, ST_READ, ST_DELETED | ST_STORED); if (r < 0) { printf ("Unable to write to %s\n", output_file); back = save_file (0, 0, ST_DELETED | ST_STORED); } else { back = save_file (0, 0, ST_READ | ST_DELETED | ST_STORED); } } if (back) printf ("%d kept in %s\n", back, mail_file); sleep (1); if ((fd = open (mail_file, O_RDONLY, 0)) >= 0) { read (fd, Buf, 1); close (fd); } if (!com) done (0); free_entry(); if (av[1] == 0) { if (!Silence) puts ("NO FROM FILE SPECIFIED"); av[1] = mail_file; av[2] = NULL; } mail_file = realloc (mail_file, strlen(av[1]) + 1); strcpy (mail_file, av[1]); str = (av[2]) ? av[2] : mail_file; output_file = realloc (output_file, strlen(str) + 1); strcpy (output_file, str); initial_load_mail(); m_select (nulav, M_RESET); pop_break(); if (!Silence) printf ("\nRF %-20s WF %-20s\n", mail_file, output_file); } do_exit(garbage, com) char *garbage; { char *str; char *nulav[3]; nulav[0] = ""; nulav[1] = ""; nulav[2] = NULL; if (!com) done (0); push_break(); free_entry(); if (av[1] == 0) { if (!Silence) puts ("NO FROM FILE SPECIFIED"); av[1] = mail_file; av[2] = NULL; } mail_file = realloc (mail_file, strlen(av[1]) + 1); strcpy (mail_file, av[1]); str = (av[2]) ? av[2] : mail_file; output_file = realloc (output_file, strlen(str) + 1); strcpy (output_file, str); initial_load_mail(); m_select (nulav, M_RESET); pop_break(); if (!Silence) printf ("\nRF %-20s WF %-20s\n", mail_file, output_file); } do_cd() { char *dir = (ac < 2) ? home_dir : av[1]; if (chdir(dir) < 0) { printf ("Cannot CD to %s\n", dir); return (-1); } return (1); } do_echo(str) char *str; { puts (next_word(str)); return (1); } do_go() { int i; if (ac < 2) { puts ("go to which article?"); } rewind_range (1); i = get_range(); if (i < 0) { if (!Silence) printf ("Message #%d does not exist\n", i); return (-1); } if (i == 0) { if (!Silence) puts ("No Message"); return (-1); } Current = indexof(i); return (1); } do_source() { char comline[1024]; FILE *fi = NULL; if (ac < 2) { puts ("No file argument to source"); return (-1); } if (push_base()) { push_break(); pop_base(); if (fi != NULL) fclose (fi); pop_break(); return (-1); } push_break(); fi = fopen (av[1], "r"); pop_break(); if (fi == NULL) { printf ("Cannot open %s\n", av[1]); return (-1); } while (fgets (comline, 1024, fi) != NULL) { comline[strlen(comline) - 1] = '\0'; exec_command (comline); } push_break(); fclose (fi); fi = NULL; pop_break(); pop_base(); return (1); } do_shell(str) char *str; { int pid, ret; char *shell; char *args; shell = getenv("SHELL"); if (shell == NULL) shell = "/bin/sh"; args = "-fc"; push_break(); str = next_word (str); if (strlen (str)) { if ((pid = vfork()) == 0) { execl (shell, shell, args, str, NULL); _exit (1); } } else { if ((pid = vfork()) == 0) { execl (shell, shell, NULL); _exit (1); } } while ((ret = wait(0)) > 0) { if (ret == pid) break; } pop_break(); return (1); } do_write() { char *file; int r, count = 0; register int i, j; if (ac < 2) { puts ("You must specify at least a file-name"); return (-1); } file = av[1]; rewind_range (2); push_break(); while (i = get_range()) { j = indexof (i); if (j >= 0 && !(Entry[j].status & ST_DELETED)) { Entry[j].status |= ST_STORED | ST_SCR; ++count; } } r = write_file (file, O_CREAT, ST_SCR, 0); rewind_range (2); if (r > 0) { while (i = get_range()) { j = indexof (i); if (j >= 0) Entry[j].status &= ~ST_SCR; } if (!Silence) printf ("%d Items written\n", count); } else { while (i = get_range()) { j = indexof (i); if (j >= 0) Entry[j].status &= ~(ST_SCR | ST_STORED); } printf ("Could not write to file %s\n", file); } pop_break(); return (1); } do_delnext() { static int warning; if (!warning && Last_operation == LAST_HEADER) { ++warning; puts ("Note that the next command is displaying headers only at"); puts ("this point. (one-time warning, NOTHING deleted"); return (-1); } if (do_mark("", ST_DELETED) > 0) return (do_next("", 1)); return (-1); } do_number(str, com) char *str; int com; { int x; x = indexof (atoi(str)); if (x < 0) { puts ("Non existant message"); return (-1); } Current = x; switch (Last_operation) { case LAST_TYPE: return (do_type()); case LAST_HEADER: return (do_header()); default: puts ("Internal Error NEXT"); return (-1); } } do_next(str, com) char *str; { int ok; push_break(); if (com > 0) { if (++Current > Entries) Current = Entries; if (fix() < 0) { puts ("End of file"); pop_break(); return (-1); } --com; } if (com < 0) { ++com; ok = 0; while (--Current >= 0) { if (Entry[Current].no && !(Entry[Current].status & ST_DELETED)) { ok = 1; break; } } if (!ok) { puts ("Start of file"); Current = 0; fix(); pop_break(); return (-1); } } pop_break(); if (!com) { switch (Last_operation) { case LAST_TYPE: return (do_type()); case LAST_HEADER: return (do_header()); } } return (1); } do_header() { char buf[1024]; Last_operation = LAST_HEADER; if (push_base()) { push_break(); pop_base(); PAGER (-1); fseek (m_fi, 0, 0); fflush (stdout); pop_break(); return (-1); } if (single_position() < 0) return (-1); PAGER (0); sprintf (Puf, "MESSAGE HEADER #%d (%d) %s\n", Entry[Current].no, Current + 1, (Entry[Current].status & ST_DELETED) ? " DELETED" : ""); PAGER (Puf); sprintf (Puf, "From %s\n", Entry[Current].from); PAGER (Puf); while (fgets (buf, 1024, m_fi) != NULL) { FPAGER (buf); if (*buf == '\n') { PAGER (-1); pop_base(); return (1); } } PAGER ("END OF FILE ENCOUNTERED"); PAGER (-1); pop_base(); return (-1); } do_type() { char buf[1024]; int i; Last_operation = LAST_TYPE; if (push_base()) { push_break(); pop_base(); PAGER (-1); fseek (m_fi, 0, 0); fflush (stdout); pop_break(); return (-1); } if (single_position() < 0) return (-1); if (skip_to_data (m_fi) < 0) { printf ("Cannot find data for message %d\n", Entry[Current].no); return (-1); } PAGER (0); sprintf (Puf, "MESSAGE TEXT #%d (%d) %s\n", Entry[Current].no, Current + 1, (Entry[Current].status & ST_DELETED) ? " DELETED" : ""); PAGER (Puf); for (i = 0; i < Listsize; ++i) { if (*Entry[Current].fields[header[i]]) { sprintf (Puf, "%-10s %s", Find[header[i]].search, Entry[Current].fields[header[i]]); PAGER (Puf); } } PAGER (""); while ((fgets (buf, 1024, m_fi) != NULL) && strncmp (buf, "From ", 5)) FPAGER (buf); Entry[Current].status |= ST_READ; PAGER (-1); pop_base(); return (1); } do_mark(garbage, mask) char *garbage; { int count = 0; register int i, j; rewind_range (1); push_break(); while (i = get_range()) { j = indexof (i); if (j >= 0) { if (mask & ST_DELETED) Last_deleted = j; if ((Entry[j].status & mask) != mask) { Entry[j].status |= mask; if (Entry[j].status & ST_DELETED) Entry[j].status &= ~(ST_STORED | ST_READ | ST_TAG); ++count; } } } if (!Silence) printf ("%d Items\n", count); pop_break(); return (1); } do_unmark(garbage, mask) char *garbage; { int count = 0; register int i, j; register struct ENTRY *en; push_break(); if (ac == 1 && (mask & ST_DELETED) && Last_deleted != -1) { en = &Entry[Last_deleted]; if (en->no) { en->status &= ~mask; printf ("Undeleted last deleted message (# %d)\n", en->no); Current = Last_deleted; Last_deleted = -1; } else { puts ("Last deleted message not within current select bounds"); pop_break(); return (-1); } pop_break(); return (1); } rewind_range (1); while (i = get_range()) { j = indexof (i); if (j >= 0) { if (Entry[j].status & mask) { Entry[j].status &= ~mask; ++count; } } } if (!Silence) printf ("%d Items\n", count); pop_break(); return ((count) ? 1 : -1); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'dmail.1'" '(7993 characters)' if test -f 'dmail.1' then echo shar: will not over-write existing file "'dmail.1'" else cat << \!Funky!Stuff! > 'dmail.1' .TH DMAIL 1 "6 December 1985" .UC .SH NAME dmail \- an original mail like interface for mail .SH SYNOPSIS dmail -v -O -f [fromfile] -o [tofile] -l rcfile -L -F [field] -F [field]... .PP dmail user user user user... .br .SH GENERAL .I Dmail is a program which hopefully simplifies reading and organizing your mail. It was designed to overcome several gripes people had over other mailers. .I Dmail 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 .IR dmail . .I Dmail keeps multiple messages per file, fully compatible with standard mail (thus folders are files rather than directories). .PP .I Dmail is not a glorified .IR Mail , 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. .I Dmail provides a mechanism in which you can separate your mail by any partial string in any Field without having to create a folder. The .I select command is usually instantanious, and with aliases, you can switch between mailing lists, personal mail, or anything you wish at a keystroke. Whenever you .I select 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. .PP 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. .I Dmail 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 .IR dmail , you specify what to include. To see the entire header, you use the .I header command from .I dmail. .PP Apart from that, .I dmail gives you set variables, aliases, folder's, and is generally faster than other mail programs. .SH OPTIONS .I Dmail may be executed with several options, putting it in one of two modes. Firstly, when executed with a user-name list, .I dmail 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). .PP dmail -O -v -f [fromfile] -o [tofile] -l rcfile -L -F [field] -F [field]... .TP .B \-O places .I dmail in interactive mode, even if there is no mail to read. .TP .B \-v Places sendmail in verbose mode when you mail, reply, or forward a message. (sets the verbose variable) .TP .B \-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 .TP .B \-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 outfile. Additionally, if both -f and -o are specified without filenames, both the fromfile and the outfile will be set to $HOME/.mbox .TP .B \-l Specify the RC file (default is $HOME/.dmailrc). The RC file consists of a list of .I dmail commands, as if they were typed in. .TP .B \-L Disables sourcing of any RC files .TP .B \-F This option tells .I dmail 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, .I dmail 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 people, however, have spool files of several hundred thousand bytes and would use this option to prevent delays later on. .PP .SH INTERRUPTS .I dmail handles the INTR character. You may use it to break out of listings, types, or anything else without worry of destroying something. .SH COMMAND OVERVIEW .PP The basic commands for .I dmail are as follows (There are, of course, many commands, these are just a few): .I list .I type .I header .I delete .I next .I select .I quit .I exit .I alias .I unalias .I set .I unset and .I mail .PP 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. .PP 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. .PP .I list displays parts of the headers of messages, one message per line, for all messages currently selected. You can set the output format with the .I setlist command. .PP .I type prints the message text of a message. By the way, only header fields specified by the .I setlist command will be printed (default- From: To: Subject:), which is useful. .PP .I header prints out the entire header of a message. .PP .I delete deletes a message or message list (you can say 'delete all' to delete all messages currently selected). .PP .I next goes to the next message and type's or header's it, depending on what last was done (type or header). .PP .I select selects what you want to look at. You can say 'select all', to select all the messages, or selects of the form: .TP select Field match match match , Field match match .... .PP 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, you can select all your personal mail with: .TP select Cc myname , To myname .PP Use the on-line help for more information on the .I select or any other command. .PP .I quit from .I dmail please. Any unread messages stay in your spool (or fromfile), any read messages are placed in your mail-box, any deleted messages are deleted. .PP .I exit from .I dmail please. Nothing is written or altered, exit without changing anything. .I alias is used to alias commands to other commands, or command sequences. For instance, you can alias 'me' to select all personal mail: .TP alias me "select Cc myname , To myname" .PP you can alias a sequence of commands: .TP alias q "select all ; pre all ; quit" .PP 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 .I unalias aliases. .PP .I set a variable to a string. You can also .I unset a variable. Several variables have special meaning, use .I help set from .I dmail for a full explanation. .PP .I mail and .I reply may be used to mail out or reply to messages. .SH BUGS .PP Please send bug reports to: ...!ucbvax!dillon dillon@ucb-vax.berkeley.edu !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'dmail.h'" '(2465 characters)' if test -f 'dmail.h' then echo shar: will not over-write existing file "'dmail.h'" else cat << \!Funky!Stuff! > 'dmail.h' /* * DMAIL.H */ #define MAXTYPE 16 /* Max number of different fields remembered */ #define EXSTART 3 /* Beginning of dynamic fields, rest are wired */ #define MAXLIST 16 /* Maximum # list elements in SETLIST */ #define LONGSTACK 64 /* Maximum # levels for the longjump stack */ #define MAILMODE 0600 /* Standard mail mode for temp. files */ #define MAXFIELDSIZE 4096 /* Maximum handlable field size (& scratch bufs) */ #define LEVEL_SET 0 /* which variable set to use */ #define LEVEL_ALIAS 1 #define LEVEL_MALIAS 2 #define R_INCLUDE 1 /* Include message For DO_REPLY() */ #define R_FORWARD 2 /* Forward message */ #define R_REPLY 3 /* Reply to message */ #define R_MAIL 4 /* Mail from scratch */ #define M_RESET 0 #define M_CONT 1 #define PAGER(Puf) _pager(Puf, 1) /* Auto newline */ #define FPAGER(Puf) _pager(Puf, 0) /* output as is */ #define push_base() (setjmp (env[1 + Longstack]) ? 1 : (++Longstack, 0)) #define pop_base() --Longstack #define push_break() ++Breakstack #define pop_break() --Breakstack #define ST_DELETED 0x0001 /* Status flag.. item has been deleted */ #define ST_READ 0x0002 /* item has been read or marked */ #define ST_STORED 0x0010 /* item has been written */ #define ST_TAG 0x0020 /* item has been taged */ #define ST_SCR 0x0080 /* scratch flag to single out messages */ #include <stdio.h> #include <setjmp.h> struct ENTRY { long fpos; int no; int status; char *from; char *fields[MAXTYPE]; }; static struct FIND { char *search; int len; int notnew; }; extern char *getenv(), *malloc(), *realloc(), *next_word(), *get_field(); extern char *alloca(); extern char *get_var(); extern char *mail_file; extern char *user_name; extern char *output_file; extern char *home_dir; extern char *visual; extern char Buf[]; extern char Puf[]; extern char *av[]; extern int _ls, Longstack, Breakstack; extern int Debug; extern int Entries, Current; extern int Silence; extern int ac; extern FILE *m_fi; extern struct ENTRY *Entry; extern struct FIND Find[]; extern jmp_buf env[]; extern int width[], header[], Listsize; extern int No_load_mail; extern char *S_sendmail; extern int S_page, S_novibreak, S_verbose, S_ask; !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'dmail.help'" '(18432 characters)' if test -f 'dmail.help' then echo shar: will not over-write existing file "'dmail.help'" else cat << \!Funky!Stuff! > 'dmail.help' 'help TOPIC' for more information on a command. Many commands take message numbers or message lists: [msg] is an optionaly specified message number (usually the operation is on the current message if no number is specified) <list> is an optionaly specified message list. Message lists consists of number ranges of the form N -N N- or N-N, and keywords (help keywords) OTHER HELP AVAILABLE: pager sendmail tilde header newmail keywords DMAIL written by Matthew Dillon, U.C. Berkeley -UCF (C) 1985 by Matthew Dillon .pager set page [rows/command] 'page' is a SET variable which determines the type of paging list and display commands will use. If not defined, no filter is used. If a numerical value (i.e. 24) is specified, the page length will be set to that value and a rather stupid, simple internal paging routine will be used. If the variable is set to an alpha-numeric string, output will be piped through that filter. For instance: unset page -no paging set page more -use more filter set page page -use 'page' filter set page > x -redirect to a file (be careful) set page 24 -use internal paging, page length 24 rows. For internal paging, use <return> to continue, or your INTR character to break out .sendmail set sendmail sendmail-path Inform DMAIL as to where the sendmail program is. The default (variable unset) is /usr/lib/sendmail. This variable is useful only for those of us who like to hack-up our own sendmail. .tilde ~ ~user directory expansion In all expressions except those within double quotes, the tilde `~` will be expanded to either your home directory, or the directory of a specified user, depending. Note that '*' and '?' are not expanded by DMAIL, though they will be by any shell commands you execute. It is probably a good idea to use ~ in any aliases, etc... in case you change directories using the 'cd' command. alias resource ~/.dmailrc -example using ~ .header set header filepath Set the location of your header file, which will appended to the scratch mail file before you are placed in the editor (usually vi). set header ~/.header -set header file to ~/.header unset header -no header file .newmail Whenever newmail arrives, it will be automatically incorporated into a running DMAIL. However, to see it, you must 'select all' (or select on anything that would include it). .keywords .range .message Many commands in DMAIL require a range of messages be given. A Range consists of message numbers (3 4 5), message ranges (1-45 -9 9-), and keywords. Keywords select certain messages from the entire SELECTED list: all -All messages tag -All TAGGED messages mark -All MARKED (read) messages deleted -All DELETED messages written -All WRITTEN messages untag -All messages NOT TAGGED unmark -All messages NOT MARKED (i.e. read) undeleted -All messages NOT DELETED unwritten -All messages NOT WRITTEN Only the first three letters need be specified. For instance, the 'all' keyword selects all the messages currently selected. You could select on some subject, say, and the 'delete all'. The message number 0 refers to the last message. the 'undeleted' keyword exists only for completeness; you will probably never use it. .delete DELETE <message list> Mark the specified messages for deletion. They will no longer show up on LISTings, (gaps will appear in message numbering). However, you can still TYPE them, if you remember the message number, and you can always UNDELETE them. Remember that the particular message # you've deleted may be different if you change the SELECT parameters. For example, message #3 selecting 'To' & 'foo' may actually be message #45 when you are selecting ALL (see SELECT). Upon a QUIT, messages marked for deletion are actually deleted from the mail file. .undelete UNDELETE <message list> UNDELETES messages. Without arguments, UNDELETE will restore the last message you deleted. Specifying 'all' (undelete all), will undelete any deleted messages in the currently selected message list. .header HEADER [message] Display the entire header of a message. This does not cause the message to be marked 'read'. TYPE, on the other hand, only displays header information specified by SETLIST. .type TYPE [message] Type the text of a message. Only header fields defined by SETLIST are displayed. Otherwise, only the text is displayed. This marks the particular message as 'read', and also makes that message the current message. .echo ECHO [string] Echo the given string onto the screen. .go GO # go to a message, don't type it out or anything. Remember that you can go to the last message by using the message # 0. By placing a keyword (help range), you can go to the first TAGGED message, etc... .reply .Reply REPLY Reply to the current letter. There are two forms of 'reply'. The first does not include the senders original letter, the second does. In both cases, Dmail will place you in VI, with the To:, Cc:, and subject lines filled out. The second form is 'Reply', with an upper case 'R'. This form includes the sender's message shifted to the right with '>'s on the left hand side. See FORWARD for another method of replying to mail. In any case, you may get the sender's letter by reading the file '#' from VI. That sequence would be ':r\\#' See MAIL for more information on VI .forward FORWARD [user user user....] Forward the current message to a list of users. The sender's entire message is placed in the text portion. The To: field will contain the user's named above, and the Subject: field will contain a 'Fo:' (you append your own subject) See MAIL for more information on VI .mail MAIL [user user user user] Mail to [users]. You are given a VI to work from, and may modify any of the header fields. the From: field is inserted automatically by SENDMAIL, but you can overide it if you wish. Quitting out of VI without writing the output file will cause an abort, and no mail will be sent. Additionaly, you may use the 'vibreak' variable to enable your INTR character (usually CTL-C) to break you out of VI. When modifying the users list in To and Cc fields, remember that they should be all comma delimited, or none comma delimited. .select SELECT ALL SELECT Field match match match match... SELECT Field !match SELECT Field match match match , Field match , ..... Select what you want to look at. Select will take the field header you supply and attempt to match arguments with that field in the mail file. You can then work on the selected material as if the rest of your mail didn't exist. For instance, 'select To dillon', will select all messages addressed to you. Note that case is checked for the FIELD-HEADER, but not for arguments, so the latter will also find anything addressed to Dillon or DILLON. You only have to give a partial match, so 'select To di' would work just as well. Select then, allows you to quickly find what you want even though you may have 12000 messages (though it may take a while with that many) You may also specify what you DON'T want to select on: select To !foo will select all letters not addressed to 'foo'. You may select on any field you wish. At any time, you may say 'select ALL' to select the entire message list. Use RESELECT to select on the current message list. SELECT always selects from the entire message-list select Cc hack , To hack will select any mail with Cc or To fields containing hack. You may have as many comma operators as you wish. The comma must be a field of its own (spaces on either side) .reselect RESELECT ALL RESELECT Field match match match match... RESELECT Field !match SEE SELECT. Reselect allows you to CONTINUE to narrow down a topic or whatever. It will select on the current message list (which you have already narrowed down with SELECT or RESELECT). .defer DEFER Deselects any marked messages .. messages marked as 'read'. This is as if you did a RESELECT on all unread messages in the current select field. Thus, the messages will be renumbered. To see these messages again, you must use SELECT. .list LIST <message list> LIST -Lists all selected messages Display header information on a message as specified by SETLIST, in a one line per message format. The default lists ALL messages. Leftword flags: r -indicates message has been read. Message will be moved to the destination file on QUIT > -indicates message is the current message w -indicates message has been written to a file. Message will be deleted from source file on QUIT T -indicates message has been taged by the user .next NEXT Execute TYPE or HEADER for the next message, depending on which of TYPE or HEADER was last executed by you ._next _NEXT Go to next message, do not print it out. .back BACK Execute TYPE or HEADER for the previous message, depending on which of TYPE or HEADER was last executed by you ._back _BACK Go to previous message, do not print it out. .dt DT Delete current message, type (or header) next message. This command will warn you when you reach the end of the message list. References: DELETE and NEXT .set SET [variable [string]] With no arguments, SET prints out all currently active variables. Note that this variable list is a different list than the ALIAS list. With one argument, the specified variable is displayed if it exists, or created if it doesn't. With more than one argument, the specified variable is set to the specified string. Variables may be references on the command line by $variable . The variable's contents will replace the reference. Unlike aliases, however, variable substitutions may take place anywhere on the command line rather than substitute just the command name. Note also that if you use a $ substitution for an argument of a command, the entire variable's contents is ONE argument (i.e. if a = "b c d", and you say something like: 'unset $a', it would attempt to unset a single variable whos name is "b c d". There are several reserved SET variables, which define options in DMAIL. Changing these will modify the option: page set paging on or to a specific command (i.e. more) sendmail set the path to the sendmail program vibreak enable your INTR character even when in VI. verbose reflects verbose option to sendmail header header file to append to any messages you send. page This variable determines what kind of paging is used for LIST, TYPE, and HEADER commands. If the variable does not exist, paging is turned off. If set to null (no string), an internal paging routine is used. If set to a value, an internal paging routine is used using the value as the page length. the 'page' variable can also be set to a command, such as 'more' or 'page', in which case, the output is piped through those commands: set page Turn paging on (internal page routine) set page 25 Internal page routine... 25 rows/screen set page more Use 'more' command to pipe output through set page page Use 'page' command to pipe output through you could also conceviably say: 'set page cat > x', or 'set page cat | lpr', but be very careful. sendmail This variable will redirect DMAIL as to where the mailer program is. The mailer program must be compatible with /usr/lib/sendmail which is the default used if the 'sendmail' variable isn't set to anything: set sendmail bin/mysendmail vibreak This variable, when set, allows the INTR character to abort a reply, mail, or forward command. Otherwise, if this variable is not present, INTR will not abort the above commands. verbose This variable, when set, causes the -v flag to be sent to sendmail. In addition, DMAIL will wait for sendmail to complete before returning your prompt. header The file specified by this variable will be appended onto the temp vi files in reply and mail. The file is appended before you go into vi, so when you do, what you see is still what you get. .alias ALIAS [variable [string]] Create an alias for a command. With no arguments, ALIAS will display all active aliases. With one argument, a particular alias is displayed (if it exists), or defined (if it did not previously exist). With more than one argument, the particular alias is set to the string list specified. alias alias hack "select From hacker , To hacker , Cc hacker" alias bye quit alias stuff "setlist 60 To ; list" Usually, any arguments following the alias are appended to the expansion. However, you can place such arguments inside the alias somewhere by using the following construction: alias myecho "%var echo $var ; list" myecho hello there ---> echo hello there ; list to unalias an alias, use the UNALIAS command. .unset .unalias UNSET var var var... UNALIAS var var var... Eradicate variables or aliases from memory. .setlist SETLIST [-s] [columns] Field [columns] Field ... -s prevents display of the list. Set the list format used for LIST and TYPE. The optional [columns] indicates how many columns to allocate for the Field specified. The Field can be a partial match. However, case is observed: setlist 18 Fro 38 Sub 10 To 0 Dat 18 columns for the From: field, etc... when TYPEing messages, the [columns] is ignored, and each field is printed in its entirety. Note that 0 columns have been allocated for the Date: field. Therefore, the Date: field will not show up on the LIST command, but will show up in the TYPE command. .cd CD PATH cd, as in csh. Changes your base directory. You can use the shell escape '! pwd' to get your current working directory. .source SOURCE file Source a file. The file is read in an executed in sequence. .preserve PRE <message list> PRESERVE messages. A message is MARKED if it has been read (has an 'r' flag from the LIST command). Marked messages are moved from your readfile into your outfile upon a QUIT. If you are reading and writing to the same file (i.e. from your mbox to your mbox), the 'r' flag has no effect. However, if you are reading from your spool file, and want to keep read messages in your spool (that is, not move them to your mbox), you want to PRESERVE them. This command simply unmarks them, so they appear not to have been read. .mark MARK <message list> Mark messages specified as being already 'read'. Remember that if you executed DMAIL without a -f option, any message 'read' at the time you quit will be moved to MBOX (or file specified by -o) .tag .untag TAG <list> UNTAG <list> The TAG command allow you to flag any message. You can tag a set of messages, then reference them all at once. For instance, if you tag interesting messages as you glance at them, you may then write them all to a file by 'write filename tag', or list them with 'list tag'. Alternately, you could delete all your taged messages with a single delete command 'delete tag'. The 'tag' operand works in the same way as the 'all' operand, except it works only on taged messages. UNTAG will untag a particular message in your message list. For instance, to untag any taged messages in the entire message list, you would: select all untag all OR untag tag Note that 'untag all' and 'untag tag' have the same effect. .write WRITE file <message list> Write the given messages or the current message to a file. The file file is appended to. Remember that you may specify 'all' to write all messages in the current select field to the file. Messages will be marked as having been writen, and will be deleted from the mail file when you 'quit'. However, you may cause them to be kept in the mail file by UNdeleting the messages (i.e. undelete all) You can also TAG the messages you want to write, and say 'write file tag' to write to the file all taged messages. .! ! [shell command] Give yourself a shell or execute a shell command. The shell forked is that specified by your SHELL enviroment variable, or /bin/sh if there is no SHELL enviroment variable. .x X (EXIT) EXIT out of DMAIL without changing any files. Usually, one exits with QUIT, which would cause deleted messages to disappear, and TYPEd messages to go to MBOX (if you did not use the -f option with DMAIL). If your outfile is the same as your infile, reading a message does not effect anything. .quit QUIT Quit out of DMAIL. Delete any messages that were marked for deletion and if you executed DMAIL on /usr/spool/mail/ (default), any mail marked 'read' will be placed in MBOX in your home directory .xswitch .qswitch XSWITCH fromfile [tofile] QSWITCH fromfile [tofile] Switch to a different set of files. XSWITCH doesn't modify your old from and to files before switching, QSWITCH works as if you had QUIT stuff before switching to another set of files. If no [tofile] is specified, the new tofile will be the same as the fromfile you specify. .help .? HELP [topic] Give me help on a topic .dmail COMMAND LINE OPTIONS FOR DMAIL dmail -O [-l rcfile] -f [file] -o [file] -F field -F field -F field ... Default conditions: Home directory gotten from password entry User gotten from password entry Visual editor set to /usr/ucb/vi VI BREAKOUT enabled READ file is /usr/spool/mail/$USER WRITE file is $HOME/mbox From:, To:, and Subject: fields will be loaded into memory. HOME enviroment variable becomes home directory USER enviroment variable becomes user name VISUAL enviroment variable becomes editor used -O Go into interactive mode, even if there is no mail to read. -f [from file] Specify spool file to get mail from. If no file Argument is given, $HOME/MBOX is used. -o [to file] Specify file to write to, If no Argument is given, $HOME/.MBOX is used. Note that the default without -o is $HOME/MBOX -f -o With no file arguments causes both READ and WRITE files to be $HOME/.MBOX -F field Append this field to those which will be used on initial load. If, During usage of the program you specify a field which is not in memory, DMAIL will be forced to re-load the entire spool file, which can take a long time if you have more than 64K in the file -l rcfile Use this as the rc file rather than .dmailrc -L Do not source the rc file on boot !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'dmkhelp.c'" '(1094 characters)' if test -f 'dmkhelp.c' then echo shar: will not over-write existing file "'dmkhelp.c'" else cat << \!Funky!Stuff! > 'dmkhelp.c' /* * DMKHELP.C * * * Matthew Dillon, 6 December 1985 * * (C) 1985 Matthew Dillon * * Standalone C source. * * Takes the file DMAIL.HELP (or that specified), and puts quotes and * commas around each line, the output to stdout. Used by Makefile to * place the help file on line (by making it a static array). See the * Makefile. * */ #include <stdio.h> #define HELPC "dmail.help" static char buf[1024]; main(argc, argv) char *argv[]; { FILE *fi; char *ptr; int len; register int i; if (argc == 1) fi = fopen (HELPC, "r"); else fi = fopen (argv[1], "r"); if (fi == NULL) { puts ("CANNOT OPEN"); exit (1); } while (fgets (buf, 1024, fi)) { len = strlen(buf) - 1; buf[len] = '\0'; putchar ('\"'); for (i = 0; i < len; ++i) { if (buf[i] == '\"') { putchar ('\\'); putchar ('\"'); } else { putchar (buf[i]); } } puts ("\","); } puts ("NULL"); fclose (fi); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'do_lists.c'" '(4118 characters)' if test -f 'do_lists.c' then echo shar: will not over-write existing file "'do_lists.c'" else cat << \!Funky!Stuff! > 'do_lists.c' /* * DO_LISTS.C * * Matthew Dillon, 6 December 1985 * * * (C) 1985 Matthew Dillon * * Global Routines: DO_SETLIST() * DO_LIST() * DO_SELECT() * DO_DEFER() * * Static Routines: None. * * LIST associated commands. * */ #include <stdio.h> #include "dmail.h" do_setlist(str) char *str; { int i, fw, idx, localecho = 1; int sac = 1; push_break(); if (strcmp (av[sac], "-s") == 0) { ++sac; localecho = 0; } hold_load(); if (ac > sac) { Listsize = 0; for (i = sac; i < ac; ++i) { fw = atoi(av[i]); if (fw > 4096) fw = 4096; if (fw < 0 || (*av[i] < '0' || *av[i] > '9')) fw = 20; else ++i; if (i >= ac) continue; idx = get_extra_ovr (av[i]); if (idx < 0) { printf ("To many entries, cannot load: %s\n", av[i]); fflush (stdout); continue; } header[Listsize] = idx; width[Listsize] = fw; ++Listsize; } } nohold_load(); pop_break(); if (localecho) { puts (""); printf ("Entry Width Field\n\n"); for (i = 0; i < Listsize; ++i) printf ("%-6d %-5d %s\n", i, width[i], Find[header[i]].search); puts (""); } return (1); } /* * Pre-position # 0 > Current article * 1 - Read already * */ do_list() { int i, j; int start = 0; int end = Entries; static char curr[10] = { " " }; if (ac == 1) { av[1] = "all"; ++ac; } if (push_base()) { push_break(); pop_base(); PAGER (-1); pop_break(); return (-1); } PAGER (0); FPAGER ("\n "); for (j = 0; j < Listsize; ++j) { if (width[j]) { sprintf (Puf, "%-*.*s", 2 + width[j], 2 + width[j], Find[header[j]].search); FPAGER (Puf); } } FPAGER ("\n"); rewind_range (1); while (i = get_range()) { i = indexof(i); if (Entry[i].no && ((Entry[i].status & ST_DELETED) == 0)) { curr[0] = (Entry[i].status & ST_TAG) ? 'T' : ' '; curr[1] = (i == Current) ? '>' : ' '; curr[2] = (Entry[i].status & ST_READ) ? 'r' : ' '; curr[3] = (Entry[i].status & ST_STORED) ? 'w' : ' '; sprintf (Puf, "%s%-3d", curr, Entry[i].no); FPAGER (Puf); for (j = 0; j < Listsize; ++j) { if (width[j]) { sprintf(Puf, " %-*.*s", width[j], width[j], Entry[i].fields[header[j]]); FPAGER (Puf); } } FPAGER ("\n"); } } FPAGER ("\n"); PAGER (-1); pop_base(); return (1); } do_select(str, mode) char *str; { int ret = 1; int localecho = 1; int avi = 1; int scr; char *nulav[3]; nulav[0] = ""; nulav[1] = ""; nulav[2] = NULL; if (strcmp (av[avi], "-s") == 0) { localecho = 0; ++avi; --ac; } switch (ac) { case 2: if (localecho) puts ("SELECT ALL"); ret = m_select (nulav, mode); break; case 1: break; default: ret = m_select (av + avi, mode); scr = indexof(0); if (scr > 0 && localecho) printf ("%d Entries selected\n", Entry[scr].no); break; } if (ret < 0 && localecho) { puts ("Null field"); return (-1); } return (1); } do_defer() { register int i, j; push_break(); j = 1; for (i = 0; i < Entries; ++i) { if (Entry[i].no) Entry[i].no = (Entry[i].status & ST_READ) ? 0 : j++; } pop_break(); return (1); } !Funky!Stuff! fi # end of overwriting check # End of shell archive exit 0