home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume10
/
pcmail2
/
part08
< prev
next >
Wrap
Text File
|
1990-01-24
|
61KB
|
1,988 lines
Newsgroups: comp.sources.misc
subject: v10i040: PC-MAIL release 2, 8/11
from: wswietse@lso.win.tue.nl (Wietse Venema)
Sender: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
Posting-number: Volume 10, Issue 40
Submitted-by: wswietse@lso.win.tue.nl (Wietse Venema)
Archive-name: pcmail2/part08
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 8 (of 11)."
# Contents: daemon/pc-mail.c main/DEFAULT.ins main/desk.c
# main/gtrans.c main/window.c
# Wrapped by wswietse@tuewsa on Mon Jan 22 17:27:20 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f daemon/pc-mail.c -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"daemon/pc-mail.c\"
else
echo shar: Extracting \"daemon/pc-mail.c\" \(11399 characters\)
sed "s/^X//" >daemon/pc-mail.c <<'END_OF_daemon/pc-mail.c'
X/*++
X/* NAME
X/* pc-mail 8
X/* SUMMARY
X/* deliver mail to nfs-based pc-mail users
X/* PROJECT
X/* pc-mail
X/* PACKAGE
X/* nfs
X/* SYNOPSIS
X/* pc-mail user
X/* DESCRIPTION
X/* This program is to be run on the nfs server that exports mail
X/* directories to MS-DOS pc-mail users. The program replaces the
X/* UNIX -> MS-DOS file transfer function of the MS-DOS \fIcico\fR
X/* program.
X/*
X/* Normally, the pc-mail delivery program is invoked by sendmail(8).
X/* Its purpose is to deliver new mail in the mail directory of the
X/* specified \fIuser\fR (default /usr/spool/pc-mail/\fIuser\fR).
X/* Any error conditions detected by the pc-mail delivery program
X/* are reported back in a sendmail-compatible manner.
X/*
X/* This program must be run with root privileges. It will assume
X/* the (uid, gid) of the specified user before delivering mail.
X/*
X/* The program attempts to create any missing directories, and to
X/* correct ownerships or protections where needed.
X/* FILES
X/* /usr/spool/pc-mail/\fIuser\fR/nNNNNN, mail message.
X/* /usr/spool/pc-mail/\fIuser\fR/hNNNNN, sender of message and subject.
X/* (NNNNN is the pc-mail "message id").
X/* SEE ALSO
X/* pc-maild(1)
X/* DIAGNOSTICS
X/* All conceivable error conditions cause the program to terminate
X/* with a non-zero exit status, after printing an error message on
X/* the standard error stream, and appending an entry to the system log.
X/* See <sysexits.h> for details.
X/* BUGS
X/* There is no way to notify a pc-mail user of the arrival of new mail.
X/* AUTHOR(S)
X/* W.Z. Venema
X/* Eindhoven University of Technology
X/* Department of Mathematics and Computer Science
X/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
X/* CREATION DATE
X/* Sun Oct 22 18:00:53 MED 1989
X/* LAST MODIFICATION
X/* 1/6/90 19:08:13
X/* VERSION/RELEASE
X/* 1.10
X/*--*/
X
X#ifndef lint
Xstatic char sccsid[] = "@(#) pc-mail.c 1.10 1/6/90 19:08:13";
X
X#endif
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <pwd.h>
X#include <varargs.h>
X
X#ifdef SYSLOG
X#include <syslog.h>
X#else
X#include "syslog.h"
X#endif
X
X#ifdef SYSV
X#include <ndir.h>
X#else
X#include <sys/dir.h>
X#endif
X
X#ifdef SYSEXITS
X#include <sysexits.h>
X#else
X#include "sysexits.h"
X#endif
X
X#include "dosunix.h"
X#include "percentm.h"
X#include "ms_parse.h"
X
X/* Stuff related to failed system calls */
X
Xextern int errno;
X
X/* External functions */
X
Xextern struct passwd *getpwnam();
Xextern long time();
Xextern char *mktemp();
Xextern void exit();
Xextern unsigned sleep();
X
X/* Local declarations */
X
X#ifndef MAILDIR
X#define MAILDIR "/usr/spool/pc-mail" /* pc-mail directory tree */
X#endif
X
X#define LOCK "pc-mail.lck" /* the lock file */
X#define STALE 1800 /* max age of lock file */
X#define MAXTRY 60 /* max retry count for lock creation */
X#define MSGFIL_FMT "n%05d" /* message file name format */
X#define SNDFIL_FMT "h%05d" /* sender file name format */
X#define MAXLINE 1024 /* max length of recipient line */
X
Xchar template[] = "pc.XXXXXX"; /* template lock file */
X
X/* local functions */
X
Xvoid sender();
Xvoid message();
Xvoid error();
X
Xchar *progname;
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X struct passwd *pwd;
X static char userdir[BUFSIZ];
X int seqno;
X
X progname = argv[0];
X
X /* Garbage in, garbage out */
X
X if (argc != 2)
X error(EX_USAGE, "usage: %s user", *argv);
X
X#ifndef DEBUG
X if (geteuid() != 0)
X error(EX_USAGE, "must run with root privileges");
X
X /* need this for SYSVR2 or mkdir(1) fails */
X#ifdef SYSV
X if (setuid(0) != 0)
X error(EX_OSERR, "cannot setuid(0)");
X#endif
X#endif
X
X if ((pwd = getpwnam(argv[1])) == 0)
X error(EX_NOUSER, "unknown user: %s", argv[1]);
X
X /* Setup a somewhat safe environment */
X
X if (putenv("PATH=/bin:/usr/bin:/usr/ucb")
X || putenv("IFS= \t\n"))
X error(EX_TEMPFAIL, "putenv() failed");
X
X /* Check the necessary directories exist */
X
X (void) sprintf(userdir, "%s/%s", MAILDIR, argv[1]);
X checkdir(userdir, pwd->pw_uid, pwd->pw_gid, 0700);
X
X /* Now with that out of the way, try to deliver the message */
X
X if (setgid(pwd->pw_gid))
X error(EX_USAGE, "setgid(%s) failed: %m", argv[1]);
X if (setuid(pwd->pw_uid))
X error(EX_USAGE, "setuid(%s) failed: %m", argv[1]);
X
X /* make sure the user mail directory is accessible */
X
X if (chdir(userdir))
X error(EX_TEMPFAIL, "can't access mail directory %s", userdir);
X
X /* deliver mail */
X
X seqno = newseqno(userdir); /* Allocate sequence number */
X message(pwd, seqno); /* Create message file */
X sender(seqno); /* Create metafile (sender) */
X exit(EX_OK); /* Done. */
X /* NOTREACHED */
X}
X
X/* message - write message file */
X
Xvoid message(pwd, seqno)
Xstruct passwd *pwd;
Xint seqno;
X{
X static char buf[BUFSIZ];
X register FILE *fp;
X
X /* Create the message file */
X
X (void) sprintf(buf, MSGFIL_FMT, seqno);
X if ((fp = fopen(buf, "w")) == 0)
X error(EX_CANTCREAT, "create error for file %s/%s: %m",
X pwd->pw_name, buf);
X if (unix2dos(stdin, fp)) {
X (void) unlink(buf);
X error(EX_CANTCREAT, "write error for file %s/%s: %m",
X pwd->pw_name, buf);
X }
X (void) fclose(fp);
X (void) chmod(buf, 0400); /* Avoid tampering */
X}
X
X/* sender - extract sender from message */
X
Xvoid sender(seqno)
Xint seqno;
X{
X register FILE *ifp;
X register FILE *ofp;
X static char fname[BUFSIZ]; /* file names */
X static char line[MAXLINE]; /* read buffer */
X static char from[MAXLINE] = "Unknown"; /* sender */
X static char subject[MAXLINE] = ""; /* subject */
X register int context = MS_UUCP;
X
X /*
X * Try to open the message file; if that fails, let the pc software scan
X * for the sender at a later time.
X *
X * We recognize the following From line formats:
X *
X * From name stuff use name
X *
X * >From name stuff use name
X *
X * From: address (full_name) use full_name
X *
X * From: full_name <address> use full_name
X *
X * From: full_name use full_name
X */
X
X (void) sprintf(fname, MSGFIL_FMT, seqno);
X if ((ifp = fopen(fname, "r")) == 0)
X return;
X
X /* Extract sender and subject from message */
X
X while (dosgets(line, sizeof(line), ifp) != 0
X && (context = ms_parse(context, line)) != MS_BODY) {
X switch (context) {
X case MS_UUCP:
X if (sscanf(line, "%*[>] From %s", from) != 1)
X (void) sscanf(line, "From %s", from);
X break;
X case MS_HEADER:
X if (hscanf(line, "Subject:", " %[^\n]", subject) == 0
X && hscanf(line, "From:", " %*s ( %[^)] )", from) == 0)
X (void) hscanf(line, "From:", " %[^<]", from);
X break;
X }
X }
X (void) fclose(ifp);
X
X /*
X * Try to create the meta file; if that fails, let the pc software try
X * again at a later time.
X */
X
X (void) sprintf(fname, SNDFIL_FMT, seqno);
X if (ofp = fopen(fname, "w")) {
X (void) fprintf(ofp, "%s\r\n%s\r\n", from, subject);
X if (fflush(ofp) || ferror(ofp) || feof(ofp) || fclose(ofp)) {
X (void) unlink(fname);
X } else {
X (void) chmod(fname, 0400); /* avoid tampering */
X }
X }
X}
X
X/* newseqno - allocate new message sequence number */
X
Xint newseqno(userdir)
Xchar *userdir;
X{
X register DIR *dd;
X register struct direct *p;
X struct stat st;
X register int seqno = 0;
X int tmp = 0;
X int i;
X char junk;
X
X /*
X * When the pc adds a file to the "mail data base", the file name is
X * composed of a single letter and a unique sequence number. The pc
X * chooses a new sequence number by adding one to the highest existing
X * sequence number.
X *
X * Now that the pc mounts its mail directory from the nfs server we must
X * avoid possible concurrency conflicts when both pc and file server try
X * to update the "mail data base".
X *
X * Since the pc does not know about concurrent access from the nfs server,
X * the server has to add 2 to the highest existing message sequence
X * number, in order to avoid conflicts. Fortunately, only one pc at a
X * time will be accessing a mail directory of a particular user.
X *
X * Further concurrency conflicts are be avoided on the server side by using
X * lock files.
X *
X * If we cannot create a lock file right now, we back off and let sendmail
X * try again later.
X */
X
X /* Get rid of stale lock files */
X
X if (stat(LOCK, &st) == 0 && st.st_mtime < time((long *) 0) - STALE)
X (void) unlink(LOCK);
X
X /* Wait until we can create the lock file */
X
X if (creat(mktemp(template), 0400) < 0)
X error(EX_TEMPFAIL, "cannot set lock in directory %s: check ownership",
X userdir);
X for (i = 0; link(template, LOCK) && i < MAXTRY; i++)
X (void) sleep(1);
X (void) unlink(template);
X if (i >= MAXTRY)
X error(EX_TEMPFAIL, "locked: %s", userdir);
X
X /* Scan the user mail directory for the highest existing message number */
X
X if ((dd = opendir(userdir)) == 0) {
X (void) unlink(LOCK);
X error(EX_TEMPFAIL, "opendir(\"%s\") failed: %m", userdir);
X }
X while (p = readdir(dd)) {
X if (strlen(p->d_name) == 6
X && sscanf(p->d_name + 1, "%d%c", &tmp, &junk) == 1 && tmp > seqno)
X seqno = tmp;
X }
X
X /* clean up and terminate */
X
X closedir(dd);
X (void) unlink(LOCK);
X return (seqno + 2);
X}
X
X/* checkdir - check/update presence/ownership/protection of directory */
X
Xcheckdir(path, uid, gid, mode)
Xchar *path;
Xint uid;
Xint gid;
Xint mode;
X{
X struct stat st;
X
X /*
X * If a user mail directory does not exist, try to create it. Otherwise,
X * make sure it has sane permissions
X */
X
X if (stat(path, &st) == -1) { /* no directory */
X if (mkdir(path, mode)) /* try to create it */
X error(EX_TEMPFAIL, "cannot create directory %s: %m", path);
X if (chown(path, uid, gid)) /* set owner, group */
X error(EX_TEMPFAIL, "cannot chown directory %s: %m", path);
X } else { /* directory exists */
X if ((st.st_mode & S_IFMT) != S_IFDIR) /* must be directory! */
X error(EX_TEMPFAIL, "%s should be a directory", path);
X if ((st.st_uid != uid || st.st_gid != gid) /* check owner/group */
X &&chown(path, uid, gid)) /* correct owner, group */
X error(EX_TEMPFAIL, "cannot chown directory %s: %m", path);
X if ((st.st_mode & 0777) != mode /* check permissions */
X && chmod(path, mode)) /* correct permissions */
X error(EX_TEMPFAIL, "cannot chmod %o directory %s: %m", mode, path);
X }
X}
X
X/* error - print diagnostic and terminate */
X
X/* VARARGS */
X
Xvoid error(va_alist) va_dcl
X{
X va_list ap;
X register int exstat;
X register char *fmt;
X char buf[BUFSIZ];
X int err = errno;
X
X /* Format the error message */
X
X va_start(ap);
X exstat = va_arg(ap, int); /* exit status */
X fmt = va_arg(ap, char *); /* format string */
X (void) vsprintf(buf, percentm(fmt, err), ap);
X va_end(ap);
X
X /* Write message to standard error stream */
X
X (void) fprintf(stderr, "%s: %s\n", progname, buf);
X
X /* Append the same message to system log */
X
X (void) openlog("pc-mail", LOG_PID, LOG_MAIL);
X (void) syslog(LOG_WARNING, "%s", buf);
X (void) closelog();
X
X /* Notify sendmail of the nature of the problem */
X
X exit(exstat);
X}
X
X#ifdef SYSV
X
X/* mkdir - create directory */
X
Xint mkdir(dir, mode)
Xchar *dir;
Xint mode;
X{
X char cmd[BUFSIZ];
X
X (void) sprintf(cmd, "mkdir %s 2>&1 >/dev/null 2>&1 && chmod %o %s",
X dir, mode, dir);
X return (system(cmd)); /* does not set errno */
X}
X
X#endif
END_OF_daemon/pc-mail.c
if test 11399 -ne `wc -c <daemon/pc-mail.c`; then
echo shar: \"daemon/pc-mail.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f main/DEFAULT.ins -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"main/DEFAULT.ins\"
else
echo shar: Extracting \"main/DEFAULT.ins\" \(11543 characters\)
sed "s/^X//" >main/DEFAULT.ins <<'END_OF_main/DEFAULT.ins'
X@(#) DEFAULT.ins 2.1 90/01/22 13:01:05
X
XThis document briefly describes how to set up a PC-mail system
Xthat uses the default PC-mail UUCP software to exchange mail with
Xits UNIX host. The examples given apply to `old' UNIX UUCP; your
Xfile names and formats may vary.
X
XTHE UNIX SIDE OF THE CONNECTION
X
XThe PC-mail programs will need a UNIX host to exchange messages
Xwith. This automatically gives access to other networks. I sug-
Xgest the following strategy:
X
X - Get a UUCP login, say, `uuxyz' on a UNIX system. This will
Xalso become the UUCP-node name of the PC. Sometimes, the UNIX
XUUCP software will refuse to work with arbitrary (uid, gid)
Xvalues; during the initial handshake messages, it will reply with
XRLOGIN instead of ROK. On these systems, the (uid, gid) values of
Xyour UUCP login should be small numbers, probably in the range
X1-100.
X
XThe UUCP systems file (/usr/lib/uucp/L.sys) should be extended
Xwith an entry for the `uuxyz' host, e.g.
X
X uuxyz Passive
X
XOn some systems one has to specify `Never' instead of `Passive'.
XIt may also be necessary to update the UUCP permissions file
X(/usr/lib/uucp/USERFILE).
X
X - Have all mail for user `uuxyz' forwarded to `uuxyz!somebody'.
XWith Berkeley UNIX, this can be achieved by placing the address
X`uuxyz!somebody' in the file ~uuxyz/.forward; with System-V UNIX
Xone may have to put the text `Forward to uuxyz!somebody' in the
Xfile /usr/mail/uuxyz, which should be read/writeable by group
Xmail. Alternatively, you can ask the UNIX system administrator to
Xdefine an alias that maps `uuxyz' to `uuxyz!somebody' if the
Xlocal mailer supports aliases.
X
XIn the above examples, `somebody' is a name that can be freely
Xchosen; it will not be used by the PC. Of course, mail can be
Xforwarded to `uuxyz!somebody' from other accounts as well.
X
XThe result of all this is that you can send mail to any user on
Xthe UNIX host by just specifying her login name; your mail will
Xappear to come from the user `uuxyz' on the UNIX host. The UUCP-
Xnode name of your PC will not appear in mail headers. People can
Xsend mail to you by specifying `uuxyz' on the UNIX host. Since
Xthe host name of the PC does not appear in mail headers there is
Xno need to register the PC in, e.g., the UUCP maps.
X
XAs a minimum, the UNIX host should support the standard UUCP `g'
Xprotocol. This protocol was developed for eight-bit data paths
Xacross dial-up links (modems). Unfortunately, more advanced
Xnetworks eat up XON/XOFF and other control characters. This must
Xbe the price of progress.
X
XTo handle non-transparent networks I have written a simple
Xstart/stop `k' protocol. It has been in use on the Eindhoven
XUniversity Sytek network since 1986 and is part of the PC-mail
Xdistribution. If you're really desperate (and have UNIX source)
Xbuild this protocol into the uucico program by adding the
Xfollowing line to struct Proto Ptbl[] in the file cntrl.c:
X
X 'k', kturnon, krdmsg, kwrmsg, krddata, kwrdata, kturnoff,
X
Xand linking the uucico objects with kio.c kp.h kphys.c kpres.c
Xand ktrans.c.
X
XTHE PC SIDE OF THE CONNECTION
X
XA warning for MS-DOS users: TSR programs may interfere with the
Xoperation of the dial-up and file transfer program.
X
XTo up bring PC-mail, copy the appropriate makefile.whatever file
Xto makefile and edit it (there are template makefiles for UNIX
Xand MS-DOS). The MS-DOS makefile is for a UNIX-compatible make
Xutility posted to usenet near the end of 1986. There is a batch
Xcommand file (DEFAULT.bat) for those who do not have a unix-
Xcompatible make utility. The make utility provided with early
Xreleases of MicroSoft C is definitely not UNIX compatible.
X
XSaying `make' should produce five programs:
X
X - mail, the menu-driven user interface
X - cmail, a program that checks if there is new mail
X - smail, a program that queues messages for transmission after
X doing alias substitution on mail addresses
X - nmail, extracts "From" and "Subject" info from new mail
X - cico, the program that performs dialups and file transfers
X
XUnder MS-DOS, the cico program has to be compiled with the small
Xmemory model; in order to handle mail messages larger than about
X10 kbyte, the mail user interface program should be compiled with
Xthe large memory model.
X
XThe programs access a common data base in the form of a spool
Xdirectory with setup file, logfile and message files. Optionally
Xthere may be header and trailer files to generate template
Xmessages. There should be no other files in the spool directory,
Xto avoid confusion. The spool directory should be created by
Xhand; the PC-mail programs will not do that.
X
XYou will have to set some environment variables before running
Xthe mail program.
X
X - MAILDIR, the location of your mail data base directory
X - EDITOR, the name of your favourite editor program
X - PATH, in order locate the PC-mail executables, and your editor
X
XIt is advised to use absolute path names that include the drive
Xname. The editor command may be an MS-DOS batch file; in that
Xcase you should include the '.bat' suffix in the command name.
X
XThe following two environment variables are optional.
X
X - MAILPRN, the name of a file, if printer output should not go to
X the default printer.
X - MAILCMD, a command that is executed on exit from the mail
X program. If this is an MS-DOS batch file you should include
X the '.bat' suffix in the command name.
X
XAt our site, these two variables are used to collect printer
Xoutput in one file, and to send it to a networked printer upon
Xexit from the program.
X
XMake sure that the limit on the number of open files is large
Xenough (20 or so). On MS-DOS, this is handled by a line with
X`files=20' in the CONFIG.SYS file.
X
XOn MS-DOS, the mail user interface requires the ANSI.SYS driver.
XThe CONFIG.SYS file should specify a line with "device=ansi.sys".
X
XRun the interactive mail program and choose the setup command.
X
XAll entries must be filled or the cico program (for dial-up and
Xfile transfer) will complain. On MS-DOS systems, only the com1
Xport is supported (see the file comport.asm). I am not interested
Xin 80*86 assembly-language programming; you will have hack your
Xown com2 support.
X
XThe first item in the setup is not used by the dial-up and file-
Xtransfer program.
X
XHere is my setup (some names changed) for getting through the
Xhorrible port selector of our local university network (it needs
Xto receive up to nine carriage returns in order to detect the
Xbaud rate of a dial-up call, can you believe it):
X
X ignore_header_lines: received message-id
X communications_port: com1
X baud_rate: 2400
X remote_host_name: eutwc1
X login_name: uutest
X dialup_sequence: atz\r OK atdt0,455215\r CONNECT \r \0 \r \0 \r
X \0 \r \0 \0 \r \0 \r \0 \r \0 \r \0 \r \0 \r ease: abc\r ease: def\r
X hoice:-\0-hoice: 1\r lled: b076\r CLOSED \r
X disconnect_sequence: \0
X
XThe dial-up sequence requires some explanation. Basically it is a
Xlist of words separated by whitespace:
X
X send1 expect1 send2 expect2 (and so on)
X
XThe first word is sent to the comm. port. The program will wait
Xuntil it receives the second word. Then it sends the third word.
XAnd so on. There is a retry facility, similar to the one in real
XUUCP or later versions of Kermit, that works as follows: instead
Xof an expect string you can specify an alternate sequence as
Xwords separated by hyphen characters:
X
X expect-altsend1-altexpect1-altsend2-altexpect2 (and so on)
X
XIf the "expect" string is not received, the first alternate
X"send" string is sent, and the program waits until it receives
Xthe first alternate "expect" string. If that fails, the second
Xalternate "send" string is sent and so on. The alternate sequence
Xis terminated until an (alternate) expect succeeds or until the
Xalternate sequence is exhausted, or due to time out.
X
XNote that carriage-return characters are not automatically
Xappended to "send" strings. In order to specify these and other
Xcontrol characters in send/expect strings I have stolen some
Xescape sequences from the C programming language, and added some:
X
X \f formfeed
X \n linefeed
X \r carriage return
X \s blank
X \t tab
X \\ backslash
X \nnn octal code for a character
X
XIn order to send or expect an empty string, use the \0 sequence.
XEmpty "send" strings introduce brief delays. An empty "expect"
Xstring always succeeds.
X
XThe following "send" strings are given special treatment:
X
X BREAK causes a null character to be sent
X EOT causes a Control-D character to be sent
X
XThe dial-up sequence specified in the setup file should terminate
Xwhen the UNIX host is about to display its "login:" prompt; the
Xremainder of the dialup sequence is wired into the software.
XThis, and having (PC node name) equal to (UUCP login name) are to
Xprevent fraud. If You have problems with this approach, you
Xshould edit the file "connect.c".
X
XThus, assuming a Hayes-compatible modem, your dialup sequence
Xcould be as simple as:
X
X atz\r OK atdt123456\r CONNECT
X
XWhen this dialup sequence succeeds, the program continues with
Xits built-in sequence:
X
X ogin: your_uucp_login\r ssword: your_uucp_password\r
X
XThe disconnect sequence uses the same escape sequences as the
Xdial-up sequence, but does not use the send/expect protocol. In
Xthe example above, the disconnect sequence is a null string,
Xwhich happens to be the default.
X
XIn order to test your dial-up sequence you can run the cico
Xprogram by hand, for example:
X
X cico -d 9 -p your_uucp_password
X
XThis will produce a lot of debugging output. Setting the
Xdebugging level to less than 9 reduces verbosity. Level 4 is
Xsufficient to monitor the dial-up and login sequence.
X
XALIAS DATABASE
X
XThe user can define aliases for (groups of) mail addresses. The
Xalias data base is a text file with on each line:
X
X alias replacement_part
X
XThe alias should be a single word; words are separated by blanks,
Xtabs or commas. The replacement part may be one or more words.
XWhenever the smail (mail spooler) program recognizes an alias, it
Xis replaced by the `replacement part'. Aliases may be defined in
Xterms of other aliases; the order in which they appear in the
Xalias data base is not important (except when an alias is defined
Xmore than once; the program remembers only the last definition of
Xan alias). The alias expansion software is smart enough to detect
Xinfinite loops and to suppress multiple occurrances of the same
Xrecipient. Alias substitution is not case-sensitive.
X
XBATCH-MODE OPERATION
X
XThe cmail program can be run from a batch file (say, each time
Xthe PC is turned on), to contact the UNIX host, and to report if
Xthere is new mail. Also, you may want to auto-execute the cmail
Xcommand when exiting from the interactive mail shell (using the
XMAILCMD environment variable described above). See the manual
Xpage in the cmail.c source.
X
XTEMPLATE FILES
X
XThe user can provide message templates with standard header and
Xtrailer lines. If the file "header" is present in the mail
Xdirectory, its contents will be included at the beginning of
Xevery mail message created by the user. Similarly, the contents
Xof a file "trailer" will be included at the end of mail messages.
XThe "header" and "trailer" files should be ordinary text files.
END_OF_main/DEFAULT.ins
if test 11543 -ne `wc -c <main/DEFAULT.ins`; then
echo shar: \"main/DEFAULT.ins\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f main/desk.c -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"main/desk.c\"
else
echo shar: Extracting \"main/desk.c\" \(12439 characters\)
sed "s/^X//" >main/desk.c <<'END_OF_main/desk.c'
X/*++
X/* NAME
X/* desk 3
X/* CATEGORY
X/* mail box display
X/* PROJECT
X/* pc-mail
X/* PACKAGE
X/* mail
X/* SYNOPSIS
X/* #include "mail.h"
X/*
X/* void init()
X/*
X/* int junk_desk()
X/*
X/* char message[];
X/* char comment[];
X/* DESCRIPTION
X/* Most functions in this module are invoked by the keyboard interpreter
X/* and are responsible for the mail box view of message summary lines.
X/*
X/* init() is the main entry point. It presents the user with a display
X/* of message categories (create, unread, already seen, unsent, sent,
X/* in preparation), and the number of messages in each category. After
X/* the user has chosen a category, an editor is invoked, or a sorted
X/* display of all messages in the respective category is displayed.
X/*
X/* junk_desk() should be invoked when the number of files in the mail box
X/* may have changed. Always returns a zero value. This function
X/* should be called when a message is added to, or deleted from, the
X/* spool directory.
X/*
X/* The strings "message" and "comment" hold path names of the currently
X/* selected message file, and its associated meta file (with message
X/* destination, origin or comments). These names are used by functions
X/* that read, delete or otherwise manipulate message files.
X/* FILES
X/* mail header files in the spool directory
X/* SEE ALSO
X/* pager(3), pager(5), kbdinp(3)
X/* DIAGNOSTICS
X/* If a selected mail message could not be found an error message
X/* is displayed instead.
X/* BUGS
X/* Since a message can be accessed only if its metafile exists,
X/* a message is "lost" when for some reason the metafile is
X/* not available.
X/* AUTHOR(S)
X/* W.Z. Venema
X/* Eindhoven University of Technology
X/* Department of Mathematics and Computer Science
X/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
X/* CREATION DATE
X/* Tue May 12 15:35:20 GMT+1:00 1987
X/* LAST MODIFICATION
X/* 90/01/22 13:01:29
X/* VERSION/RELEASE
X/* 2.1
X/*--*/
X
X#include <stdio.h>
X#include <ctype.h>
X#include <errno.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <time.h>
X
X#include "defs.h"
X#include "mail.h"
X#include "path.h"
X#include "ndir.h"
X#include "pager.h"
X#include "screen.h"
X#include "status.h"
X#include "window.h"
X#include "ascf.h"
X#include "snapshot.h"
X
Xhidden void make_desk(); /* forward declarations */
Xhidden int pick_desk();
Xhidden int show_desk();
Xhidden void desk();
Xhidden void make_init();
Xhidden int pick_init();
Xhidden int show_init();
Xhidden char *singular();
X
Xhidden File *deskfile = 0; /* mail box pager file */
Xhidden File *initfile = 0; /* initial screen */
Xpublic char message[BUFSIZ]; /* path to message file */
Xpublic char comment[BUFSIZ]; /* path to meta file */
X
X/* Definitions of the various message categories */
X
Xtypedef struct {
X char *type; /* work, incoming, outgoing, ... */
X char mesg; /* message-file prefix */
X char meta; /* meta-file prefix */
X int (*access) (); /* message access function */
X int flags; /* see below */
X char *whatsit; /* explanation */
X} CATEGORY;
X
X#define MULT 1 /* multi-message category */
X#define DMON 2 /* subject to daemon activity */
X
Xhidden CATEGORY categories[] = {
X "Create", WORK_MESG, WORK_META, create,0, "Create a new message",
X "Work", WORK_MESG, WORK_META, work, MULT, "Message%S in preparation",
X "New", NEW_MESG, NEW_META, mbox, MULT|DMON,"Unread message%S",
X "In", OLD_MESG, OLD_META, mbox, MULT, "Message%S already read",
X "Out", OUT_MESG, OUT_META, mbox, MULT|DMON,"Message%S not-yet sent",
X "Sent", SENT_MESG, SENT_META, mbox, MULT|DMON,"Message%S already sent",
X 0, /* terminator */
X};
X
Xhidden CATEGORY *category; /* selected message category */
X
X/* table with all meta-file name prefixes */
X
Xhidden char metalist[] = {
X WORK_META, NEW_META, OLD_META, OUT_META, SENT_META, 0,
X};
X
Xhidden char initsingle[] = "%-6s %5s %s";
Xhidden char initmulti[] = "%-6s %5u %s";
Xhidden char scanmulti[] = "%s %u";
X
Xhidden char dispfmt[] = "%5u %.16s %.53s";
Xhidden char dispfmts[] = "%5u %.16s %.40s \"%s\"";
Xhidden char scanfmt[] = "%u";
X
X/* init - main entry point for message manipulations */
X
Xpublic void init()
X{
X static Screen screen[] = {
X 'C', "Close", 0, "Terminate the program",
X 'F', "File", file, "Mail a copy of an ordinary file",
X#ifndef DAEMON
X 'N', "Network", call, "Exchange mail with the network",
X#endif
X 'S', "Setup", setup, "Set communications parameters",
X 'A', "Alias", alias, "Display the alias data base",
X 'P', "Print", print, "Print contents of this display",
X UP, "Up", up_pager, csrup,
X DOWN, "Down", dn_pager, csrdn,
X ENTER, "Enter", pick_init, "Select message category",
X 0, 0, show_init,
X "Select a message category with cursor keys and press ENTER\n\
Xor select one of the commands in the top line."
X };
X
X kbdinp(screen); /* and there they go... */
X}
X
X/* show_init - create or refresh the initial screen */
X
Xhidden int show_init()
X{
X if (initfile == 0) { /* no initial screen file */
X patience(); /* one moment please */
X make_init(initfile = open_pager()); /* build initial screen */
X } else { /* pager file exists */
X set_pager(initfile); /* select pager file */
X }
X ds_pager(); /* display it */
X return (0); /* screen is ok */
X}
X
X/* junk_init - force re-scan of mail directory and re-build of initial screen */
X
Xhidden void junk_init()
X{
X if (initfile) {
X close_pager(initfile);
X initfile = 0;
X }
X snap_junk(); /* re-scan mail directory */
X}
X
X/* make_init - build initial screen */
X
Xhidden void make_init(pp)
XFile *pp;
X{
X register SNAP_SHOT *s;
X register unsigned count;
X register CATEGORY *c;
X
X /*
X * In case of multi-message categories, show the number of messages in
X * that category.
X */
X
X for (c = categories; c->type; c++) {
X if (c->flags & MULT) { /* multi-message category */
X for (count = 0, s = snap_shot(metalist); s->prefix; s++)
X if (c->meta == s->prefix)
X count++;
X app_pager(pp, strcons(initmulti, c->type, count,
X singular(count, c->whatsit)));
X } else { /* single-message category */
X app_pager(pp, strcons(initsingle, c->type, "", c->whatsit));
X }
X }
X pp->opts |= PG_NOEND; /* suppress 'end-of-display' */
X}
X
X/* exec_msg - execute access function for a particular message */
X
Xhidden int exec_msg(msgno)
Xunsigned msgno;
X{
X (void) strcpy(message, mesg_file(category->mesg, msgno));
X (void) strcpy(comment, meta_file(category->meta, msgno));
X return (CALL(category->access) (category->meta, msgno));
X}
X
X/* pick_init - user selected a message category */
X
Xhidden int pick_init()
X{
X char type[BUFSIZ];
X register CATEGORY *c;
X unsigned count;
X
X /*
X * Read the message type (in, out, work etc) from the summary line in the
X * initial display.
X *
X * On systems that do not use daemons for message delivery, disallow
X * selection an empty message category.
X */
X
X count = type[0] = 0; /* initialize */
X (void) sscanf(gets_pager(), scanmulti, type, &count);
X
X for (c = categories; c->type; c++) { /* try to recognize the */
X if (strcmp(c->type, type) == 0) { /* message type */
X category = c; /* GLOBAL! */
X if ((c->flags & MULT) == 0) { /* create-message category */
X return (exec_msg(newseqno()));
X#ifndef DAEMON
X } else if (count == 0) { /* multi-message, empty */
X break;
X#endif
X } else { /* multi-message */
X desk();
X return (S_REDRAW);
X }
X }
X }
X beep(); /* error */
X return (0); /* nothing happened */
X}
X
X/* desk - manipulate one category of messages */
X
Xhidden void desk()
X{
X static Screen screen[] = {
X 'C', "Close", 0, initscreen,
X 'F', "File", file, "Mail a copy of an ordinary file",
X#ifndef DAEMON
X 'N', "Network", call, "Exchange mail with the network",
X#endif
X 'S', "Setup", setup, "Set communications parameters",
X 'A', "Alias", alias, "Display the alias data base",
X 'P', "Print", print, "Print contents of this display",
X PGUP, PgUp, pu_pager,pageup,
X PGDN, PgDn, pd_pager,pagedn,
X UP, "Up", up_pager,csrup,
X DOWN, "Down", dn_pager,csrdn,
X ENTER, "Enter", pick_desk,"Select message",
X 0, 0, show_desk,
X "Select a message with the cursor keys and press ENTER\n\
Xor select one of the commands in the top line."
X };
X
X /*
X * On systems where daemon processes take care of message delivery, we
X * re-scan the mail directory if the user selects a message category that
X * can be affected by daemon activity, and force a re-scan of the mail
X * directory upon return to the initial screen (unless that just happened
X * as a result of some user action).
X */
X
X#ifdef DAEMON
X if (category->flags & DMON) /* if affected by daemons */
X junk_init(); /* re-scan mail directory */
X#endif
X kbdinp(screen);
X#ifdef DAEMON
X junk_init(); /* re-scan mail directory */
X#endif
X close_pager(deskfile);
X deskfile = 0;
X}
X
X/* show_desk - create or refresh a display of a mail box selection */
X
Xhidden int show_desk()
X{
X if (deskfile == 0) { /* no mail box pager file */
X patience(); /* one moment please... */
X make_desk(deskfile = open_pager()); /* build mail box display */
X } else { /* pager file exists */
X set_pager(deskfile); /* select pager file */
X }
X ds_pager(); /* display it */
X return (0); /* screen is ok */
X}
X
X/* make_desk - build display of summary lines of selected message type */
X
Xhidden void make_desk(pp)
XFile *pp;
X{
X FILE *fp; /* used to read meta info */
X char ident[MAXLINE]; /* usually person\'s name or address */
X char subj[MAXLINE]; /* subject info */
X char *line;
X register SNAP_SHOT *s; /* snapshot table */
X struct stat st;
X
X /*
X * The message sequence number and type are already known; we just have
X * to retrieve from the meta file: a person\'s name or address (first
X * line) and an optional subject (second line).
X *
X * If Subject: information is present we truncate the person\'s name or
X * address to (screen width - 40) columns. Any text that extends beyond
X * the width of the screen is truncated as well.
X */
X
X for (s = snap_shot(metalist); s->prefix; s++) {
X if ((s->prefix == category->meta)
X && (fp = ascopen(meta_file(s->prefix, s->msgno), "r"))) {
X if ((ascgets(ident, sizeof(ident), fp) != 0)
X && (fstat(fileno(fp), &st) == 0)) {
X if (ascgets(subj, sizeof(subj), fp) && subj[0]) {
X /* subject found; truncate person\'s name or address */
X if (strlen(ident) > CO - 40)
X (void) strcpy(ident + CO - 42, "..");
X line = strcons(dispfmts, s->msgno,
X tstamp(&(st.st_mtime)), ident, subj);
X } else {
X /* no subject info */
X line = strcons(dispfmt, s->msgno,
X tstamp(&(st.st_mtime)), ident);
X }
X /* truncate final result anyway */ if (strlen(line) >= CO - 1)
X (void) strcpy(line + CO - 3, "..");
X app_pager(pp, line);
X }
X ascclose(fp);
X }
X }
X
X/* sort summary lines in reverse order, i.e. newest comes first */
X
X sort_pager(pp, BACK_SORT);
X}
X
X/* pick_desk - user selected a message */
X
Xhidden int pick_desk()
X{
X unsigned msgno;
X
X /*
X * Read message sequence number from summary line in the mail box
X * display. Build actual message file and meta file names. Then call the
X * appropriate function to access that message.
X */
X
X msgno = 0; /* initialize */
X (void) sscanf(gets_pager(), scanfmt, &msgno);
X
X if (msgno) {
X return (exec_msg(msgno));
X } else {
X beep(); /* unrecognized message id */
X return (0); /* nothing happened */
X }
X}
X
X/* junk_desk - force rebuilding of mail box display */
X
Xpublic int junk_desk()
X{
X if (deskfile) {
X close_pager(deskfile); /* delete pager file */
X deskfile = 0; /* say it's gone */
X }
X junk_init(); /* and re-scan mail directory */
X return (0); /* in case one wants it */
X}
X
X/* singular - replace %S depending on whether a count is 1 */
X
Xhidden char *singular(c, s)
Xint c;
Xregister char *s;
X{
X static char buf[BUFSIZ];
X register char *bp = buf;
X
X while (*s) {
X if (*s == '%') {
X if (s[1] == 'S') { /* expand %S */
X if (c != 1)
X *bp++ = 's';
X s += 2;
X } else if (s[1] == '\0') { /* don\'t fall off end */
X *bp++ = *s++;
X } else { /* leave %<any> alone */
X *bp++ = *s++, *bp++ = *s++;
X }
X } else {
X *bp++ = *s++;
X }
X }
X *bp = '\0';
X return (buf);
X}
END_OF_main/desk.c
if test 12439 -ne `wc -c <main/desk.c`; then
echo shar: \"main/desk.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f main/gtrans.c -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"main/gtrans.c\"
else
echo shar: Extracting \"main/gtrans.c\" \(10252 characters\)
sed "s/^X//" >main/gtrans.c <<'END_OF_main/gtrans.c'
X/*++
X/* NAME
X/* gtrans 3
X/* SUMMARY
X/* g protocol strategy functions
X/* PROJECT
X/* pc-mail
X/* PACKAGE
X/* cico
X/* SYNOPSIS
X/* #include "gp.h"
X/*
X/* int ginit(fd)
X/* int fd;
X/*
X/* Packet *galloc()
X/*
X/* void gsproto(fd,pk)
X/* int fd;
X/* Packet *pk;
X/*
X/* Packet *grproto(fd)
X/* int fd;
X/*
X/* void gfree(pk)
X/* Packet *pk;
X/*
X/* int gfinit(fd)
X/* int fd;
X/* DESCRIPTION
X/* ginit() exchanges the initial g protocol messages and allocates
X/* memory for packet buffers.
X/*
X/* galloc() returns a pointer to a free packet, after filling
X/* in its k and len fields. This packet is supposed to be filled
X/* with data, and to be subsequently queued with gsproto().
X/*
X/* grproto() extracts the next packet from the input queue.
X/* The packet should be returned to the free pool with gfree().
X/*
X/* gfinit() sends protocol termination messages until it receives one
X/* or until it gets bored.
X/* FUNCTIONS AND MACROS
X/* gsctrl(), gsdata(), grpack(), gfail()
X/* DIAGNOSTICS
X/* ginit(), gfinit() return a nonzero value if there was a problem.
X/*
X/* The other functions return through a call of gfail() in case of
X/* unrecoverable problems.
X/* BUGS
X/* Window size is equal to one. This implies that the program
X/* only sends new data when the previous packet was acknowledged.
X/* However, only the functions in *this* module need to be adapted
X/* to accomodate larger transmission window sizes.
X/* AUTHOR(S)
X/* W.Z. Venema
X/* Eindhoven University of Technology
X/* Department of Mathematics and Computer Science
X/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
X/* CREATION DATE
X/* Sun Apr 19 17:30:08 GMT+1:00 1987
X/* LAST MODIFICATION
X/* 90/01/22 13:01:44
X/* VERSION/RELEASE
X/* 2.1
X/*--*/
X
X#include "gp.h"
X
X/*
X* "The protocol is defined in terms of message transmissions of 8-bit bytes."
X* "Each message includes one control byte plus a data segment of zero or more"
X* "information bytes. The allowed data segment sizes range between 32 and"
X* "4096 as determined by the formula 32*2^k where k is a 3-bit number."
X*/
X
Xint seglen[] = { /* data segment sizes */
X 1,32,64,128,256,512,1024,2048,4096,
X};
X
Xstatic int sndseg; /* data segment k-value they want */
Xstatic int sndlen; /* data segment length they want */
Xstatic int sndwin; /* transmission window size they want */
X
X#define ourseg 2 /* data segment k-value we want */
X#define ourlen 64 /* data segment length we want */
X#define ourwin 1 /* transmission window size we want */
X
Xstatic Packet *inpk = 0; /* receive packet "pool" */
Xstatic Packet *outpk = 0; /* send packet "pool" */
X
Xstatic int rval = 0; /* our R value */
Xstatic int sval = 1; /* our S value */
X
X/*
X* "Initial synchronization is accomplished with two 3-way handshakes:"
X* "two each of INITA/INITB/INITC. Each sender transmits INITA messages"
X* "repeatedly. When an INITA message is received, INITB is sent in return."
X* "When an INITB message is received *and* an INITB message has been sent,"
X* "an INITC message is sent. The INITA and INITB messages carry with them"
X* "the packet and window size that each receiver wants to use, and the"
X* "senders are supposed to comply. When a receiver has seen all three INIT"
X* "messages, the channel is considered to be open. (...) the INIT messages"
X* "are ignored elsewhere. (...)"
X* "After initial synchronization each receiver sets a modulo-8"
X* "incrementing counter R to 0; each sender sets a similar counter S to 1."
X* "The value of R is always the number of the most recent correctly received"
X* "packet. The value of S is always the first sequence number in the output"
X* "window."
X*
X* Since INIT messages are ignored once the channel has been opened, we
X* set the initial values of R and S at compile time.
X*/
X
X/* ginit - g-protocol start-up */
X
Xint ginit(fd)
Xint fd;
X{
X register int state = 0;
X register int next = 0;
X int count = 0;
X
X /* set up receive packet buffers */
X
X if ((inpk = (Packet *) malloc((unsigned)sizeof(Packet)+ourlen)) == 0) {
X DEBUG(7,"gopen: malloc failed\n","");
X return(FAIL);
X }
X
X /*
X * Very simple automaton for initial message exchanges.
X * We send a packet, receive a packet and so on. The
X * automaton terminates when it reaches its accepting state,
X * when a time-out error occurs, or when it seems to get
X * stuck in one state.
X */
X
X while (state != INITC) {
X
X /* select action to be done in this state */
X
X switch (state) {
X case 0: /* initial state */
X gsctrl(fd,INITA|IFLD(ourwin)); /* send INITA message */
X break;
X case INITA: /* we received INITA */
X gsctrl(fd,INITB|IFLD(ourseg-1)); /* send INITB in response */
X break;
X case INITB: /* we received INITB */
X gsctrl(fd,INITC|IFLD(ourwin)); /* assume we sent INITB */
X break;
X }
X
X /*
X * Transition part of the automaton. Receive a packet and process
X * its contents. Depending on the packet and the current state
X * select a new state. Stay in the current state when a corrupted
X * packet is received or when we receive an unexpected packet.
X * If no packet is received assume we have lost contact and terminate.
X */
X
X switch (next = grpack(fd,inpk)) { /* see what we get */
X case INITA:
X sndwin = IVAL(inpk->c); /* transmission window size */
X state = next;
X break;
X case INITB:
X sndseg = IVAL(inpk->c)+1; /* send-segment type */
X sndlen = seglen[sndseg]; /* send-segment length */
X state = (state == INITA ? next : state);
X break;
X case INITC:
X state = (state == INITB ? next : state);
X break;
X case FAIL: /* corrupted message received */
X break;
X case TIME: /* no message received */
X return(FAIL);
X }
X
X /* check we don't stay in the same state forever */
X
X if (state == next) {
X count = 0;
X } else if (count++ > MAXTRY) {
X return(FAIL);
X }
X }
X
X /* set up transmission buffer "pool" */
X
X if ((outpk = (Packet *) malloc((unsigned)sizeof(Packet)+sndlen)) == 0) {
X DEBUG(7,"gopen: malloc failed\n","");
X return(FAIL);
X }
X return(0);
X}
X
X/*
X* The current version used a window size of 1, i.e. no further data
X* transmissions until the last transmitted data have been acknowledged.
X* The following routines anticipate on future versions with a real pool of
X* transmit and receive buffers.
X*/
X
X/* galloc - allocate send packet, fill in size info */
X
XPacket *galloc()
X{
X register Packet *pk = outpk;
X
X pk->k = sndseg; /* data segment type */
X pk->len = sndlen; /* data segment size */
X return(pk);
X}
X
X/* gfree - release receive packet */
X
Xvoid gfree(pk)
Xregister Packet *pk;
X{
X /* this function intentionally left blank */
X}
X
X/*
X* The central part of the protocol is in the routines gsproto() and
X* grproto(). These are the functions that negotiate with the other
X* host about what data to (re)transmit and to (n)ack.
X* Major changes are to be expected here when larger transmission
X* window sizes are to be supported.
X*/
X
X/* gsproto - queue one packet for transmission */
X
Xvoid gsproto(fd,pk)
Xint fd;
XPacket *pk;
X{
X int numtry = 0; /* retry count */
X
X gsdata(fd,pk,SFLD(sval)|RFLD(rval)); /* send data packet */
X
X inpk->k = ourseg; /* "allocate" receive packet */
X inpk->len = ourlen;
X
X while (numtry < MAXTRY) {
X switch (grpack(fd,inpk)) { /* what is the reply */
X case SHORT: /* SHORT DATA */
X case DATA: /* LONG DATA */
X gsctrl(fd,RJ|RFLD(rval)); /* not now please */
X case RJ: /* REJECT */
X case RR: /* RECEIVER READY */
X if (RVAL(inpk->c) == sval) { /* check their R value */
X sval = (sval+1)&07; /* update our S value */
X return;
X }
X case FAIL: /* bad packet received */
X case TIME: /* no packet received */
X gsdata(fd,pk,SFLD(sval)|RFLD(rval));/* send data packet again */
X numtry++; /* but not forever */
X break;
X case CLOSE:
X gfail(); /* surprise! */
X /* NOTREACHED */
X }
X }
X gfail(); /* too may retries, abort */
X /* NOTREACHED */
X}
X
X/* grproto - take one packet from input queue */
X
XPacket *grproto(fd)
Xint fd;
X{
X int numtry = 0; /* retry count */
X int xpct = (rval+1)&07; /* expected sequence nr */
X register Packet *pk = inpk; /* take one from the "pool" */
X
X pk->k = ourseg; /* initialize receive packet */
X pk->len = ourlen;
X
X while (numtry < MAXTRY) { /* don't loop forever */
X switch (grpack(fd,pk)) { /* see what we got */
X case DATA: /* LONG DATA */
X case SHORT: /* SHORT DATA */
X if (SVAL(pk->c) == xpct) { /* you're the 1 that I want */
X gsctrl(fd,RR|RFLD(rval = xpct));/* update R and acknowledge */
X return(pk); /* we are done here */
X } /* else ignore the packet */
X case FAIL: /* bad packet */
X gsctrl(fd,RJ|RFLD(rval)); /* reset their S value */
X case TIME: /* no packet, no nak */
X numtry++; /* don't loop forever */
X break; /* read another packet */
X case RR: /* RECEIVER READY */
X case RJ: /* REJECT */
X break; /* ignore */
X case CLOSE: /* surprise! */
X gfail(); /* boy, am I confused */
X /* NOTREACHED */
X }
X }
X gfail(); /* too may retries, abort */
X /* NOTREACHED */
X}
X
X/*
X* "The CLOSE message is used to terminate communications. Software on"
X* "either or both ends of the communication channel may initiate"
X* "termination. In any case when one end wants to terminate it sends"
X* "CLOSE messages until one is received from the other end or until a"
X* "programmable limit on the number of CLOSE messages is reached. Receipt"
X* "of a CLOSE message causes a CLOSE message to be sent."
X*
X* Normally systems decide together when to turn off the protocol so
X* that each system will start sending CLOSE messages at the same time.
X*
X* When a CLOSE message is received in the middle of a conversation
X* a protocol error is generated in grproto() or gsproto(). Then
X* gfinit() is called, so that the other system still sees a few CLOSE
X* messages.
X*/
X
X/* gfinit - shut down the g protocol */
X
Xint gfinit(fd)
Xint fd;
X{
X register int numtry;
X
X for (numtry = 0; numtry < MAXTRY; numtry++) { /* programmable limit */
X gsctrl(fd,CLOSE); /* send CLOSE message */
X if (grpack(fd,inpk) == CLOSE) /* hope for same */
X return(0); /* got it */
X }
X return(FAIL); /* no CLOSE received */
X}
END_OF_main/gtrans.c
if test 10252 -ne `wc -c <main/gtrans.c`; then
echo shar: \"main/gtrans.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f main/window.c -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"main/window.c\"
else
echo shar: Extracting \"main/window.c\" \(10368 characters\)
sed "s/^X//" >main/window.c <<'END_OF_main/window.c'
X/*++
X/* NAME
X/* window 3
X/* SUMMARY
X/* screen manipulator
X/* PROJECT
X/* pc-mail
X/* PACKAGE
X/* mail
X/* SYNOPSIS
X/* #include "window.h"
X/*
X/* int printcl(wp,line,s)
X/* WIN *wp;
X/* int line;
X/* char *s;
X/*
X/* int printat(wp,line,s)
X/* WIN *wp;
X/* int line;
X/* char *s;
X/*
X/* void setwin(wp)
X/* WIN *wp;
X/*
X/* int wputc(c)
X/* int c;
X/*
X/* int wputs(s)
X/* char *s;
X/*
X/* void clrtoeol()
X/*
X/* void clrtobot()
X/*
X/* void clrscreen()
X/*
X/* void beep()
X/*
X/* int fputchar(c)
X/* int c;
X/*
X/* void wininit()
X/* DESCRIPTION
X/* The window manipulator is responsable for three screen windows:
X/* the top window for key labels, the middle window for
X/* information, and the lower window for messages and dialogue.
X/* These can be manipulated via the window handles topwin, midwin
X/* and botwin, respectively.
X/* Use is made of the terminal capability database termcap, or terminfo,
X/* or something else, depending on the OS we are dealing with.
X/*
X/* For MS-DOS systems, there is a termcap facility that generates
X/* escape sequences for the ANSI.SYS terminal driver.
X/*
X/* Appropriate macros for window selection are given in window.h.
X/* Needless to say, all screen output should proceed through
X/* functions in this module.
X/*
X/* All character output functions return the number of screen lines
X/* used for outputting the text (at least 1). All routines that
X/* have a window agrument set the current window.
X/*
X/* printat() writes the specified line in the specified window,
X/* starting at the left margin.
X/*
X/* printcl() performs the same functions as printat() and erases to
X/* the end of the line.
X/*
X/* setwin() sets the current window. The cursor is moved to the
X/* current (row, column) of that window.
X/*
X/* wputs() writes a character string to the current cursor location
X/* in the current window.
X/*
X/* wputc() does the same for characters.
X/*
X/* cltroeol(), clrtobot() erase the screen from the cursor to the
X/* end of the line and screen respectively. beep() makes some noise.
X/*
X/* fputchar() outputs a character to stdout, just as putchar,
X/* but it is not a macro.
X/*
X/* wininit() initializes the window manipulator. It reads the
X/* terminal capabilities from the termcap database.
X/* FILES
X/* /etc/termcap, $TERMCAP on V7 or BSD UNIX
X/* /usr/lib/terminfo, $TERMINFO on System-V UNIX
X/* SEE ALSO
X/* window(5) window manipulator definitions
X/* DIAGNOSTICS
X/* The program is terminated with an error message if no terminal
X/* descriptions could be found, if the terminal lacks some
X/* essential features or if an attempt is made to write outside
X/* a window.
X/* BUGS
X/* All functions that do not take a "window" argument should not be
X/* called before anything has appeared on the screen.
X/*
X/* This module should be replaced by a PD curses/termcap library.
X/* AUTHOR(S)
X/* W.Z. Venema
X/* Eindhoven University of Technology
X/* Department of Mathematics and Computer Science
X/* Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
X/* CREATION DATE
X/* Wed Apr 1 21:14:53 GMT+1:00 1987
X/* LAST MODIFICATION
X/* 90/01/22 13:02:55
X/* VERSION/RELEASE
X/* 2.1
X/*--*/
X
X#include <stdio.h>
X#include <ctype.h>
X
X#include "defs.h"
X#include "window.h"
X
X#define BUFFERSIZE 1024 /* max. length of termcap entry */
X
Xhidden char outbuf[BUFSIZ]; /* for stdio */
X
Xhidden char tcapent[BUFFERSIZE]; /* storage for termcap entry */
Xhidden char capst[BUFFERSIZE]; /* storage for tgetstr */
Xhidden char *capptr = capst; /* pointer to next tgetstr output */
X
Xextern char *tgetstr(); /* returns capability string */
Xextern char *getenv();
Xextern char *tgoto(); /* returns cursor addressing code */
X
X/* function-key strings, keypad control */
X
Xpublic char *KU, *KD, *KL, *KR, *PU, *PD;
Xpublic char *KS, *KE;
X
X/* screen control capabilities */
X
Xhidden char *CL, *CD, *CM, *CE, *SO, *SE;
X
Xpublic int CO, LI; /* screen size */
X
X/* Storage for convenient capability lookup */
X
Xstruct strcaps {
X char **dest; /* pointer to storage */
X char *name; /* capability name */
X};
X
Xstruct intcaps {
X int *dest; /* pointer to storage */
X char *name; /* capability name */
X};
X
X/* Required string-valued termcap capabilities */
X
Xhidden struct strcaps reqd_str[] = {
X &CE, "ce", /* clear to end of line */
X &CD, "cd", /* clear to end of screen */
X &CL, "cl", /* clear to end of screen */
X#ifdef someday
X &SO, "so", /* stand-out on */
X &SE, "se", /* stand-out off */
X#endif
X &CM, "cm", /* cursor movement */
X &KU, "ku", /* up-arrow */
X &KD, "kd", /* down_arrow */
X &KL, "kl", /* left-arrow */
X &KR, "kr", /* right-arrow */
X#ifdef unix
X &PU, "k1", /* page-up (F1) */
X &PD, "k2", /* page down (F2) */
X#endif
X#ifdef MSDOS
X &PU, "PU", /* really PgUp */
X &PD, "PD", /* really PgDn */
X#endif
X 0, 0,
X};
X
X/* Required integer-valued terminal capabilities */
X
Xhidden struct intcaps reqd_int[] = {
X &CO, "co", /* number of columns */
X &LI, "li", /* number of lines */
X 0, 0,
X};
X
X/* Optional string-valued terminal capabilities */
X
Xhidden struct strcaps opt_str[] = {
X &KS, "ks", /* keypad on */
X &KE, "ke", /* keypad off */
X 0, 0,
X};
X
X/* Optional integer-valued terminal capabilities */
X
Xhidden struct intcaps opt_int[] = {
X 0, 0,
X};
X
X/* Window bases and sizes */
X
XWIN wins[3] = {
X {0, 2, 0, 0}, /* top */
X {2, 0, 0, 0}, /* middle */
X {0, 5, 0, 0}, /* bottom */
X};
X
X/* Stuff related to where we are on the screen */
X
Xhidden WIN *currwin = 0; /* what window we are in */
X
X/* convenient macros to update and set cursor location */
X
X#define moveto(wp) tputs(tgoto(CM,wp->x,wp->y+wp->base),1,fputchar)
X#define moveset(wp,c,l) { wp->x = c; wp->y = l; moveto(wp); }
X
Xhidden void winout();
X
X/* checkline - validate line number */
X
Xhidden WIN *checkline(wp, line)
XWIN *wp;
Xint line;
X{
X if (line < 0 || line >= wp->size)
X fatal("line %d not in window %d", line, wp - wins);
X}
X
X/* printcl - print one line in a window, then clear to end of line */
X
Xpublic int printcl(wp, line, s)
XWIN *wp;
Xint line;
Xchar *s;
X{
X checkline(currwin = wp, line);
X
X moveset(wp, 0, line);
X winout(s);
X if (wp->y < wp->size)
X tputs(CE, 1, fputchar);
X (void) fflush(stdout);
X return (wp->y - line + 1);
X}
X
X/* printat - print one line in a window */
X
Xpublic int printat(wp, line, s)
XWIN *wp;
Xint line;
Xchar *s;
X{
X checkline(currwin = wp, line);
X
X moveset(wp, 0, line);
X winout(s);
X (void) fflush(stdout);
X return (wp->y - line + 1);
X}
X
X/* setwin - set focus and cursor */
X
Xpublic void setwin(wp)
Xregister WIN *wp;
X{
X currwin = wp;
X moveto(wp);
X}
X
X/* wputc - put character at current location in current window */
X
Xpublic int wputc(c)
Xint c;
X{
X register int line = currwin->y;
X static char buf[] = "?";
X
X buf[0] = c;
X winout(buf);
X (void) fflush(stdout);
X return (currwin->y - line + 1);
X}
X
X/* wputs - print string at current location in current window */
X
Xpublic int wputs(s)
Xchar *s;
X{
X register int line = currwin->y;
X
X winout(s);
X (void) fflush(stdout);
X return (currwin->y - line + 1);
X}
X
X/* winout - update current window and keep track of where we are */
X
Xhidden void winout(s)
Xregister char *s;
X{
X register int ch;
X register WIN *wp;
X static char dectrl[] = "^?";
X
X for (wp = currwin; (ch = (*s & 0177)) && wp->y < wp->size; s++) {
X if (isprint(ch) || ch == ' ') { /* if printable */
X putchar(ch), wp->x++; /* leave it alone */
X } else if (ch == '\t') {
X do {
X winout(" "); /* expand it */
X } while ((wp->x & 7) && wp->y < wp->size);
X } else if (ch == '\b') {
X if (wp->x > 0 || wp->y > 0) { /* don\'t leave the window */
X if (wp->x-- == 0) { /* at beginning of line */
X wp->x = CO - 1;
X wp->y--;
X moveto(wp);
X } else {
X putchar(ch);
X }
X }
X } else if (ch == '\n') {
X tputs(CE, 1, fputchar); /* erase rest of line */
X moveset(wp, 0, wp->y + 1); /* advance */
X } else if (ch == '\r') {
X (wp->x = 0), moveto(wp); /* back to left margin */
X } else if (ch == '\07') {
X putchar(ch); /* make them sound */
X } else {
X dectrl[1] = ch ^ 0100; /* uncontrollify */
X winout(dectrl); /* and output */
X }
X if (wp->x >= CO) /* wrap at end of line */
X moveset(wp, 0, wp->y + 1);
X }
X}
X
X#ifdef unix
X/* fputchar - output a character on stdout */
X
Xpublic int fputchar(c)
Xint c;
X{
X return (putchar(c));
X}
X
X#endif /* unix */
X
X/* clrtoeol - clear to end of line */
X
Xpublic void clrtoeol()
X{
X tputs(CE, 1, fputchar);
X}
X
X/* clrtobot - clear to end of screen */
X
Xpublic void clrtobot()
X{
X tputs(CD, 1, fputchar);
X}
X
X/* clrscreen - clear screen */
X
Xpublic void clrscreen()
X{
X tputs(CL, 1, fputchar);
X}
X
X/* beep - ring the bell */
X
Xpublic void beep()
X{
X (void) putchar('\07');
X (void) fflush(stdout);
X}
X
X/* wininit - extract terminal info and initialize window routines */
X
Xpublic void wininit()
X{
X char *term;
X struct strcaps *cp;
X struct intcaps *ip;
X
X /* selected buffered standard output */
X
X setbuf(stdout, outbuf);
X
X /* make sure our terminal is known */
X
X#ifdef unix
X if ((term = getenv("TERM")) == 0)
X fatal("TERM not set");
X#endif
X
X switch (tgetent(tcapent, term)) {
X case -1:
X fatal("no terminal database\n");
X /* NOTREACHED */
X case 0:
X fatal("unknown terminal: %s\n", term);
X /* NOTREACHED */
X }
X /* extract required terminal capabilities */
X
X for (cp = reqd_str; cp->name; cp++)
X if ((*cp->dest = tgetstr(cp->name, &capptr)) == 0)
X fatal("Your terminal is too dumb");
X for (ip = reqd_int; ip->name; ip++)
X if ((*ip->dest = tgetnum(ip->name)) == 0)
X fatal("Your terminal is too dumb");
X
X /* set up per-window base and size */
X
X if (CO < 80)
X fatal("Terminal screen is to narrow");
X botwin->base = LI - botwin->size;
X if ((midwin->size = botwin->base - midwin->base) < 10)
X fatal("Not enough lines on this terminal");
X
X /* extract optional terminal capabilities */
X
X for (cp = opt_str; cp->name; cp++)
X *cp->dest = tgetstr(cp->name, &capptr);
X for (ip = opt_int; ip->name; ip++)
X *ip->dest = tgetnum(ip->name);
X}
END_OF_main/window.c
if test 10368 -ne `wc -c <main/window.c`; then
echo shar: \"main/window.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 8 \(of 11\).
cp /dev/null ark8isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 11 archives.
rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0