ast@cs.vu.nl (Andy Tanenbaum) (12/04/88)
I get a LOT of mail. That probably surprises no one. Nevertheless, I am very conscientious about answering all mail in which people ask specific questions. A certain fraction of this mail bounces, and I don't know what to do. If you send me mail asking a specific question and don't have an answer within, say, a week (unless I have announced that I am going to be away), you can assume I was unable to reply. In that case, please resend the mail along with your exact path from uunet or mcvax, included in the text of the message. Andy Tanenbaum (ast@cs.vu.nl)
parke@jfcl.dec.com (Bill Parke) (12/06/88)
From article <1753@ast.cs.vu.nl>, by ast@cs.vu.nl (Andy Tanenbaum): > ... > In that case, please > resend the mail along with your exact path from uunet or mcvax, included > in the text of the message. > > Andy Tanenbaum (ast@cs.vu.nl) Andy, Would you please post an anternate to your cs.vu.nl mailing address as I have been getting bounced when I use it to send you mail. Thanks, Bill Parke ...!decwrl!jfcl!parke or parke@jfcl.dec.com
ast@cs.vu.nl (Andy Tanenbaum) (12/08/88)
In article <421@jfcl.dec.com> parke@jfcl.dec.com (Bill Parke) writes: >Would you please post an anternate to your cs.vu.nl mailing address The "bang" path is" ...uunet!mcvax!vu.cs!ast Andy Tanenbaum (ast@cs.vu.nl)
ast@cs.vu.nl (Andy Tanenbaum) (10/23/89)
Fred van Kempen of the MINIX Users Group Holland has written a mail system. He sent it to me for posting and I did so. Little did I realize that he sent me raw binary files (not uuencoded). I will have a little talk with him. In any event, this posting and the next two contain the files in ASCII as shar files. Please direct comments to him as he gets email, but not news (yet). Andy Tanenbaum (ast@cs.vu.nl) : This is a shar archive. Extract with sh, not csh. : This archive ends with exit, so do not worry about trailing junk. : --------------------------- cut here -------------------------- PATH=/bin:/usr/bin:/usr/ucb echo Extracting 'README' sed 's/^X//' > 'README' << '+ END-OF-FILE ''README' X W-MAIL Local Mail Agent (MINIX) X X MicroWalt W-MAIL V2.5 X ===================== X XThis archive contains the sources of the W-MAIL program; Xwhich is a MINIX version of the MAILX program available Xon most UNIXes. X XIt does not need the presence of UUCP in order to run. XHowever, for non-local mail delivery a remote mailer X(like MicroWalt UMAIL) is required to be present. X XEnjoy! X+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ X| MINIX User Group (Holland) UUCP: hp4nl!kyber!minixug!waltje | X| c/o Fred van Kempen, or: minixug!waltje@kyber.UUCP | X| Hoefbladhof 27 | X| 2215 DV VOORHOUT | X| The Netherlands "A good programmer knows his Sources" | X+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ X + END-OF-FILE README chmod 'u=rw,g=r,o=r' 'README' set `wc -c 'README'` count=$1 case $count in 756) :;; *) echo 'Bad character count in ''README' >&2 echo 'Count should be 756' >&2 esac echo Extracting 'Makefile' sed 's/^X//' > 'Makefile' << '+ END-OF-FILE ''Makefile' X# X# Makefile for W-MAIL Local Mail Agent (MINIX) X# X# Makefile @(#)1.3 MicroWalt W-MAIL V2.5 X# X# Define UMAILER if your remote mailer understands the X# "-i datafile" option. Otherwise "< datafile" will be used. X# X# Define V7MAIL if you do not want the "To:"-line to be X# included in the message header. X# XPROG = /usr/lib/uucp XBIN = /usr/bin X XOPTS = -DUMAILER XOPSYS = MINIX X XCFLAGS = -D$(OPSYS) -DUWALT -S $(OPTS) XLDFLAGS = -i X XOBJS = wmmain.s wmread.s wmcreate.s wminteract.s \ X wmdeliver.s wmtime.s XSRCS = wmmain.c wmread.c wmcreate.c wminteract.c \ X wmdeliver.c wmtime.c XOTHERS = README wmail.doc X X Xwmail: $(OBJS) X cc $(LDFLAGS) -o wmail $(OBJS) X @chmem =20000 wmail >/dev/null X Xinstall: wmail X @echo 'Copying files...' X @rm -f $(BIN)/wmail $(BIN)/lmail $(BIN)/Mail \ X $(BIN)/mailx $(BIN)/mail X @cp wmail $(BIN) X @echo 'Setting up links...' X @ln $(BIN)/wmail $(BIN)/lmail X @ln $(BIN)/wmail $(BIN)/mail X @ln $(BIN)/wmail $(BIN)/Mail X @ln $(BIN)/wmail $(BIN)/mailx X @echo 'Setting up permissions...' X chown root.root $(BIN)/wmail X chmod 6555 $(BIN)/wmail X Xshar: wmail X @rm -f wmail.shar X @shar -v -o wmail.shar $(OTHERS) wmail Makefile $(SRCS) X Xtar: wmail X @rm -f wmail.tar X @tar c wmail.tar $(OTHERS) wmail Makefile $(SRCS) X Xwmmain.s: wmail.h wmmain.c X cc $(CFLAGS) wmmain.c X Xwmread.s: wmail.h wmread.c X cc $(CFLAGS) wmread.c X Xwmcreate.s: wmail.h wmcreate.c X cc $(CFLAGS) wmcreate.c X Xwminteract.s: wmail.h wminteract.c X cc $(CFLAGS) wminteract.c X Xwmdeliver.s: wmail.h wmdeliver.c X cc $(CFLAGS) wmdeliver.c X Xwmtime.s: wmtime.c X cc $(CFLAGS) wmtime.c X + END-OF-FILE Makefile chmod 'u=rw,g=r,o=r' 'Makefile' set `wc -c 'Makefile'` count=$1 case $count in 1595) :;; *) echo 'Bad character count in ''Makefile' >&2 echo 'Count should be 1595' >&2 esac echo Extracting 'wmail.doc' sed 's/^X//' > 'wmail.doc' << '+ END-OF-FILE ''wmail.doc' XWMAIL(1) MINIX User's Manual WMAIL(1) X X XNAME X wmail - read or send electronic mail. X XSYNOPSIS X wmail [-epqrv] [-f mailbox] X wmail [-dvt] [-i inputfile] [-s subject] <user> ... X lmail [-v] [-i inputfile] <user> ... X XDESCRIPTION X Wmail is used to read or create messages. Wmail only X knows about local users; messages to non-local users X will be handed to the Remote Mailer of the system. X X Wmail actually consists of two parts; namely the user X agent (the part that reads or creates a message) and the X local delivery agent, which takes care of delivering a X message to the mailbox of a local user. X X When Wmail is called without the 'user' argument, it X will read the contents of the users's mailbox and X display a summary of messages. This summary contains one X line for each message indicating message number, sender, X date of creation and the subject of the message. X Wmail will then display a prompt (containing the number X of the current message), and waits for the user to type X a command. A list of this commands can be requested by X typing the '?' command. X X When the program is called with the 'user' argument, it X is assumed that the calling user wants to send a message X to that user. Depending on the name by which Wmail was X called ('lmail' or something else), the message will be X generated and then delivered to the user. X If the program name is 'lmail', then the program only X delivers the message to the indicated user. This is X used by intelligent mail routing programs that cannot do X local message delivery themselves. X X Otherwise, if the name was not 'lmail', the message is X prepended with a message header, which contains the X following lines: X X From <user> <date> X To: <adressee> X Subject: <subject of message> X <empty line> X X The 'To:' and 'Subject:' lines are optional, but often X they are present in the message. After this message X header, there is the message itself. X X After the message has been completed (by typing a '.' on X an empty line or by typing an EOT (CTRL-D) character), X Wmail checks if a signature file exists. This is a file X which contains a standard end-of-letter text for that X user. Wmail checks the user's HOME directory for the X presence of a '.signature' file. If that file exists, it X is appended to the message. X X -- 1 -- X X X XWMAIL(1) MINIX User's Manual WMAIL(1) X X X If that file does not exist, Wmail checks if there is an X environment variable called 'SIGNATURE'. If that exists, X its value is assumed to be the path name of a signature X file which will be appended to the message if it exists. X X Finally, the message is delivered to the adressed user. X Note, that if the adressee is not a local user, the X message will be passed on to the 'rmail' program, which X handles all remote-mail requests. X If the adressee is just a local user, Wmail will do the X work itself. If any errors occur during the local X delivery, the message is stored in a file called in the X user's HOME directory with the name 'dead.letter', and X some diagnostics will be printed on theterminal screen. X XOPTIONS X Some command-line options are available to alter the X "standard" behaviour of the program. While functioning X as a mail reader, the following options areavailable: X X -e Only check for the presence of mail. X When this option is given, Wmail X checks the user's mailbox and returns X to the command level with one of the X following exit values: X X 0 There is mail. X 1 No mail. X 2 Error. X X This feature can be used in the X system profile to warn the user if he X or she has mail. X X -f filename Use 'filename' as the mailbox instead X of the standard system mailbox. X X -p Print the messages; no interactive X selection is needed. X X -q Quit after an interrupt occurs. X Usually Wmail catches all interrupt; X this option may be given to override X the interrupt catching mechanism. X X -r Print mail in Reverse order. X This option is recognized for compa- X tibility with older programs only. It X does not do anything, as the messages X are usually selected by the user. X X When used as a message creating agent, the following X options are available: X X -d Deliver; call the message delivery X software immediately after queueing X the message. This is only valid for X mail that needs to be delivered by X X -- 2 -- X X X XWMAIL(1) MINIX User's Manual WMAIL(1) X X X some other mailer, e.g. Remote Mail. X X -i infile Input; use file 'infile' as the mes- X sage file instead of standard input. X X X -t Include all adressees in the 'To:' X line of the message header; not only X the first adressed user. X X -s subject Use 'subject' as the value of the X 'Subject:' line of the message header X and do not ask for it later. When an X empty subject (only NEWLINE) is typed X no 'Subject:' line will be inserted X in the message header. X X -v Verbose; generate some useful output X while processing mail. X X The 'user' argument identifies the user to send the X message to. If it contains any '!' or '@' characters, X the adressee is assumed to be on a remote system, and X the message will be handed over to the remote mailer. X Otherwise it is assumed to be an existing local user, X and the '/etc/passwd' file will be checked for the X existence of that user. If the user is non-existant, X a diagnostic message is printed and the message is X saved in the 'dead.letter' file. X XAUTHOR X Peter S. Housel (first draft of mail program) X Fred van Kempen, waltje@kyber.UUCP X XFILES X /etc/passwd check existence of users X /usr/spool/mail/user maildrop file X /usr/spool/mail/user.lock lock for maildrop files X /tmp/wm* temporary file X $HOME/dead.letter message after delivery error X XSEE ALSO X umail.doc, "MINIX/UUCP Reference Manual" X X X X X X X X X X X X X X X X X -- 3 -- X + END-OF-FILE wmail.doc chmod 'u=rw,g=r,o=r' 'wmail.doc' set `wc -c 'wmail.doc'` count=$1 case $count in 6366) :;; *) echo 'Bad character count in ''wmail.doc' >&2 echo 'Count should be 6366' >&2 esac echo Extracting 'wmcreate.c' sed 's/^X//' > 'wmcreate.c' << '+ END-OF-FILE ''wmcreate.c' X/* X * WMAIL - MicroWalt Extended Mail Agent. X * This is the MicroWalt Mail Agent; which is derived X * from the "Mini-Mail" written by Peter S. Housel. X * This version has a better user-interface, and it X * also "knows" about things like forwarding, replies X * etc. Overall, it looks like the Mail (uppercase M) X * on our local DYNIX(tm) (==BSD) system... X * The paging-code (for paging letters on the screen) X * was taken from "more.c", by Brandon Allbery. X * X * M A I L G E N E R A T I O N M O D U L E X * X * Author: Fred van Kempen, MicroWalt Corporation X */ X#include <stdio.h> X#include <string.h> X#include <pwd.h> X#include "wmail.h" X X X/* X * Create a message, including the Mail-header and the final signature. X * If this is a link to LMAIL and/or if the message is read from a file X * leave out the signature. X * This routine copies lines of text from 'stdin' to 'tempfp', until an X * EOF is typed, or a line containing only a '.'. X * We complicate things by not setting a line limit. X * Define V7MAIL to skip the "To:"-field. X */ XFILE *edit_mail(void) X{ X register FILE *tempfp, *sigfp; /* temp file used for copy */ X register char *sp, *bp; X register int c; X char cpbuff[1024]; /* copy buffer */ X char tmp[PATHLEN]; X int state, done; X X if ((tempfp = fopen(msg_temp, "w")) == (FILE *)NULL) { X fprintf(stderr, "%s: cannot create temp file \"%s\"\n", X progname, msg_temp); X return((FILE *)NULL); X } X X /* X * Create the header. This has the form: X * X * From <user> <date> X * Subject: <text> (if -s on command line or stdin) X * To: <userlist> (all recipients) X */ X if (loclink == FALSE) { /* only create header if not LMAIL! */ X fprintf(tempfp, "From %s %s\n", sender, xtime()); X if (subject[0]=='\0') { X if (isatty(fileno(infp))) { X fprintf(stderr, "Subject: "); X gets(subject); X sp = strrchr(subject, '\n'); X if (sp != NIL) *sp = '\0'; X fprintf(stderr, "\n"); X } X } X#ifndef V7MAIL X fprintf(tempfp, "To: %s\n", recipients); X#endif X if (subject[0] != '\0') fprintf(tempfp, "Subject: %s\n", subject); X X fputc('\n', tempfp); X done = state = 0; X do { X if ((c = fgetc(infp)) != EOF) { X if (c=='.' && state==1) { X done = 1; X } else { X if (c == '\n') { X state = 1; X } else state = 0; X } X if (done == 0) fputc(c, tempfp); X } else done = 1; X } while (done == 0); X X /* X * Add a .signature file after the message X * Skip this if infp is a file. X */ X if (isatty(fileno(infp))) { X sp = getenv("SIGNATURE"); X if (sp == NIL) { X sp = getenv("HOME"); X if (sp != NIL) { X strcpy(tmp, sp); X if (strlen(tmp) > 1) strcat(tmp, "/"); X } else strcpy(tmp, ""); X strcat(tmp, SIGNATURE); X } else strcpy(tmp, sp); X X if ((sigfp = fopen(tmp, "r")) != (FILE *)NULL) { X while ((c = fgetc(sigfp)) != EOF) fputc(c, tempfp); X fclose(sigfp); X } X } X } else { /* fast file copy for local delivery */ X /* copy temp. file to mailbox */ X while (TRUE) { X if (fgets(cpbuff, sizeof(cpbuff), infp) == (char *)NULL) X break; X fwrite(cpbuff, sizeof(char), strlen(cpbuff), tempfp); X } X } X X if (ferror(tempfp) || fclose(tempfp)) { X fprintf(stderr, "%s: could not copy letter to temporary file\n", X progname); X return((FILE *)NULL); X } X X fclose(tempfp); X tempfp = fopen(msg_temp, "r"); X return(tempfp); X} X + END-OF-FILE wmcreate.c chmod 'u=rw,g=r,o=r' 'wmcreate.c' set `wc -c 'wmcreate.c'` count=$1 case $count in 3417) :;; *) echo 'Bad character count in ''wmcreate.c' >&2 echo 'Count should be 3417' >&2 esac echo Extracting 'wmdeliver.c' sed 's/^X//' > 'wmdeliver.c' << '+ END-OF-FILE ''wmdeliver.c' X/* X * WMAIL - MicroWalt Extended Mail Agent. X * This is the MicroWalt Mail Agent; which is derived X * from the "Mini-Mail" written by Peter S. Housel. X * This version has a better user-interface, and it X * also "knows" about things like forwarding, replies X * etc. Overall, it looks like the Mail (uppercase M) X * on our local DYNIX(tm) (==BSD) system... X * The paging-code (for paging letters on the screen) X * was taken from "more.c", by Brandon Allbery. X * X * L O C A L D E L I V E R Y M O D U L E X * X * Author: Fred van Kempen, MicroWalt Corporation X * X * To Do: X * - Aliases. X */ X#include <sys/stat.h> X#include <stdio.h> X#include <string.h> X#include <signal.h> X#include <errno.h> X#include <pwd.h> X#include "wmail.h" X X Xextern int errno; X X X/* X * Update the mail-box file. X */ Xvoid updatebox(void) X{ X char cpbuff[1024]; /* copy buffer */ X FILE *copyfp; /* fp for tempfile */ X char lockname[PATHLEN]; /* maildrop lock */ X char copytemp[PATHLEN]; /* temporary copy file */ X int locktries = 0; /* tries when box is locked */ X LETTER *let; /* current letter */ X int c; X X strcpy(copytemp, COPYTEMP); X mktemp(copytemp); X X sprintf(lockname, LOCKNAME, sender); X X /* create a new mailbox-file */ X if ((copyfp = fopen(copytemp, "w")) == (FILE *)NULL) { X fprintf(stderr, "%s: cannot create temp file \"%s\"\n", X progname, copytemp); X return; X } X X /* copy letters from old file to new file */ X for (let = firstlet; let != NIL_LET; let = let->next) { X if (let->status != DELETED) printlet(let, copyfp); X } X X if ((copyfp = freopen(copytemp, "r", copyfp)) == (FILE *)NULL) { X sprintf(cpbuff, "%s: temporary file write error", progname); X perror(cpbuff); X if (usedrop) unlink(copytemp); X return; X } X X /* shut off signals during the update */ X signal(SIGINT, SIG_IGN); X signal(SIGHUP, SIG_IGN); X signal(SIGQUIT, SIG_IGN); X X if (usedrop) while(link(mailbox, lockname) != 0) { X if (++locktries >= LOCKTRIES) { X fprintf(stderr, "%s: could not lock maildrop for update\n", X progname); X return; X } X sleep(LOCKWAIT); X } X X if ((boxfp = freopen(mailbox, "w", boxfp)) == (FILE *)NULL) { X sprintf(cpbuff, "%s: could not reopen maildrop", progname); X fprintf(stderr, "%s\nMail may have been lost; look in %s\n", X cpbuff, copytemp); X unlink(lockname); X return; X } X X /* copy temp. file to mailbox */ X while (TRUE) { X if (fgets(cpbuff, sizeof(cpbuff), copyfp) == (char *)NULL) break; X fwrite(cpbuff, sizeof(char), strlen(cpbuff), boxfp); X } X fflush(copyfp); fflush(boxfp); X fclose(boxfp); fclose(copyfp); X unlink(copytemp); X X if (usedrop) unlink(lockname); X} X X X/* X * Send a message to a remote user. X * Define UMAILER if your mailer knows about the "-i datafile" option. X */ Xvoid send_remote(name) Xchar *name; X{ X char cmdbuff[128]; X X#ifdef UMAILER /* faster than redirecting! */ X sprintf(cmdbuff, "exec %s -i%s %s %s %s >/dev/null", RMAIL, msg_temp, X#else X sprintf(cmdbuff, "exec %s <%s %s %s %s >/dev/null", RMAIL, msg_temp, X#endif UMAILER X immediate ? "-n" : "", verbose ? "-v" : "", adressee); X system(cmdbuff); /* call the mailer! */ X} X X X/* X * Deliver a message to a user. X * First of all, check if we can use a more intelligent mailer X * for this job. If not, deliver it ! X */ Xint deliver(count, vec) Xint count; char *vec[]; X{ X char cpbuff[1024]; /* copy buffer */ X register int i, c; X int errs = 0; /* count of errors */ X int dropfd; /* file descriptor for user's drop */ X int created = 0; /* true if we created the maildrop */ X FILE *mailfp; /* fp for mail */ X struct stat stb; /* for checking drop modes, owners */ X int (*sigint)(), (*sighup)(), (*sigquit)(); /* saving signal state */ X char lockname[PATHLEN]; /* maildrop lock */ X int locktries; /* tries when box is locked */ X struct passwd *pw; /* sender and recipent */ X X if (count > MAXRCPT) { X fprintf(stderr, "%s: too many recipients\n", progname); X return(-1); X } X X strcpy(recipients, ""); X if (sayall) { X for (i=0; i<count; i++) { X strcat(recipients, vec[i]); X /* RFC-822: separate with comma */ X strcat(recipients, ","); X } X recipients[strlen(recipients)-1] = '\0'; /* kill last comma */ X } else strcat(recipients, vec[0]); X X mailfp = edit_mail(); /* input the message */ X X /* shut off signals during the delivery */ X sigint = signal(SIGINT, SIG_IGN); X sighup = signal(SIGHUP, SIG_IGN); X sigquit = signal(SIGQUIT, SIG_IGN); X X /* X * We have the message, now deliver it to all recipients! X * Do this on a per-user basis! X */ X for (i = 0; i < count; ++i) { X if (count > 1) rewind(mailfp); /* rewind data-file */ X strcpy(adressee, vec[i]); /* get name of adressee */ X forward[0] = '\0'; /* Clear 'Forward' flag */ X X /* OK, 'adressee' is the recipient. Check if local. */ X if (strchr(adressee, '!') || strchr(adressee, '@')) { X remote = 1; /* the guy is remote... */ X send_remote(adressee); /* call RMAIL or SMAIL for this... */ X continue; X } X X /* Hmm, it is a local user. Do we know him? */ X if ((pw = getpwnam(adressee)) == (struct passwd *)NULL) { X fprintf(stderr, "%s: user %s unknown\n", progname, adressee); X ++errs; X dead_letter(); X continue; X } else { X sprintf(mailbox, DROPNAME, adressee); X sprintf(lockname, LOCKNAME, adressee); X } X X /* OK, we know him. Check if we have to forward his mail. */ X if (chk_box() == 1) { /* forward messages to 'forward' */ X strcpy(adressee, forward); X X /* We now have the final adressee. X * Check for remote users again. X */ X if (strchr(adressee, '!') || strchr(adressee, '@')) { X remote = 1; X send_remote(adressee); /* No, call RMAIL/SMAIL. */ X continue; X } else { /* Check if this guy is known. */ X if ((pw = getpwnam(adressee)) == X (struct passwd *)NULL) { X fprintf(stderr, X "%s: forward-user %s unknown\n", X progname, adressee); X ++errs; X dead_letter(); X continue; X } else { X sprintf(mailbox, DROPNAME, adressee); X sprintf(lockname, LOCKNAME, adressee); X } X } X } X X /* X * We now have a local user 'adressee' who exists. X * Lock the maildrop while we're messing with it. Races are possible X * (though not very likely) when we have to create the maildrop, but X * not otherwise. If the box is already locked, wait awhile and try X * again. X */ X locktries = created = 0; Xtrylock: X if (link(mailbox, lockname) != 0) { X if (errno == ENOENT) { /* user doesn't have a drop yet */ X if ((dropfd = creat(mailbox, 0600)) < 0) { X fprintf(stderr, X "%s: could not create maildrop for %s\n", X progname, vec[i]); X ++errs; X dead_letter(); X continue; X } X ++created; X goto trylock; X } else { /* somebody else has it locked, it seems - wait */ X if (++locktries >= LOCKTRIES) { X fprintf(stderr, X "%s: could not lock maildrop for %s\n", X progname, vec[i]); X ++errs; X dead_letter(); X continue; X } X sleep(LOCKWAIT); X goto trylock; X } X } X X if (created) { X chown(mailbox, pw->pw_uid, pw->pw_gid); X boxfp = fdopen(dropfd, "a"); X } else boxfp = fopen(mailbox, "a"); X X if (boxfp==(FILE *)NULL || stat(mailbox, &stb) < 0) { X fprintf(stderr, "%s: serious maildrop problems for %s\n", X progname, vec[i]); X unlink(lockname); X ++errs; X dead_letter(); X continue; X } X X if (stb.st_uid != pw->pw_uid || (stb.st_mode & S_IFMT) != S_IFREG) { X fprintf(stderr, "%s: mailbox for user %s is illegal\n", X progname, vec[i]); X unlink(lockname); X ++errs; X dead_letter(); X continue; X } X X /* copy temp. file to mailbox */ X while (TRUE) { X if (fgets(cpbuff, sizeof(cpbuff), mailfp) == (char *)NULL) X break; X fwrite(cpbuff, sizeof(char), strlen(cpbuff), boxfp); X } X X /* to make sure! */ X fputc('\n', boxfp); X if (ferror(boxfp) || fclose(boxfp) != 0) { X fprintf(stderr, "%s: error delivering to user %s", X progname, vec[i]); X perror(""); X dead_letter(); X ++errs; X } X X unlink(lockname); X } X X fclose(mailfp); X X /* put signals back the way they were */ X signal(SIGINT, sigint); X signal(SIGHUP, sighup); X signal(SIGQUIT, sigquit); X X return (errs == 0) ? 0 : -1; X} X X X/* X * Save the current message to file 'dead.letter'. X * This is sometimes needed (when delivery fails!). X */ Xvoid dead_letter(void) X{ X char cpbuff[1024]; X char fname[PATHLEN]; X char *sp; X register FILE *inf, *outf; X X fname[0] = '\0'; X inf = fopen(msg_temp, "r"); X if ((sp = getenv("HOME")) != NULL) { X strcpy(fname, sp); X if (strlen(sp) > 1) X strcat(fname, "/"); X } X strcat(fname, DEADLETTER); X X inf = fopen(msg_temp, "r"); X if (inf == (FILE *)NULL) { X fprintf(stderr, "%s: cannot open \"%s\"\n", progname, msg_temp); X return; X } X X outf = fopen(fname, "w"); X if (outf == (FILE *)NULL) { X fprintf(stderr, "%s: cannot create \"%s\"\n", progname, fname); X return; X } X X /* copy temp. file to dead.letter */ X while (TRUE) { X if (fgets(cpbuff, sizeof(cpbuff), inf) == (char *)NULL) break; X fwrite(cpbuff, sizeof(char), strlen(cpbuff), outf); X } X X fclose(inf); X fclose(outf); X X chown(fname, old_uid, old_gid); X fprintf(stderr, "%s: dumped message on file \"%s\"\n", progname, fname); X} + END-OF-FILE wmdeliver.c chmod 'u=rw,g=r,o=r' 'wmdeliver.c' set `wc -c 'wmdeliver.c'` count=$1 case $count in 9423) :;; *) echo 'Bad character count in ''wmdeliver.c' >&2 echo 'Count should be 9423' >&2 esac echo Extracting 'wminteract.c' sed 's/^X//' > 'wminteract.c' << '+ END-OF-FILE ''wminteract.c' X/* X * WMAIL - MicroWalt Extended Mail Agent. X * This is the MicroWalt Mail Agent; which is derived X * from the "Mini-Mail" written by Peter S. Housel. X * This version has a better user-interface, and it X * also "knows" about things like forwarding, replies X * etc. Overall, it looks like the Mail (uppercase M) X * on our local DYNIX(tm) (==BSD) system... X * The paging-code (for paging letters on the screen) X * was taken from "more.c", by Brandon Allbery. X * X * I N T E R A C T I O N M O D U L E X * X * Author: Fred van Kempen, MicroWalt Corporation X * X * To Do: X * - Builtin escapes (~i and friends) X */ X#include <stdio.h> X#include <ctype.h> X#include <string.h> X#include <signal.h> X#include "wmail.h" X X X/* X * Send a reply to a message. X */ Xstatic int do_reply(let) XLETTER *let; X{ X char repuser[512]; X char *who[2]; X X strcpy(repuser, let->sender); X who[0] = &repuser[0]; X who[1] = (char *)NULL; X sprintf(subject, "Re: %s", let->subject); X X printf("\n*** From: %s %-24.24s\n", sender, xtime()); X printf("*** To: %s\n", who[0]); X printf("*** Subject: %s\n\n", subject); X X deliver(1, who); X} X X X/* X * Execute a shell (or a command only) X */ Xstatic void do_shell(command) Xchar *command; X{ X int waitstat, pid; X char *shell; X X if ((shell = getenv("SHELL")) == NIL) shell = SHELL; X X if ((pid = fork()) < 0) { X fprintf(stderr, "%s: could not fork", progname); X return; X } else if (pid != 0) { /* parent */ X wait(&waitstat); X return; X } X X /* child */ X setgid(old_gid); /* UUCP or USER */ X setuid(old_uid); /* UUCP or USER */ X umask(oldmask); X X execl(shell, shell, "-c", command, NIL); X fprintf(stderr, "%s: cannot exec shell", progname); X exit(127); X} X X X/* X * Goto a specific letter. X */ Xstatic LETTER *goto_letter(num) Xint num; X{ X LETTER *let; X X let = firstlet; X while (let != NIL_LET) { X if (let->seqno == num) return(let); X let = let->next; X } X return(NIL_LET); X} X X X/* X * Skip the header of message 'let'. X * Do this by just updating the 'boxfp' pointer... X */ Xoff_t skiphead(let) XLETTER *let; X{ X char xbuf[1024]; X off_t count; X X count = (off_t) 0; X while (fgets(xbuf, sizeof(xbuf), boxfp) != NIL) { X count += (off_t) strlen(xbuf); X if (xbuf[0] == '\n') break; /* end of header */ X } X return(count); X} X X X/* X * Save the current letter to a disk-file. X */ Xvoid savelet(let, savefile, withhead) XLETTER *let; Xchar *savefile; Xint withhead; X{ X int c; X off_t curr, limit, oldpos; X register char *bp; X FILE *savefp; X X bp = savefile; X while (*bp && *bp!='\n') bp++; X *bp = '\0'; X X if ((savefp = fopen(savefile, "a")) == (FILE *)NULL) { X fprintf(stderr, "%s: cannot append to savefile \"%s\"\n", X progname, savefile); X return; X } X X oldpos = ftell(boxfp); X fseek(boxfp, (curr = let->location), 0); X limit = (let->next != NIL_LET) ? let->next->location : -1L; X X if (withhead == 0) curr += skiphead(let); /* skip the message header */ X while(curr != limit && (c = fgetc(boxfp)) != EOF) { X fputc(c, savefp); X ++curr; X } X fflush(savefp); X fseek(boxfp, oldpos, 0); X X if ((ferror(savefp) != 0) | (fclose(savefp) != 0)) { X fprintf(stderr, "%s: savefile write error:", progname); X } X X chown(savefile, old_uid, old_gid); X} X X X/* X * Give a list of possible Interact Commands. X */ Xstatic void do_help(void) X{ X printf("\n ** W-MAIL Commands **\n\n"); X printf("?\tThis help\n"); X printf("!\tShell Command Escape\n"); X printf("-\tPrevious letter\n"); X printf("+\tNext letter\n"); X printf("<ENTER>\tNext letter\n"); X printf("d\tDelete current letter\n"); X printf("i\tDisplay a summary of letters\n"); X printf("p\tPrint a letter again\n"); X printf("q\tQuit, update mailbox\n"); X printf("r\tReply to the current letter\n"); X printf("s\tSave current letter\n"); X printf("t\tType a letter, no paging\n"); X printf("w\tSave letter without header\n"); X printf("x\tExit, do not update mailbox\n"); X printf("\n"); X} X X X/* X * Give a summary of letters./ X */ Xstatic void summary(void) X{ X register LETTER *let; X register int i; X register char *sp; X X let = firstlet; X printf(" No. Sender Date Subject\n"); X printf(" ----------------------------------------------"); X printf("--------------------------------\n"); X X for (i=0; i<numlet; i++) { X sp = let->date; X if (strchr(sp, ',')) sp += 5; /* new-style ctime() */ X else sp += 4; X printf("%c%c %-3.3d %-8.8s %-15.15s \"%.40s\"\n", X (nextlet == let->seqno) ? '>': ' ', X (let->status == DELETED) ? '*': ' ', X let->seqno, basepath(let->sender), sp, let->subject); X let = let->next; X } X X printf("\n"); X} X X X/* X * Interactively read the mail-box. X */ Xvoid interact(void) X{ X char input[512]; /* user input line */ X char *p; X LETTER *let, dummy; /* current and next letter */ X LETTER *templet; X int interrupted = 0; /* SIGINT hit during letter print */ X char *savefile; /* filename to save into */ X int i, temp; X X if (firstlet == NIL_LET) { X printf("No mail for %s.\n", sender); X return; X } X X printf("W-MAIL %s. Type ? for Help.\n", Version); X printf("\"%s\": %d message(s)\n\n", mailbox, numlet); X X nextlet = 1; X dummy.seqno = nextlet; X dummy.next = firstlet; X let = &dummy; X X summary(); X X while(1) { X nextlet = let->seqno; X X if (!quitmode) { X interrupted = setjmp(printjump); X signal(SIGINT, onint); X } X X if (interrupted) printf("\n"); X printf(PROMPT, let->seqno); X fflush(stdout); X X if (fgets(input, sizeof(input), stdin) == NIL) break; X X if (!quitmode) signal(SIGINT, SIG_IGN); X X switch(input[0]) { X case '!': X do_shell(input + 1); X continue; X case '?': X do_help(); X continue; X case '-': X if (let->prev != NIL_LET) let = let->prev; X else printf("Top of mailbox\n"); X continue; X case '+': X case '\n': X if (let->next != NIL_LET) { X let = let->next; X if (!interrupted) { X if (let->status != DELETED) X let->status = READ; X printlet(let, stdout); X } X } else printf("At EOF\n"); X continue; X case 'd': X let->status = DELETED; X if (let->next != NIL_LET) let = let->next; X needupdate = 1; X continue; X case 'i': X summary(); X continue; X case 'p': X if (!interrupted) printlet(let, stdout); X continue; X case 'q': X return; X case 'r': X do_reply(let); X break; X case 's': X savefile = &input[1]; X if (*savefile != '\0') { X while (*savefile==' ' || *savefile=='\t') X savefile++; X } else savefile = SAVEFILE; X savelet(let, savefile, 1); X continue; X case 't': X temp = printmode; X printmode = 1; X if (!interrupted) printlet(let, stdout); X printmode = temp; X continue; X case 'w': X savefile = &input[1]; X if (*savefile != '\0') { X while (*savefile==' ' || *savefile=='\t') X savefile++; X } else savefile = SAVEFILE; X savelet(let, savefile, 0); X continue; X case 'x': X exit(0); X default: X if (isdigit(input[0])) { X templet = goto_letter(atoi(input)); X if (templet != NIL_LET) { X let = templet; X printlet(let, stdout); X } else printf("Illegal message-number\n"); X } else printf("Illegal command\n"); X continue; X } X } X} X + END-OF-FILE wminteract.c chmod 'u=rw,g=r,o=r' 'wminteract.c' set `wc -c 'wminteract.c'` count=$1 case $count in 7139) :;; *) echo 'Bad character count in ''wminteract.c' >&2 echo 'Count should be 7139' >&2 esac echo Extracting 'wmmain.c' sed 's/^X//' > 'wmmain.c' << '+ END-OF-FILE ''wmmain.c' X/* X * WMAIL - MicroWalt Extended Mail Agent. X * This is the MicroWalt Mail Agent; which is derived X * from the "Mini-Mail" written by Peter S. Housel. X * This version has a better user-interface, and it X * also "knows" about things like forwarding, replies X * etc. Overall, it looks like the Mail (uppercase M) X * on our local DYNIX(tm) (==BSD) system... X * The paging-code (for paging letters on the screen) X * was taken from "more.c", by Brandon Allbery. X * X * Author: Fred van Kempen, MicroWalt Corporation X * X * To Do: X */ X#include <sys/stat.h> X#include <stdio.h> X#include <string.h> X#include <errno.h> X#include <pwd.h> X#include "wmail.h" X X Xchar *Version = VERSION; Xint remote = FALSE; /* use RMAIL to deliver (if any) */ Xint loclink = FALSE; /* LMAIL: local delivery only! */ Xint old_uid, old_gid; /* UID/GID of calling user */ Xint printmode = FALSE; /* print-and-exit mode */ Xint immediate = FALSE; /* send remote immediately! */ Xint quitmode = FALSE; /* take interrupts */ Xint usedrop = TRUE; /* read the maildrop (no -f given) */ Xint verbose = FALSE; /* pass "-v" flag on to mailer */ Xint needupdate = FALSE; /* need to update mailbox */ Xint sayall = FALSE; /* include line with all recipients */ Xint checkonly = FALSE; /* only chack if there is mail */ Xchar sender[PATHLEN]; /* who sent the message? */ Xchar forward[PATHLEN]; /* who is the message forwarded to? */ Xchar adressee[PATHLEN]; /* current recipient */ Xchar recipients[PATHLEN]; /* also to... users */ Xchar mailbox[PATHLEN]; /* user's mailbox/maildrop */ Xchar subject[PATHLEN]; /* subject of message */ Xchar msg_temp[PATHLEN]; /* temporary file */ Xchar findbuff[128]; Xchar inbuff[512]; Xchar lbuff[512]; XFILE *infp = (FILE *)NULL; /* current message input file */ XFILE *boxfp = (FILE *)NULL; /* mailbox file */ Xchar *progname; /* program name */ Xjmp_buf printjump; /* for quitting out of letters */ XLETTER *firstlet, *lastlet; /* message pointers for readbox() */ Xint numlet, nextlet, seqno; /* number of active letters */ Xunsigned oldmask; /* saved umask() */ X X Xextern char *optarg; /* from the GETOPT(3) package */ Xextern int getopt(), optind; Xextern int errno; /* from STDIO */ X X Xvoid onint(void) X{ X longjmp(printjump, 1); X} X X X/* X * Chop off the last (file) part of a filename. X */ Xchar *basename(name) Xchar *name; X{ X char *p; X char *strrchr(); X X p = strrchr(name, '/'); X if (p == NIL) return(name); /* no pathname */ X else return(p + 1); X} X X X/* X * Chop off the last (user) part of a UUCP path-name. X * This is needed for the summary() routine. X */ Xchar *basepath(name) Xchar *name; X{ X char *p; X char *strrchr(); X X p = strrchr(name, '!'); X if (p == NIL) return(name); /* no pathname */ X else return(p + 1); X} X X X/* X * return ASCII text of our login-name. X */ Xchar *whoami(void) X{ X struct passwd *pw; X X if ((pw = getpwuid(getuid())) != (struct passwd *)NULL) X return(pw->pw_name); X else return("nobody"); X} X X X/* X * Find the given antry in the mail-header X * Search for the first occurence of string 'text' in the header. X * Copy the text following it into the 'let' structure. X * Return buffer if found, else NULL. X */ Xchar *find_string(let, text) XLETTER *let; Xchar *text; X{ X int all; X off_t curr, limit; X register char *sp; X X fseek(boxfp, let->location, 0); X limit = (off_t) -1L; X if (let->next != NIL_LET) limit = let->next->location; X X all = 0; X curr = let->location; X while (curr != limit && !all) { X if (fgets(inbuff, sizeof(inbuff), boxfp) == NIL) all = 1; X if (inbuff[0] == '\0') all = 1; /* end-of-header */ X X if (!strncmp(inbuff, text, strlen(text))) { X sp = &inbuff[0]; /* remove '\n' */ X while (*sp && *sp!='\n') sp++; X *sp = '\0'; X sp = &inbuff[0] + strlen(text); /* copy to static buff */ X strcpy(findbuff, sp); X return(findbuff); /* return adress of buff */ X } X X curr += (off_t) strlen(inbuff); /* update message offset */ X X if (!all && limit > 0L) /* quit if past message */ X if (curr >= limit) all = 1; X } X return(NIL); X} X X X/* X * Check is the first line of the mailbox contains a line like X * X * Forward to XXXX X * X * then all mail for the calling user is being forwarded X * to user XXXX. Return a 1 value if this is the case. X * Otherwise, return 0 (or -1 for error). X */ Xint chk_box(void) X{ X char xbuf[128]; X char *bp; X FILE *fp; X X if (access(mailbox, 4) < 0 || X (fp = fopen(mailbox, "r")) == (FILE *)NULL) { X if (usedrop && errno==ENOENT) return(-1); X fprintf(stderr, "%s: cannot access mailbox ", progname); X perror(mailbox); X exit(1); X } X X bp = fgets(xbuf, sizeof(xbuf), fp); X fclose(fp); X X if (bp!=NIL && !strncmp(xbuf, "Forward to ", 11)) { X strcpy(forward, strrchr(xbuf, ' ') + 1); /* get username */ X forward[strlen(forward)-1] = '\0'; /* remove \n */ X return(1); X } X return(0); X} X X X/* X * Read the contents of the Mail-Box into memory. X */ Xint readbox(void) X{ X register LETTER *let; X register char *sp, *bp; X off_t current; X X firstlet = lastlet = NIL_LET; X numlet = 0; X seqno = 1; X X if (chk_box() == 1) return(1); /* mail is being forwarded... */ X X if ((boxfp = fopen(mailbox, "r")) == (FILE *)NULL) { X if (usedrop && errno==ENOENT) return(-1); X fprintf(stderr, "%s: cannot access mailbox ", progname); X perror(mailbox); X exit(1); X } X X /* X * Determine where all messages start. X * This should be done with an index file in the future! X */ X current = 0L; X while(fgets(lbuff, sizeof(lbuff), boxfp) != NIL) { X current = ftell(boxfp); X if (!strncmp(lbuff, "From ", 5)) { X if ((let = (LETTER *)malloc(sizeof(LETTER))) == NIL_LET) { X fprintf(stderr, "%s: out of memory.\n", progname); X exit(1); X } X if (lastlet == NIL_LET) { X firstlet = let; X let->prev = NIL_LET; X } else { X let->prev = lastlet; X lastlet->next = let; X } X lastlet = let; X let->next = NIL_LET; X X let->status = UNREAD; X let->location = current - (off_t) strlen(lbuff); X let->seqno = seqno++; X numlet++; X } X } X X /* X * We now know where the messages are, read message headers. X */ X let = firstlet; X while (let != NIL_LET) { X sp = find_string(let, "From "); X if (sp == NIL) sp = "<unknown>"; /* should never occur! */ X strncpy(let->sender, sp, 511); X sp = let->sender; X while (*sp && *sp!=' ') sp++; X *sp = '\0'; X X sp = find_string(let, "Subject: "); X if (sp == NIL) sp = "<none>"; X strncpy(let->subject, sp, 79); X X sp = find_string(let, "From "); X while (*sp && *sp!=' ') sp++; X sp++; X strncpy(let->date, sp, 31); X X let = let->next; X } X} X X X/* X * Check if there is any mail for the calling user. X * Return 0 if there is mail, or 1 for NO MAIL. X */ Xstatic int chk_mail(void) X{ X FILE *fp; X char temp[512]; X X if ((fp = fopen(mailbox, "r")) != (FILE *)NULL) { X if (fgets(temp, sizeof(temp), fp) == NIL) { X fclose(fp); /* empty mailbox, no mail! */ X return(1); X } X if (!strncmp(temp, "Forward to ", 11)) { X fclose(fp); /* FORWARD line in mailbox */ X return(2); /* so no mail. */ X } X fclose(fp); /* another line, so we have mail! */ X return(0); X } X return(1); X} X X Xstatic void usage(void) X{ X fprintf(stderr, "Usage:\n"); X fprintf(stderr, "\t%s [-epqrv] [-f file]\n", progname); X fprintf(stderr, "\t%s [-dtv] [-i file] [-s subject] user ...\n", progname); X fprintf(stderr, "\t%s [-lv] [-i file] user\n\n", progname); X} X X Xmain(argc, argv) Xint argc; char *argv[]; X{ X int c, st; X X strcpy(sender, whoami()); X old_uid = getuid(); /* get calling user and save */ X old_gid = getgid(); X setuid(geteuid()); /* set UID to ROOT (SU) */ X setgid(getegid()); /* set GID to ROOT (SU) */ X X progname = basename(argv[0]); /* how are we called? */ X if (*progname == 'l') { X remote = 0; /* 'lmail' link? */ X loclink = 1; X } X strcpy(msg_temp, MAILTEMP); X mktemp(msg_temp); /* name the temp file */ X oldmask = umask(077); /* change umask for security */ X infp = stdin; /* set INPUT to stdin */ X X while ((c = getopt(argc, argv, "def:i:lpqrs:tv")) != EOF) switch(c) { X case 'd': X immediate++; X break; X case 'e': X checkonly++; X break; X case 'f': X usedrop = 0; X strncpy(mailbox, optarg, PATHLEN - 1); X break; X case 'i': X infp = fopen(optarg, "r"); X if (infp == (FILE *)NULL) { X fprintf(stderr, X "%s: cannot open %s\n", progname, optarg); X exit(-1); X } X break; X case 'l': X loclink = 1; X break; X case 'p': X printmode++; X break; X case 'q': X quitmode++; X break; X case 'r': X break; /* no reverse order supported! */ X case 's': X strcpy(subject, optarg); X break; X case 't': X sayall++; X break; X case 'v': X verbose++; X break; X default: X usage(); X exit(2); X } X X if (optind >= argc) { X if (usedrop) sprintf(mailbox, DROPNAME, sender); X X if (checkonly) { X st = chk_mail(); X exit(st); X } X X if ((st = readbox()) == 1) { X fprintf(stderr, "Your mail is being forwarded to %s.\n", X forward); X exit(1); X } else { X st = 0; X if (printmode) printall(); X else interact(); X X if (needupdate) updatebox(); X } X } else st = deliver(argc - optind, argv + optind); X X unlink(msg_temp); X exit(st); X} + END-OF-FILE wmmain.c chmod 'u=rw,g=r,o=r' 'wmmain.c' set `wc -c 'wmmain.c'` count=$1 case $count in 9095) :;; *) echo 'Bad character count in ''wmmain.c' >&2 echo 'Count should be 9095' >&2 esac echo Extracting 'wmread.c' sed 's/^X//' > 'wmread.c' << '+ END-OF-FILE ''wmread.c' X/* X * WMAIL - MicroWalt Extended Mail Agent. X * This is the MicroWalt Mail Agent; which is derived X * from the "Mini-Mail" written by Peter S. Housel. X * This version has a better user-interface, and it X * also "knows" about things like forwarding, replies X * etc. Overall, it looks like the Mail (uppercase M) X * on our local DYNIX(tm) (==BSD) system... X * The paging-code (for paging letters on the screen) X * was taken from "more.c", by Brandon Allbery. X * X * R E A D M A I L M O D U L E X * X * Author: Fred van Kempen, MicroWalt Corporation X * X * To Do: X * - TERMCAP/TERMINFO use ! X */ X#include <stdio.h> X#include <string.h> X#include <sgtty.h> X#include "wmail.h" X X Xstatic int mline; /* current terminal line */ Xstatic int mcol; /* current terminal column */ Xstatic char mobuf[512]; /* output buffer */ Xstatic int mobc; /* position in output buffer (== chars in) */ Xstatic int misdone; /* flag: return EOF next read even if not */ X X X/* X * Write one line of text to the screen. X * Return 1 if a Quit command was given. X */ Xstatic int lwrite(fd, buf, len) Xint fd; Xchar *buf; Xunsigned len; X{ X unsigned here, start; X char cmd; X X start = 0; X here = 0; X while (here != len) { X cmd = '\0'; X switch (buf[here++]) { X case '\n': X mcol = 0; X if (++mline == 23) { X fflush(stdout); X write(1, buf + start, here - start); X fflush(stdout); X fprintf(stderr, "\007\033[7m--More--\033[0m"); X do { X read(fd, &cmd, 1); X } while (strchr(" \r\nqQ'nN", cmd) X == (char *)NULL); X fprintf(stderr, "\r\033[K"); X mline = 0; X start = here; X } X break; X case '\r': X mcol = 0; X break; X case '\b': X if (mcol != 0) mcol--; X else { X mline--; X mcol = 80 - 1; X } X break; X case '\t': X do { X mcol++; X } while (mcol % 8 != 0); X break; X default: X if ( buf[here-1] < ' ' || (buf[here-1] & 0x80) ) X buf[here-1] = '?'; X if (++mcol == 80) { X mcol = 0; X if (++mline == 23) { X fflush(stdout); X write(1, buf + start, here - start); X fflush(stdout); X fprintf(stderr, X "\007\033[7m--More--\033[0m"); X do { X read(fd, &cmd, 1); X } while (strchr(" \r\nqQ'nN", cmd) X == (char *)NULL); X fprintf(stderr, "\r\033[K"); X mline = 0; X start = here; X } X } X } X switch (cmd) { X case '\0': X break; X case ' ': X mline = 0; X break; X case '\r': X case '\n': X mline = 23 - 1; X break; X case 'q': X case 'Q': X misdone = 1; X return(1); X case 'n': X case 'N': X misdone = 1; X return(1); X default: X break; X } X } X if (here != start) { X fflush(stdout); X write(1, buf + start, here - start); X fflush(stdout); X } X} X X X/* X * Display the given letter on the screen. X * Do this on a per-page basis... X */ Xvoid showlet(let) XLETTER *let; X{ X struct sgttyb ttymode; X off_t curr, limit; X int c, fd, st; X X if ((fd = open("/dev/tty", 0)) == -1) { X fprintf(stderr, "%s: cannot open /dev/tty\n", progname); X return; X } X ioctl(fd, TIOCGETP, &ttymode); X ttymode.sg_flags |= CBREAK; X ttymode.sg_flags &= ~ECHO; X ioctl(fd, TIOCSETP, &ttymode); X X fseek(boxfp, (curr = let->location), 0); X limit = (let->next != NIL_LET) ? let->next->location : -1L; X X st = mline = mcol = mobc = misdone = 0; X printf("Message %d:\n", let->seqno); X while(curr != limit && ((c = fgetc(boxfp)) != EOF) && st==0) { X if (mobc == sizeof(mobuf)) { X st = lwrite(fd, mobuf, strlen(mobuf)); X mobc = 0; X } X mobuf[mobc++] = (char) c; X ++curr; X } X if (st == 0) lwrite(fd, mobuf, mobc); X mobc = 0; X fflush(stdout); X X ttymode.sg_flags &= ~CBREAK; X ttymode.sg_flags |= ECHO; X ioctl(fd, TIOCSETP, &ttymode); X close(fd); X} X X X/* X * Print the contents of letter 'let' to file 'tofp' X */ Xvoid printlet(let, tofp) XLETTER *let; XFILE *tofp; X{ X off_t curr, limit, oldpos; X int c; X X if (tofp==stdout && !printmode) { X showlet(let); X return; X } X X oldpos = ftell(boxfp); X fseek(boxfp, (curr = let->location), 0); X limit = (let->next != NIL_LET) ? let->next->location : -1L; X X if (tofp == stdout) printf("Message %d:\n", let->seqno); X while(curr != limit && (c = fgetc(boxfp)) != EOF) { X fputc(c, tofp); X ++curr; X } X fflush(tofp); X fseek(boxfp, oldpos, 0); X} X X X/* X * Print all letters and quit. X */ Xvoid printall() X{ X LETTER *let; X X let = firstlet; X if (let == NIL_LET) { X fprintf(stderr, "No mail for %s.\n", sender); X return; X } X X while(let != NIL_LET) { X printlet(let, stdout); X let = let->next; X } X} + END-OF-FILE wmread.c chmod 'u=rw,g=r,o=r' 'wmread.c' set `wc -c 'wmread.c'` count=$1 case $count in 4664) :;; *) echo 'Bad character count in ''wmread.c' >&2 echo 'Count should be 4664' >&2 esac echo Extracting 'wmtime.c' sed 's/^X//' > 'wmtime.c' << '+ END-OF-FILE ''wmtime.c' X/* X * XTIME - Create ASCII string of the given time. X * This file contains a modified version X * of the ctime(3) function from the MINIX X * C library. The format of the string is: X * X * Sat, Oct 14 89 20:26:00\0 X * X */ X#include <time.h> X Xstatic int days_per_month[] = { X 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 X}; Xstatic char *months[] = { X "Jan", "Feb", "Mar", "Apr", "May", "Jun", X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" X}; Xstatic char *days[] = { X "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" X}; X X#define MIN 60L /* # seconds in a minute */ X#define HOUR (60 * MIN) /* # seconds in an hour */ X#define DAY (24 * HOUR) /* # seconds in a day */ X#define YEAR (365 * DAY) /* # seconds in a year */ X Xchar *xtime(void) X{ X static struct tm tm; X static char xtmbuf[30]; X long t; X long year; X X time(&t); X tm.tm_year = 0; X tm.tm_mon = 0; X tm.tm_mday = 1; X tm.tm_hour = 0; X tm.tm_min = 0; X tm.tm_sec = 0; X X /* t is elapsed time in seconds since Jan 1, 1970. */ X tm.tm_wday = (int) (t/DAY + 4L) % 7; /* Jan 1, 1970 is 4th wday */ X while (t >= (year=((tm.tm_year%4)==2) ? YEAR+DAY : YEAR)) { X tm.tm_year += 1; X t -= year; X } X tm.tm_year += 1970; X X /* t is now the offset into the current year, in seconds. */ X tm.tm_yday = (t/DAY); /* day # of the year, Jan 1 = 0 */ X X days_per_month[1] = 28; X if ((tm.tm_year % 4) == 0) /* check for leap year */ X days_per_month[1]++; X X /* Compute month. */ X while (t >= (days_per_month[tm.tm_mon] * DAY)) X t -= days_per_month[tm.tm_mon++] * DAY; X X /* Month established, now compute day of the month */ X while (t >= DAY) { X t -= DAY; X tm.tm_mday++; X } X X /* Day established, now do hour. */ X while (t >= HOUR) { X t -= HOUR; X tm.tm_hour++; X } X X /* Hour established, now do minute. */ X while (t >= MIN) { X t -= MIN; X tm.tm_min++; X } X X /* Residual time is # seconds. */ X tm.tm_sec = (int) t; X X /* Generate output in ASCII in _buf_. */ X sprintf(xtmbuf, "%s, %2.2d %s %2.2d %02d:%02d:%02d", X days[tm.tm_wday], tm.tm_mday, months[tm.tm_mon], X tm.tm_year - 1900, tm.tm_hour, tm.tm_min, tm.tm_sec); X return(xtmbuf); X} + END-OF-FILE wmtime.c chmod 'u=rw,g=r,o=r' 'wmtime.c' set `wc -c 'wmtime.c'` count=$1 case $count in 2099) :;; *) echo 'Bad character count in ''wmtime.c' >&2 echo 'Count should be 2099' >&2 esac exit 0