sources-request@mirror.TMC.COM (03/09/87)
Submitted by: Dave Taylor <hplabs!taylor> Mod.sources: Volume 9, Issue 4 Archive-name: elm2/Part04 #! /bin/sh # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # If this archive is complete, you will see the message: # "End of archive 4 (of 19)." # Contents: Overview filter/filter.c filter/utils2.c src/aliaslib.c # src/edit.c src/file_utils.c src/mailtime.c src/opt_utils.c # src/sort.c src/syscall.c PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo shar: Extracting \"Overview\" \(5333 characters\) if test -f Overview ; then echo shar: Will not over-write existing file \"Overview\" else sed "s/^X//" >Overview <<'END_OF_Overview' X An Overview of the Elm Mail System X ---------------------------------- X XIntroduction X X This file discusses the functionality of the Elm mail system Xand explains some of the motivation behind the creation and of various Xfeatures. X X1. What is Elm? X X Currently on Unix, there seems to be a preponderence of line-oriented Xsoftware. This is most unfortunate as most of the software on Unix tends to Xbe pretty darn hard to use! I believe that there is more than a slight Xcorrelation between the two, and, since I was myself having problems using X"mailx" with high-volume mail, I created a new mail system. X X In the lingo of the mail guru, Elm is a "User Agent" system, it's Xdesigned to run with "sendmail" or "/bin/rmail" (according to what's on Xyour system) and is a full replacement of programs like "/bin/mail" and X"mailx". The system is more than just a single program, however, and Xincludes programs like "from" to list a 'table of contents' of your Xmail, "printmail" to quickly paginate mail files (to allow 'clean' Xprintouts), and "autoreply", a systemwide daemon that can autoanswer Xmail for people while they're on vacation without having multiple Xcopies spawned on the system. X X2. What's New about Elm? X X The most significant difference between Elm and earlier mail Xsystems is that Elm is screen-oriented. Upon further use, however, Xusers will find that Elm is also quite a bit easier to use, and quite Xa bit more "intelligent" about sending mail and so on. For example, Xsay you're on "usenet" and receive a message from someone on the XARPANET. The sender also "cc'd" another person on ARPA. With Elm Xyou can simply G)roup reply and it will build the correct return Xaddresses. X X There are lots of subtleties like that in the program, most of Xwhich you'll probably find when you need them. X X3. What systems does it work on? X X Elm was originally written on HP-UX, HP's proprietary version Xof Bell system V, with a little BSD thrown in. Since then, it has been Xported to Bell, Berkeley, Sun, UTS and the Pyramid and should run on Xall these systems without any modifications (if there turn out to be Xmodifications, please notify the author as soon as possible). X X Some people have expressed interest in porting the mail system Xto Xenix. If it is indeed 100% system V compatible it should be rather Xtrivial... X X4. Does it obey existing mail standards? X X Yes! That's another of the basic reasons the program was Xoriginally written! To ensure that the date field, the "From:" line Xand so on were all added in the correct format. The program is 100% Xcorrect according to the RFC-822 electronic mail header protocol Xguide. X X5. What were the main motivating factors? X X The first two I've already mentioned, but here's a (somewhat Xpartial) list; X X - To have a mail system that exploited the CRT instead of X assuming I'm on a teletype. X X - To have a mailer that was 100% correct when dealing with X network mail (ie RFC-822). X X - To create a system that needed no documentation for the X casual user, but was still powerful enough and sophisticated X enough for a mail expert. X X - To write a "significant" piece of software as a learning X experience (I admit it!) X X - To find out how reasonable it is to try to modify a program X to meet the expectations of the users, rather than vice-versa. X X - To basically correct some of the dumb things that the current X mailers do, like letting you send mail to addresses that it X could trivially figure out are going to result in 'dead.letter' X X - To tie in intimately with the pathalias program output, and X allow users to specify machine!user or user@machine and have X the COMPUTER do the work of figuring out addresses... X X6. Is it reliable? X X The mailer, in various incarnations, has logged literally Xthousands upon thousands of hours without any problems that aren't Xnow corrected. As new problems arise they're dealt with in as Xrapid a manner as possible... X X7. What should I do now? X X The first step would be to install the mail system and have Xthe "elm" mailbox/alias expand to my email address (hplabs!taylor). XThen, once it's all up and running, drop me a line letting me know Xthat your site is running the system (bookkeeping) and what you and Xyour site think of it. X X REMEMBER: The product is evolving so if you'd like to have a Xsomething change, or have something new added, LET ME KNOW!!! I'd Xmuch rather make the change myself than start getting change reports Xmailed from around the world!! X X8. Disclaimers X X The author of this program will deny all liability for any Xdamages, either real or imagined, due to the execution of this program Xor anything related to either the software or the system. Furthermore, Xthe entire system and all source within, including the presentation Xscreens and commands, are legally copyrighted by the author, and while Xthey can be used, and abused for public domain systems, will be in Xviolation of the law if used in systems or programs sold for profit. X X By installing the mailer or even extracting it from the network, Xyou are agreeing to the above disclaimer. X X9. Finally X X I think it's a good program, and I can cite at least 75 people Xwho would (begrudgingly, I'm sure) agree. You should most certainly Xinstall the program and try it!! X X X -- Dave Taylor X X hplabs!taylor X XMarch 13th, 1986 END_OF_Overview if test 5333 -ne `wc -c <Overview`; then echo shar: \"Overview\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"filter/filter.c\" \(5430 characters\) if test -f filter/filter.c ; then echo shar: Will not over-write existing file \"filter/filter.c\" else sed "s/^X//" >filter/filter.c <<'END_OF_filter/filter.c' X/** filter.c **/ X X/** This program is used as a filter within the users ``.forward'' file X and allows intelligent preprocessing of mail at the point between X when it shows up on the machine and when it is actually put in the X mailbox. X X The program allows selection based on who the message is FROM, TO, or X what the subject is. Acceptable actions are for the program to DELETE X the message, SAVE the message in a specified folder, FORWARD the message X to a specified user, SAVE the message in a folder, but add a copy to the X users mailbox anyway, or simply add the message to the incoming mail. X X Filter also keeps a log of what it does as it goes along, and at the X end of each `quantum' mails a summary of actions, if any, to the user. X X Uses the files: $HOME/.filter for instructions to this program, and X $HOME/.filterlog for a list of what has been done since last summary. X X (C) Copyright 1986, Dave Taylor X**/ X X X#include <stdio.h> X#include <pwd.h> X#include <ctype.h> X#ifdef BSD X# include <sys/time.h> X#else X# include <time.h> X#endif X#include <fcntl.h> X X#include "defs.h" X X#define MAIN_ROUTINE /* for the filter.h file, of course! */ X#include "filter.h" X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X FILE *fd; /* for output to temp file! */ X struct passwd *passwd_entry, X *getpwuid(); /* for /etc/passwd */ X char filename[SLEN], /* name of the temp file */ X buffer[LONG_SLEN]; /* input buffer space */ X int in_header = TRUE, /* for header parsing */ X in_to = FALSE, /* are we on 'n' line To: ? */ X c; /* var for getopt routine */ X X /* first off, let's get the info from /etc/passwd */ X X if ((passwd_entry = getpwuid(getuid())) == NULL) X leave("Cannot get password entry for this uid!"); X X strcpy(home, passwd_entry->pw_dir); X strcpy(username, passwd_entry->pw_name); X X gethostname(hostname, sizeof(hostname)); X X /* now parse the starting arguments... */ X X while ((c = getopt(argc, argv, "aclnrSsv")) > 0) { X switch (c) { X case 'a' : audible = TRUE; break; X case 'c' : clear_logs = TRUE; break; X case 'l' : log_actions_only = TRUE; break; X case 'r' : if (get_filter_rules() == -1) X fprintf(stderr,"Couldn't get rules!\n"); X else X print_rules(); X exit(0); X case 's' : if (get_filter_rules() == -1) exit(1); X show_summary(); exit(0); X X case 'S' : long_summary = TRUE; X show_summary(); exit(0); X X case 'n' : show_only = TRUE; break; X case 'v' : verbose = TRUE; break; X } X } X X if (c < 0) { X fprintf(stderr, "Usage: | filter [-nrv]\n\ or: filter [-c] -s\n"); X exit(1); X } X X /* next, create the tempfile and save the incoming message */ X X sprintf(filename, "%s.%d", filter_temp, getpid()); X X if ((fd = fopen(filename,"w")) == NULL) X leave("Cannot open temporary file!"); X X while (gets(buffer) != NULL) { X if (in_header) { X X if (! whitespace(buffer[0])) X in_to = FALSE; X X if (the_same(buffer, "From ")) X save_from(buffer); X else if (the_same(buffer, "Subject:")) X save_subject(buffer); X else if (the_same(buffer, "To:")) { X in_to++; X save_to(buffer); X } X else if (the_same(buffer, "X-Filtered-By:")) X already_been_forwarded++; /* could be a loop here! */ X else if (strlen(buffer) < 2) X in_header = 0; X else if (whitespace(buffer[0]) && in_to) X strcat(to, buffer); X } X X fprintf(fd, "%s\n", buffer); /* and save it regardless! */ X fflush(fd); X lines++; X } X X fclose(fd); X X /** next let's see if the user HAS a filter file, and if so what's in X it (and so on) **/ X X if (get_filter_rules() == -1) X mail_message(username); X else { X switch (action_from_ruleset()) { X X case DELETE : if (verbose) X printf("%sfilter (%s): Message deleted\n", X BEEP, username); X log(DELETE); break; X X case SAVE : if (save_message(rules[rule_choosen].argument2)) { X mail_message(username); X log(FAILED_SAVE); X } X else X log(SAVE); break; X X case SAVECC : if (save_message(rules[rule_choosen].argument2)) X log(FAILED_SAVE); X else X log(SAVECC); X mail_message(username); break; X X case FORWARD: mail_message(rules[rule_choosen].argument2); X log(FORWARD); break; X X case EXEC : execute(rules[rule_choosen].argument2); X log(EXEC); break; X X case LEAVE : mail_message(username); X log(LEAVE); break; X } X } X X (void) unlink(filename); /* remove the temp file, please! */ X exit(0); X} X Xsave_from(buffer) Xchar *buffer; X{ X /** save the SECOND word of this string as FROM **/ X X register int i, j; X X for (i=0; buffer[i] != ' '; i++) ; /* get to word */ X X for (i++, j=0; buffer[i] != ' ' && i < strlen(buffer); i++) X from[j++] = buffer[i]; /* copy it and */ X X from[j++] = '\0'; /* Null terminate! */ X} X Xsave_subject(buffer) Xchar *buffer; X{ X /** save all but the word "Subject:" for the subject **/ X X register int skip = 8; /* skip "Subject:" initially */ X X while (buffer[skip] == ' ') skip++; X X strcpy(subject, (char *) buffer + skip); X} X Xsave_to(buffer) Xchar *buffer; X{ X /** save all but the word "To:" for the to list **/ X X register int skip = 3; /* skip "To:" initially */ X X while (buffer[skip] == ' ') skip++; X X strcpy(to, (char *) buffer + skip); X} END_OF_filter/filter.c if test 5430 -ne `wc -c <filter/filter.c`; then echo shar: \"filter/filter.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"filter/utils2.c\" \(4993 characters\) if test -f filter/utils2.c ; then echo shar: Will not over-write existing file \"filter/utils2.c\" else sed "s/^X//" >filter/utils2.c <<'END_OF_filter/utils2.c' X/** utils2.c **/ X X/** More miscellanous utilities for the filter program and such... X X (C) Copyright 1986, Dave Taylor X**/ X X#include <stdio.h> X X#ifdef BSD X# include <pwd.h> X#endif X X#ifdef NEED_GETHOSTNAME X# include <sys/utsname.h> X#endif X X#ifdef NEED_GETHOSTNAME X Xgethostname(hostname,size) /* get name of current host */ Xint size; Xchar *hostname; X{ X /** Return the name of the current host machine. UTS only **/ X X /** This routine compliments of Scott McGregor at the HP X Corporate Computing Center **/ X X int uname(); X struct utsname name; X X (void) uname(&name); X (void) strncpy(hostname,name.nodename,size-1); X hostname[size] = '\0'; X X} X X#endif X X#ifdef BSD X X/** some supplementary string functions for Berkeley Unix systems **/ X Xstrspn(source, keys) Xchar *source, *keys; X{ X /** This function returns the length of the substring of X 'source' (starting at zero) that consists ENTIRELY of X characters from 'keys'. This is used to skip over a X defined set of characters with parsing, usually. X **/ X X register int loc = 0, key_index = 0; X X while (source[loc] != '\0') { X key_index = 0; X while (keys[key_index] != source[loc]) X if (keys[key_index++] == '\0') X return(loc); X loc++; X } X X return(loc); X} X Xstrcspn(source, keys) Xchar *source, *keys; X{ X /** This function returns the length of the substring of X 'source' (starting at zero) that consists entirely of X characters NOT from 'keys'. This is used to skip to a X defined set of characters with parsing, usually. X NOTE that this is the opposite of strspn() above X **/ X X register int loc = 0, key_index = 0; X X while (source[loc] != '\0') { X key_index = 0; X while (keys[key_index] != '\0') X if (keys[key_index++] == source[loc]) X return(loc); X loc++; X } X X return(loc); X} X Xchar *strtok(source, keys) Xchar *source, *keys; X{ X /** This function returns a pointer to the next word in source X with the string considered broken up at the characters X contained in 'keys'. Source should be a character pointer X when this routine is first called, then NULL subsequently. X When strtok has exhausted the source string, it will X return NULL as the next word. X X WARNING: This routine will DESTROY the string pointed to X by 'source' when first invoked. If you want to keep the X string, make a copy before using this routine!! X **/ X X register int last_ch; X static char *sourceptr; X char *return_value; X X if (source != NULL) X sourceptr = source; X X if (*sourceptr == '\0') X return(NULL); /* we hit end-of-string last time!? */ X X sourceptr += strspn(sourceptr, keys); /* skip leading crap */ X X if (*sourceptr == '\0') X return(NULL); /* we've hit end-of-string */ X X last_ch = strcspn(sourceptr, keys); /* end of good stuff */ X X return_value = sourceptr; /* and get the ret */ X X sourceptr += last_ch; /* ...value */ X X if (*sourceptr != '\0') /* don't forget if we're at END! */ X sourceptr++; /* and skipping for next time */ X X return_value[last_ch] = '\0'; /* ..ending right */ X X return((char *) return_value); /* and we're outta here! */ X} X Xchar *strchr(buffer, ch) Xchar *buffer, ch; X{ X /** Returns a pointer to the first occurance of the character X 'ch' in the specified string or NULL if it doesn't occur **/ X X char *address; X X address = buffer; X X while (*address != ch) { X if (*address == '\0') X return (NULL); X address++; X } X X return ( (char *) address); X} X X#endif X X#ifndef NULL X# define NULL 0 X#endif X X#define DONE 0 X#define ERROR -1 X Xchar *optional_arg; /* optional argument as we go */ Xint opt_index; /* argnum + 1 when we leave */ X Xint _indx = 1, _argnum = 1; X Xint Xgetopt(argc, argv, options) Xint argc; Xchar *argv[], *options; X{ X /** Returns the character argument next, and optionally instantiates X "argument" to the argument associated with the particular option X **/ X X char *word, *strchr(); X X if (_argnum >= argc) { /* quick check first - no arguments! */ X opt_index = argc; X return(DONE); X } X X if (_indx >= strlen(argv[_argnum]) && _indx > 1) { X _argnum++; X _indx = 1; /* zeroeth char is '-' */ X } X X if (_argnum >= argc) { X opt_index = _argnum; /* no more args */ X return(DONE); X } X X if (argv[_argnum][0] != '-') { X opt_index = _argnum; X return(DONE); X } X X word = strchr(options, argv[_argnum][_indx++]); X X if (word == NULL) X return(ERROR); /* Sun compatibility */ X X if (word == NULL || strlen(word) == 0) X return(ERROR); X X if (word[1] == ':') { X X /** Two possibilities - either tailing end of this argument or the X next argument in the list **/ X X if (_indx < strlen(argv[_argnum])) { /* first possibility */ X optional_arg = (char *) (argv[_argnum] + _indx); X _argnum++; X _indx = 1; X } X else { /* second choice */ X if (++_argnum >= argc) X return(ERROR); /* no argument!! */ X X optional_arg = (char *) argv[_argnum++]; X _indx = 1; X } X } X X return((int) word[0]); X} END_OF_filter/utils2.c if test 4993 -ne `wc -c <filter/utils2.c`; then echo shar: \"filter/utils2.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"src/aliaslib.c\" \(4735 characters\) if test -f src/aliaslib.c ; then echo shar: Will not over-write existing file \"src/aliaslib.c\" else sed "s/^X//" >src/aliaslib.c <<'END_OF_src/aliaslib.c' X/** aliaslib.c **/ X X/** Library of functions dealing with the alias system... X X (C) Copyright 1986 Dave Taylor X **/ X X#include "headers.h" X Xchar *expand_group(), *get_alias_address(), *expand_system(); Xchar *get_token(), *strpbrk(); Xlong lseek(); X Xchar *get_alias_address(name, mailing, depth) Xchar *name; Xint mailing, depth; X{ X /** return the line from either datafile that corresponds X to the specified name. If 'mailing' specified, then X fully expand group names. Depth specifies the nesting X depth - the routine should always initially be called X with this equal 0. Returns NULL if not found **/ X X static char buffer[VERY_LONG_STRING]; X int loc; X X if (strlen(name) == 0) X return( (char *) NULL); X X if (! read_in_aliases) { X read_alias_files(); X read_in_aliases = TRUE; X } X X if (user_files) X if ((loc = find(name, user_hash_table, MAX_UALIASES)) >= 0) { X lseek(user_data, user_hash_table[loc].byte, 0L); X get_line(user_data, buffer); X if (buffer[0] == '!' && mailing) X return(expand_group(buffer, depth)); X else if (strpbrk(buffer,"!@:") != NULL) /* has a hostname */ X#ifdef DONT_TOUCH_ADDRESSES X return((char *) buffer); X#else X return(expand_system(buffer, TRUE)); X#endif X else X return((char *) buffer); X } X X if (system_files) X if ((loc = find(name, system_hash_table, MAX_SALIASES)) >= 0) { X lseek(system_data, system_hash_table[loc].byte, 0L); X get_line(system_data, buffer); X if (buffer[0] == '!' && mailing) X return(expand_group(buffer, depth)); X else if (strpbrk(buffer,"!@:") != NULL) /* has a hostname */ X#ifdef DONT_TOUCH_ADDRESSES X return((char *) buffer); X#else X return(expand_system(buffer, TRUE)); X#endif X else X return((char *) buffer); X } X X return( (char *) NULL); X} X Xchar *expand_system(buffer, show_errors) Xchar *buffer; Xint show_errors; X{ X /** This routine will check the first machine name in the given path X (if any) and expand it out if it is an alias...if not, it will X return what it was given. If show_errors is false, it won't X display errors encountered... X **/ X X dprint2(6, "expand_system(%s, show-errors=%s)\n", buffer, X onoff(show_errors)); X findnode(buffer, show_errors); X X return( (char *) buffer); X} X Xchar *expand_group(members, depth) Xchar *members; Xint depth; X{ X /** Given a group of names separated by commas, this routine X will return a string that is the full addresses of each X member separated by spaces. Depth is an internal counter X that keeps track of the depth of nesting that the routine X is in...it's for the get_token routine! **/ X X static char buffer[VERY_LONG_STRING]; X char buf[LONG_STRING], *word, *address, *bufptr; X char *strcpy(); X X strcpy(buf, members); /* parameter safety! */ X if (depth == 0) buffer[0] = '\0'; /* nothing in yet! */ X bufptr = (char *) buf; /* grab the address */ X depth++; /* one deeper! */ X X while ((word = get_token(bufptr, "!, ", depth)) != NULL) { X if ((address = get_alias_address(word, 1, depth)) == NULL) { X if (! valid_name(word)) { X dprint2(3, "Encountered illegal address %s (%s)\n", X word, "expand_group"); X error1("%s is an illegal address!", word); X return( (char *) NULL); X } X else if (strcmp(buffer, word) != 0) X sprintf(buffer, "%s%s%s", buffer, X (strlen(buffer) > 0)? ", ":"", word); X } X else if (strcmp(buffer, address) != 0) X sprintf(buffer,"%s%s%s", buffer, X (strlen(buffer) > 0)? ", ":"", address); X X bufptr = NULL; X } X X return( (char *) buffer); X} X Xint Xfind(word, table, size) Xchar *word; Xstruct alias_rec table[]; Xint size; X{ X /** find word and return loc, or -1 **/ X register int loc; X X if (strlen(word) > 20) { X dprint2(3, "Too long alias name entered [%s] (%s)\n", word, "find"); X error1("Bad alias name: %s. Too long.\n", word); X return(-1); X } X X loc = hash_it(word, size); X X while (strcmp(word, table[loc].name) != 0) { X if (table[loc].name[0] == '\0') X return(-1); X loc = (loc + 1) % size; X } X X return(loc); X} X Xint Xhash_it(string, table_size) Xchar *string; Xint table_size; X{ X /** compute the hash function of the string, returning X it (mod table_size) **/ X X register int i, sum = 0; X X for (i=0; string[i] != '\0'; i++) X sum += (int) string[i]; X X return(sum % table_size); X} X Xget_line(fd, buffer) Xint fd; Xchar *buffer; X{ X /* Read from file fd. End read upon reading either X EOF or '\n' character (this is where it differs X from a straight 'read' command!) */ X X register int i= 0; X char ch; X X while (read(fd, &ch, 1) > 0) X if (ch == '\n' || ch == '\r') { X buffer[i] = 0; X return; X } X else X buffer[i++] = ch; X} END_OF_src/aliaslib.c if test 4735 -ne `wc -c <src/aliaslib.c`; then echo shar: \"src/aliaslib.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"src/edit.c\" \(5274 characters\) if test -f src/edit.c ; then echo shar: Will not over-write existing file \"src/edit.c\" else sed "s/^X//" >src/edit.c <<'END_OF_src/edit.c' X/** edit.c **/ X X/** This routine is for allowing the user to edit their current mailbox X as they wish. X X (C) Copyright 1986 Dave Taylor X**/ X X#include "headers.h" X#include <errno.h> X#include <sys/types.h> X#include <sys/stat.h> X Xextern int errno; X Xchar *error_name(), *error_description(), *strcpy(); Xlong bytes(); Xunsigned long sleep(); X Xedit_mailbox() X{ X /** Allow the user to edit their mailbox, always resynchronizing X afterwards. Due to intense laziness on the part of the X programmer, this routine will invoke $EDITOR on the entire X file. The mailer will ALWAYS resync on the mailbox file X even if nothing has changed since, not unreasonably, it's X hard to figure out what occurred in the edit session... X X Also note that if the user wants to edit their incoming X mailbox they'll actually be editing the tempfile that is X an exact copy. More on how we resync in that case later X in this code. X **/ X X FILE *real_mailbox, *temp_mailbox; X char filename[SLEN], buffer[LONG_SLEN], temp_infile[SLEN]; X struct stat stat_buffer; X int loaded_stat_buffer = FALSE; X X PutLine0(LINES-1,0,"invoking editor..."); X X if (mbox_specified == 0) { X sprintf(filename, "%s%s", temp_mbox, username); X chown(filename, userid, groupid); /* make sure we can! */ X } X else X strcpy(filename, infile); X X /** now get and save the ownership and permissions... **/ X X if (stat(infile, &stat_buffer)) { X error("Warning: couldn't 'stat' file, perms might get mangled"); X sleep(2); X } X else X loaded_stat_buffer = TRUE; X X sprintf(buffer, "%s %s", alternative_editor, filename); X X Raw(OFF); X X if (system_call(buffer, SH) != 0) { X error1("Problems invoking editor %s!", alternative_editor); X Raw(ON); X sleep(2); X return(0); X } X X Raw(ON); X X if (mbox_specified == 0) { /* uh oh... now the toughie... */ X X sprintf(temp_infile, "%s%s.temp", mailhome, username); X unlink(temp_infile); /* remove it if it's there... */ X X if (bytes(infile) != mailfile_size) { X X /* SIGH. We've received mail since we invoked the editor X on the mailbox. We'll have to do some strange stuff to X remedy the problem... */ X X PutLine0(LINES, 0, "Warning: new mail received..."); X CleartoEOLN(); X X if ((temp_mailbox = fopen(filename, "a")) == NULL) { X dprint2(1, "Attempt to open %s to append failed! (%s)\n", X filename, "edit_mailbox"); X set_error("Couldn't reopen tempfile. Edit LOST!"); X return(1); X } X /** Now let's lock the mailbox up and stream the new stuff X into the temp file... **/ X X lock(OUTGOING); X if ((real_mailbox = fopen(infile, "r")) == NULL) { X dprint2(1, X "Attempt to open %s for reading new mail failed! (%s)\n", X infile, "edit_mailbox"); X sprintf(buffer, "Couldn't open %s for reading! Edit LOST!", X infile); X set_error(buffer); X unlock(); X return(1); X } X if (fseek(real_mailbox, mailfile_size, 0) != 0) { X dprint2(1, "Couldn't seek to end of infile (offset %ld) (%s)\n", X mailfile_size, "edit_mailbox"); X set_error("Couldn't seek to end of mailbox. Edit LOST!"); X unlock(); X return(1); X } X X /** Now we can finally stream the new mail into the tempfile **/ X X while (fgets(buffer, LONG_SLEN, real_mailbox) != NULL) X fprintf(temp_mailbox, "%s", buffer); X X fclose(real_mailbox); X fclose(temp_mailbox); X } X else X lock(OUTGOING); /* create a lock file if we're replacing mailbox */ X X /** link to the temporary mailbox in the mailhome directory... **/ X X if (link(filename, temp_infile) != 0) X if (errno == EXDEV) { /* attempt to link across file systems */ X if (copy(filename, temp_infile) != 0) { X error("Couldn't copy temp file to mailbox!"); X unlock(); /* ciao!*/ X emergency_exit(); X } X } X else { X Write_to_screen("\n\rCouldn't link %s to mailfile %s...\n\r",2, X filename, temp_infile); X Write_to_screen("** %s - %s **\n\r", 2, X error_name(errno), error_description(errno)); X emergency_exit(); X } X X /*** G U L P ... let's remove the incoming mail file... ***/ X X unlink(infile); X X /** and quickly now... **/ X X if (link(temp_infile, infile) != 0) { X Write_to_screen( X "\n\rCouldn't internally link %s to mailfile %s...\n\r", X 2, temp_infile, infile); X Write_to_screen( X "\n\rYou'll need to check out %s for your mail...\n\r", X 1, temp_infile); X Write_to_screen("** %s - %s **\n\r", 2, X error_name(errno), error_description(errno)); X emergency_exit(); X } X X /** And let's remove the lock file! We're DONE!!! **/ X X unlock(); X unlink(temp_infile); /* remove the temp file too */ X unlink(filename); /* remove the temp file too */ X error("edit changes incorporated into new mail..."); X } X else X error("Resynchronizing with new version of mailbox..."); X X sleep(2); X resync(); X X current = 1; /* don't leave the user hanging! */ X X /** finally restore the permissions... **/ X X if (loaded_stat_buffer) { /* if not, it's junk! */ X chown(infile, stat_buffer.st_uid, stat_buffer.st_gid); X chmod(infile, stat_buffer.st_mode); X } X X return(1); X} END_OF_src/edit.c if test 5274 -ne `wc -c <src/edit.c`; then echo shar: \"src/edit.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"src/file_utils.c\" \(5169 characters\) if test -f src/file_utils.c ; then echo shar: Will not over-write existing file \"src/file_utils.c\" else sed "s/^X//" >src/file_utils.c <<'END_OF_src/file_utils.c' X/** file_utils.c **/ X X/** File oriented utility routines for ELM X X (C) Copyright 1986 Dave Taylor X**/ X X#include "headers.h" X#include <sys/types.h> X#include <sys/stat.h> X#include <ctype.h> X#include <errno.h> X X#ifdef BSD X# undef tolower X#endif X X#include <signal.h> X#include <errno.h> X X#ifdef BSD X# include <sys/wait.h> X#endif X Xextern int errno; /* system error number */ X Xchar *error_name(), *error_description(), *strcpy(), *getlogin(); X Xlong Xbytes(name) Xchar *name; X{ X /** return the number of bytes in the specified file. This X is to check to see if new mail has arrived.... **/ X X int ok = 1; X extern int errno; /* system error number! */ X struct stat buffer; X X if (stat(name, &buffer) != 0) X if (errno != 2) { X dprint2(1,"Error: errno %s on fstat of file %s (bytes)\n", X error_name(errno), name); X Write_to_screen("\n\rError attempting fstat on file %s!\n\r", X 1, name); X Write_to_screen("** %s - %s **\n\r", 2, error_name(errno), X error_description(errno)); X emergency_exit(); X } X else X ok = 0; X X return(ok ? (long) buffer.st_size : 0L); X} X Xint Xcan_access(file, mode) Xchar *file; Xint mode; X{ X /** returns ZERO iff user can access file or "errno" otherwise **/ X X int the_stat = 0, pid, w; X void _exit(), exit(); X#ifdef BSD X union wait status; X#else X int status; X#endif X register int (*istat)(), (*qstat)(); X X#ifdef NO_VM /* machine without virtual memory!! */ X if ((pid = fork()) == 0) { X#else X if ((pid = vfork()) == 0) { X#endif X setgid(groupid); X setuid(userid); /** back to normal userid **/ X X errno = 0; X X if (access(file, mode) == 0) X _exit(0); X else X _exit(errno != 0? errno : 1); /* never return zero! */ X _exit(127); X } X X istat = signal(SIGINT, SIG_IGN); X qstat = signal(SIGQUIT, SIG_IGN); X X while ((w = wait(&status)) != pid && w != -1) X ; X X#ifdef BSD X the_stat = status.w_retcode; X#else X the_stat = status; X#endif X X signal(SIGINT, istat); X signal(SIGQUIT, qstat); X X return(the_stat); X} X Xint Xcan_open(file, mode) Xchar *file, *mode; X{ X /** Returns 0 iff user can open the file. This is not X the same as can_access - it's used for when the file might X not exist... **/ X X FILE *fd; X int the_stat = 0, pid, w; X void _exit(), exit(); X#ifdef BSD X union wait status; X#else X int status; X#endif X register int (*istat)(), (*qstat)(); X X#ifdef NO_VM /* machine without virtual memory!! */ X if ((pid = fork()) == 0) { X#else X if ((pid = vfork()) == 0) { X#endif X setgid(groupid); X setuid(userid); /** back to normal userid **/ X errno = 0; X if ((fd = fopen(file, mode)) == NULL) X _exit(errno); X else { X fclose(fd); /* don't just LEAVE it! */ X _exit(0); X } X _exit(127); X } X X istat = signal(SIGINT, SIG_IGN); X qstat = signal(SIGQUIT, SIG_IGN); X X while ((w = wait(&status)) != pid && w != -1) X ; X X#ifdef BSD X the_stat = status.w_retcode; X#else X the_stat = status; X#endif X X signal(SIGINT, istat); X signal(SIGQUIT, qstat); X X return(the_stat); X} X Xint Xcopy(from, to) Xchar *from, *to; X{ X /** this routine copies a specified file to the destination X specified. Non-zero return code indicates that something X dreadful happened! **/ X X FILE *from_file, *to_file; X char buffer[VERY_LONG_STRING]; X X if ((from_file = fopen(from, "r")) == NULL) { X dprint1(1,"Error: could not open %s for reading (copy)\n", from); X error1("could not open file %s", from); X return(1); X } X X if ((to_file = fopen(to, "w")) == NULL) { X dprint1(1,"Error: could not open %s for writing (copy)\n", to); X error1("could not open file %s", to); X return(1); X } X X while (fgets(buffer, VERY_LONG_STRING, from_file) != NULL) X fputs(buffer, to_file); X X fclose(from_file); X fclose(to_file); X X return(0); X} X Xint Xappend(fd, filename) XFILE *fd; Xchar *filename; X{ X /** This routine appends the specified file to the already X open file descriptor.. Returns non-zero if fails. **/ X X FILE *my_fd; X char buffer[VERY_LONG_STRING]; X X if ((my_fd = fopen(filename, "r")) == NULL) { X dprint1(1,"Error: could not open %s for reading (append)\n", filename); X return(1); X } X X while (fgets(buffer, VERY_LONG_STRING, my_fd) != NULL) X fputs(buffer, fd); X X fclose(my_fd); X X return(0); X} X Xcheck_mailfile_size() X{ X /** Check to ensure we have mail. Only used with the '-z' X starting option. **/ X X char filename[SLEN], *getlogin(); X struct stat buffer; X X strcpy(username,getlogin()); X if (strlen(username) == 0) X cuserid(username); X X sprintf(filename,"%s%s", mailhome, username); X X if (stat(filename, &buffer) == -1) { X printf(" You have no mail.\n"); X exit(0); X } X else if (buffer.st_size < 2) { /* maybe one byte??? */ X printf("You have no mail to read.\n"); X exit(0); X } X} X Xcreate_readmsg_file() X{ X /** Creates the file ".current" in the users home directory X for use with the "readmsg" program. X **/ X X FILE *fd; X char buffer[SLEN]; X X sprintf(buffer,"%s/%s", home, readmsg_file); X X if ((fd = fopen (buffer, "w")) == NULL) { X dprint3(1,"Error: couldn't create file %s - error %s (%s)\n", X buffer, error_name(errno), "create_readmsg_file"); X return; /* no error to user */ X } X X fprintf(fd, "%d\n", header_table[current-1].index_number); X fclose(fd); X} END_OF_src/file_utils.c if test 5169 -ne `wc -c <src/file_utils.c`; then echo shar: \"src/file_utils.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"src/mailtime.c\" \(4749 characters\) if test -f src/mailtime.c ; then echo shar: Will not over-write existing file \"src/mailtime.c\" else sed "s/^X//" >src/mailtime.c <<'END_OF_src/mailtime.c' X/** mailtime.c **/ X X/** This set of routines is used to figure out when the user last read X their mail and to also figure out if a given message is new or not. X X (C) Copyright 1986 Dave Taylor X**/ X X#include "headers.h" X X#include <sys/types.h> X#include <sys/stat.h> X#ifdef BSD X# ifndef BSD4.1 X# include <sys/time.h> X# else X# include <time.h> X# include <sys/timeb.h> X# endif X#else X# include <time.h> X#endif X Xresolve_received(entry) Xstruct header_rec *entry; X{ X /** Entry has the data for computing the time and date the X message was received. Fix it and return **/ X X switch (tolower(entry->month[0])) { X case 'j' : if (tolower(entry->month[1]) == 'a') X entry->received.month = JANUARY; X else if (tolower(entry->month[2]) == 'n') X entry->received.month = JUNE; X else X entry->received.month = JULY; X break; X case 'f' : entry->received.month = FEBRUARY; X break; X case 'm' : if (tolower(entry->month[2]) == 'r') X entry->received.month = MARCH; X else X entry->received.month = MAY; X break; X case 'a' : if (tolower(entry->month[1]) == 'p') X entry->received.month = APRIL; X else X entry->received.month = AUGUST; X break; X case 's' : entry->received.month = SEPTEMBER; X break; X case 'o' : entry->received.month = OCTOBER; X break; X case 'n' : entry->received.month = NOVEMBER; X break; X case 'd' : entry->received.month = DECEMBER; X break; X } X X sscanf(entry->day, "%d", &(entry->received.day)); X X sscanf(entry->year, "%d", &(entry->received.year)); X if (entry->received.year > 100) entry->received.year -= 1900; X X sscanf(entry->time, "%d:%d", &(entry->received.hour), X &(entry->received.minute)); X} X Xget_mailtime() X{ X /** Instantiate the values of the last_read_mail stat X variable based on the file access time/date of the X file mailtime_file. IF the file doesn't exist, X then assume all mail is new. **/ X X struct stat buffer; X struct tm *timebuf; X char filename[SLEN]; X#ifdef BSD X extern struct tm *localtime(); X#endif X X sprintf(filename, "%s/%s", home, mailtime_file); X X if (stat(filename, &buffer) == -1) { X last_read_mail.month = 0; X last_read_mail.day = 0; X last_read_mail.year = 0; X last_read_mail.hour = 0; X last_read_mail.minute = 0; X } X else { /* stat okay... */ X timebuf = (struct tm *) localtime(&(buffer.st_mtime)); X X last_read_mail.month = timebuf->tm_mon; X last_read_mail.day = timebuf->tm_mday; X last_read_mail.year = timebuf->tm_year; X last_read_mail.hour = timebuf->tm_hour; X last_read_mail.minute = timebuf->tm_min; X } X} X Xupdate_mailtime() X{ X /** This routine updates the last modified time of the X .last_read_mail file in the users home directory. X If the file doesn't exist, it creates it!! **/ X X char filename[SLEN]; X X#ifdef BSD X# ifdef BSD4.1 X struct timeb loc_time; X time_t tval; X# else X struct timeval tval[2]; X struct timezone tzone; X# endif X#endif X X sprintf(filename, "%s/%s", home, mailtime_file); X X#ifdef BSD X# ifdef BSD4.1 X tval = (time_t) time((long *) 0); X if (utime(filename, &tval) == -1) X# else X gettimeofday(&tval[0], &tzone); X gettimeofday(&tval[1], &tzone); X X if (utimes(filename, tval) == -1) /* note the "S" */ X# endif X#else X if (utime(filename, NULL) == -1) /* note no "S" */ X#endif X /** That's what I like about programming for BSD & USG - the easy X portability between 'em. Especially the section 2 calls!! **/ X X (void) creat(filename, 0777); X} X Xnew_msg(entry) Xstruct header_rec entry; X{ X /** Return true if the current message is NEW. This can be X easily tested by seeing 1) if we're reading the incoming X mailbox and then, if so, 2) if the received_on_machine X date is more recent than the last_read_mail date. X **/ X X if (mbox_specified != 0) return(FALSE); /* not incoming */ X X /** Two tests - if received is OLDER than last read mail, then X immediately return FALSE. If received is NEWER than last X read mail then immediately return TRUE **/ X X if (entry.received.year < last_read_mail.year) X return(FALSE); X X if (entry.received.year > last_read_mail.year) X return(TRUE); X X if (entry.received.month < last_read_mail.month) X return(FALSE); X X if (entry.received.month > last_read_mail.month) X return(TRUE); X X if (entry.received.day < last_read_mail.day) X return(FALSE); X X if (entry.received.day > last_read_mail.day) X return(TRUE); X X if (entry.received.hour < last_read_mail.hour) X return(FALSE); X X if (entry.received.hour > last_read_mail.hour) X return(TRUE); X X if (entry.received.minute < last_read_mail.minute) X return(FALSE); X X return(TRUE); X} END_OF_src/mailtime.c if test 4749 -ne `wc -c <src/mailtime.c`; then echo shar: \"src/mailtime.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"src/opt_utils.c\" \(5098 characters\) if test -f src/opt_utils.c ; then echo shar: Will not over-write existing file \"src/opt_utils.c\" else sed "s/^X//" >src/opt_utils.c <<'END_OF_src/opt_utils.c' X/** opt_utils.c **/ X X/** This file contains routines that might be needed for the various X machines that the mailer can run on. Please check the Makefile X for more help and/or information. X X (C) Copyright 1986 Dave Taylor X**/ X X#include <stdio.h> X#include "headers.h" X X#ifdef BSD X# include <pwd.h> X#endif X X#ifdef NEED_GETHOSTNAME X# include <sys/utsname.h> X#endif X X#ifdef UTS X# include <sys/tubio.h> X# define TTYIN 0 /* standard input */ X#endif X X#ifdef NEED_GETHOSTNAME X Xgethostname(hostname,size) /* get name of current host */ Xint size; Xchar *hostname; X{ X /** Return the name of the current host machine. UTS only **/ X X /** This routine compliments of Scott McGregor at the HP X Corporate Computing Center **/ X X int uname(); X struct utsname name; X X (void) uname(&name); X (void) strncpy(hostname,name.nodename,size-1); X hostname[size] = '\0'; X X} X X#endif X X#ifdef UTS X Xint Xisa3270() X{ X /** Returns TRUE and sets LINES and COLUMNS to the correct values X for an Amdahl (IBM) tube screen, or returns FALSE if on a normal X terminal (of course, next to a 3270, ANYTHING is normal!!) **/ X X struct tubiocb tubecb; X X dprint0(3,"Seeing if we're a 3270..."); X X if (ioctl(TTYIN, TUBGETMOD, &tubecb) == -1) { X dprint0(3,"We're not!\n"); X return(FALSE); /* not a tube! */ X } X X LINES = tubecb->line_cnt - 1; X COLUMNS = tubecb->col_cnt; X X dprint2(3,"We are! %d lines and %d columns!\n", X LINES, COLUMNS); X return(TRUE); X} X X#endif /* def UTS */ X X#ifdef BSD X Xcuserid(uname) Xchar *uname; X{ X /** Added for compatibility with Bell systems, this is the last-ditch X attempt to get the users login name, after getlogin() fails. It X instantiates "uname" to the name of the user... X **/ X X struct passwd *password_entry, *getpwuid(); X X password_entry = getpwuid(getuid()); X X strcpy(uname, password_entry->pw_name); X} X X/** some supplementary string functions for Berkeley Unix systems **/ X Xstrspn(source, keys) Xchar *source, *keys; X{ X /** This function returns the length of the substring of X 'source' (starting at zero) that consists ENTIRELY of X characters from 'keys'. This is used to skip over a X defined set of characters with parsing, usually. X **/ X X register int loc = 0, key_index = 0; X X while (source[loc] != '\0') { X key_index = 0; X while (keys[key_index] != source[loc]) X if (keys[key_index++] == '\0') X return(loc); X loc++; X } X X return(loc); X} X Xstrcspn(source, keys) Xchar *source, *keys; X{ X /** This function returns the length of the substring of X 'source' (starting at zero) that consists entirely of X characters NOT from 'keys'. This is used to skip to a X defined set of characters with parsing, usually. X NOTE that this is the opposite of strspn() above X **/ X X register int loc = 0, key_index = 0; X X while (source[loc] != '\0') { X key_index = 0; X while (keys[key_index] != '\0') X if (keys[key_index++] == source[loc]) X return(loc); X loc++; X } X X return(loc); X} X Xchar *strtok(source, keys) Xchar *source, *keys; X{ X /** This function returns a pointer to the next word in source X with the string considered broken up at the characters X contained in 'keys'. Source should be a character pointer X when this routine is first called, then NULL subsequently. X When strtok has exhausted the source string, it will X return NULL as the next word. X X WARNING: This routine will DESTROY the string pointed to X by 'source' when first invoked. If you want to keep the X string, make a copy before using this routine!! X **/ X X register int last_ch; X static char *sourceptr; X char *return_value; X X if (source != NULL) X sourceptr = source; X X if (*sourceptr == '\0') X return(NULL); /* we hit end-of-string last time!? */ X X sourceptr += strspn(sourceptr, keys); /* skip leading crap */ X X if (*sourceptr == '\0') X return(NULL); /* we've hit end-of-string */ X X last_ch = strcspn(sourceptr, keys); /* end of good stuff */ X X return_value = sourceptr; /* and get the ret */ X X sourceptr += last_ch; /* ...value */ X X if (*sourceptr != '\0') /* don't forget if we're at END! */ X sourceptr++; /* and skipping for next time */ X X return_value[last_ch] = '\0'; /* ..ending right */ X X return((char *) return_value); /* and we're outta here! */ X} X Xchar *strpbrk(source, keys) Xchar *source, *keys; X{ X /** Returns a pointer to the first character of source that is any X of the specified keys, or NULL if none of the keys are present X in the source string. X **/ X X register int loc = 0, key_index = 0; X X while (source[loc] != '\0') { X key_index = 0; X while (keys[key_index] != '\0') X if (keys[key_index++] == source[loc]) X return((char *) (source + loc)); X loc++; X } X X return(NULL); X} X Xchar *strchr(buffer, ch) Xchar *buffer, ch; X{ X /** Returns a pointer to the first occurance of the character X 'ch' in the specified string or NULL if it doesn't occur **/ X X char *address; X X address = buffer; X X while (*address != ch) { X if (*address == '\0') X return (NULL); X address++; X } X X return ( (char *) address); X} X X#endif END_OF_src/opt_utils.c if test 5098 -ne `wc -c <src/opt_utils.c`; then echo shar: \"src/opt_utils.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"src/sort.c\" \(4826 characters\) if test -f src/sort.c ; then echo shar: Will not over-write existing file \"src/sort.c\" else sed "s/^X//" >src/sort.c <<'END_OF_src/sort.c' X/** sort.c **/ X X/** Sort mailbox header table by the field specified in the global X variable "sortby"...if we're sorting by something other than X the default SENT_DATE, also put some sort of indicator on the X screen. X X (C) Copyright 1986, Dave Taylor X**/ X X#include "headers.h" X Xchar *sort_name(); Xvoid qsort(); X Xsort_mailbox(entries, visible) Xint entries, visible; X{ X /** Sort the header_table definitions... If 'visible', then X put the status lines etc **/ X X int last_index = -1; X int compare_headers(); /* for sorting */ X X dprint1(2,"\n** sorting mailbox by %s **\n\n", sort_name(FULL)); X X if (entries > 0) X last_index = header_table[current-1].index_number; X X if (entries > 30 && visible) X error1("sorting messages by %s", sort_name(FULL)); X X qsort(header_table, (unsigned) entries, sizeof (struct header_rec), X compare_headers); X X if (last_index > -1) X find_old_current(last_index); X X clear_error(); X} X Xint Xcompare_headers(first, second) Xstruct header_rec *first, *second; X{ X /** compare two headers according to the sortby value. X X Sent Date uses a routine to compare two dates, X Received date is keyed on the file offsets (think about it) X Sender uses the truncated from line, same as "build headers", X and size and subject are trivially obvious!! X **/ X X char from1[SLEN], from2[SLEN]; /* sorting buffers... */ X int sign = 1; X X if (sortby < 0) X sign = -1; X X switch (abs(sortby)) { X X case SENT_DATE : return( sign*compare_dates(first, second)); X X case RECEIVED_DATE: return( sign* X compare_parsed_dates(first->received, X second->received)); X X case SENDER : tail_of(first->from, from1, TRUE); X tail_of(second->from, from2, TRUE); X return( sign*strcmp(from1, from2)); X X case SIZE : return( sign*(first->lines - second->lines)); X X case SUBJECT : /* need some extra work 'cause of STATIC buffers */ X strcpy(from1, shift_lower(first->subject)); X return( X sign*strcmp(from1, shift_lower(second->subject))); X X case STATUS : return( sign*(first->status - second->status)); X } X X return(0); /* never get this! */ X} X Xchar *sort_name(type) Xint type; X{ X /** return the name of the current sort option... X type can be "FULL", "SHORT" or "PAD" X **/ X int pad, abr; X X pad = (type == PAD); X abr = (type == SHORT); X X if (sortby < 0) { X switch (- sortby) { X case SENT_DATE : return( X pad? "Reverse Date Mail Sent " : X abr? "Reverse-Sent" : X "Reverse Date Mail Sent"); X case RECEIVED_DATE: return( X abr? "Reverse-Received": X "Reverse Date Mail Rec'vd"); X case SENDER : return( X pad? "Reverse Message Sender " : X abr? "Reverse-From": X "Reverse Message Sender"); X case SIZE : return( X abr? "Reverse-Lines" : X "Reverse Lines in Message"); X case SUBJECT : return( X pad? "Reverse Message Subject " : X abr? "Reverse-Subject" : X "Reverse Message Subject"); X case STATUS : return( X pad? "Reverse Message Status " : X abr? "Reverse-Status": X "Reverse Message Status"); X } X } X else { X switch (sortby) { X case SENT_DATE : return( X pad? "Date Mail Sent " : X abr? "Sent" : X "Date Mail Sent"); X case RECEIVED_DATE: return( X pad? "Date Mail Rec'vd " : X abr? "Received" : X "Date Mail Rec'vd"); X case SENDER : return( X pad? "Message Sender " : X abr? "From" : X "Message Sender"); X case SIZE : return( X pad? "Lines in Message " : X abr? "Lines" : X "Lines in Message"); X case SUBJECT : return( X pad? "Message Subject " : X abr? "Subject" : X "Message Subject"); X case STATUS : return( X pad? "Message Status " : X abr? "Status" : X "Message Status"); X } X } X X return("*UNKNOWN-SORT-PARAMETER*"); X} X Xfind_old_current(index) Xint index; X{ X /** Set current to the message that has "index" as it's X index number. This is to track the current message X when we resync... **/ X X register int i; X X dprint1(2,"find-old-current(%d)\n", index); X X for (i = 0; i < message_count; i++) X if (header_table[i].index_number == index) { X current = i+1; X dprint1(2,"\tset current to %d!\n", current); X return; X } X X dprint1(2,"\tcouldn't find current index. Current left as %d\n", X current); X return; /* can't be found. Leave it alone, then */ X} END_OF_src/sort.c if test 4826 -ne `wc -c <src/sort.c`; then echo shar: \"src/sort.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: Extracting \"src/syscall.c\" \(5245 characters\) if test -f src/syscall.c ; then echo shar: Will not over-write existing file \"src/syscall.c\" else sed "s/^X//" >src/syscall.c <<'END_OF_src/syscall.c' X/** syscall.c **/ X X/** These routines are used for user-level system calls, including the X '!' command and the '|' commands... X X (C) Copyright 1986 Dave Taylor X**/ X X#include "headers.h" X X#include <signal.h> X X#ifdef BSD X# include <sys/wait.h> X#endif X Xchar *argv_zero(); Xvoid _exit(); X Xint Xsubshell() X{ X /** spawn a subshell with either the specified command X returns non-zero if screen rewrite needed X **/ X X char command[SLEN]; X int ret; X X PutLine0(LINES-3,COLUMNS-40,"(use the shell name for a shell)"); X PutLine0(LINES-2,0,"Shell Command: "); X command[0] = '\0'; X (void) optionally_enter(command, LINES-2, 15, FALSE); X if (strlen(command) == 0) { X MoveCursor(LINES-2,0); CleartoEOLN(); X return(0); X } X X MoveCursor(LINES,0); CleartoEOLN(); X Raw(OFF); X if (cursor_control) transmit_functions(OFF); X X ret = system_call(command, USER_SHELL); X X PutLine0(LINES, 0, "\n\nPress <return> to return to ELM: "); X X Raw(ON); X (void) getchar(); X if (cursor_control) transmit_functions(ON); X X if (ret != 0) error1("Return code was %d", ret); X return(1); X} X Xsystem_call(string, shell_type) Xchar *string; Xint shell_type; X{ X /** execute 'string', setting uid to userid... **/ X /** if shell-type is "SH" /bin/sh is used regardless of the X users shell setting. Otherwise, "USER_SHELL" is sent **/ X X int stat = 0, pid, w; X#ifdef BSD X union wait status; X#else X int status; X#endif X register int (*istat)(), (*qstat)(); X X dprint2(2,"System Call: %s\n\t%s\n", shell_type == SH? "/bin/sh" : shell, X string); X X#ifdef NO_VM /* machine without virtual memory! */ X if ((pid = fork()) == 0) { X#else X if ((pid = vfork()) == 0) { X#endif X setgid(groupid); /* and group id */ X setuid(userid); /* back to the normal user! */ X X if (strlen(shell) > 0 && shell_type == USER_SHELL) { X execl(shell, argv_zero(shell), "-c", string, (char *) 0); X } X else X execl("/bin/sh", "sh", "-c", string, (char *) 0); X _exit(127); X } X X istat = signal(SIGINT, SIG_IGN); X qstat = signal(SIGQUIT, SIG_IGN); X X while ((w = wait(&status)) != pid && w != -1) X ; X X#ifdef BSD X if (status.w_retcode != 0) stat = status.w_retcode; X#else X if (w == -1) stat = status; X#endif X X signal(SIGINT, istat); X signal(SIGQUIT, qstat); X X return(stat); X} X Xint Xdo_pipe() X{ X /** pipe the tagged messages to the specified sequence.. **/ X X char command[SLEN], buffer[LONG_SLEN], message_list[SLEN]; X register int ret, tagged = 0, i; X X message_list[0] = '\0'; /* NULL string to start... */ X X for (i=0; i < message_count; i++) X if (ison(header_table[i].status, TAGGED)) { X sprintf(message_list,"%s %d", message_list, X header_table[i].index_number); X tagged++; X } X X if (tagged > 1) X PutLine0(LINES-2,0,"Pipe tagged msgs to: "); X else if (tagged) X PutLine0(LINES-2,0,"Pipe tagged msg to : "); X else { X PutLine0(LINES-2,0,"Pipe current msg to: "); X sprintf(message_list,"%d", header_table[current-1].index_number); X } X X command[0] = '\0'; X X (void) optionally_enter(command, LINES-2, 21, FALSE); X if (strlen(command) == 0) { X MoveCursor(LINES-2,0); CleartoEOLN(); X return(0); X } X X MoveCursor(LINES,0); CleartoEOLN(); X Raw(OFF); X X if (cursor_control) transmit_functions(OFF); X X sprintf(buffer, "%s -f %s -h %s | %s", X readmsg, X infile, X message_list, X command); X X ret = system_call(buffer, USER_SHELL); X X PutLine0(LINES, 0, "\n\nPress <return> to return to ELM: "); X Raw(ON); X (void) getchar(); X if (cursor_control) transmit_functions(ON); X X if (ret != 0) error1("Return code was %d", ret); X return(1); X} X Xprintmsg() X{ X /** Print current message or tagged messages using 'printout' X variable. Error message iff printout not defined! **/ X X char buffer[LONG_SLEN], filename[SLEN], printbuffer[LONG_SLEN]; X char message_list[SLEN]; X register int retcode, tagged = 0, i; X X if (strlen(printout) == 0) { X error("Don't know how to print - option \"printmail\" undefined!"); X return; X } X X message_list[0] = '\0'; /* reset to null... */ X X for (i=0; i < message_count; i++) X if (header_table[i].status & TAGGED) { X sprintf(message_list, "%s %d", message_list, X header_table[i].index_number); X tagged++; X } X X if (! tagged) X sprintf(message_list," %d", header_table[current-1].index_number); X X sprintf(filename,"%s%d", temp_print, getpid()); X X if (in_string(printout, "%s")) X sprintf(printbuffer, printout, filename); X else X sprintf(printbuffer, "%s %s", printout, filename); X X sprintf(buffer,"(%s -p -f %s%s > %s; %s 2>&1) > /dev/null", X readmsg, infile, message_list, X filename, X printbuffer); X X dprint0(2,"Printing system call...\n"); X X Centerline(LINES, "queueing..."); X X if ((retcode = system_call(buffer, SH)) == 0) { X sprintf(buffer, "Message%s queued up to print", plural(tagged)); X Centerline(LINES, buffer); X } X else X error1("Printout failed with return code %d", retcode); X X unlink(filename); /* remove da temp file! */ X} X Xlist_folders() X{ X /** list the folders in the users FOLDERHOME directory. This is X simply a call to "ls -C" X **/ X X char buffer[SLEN]; X X CleartoEOS(); /* don't leave any junk on the bottom of the screen */ X sprintf(buffer, "cd %s;ls -C", folders); X printf("\n\rContents of your folder directory:\n\r\n\r"); X system_call(buffer, SH); X printf("\n\r"); X} X END_OF_src/syscall.c if test 5245 -ne `wc -c <src/syscall.c`; then echo shar: \"src/syscall.c\" unpacked with wrong size!? fi # end of overwriting check fi echo shar: End of archive 4 \(of 19\). cp /dev/null ark4isdone DONE=true for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do if test ! -f ark${I}isdone ; then echo shar: You still need to run archive ${I}. DONE=false fi done if test "$DONE" = "true" ; then echo You have unpacked all 19 archives. echo "See the Instructions file" rm -f ark[1-9]isdone ark[1-9][0-9]isdone fi ## End of shell archive. exit 0