home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / pc / source / pcmail2.lzh / pcmail.8 < prev    next >
Encoding:
Text File  |  1990-01-31  |  59.5 KB  |  1,981 lines

  1.  
  2. #! /bin/sh
  3. # This is a shell archive.  Remove anything before this line, then unpack
  4. # it by saving it into a file and typing "sh file".  To overwrite existing
  5. # files, type "sh file -c".  You can also feed this as standard input via
  6. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  7. # will see the following message at the end:
  8. #        "End of archive 8 (of 11)."
  9. # Contents:  daemon/pc-mail.c main/DEFAULT.ins main/desk.c
  10. #   main/gtrans.c main/window.c
  11. # Wrapped by wswietse@tuewsa on Mon Jan 22 17:27:20 1990
  12. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  13. if test -f daemon/pc-mail.c -a "${1}" != "-c" ; then 
  14.   echo shar: Will not over-write existing file \"daemon/pc-mail.c\"
  15. else
  16. echo shar: Extracting \"daemon/pc-mail.c\" \(11399 characters\)
  17. sed "s/^X//" >daemon/pc-mail.c <<'END_OF_daemon/pc-mail.c'
  18. X/*++
  19. X/* NAME
  20. X/*    pc-mail 8
  21. X/* SUMMARY
  22. X/*    deliver mail to nfs-based pc-mail users
  23. X/* PROJECT
  24. X/*    pc-mail
  25. X/* PACKAGE
  26. X/*    nfs
  27. X/* SYNOPSIS
  28. X/*    pc-mail user
  29. X/* DESCRIPTION
  30. X/*    This program is to be run on the nfs server that exports mail
  31. X/*    directories to MS-DOS pc-mail users. The program replaces the
  32. X/*    UNIX -> MS-DOS file transfer function of the MS-DOS \fIcico\fR
  33. X/*    program.
  34. X/*
  35. X/*    Normally, the pc-mail delivery program is invoked by sendmail(8).
  36. X/*    Its purpose is to deliver new mail in the mail directory of the
  37. X/*    specified \fIuser\fR (default /usr/spool/pc-mail/\fIuser\fR).
  38. X/*    Any error conditions detected by the pc-mail delivery program
  39. X/*    are reported back in a sendmail-compatible manner.
  40. X/*
  41. X/*    This program must be run with root privileges. It will assume
  42. X/*    the (uid, gid) of the specified user before delivering mail.
  43. X/*
  44. X/*    The program attempts to create any missing directories, and to
  45. X/*    correct ownerships or protections where needed.
  46. X/* FILES
  47. X/*    /usr/spool/pc-mail/\fIuser\fR/nNNNNN, mail message.
  48. X/*    /usr/spool/pc-mail/\fIuser\fR/hNNNNN, sender of message and subject.
  49. X/*    (NNNNN is the pc-mail "message id").
  50. X/* SEE ALSO
  51. X/*    pc-maild(1)
  52. X/* DIAGNOSTICS
  53. X/*    All conceivable error conditions cause the program to terminate
  54. X/*    with a non-zero exit status, after printing an error message on
  55. X/*    the standard error stream, and appending an entry to the system log.
  56. X/*    See <sysexits.h> for details.
  57. X/* BUGS
  58. X/*    There is no way to notify a pc-mail user of the arrival of new mail.
  59. X/* AUTHOR(S)
  60. X/*    W.Z. Venema
  61. X/*    Eindhoven University of Technology
  62. X/*    Department of Mathematics and Computer Science
  63. X/*    Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
  64. X/* CREATION DATE
  65. X/*    Sun Oct 22 18:00:53 MED 1989
  66. X/* LAST MODIFICATION
  67. X/*    1/6/90 19:08:13
  68. X/* VERSION/RELEASE
  69. X/*    1.10
  70. X/*--*/
  71. X
  72. X#ifndef lint
  73. Xstatic char sccsid[] = "@(#) pc-mail.c 1.10 1/6/90 19:08:13";
  74. X
  75. X#endif
  76. X
  77. X#include <stdio.h>
  78. X#include <sys/types.h>
  79. X#include <sys/stat.h>
  80. X#include <pwd.h>
  81. X#include <varargs.h>
  82. X
  83. X#ifdef SYSLOG
  84. X#include <syslog.h>
  85. X#else
  86. X#include "syslog.h"
  87. X#endif
  88. X
  89. X#ifdef SYSV
  90. X#include <ndir.h>
  91. X#else
  92. X#include <sys/dir.h>
  93. X#endif
  94. X
  95. X#ifdef    SYSEXITS
  96. X#include <sysexits.h>
  97. X#else
  98. X#include "sysexits.h"
  99. X#endif
  100. X
  101. X#include "dosunix.h"
  102. X#include "percentm.h"
  103. X#include "ms_parse.h"
  104. X
  105. X/* Stuff related to failed system calls */
  106. X
  107. Xextern int errno;
  108. X
  109. X/* External functions */
  110. X
  111. Xextern struct passwd *getpwnam();
  112. Xextern long time();
  113. Xextern char *mktemp();
  114. Xextern void exit();
  115. Xextern unsigned sleep();
  116. X
  117. X/* Local declarations */
  118. X
  119. X#ifndef    MAILDIR
  120. X#define    MAILDIR    "/usr/spool/pc-mail"    /* pc-mail directory tree */
  121. X#endif
  122. X
  123. X#define    LOCK    "pc-mail.lck"        /* the lock file */
  124. X#define    STALE    1800            /* max age of lock file */
  125. X#define    MAXTRY    60            /* max retry count for lock creation */
  126. X#define    MSGFIL_FMT    "n%05d"        /* message file name format */
  127. X#define    SNDFIL_FMT    "h%05d"        /* sender file name format */
  128. X#define    MAXLINE    1024            /* max length of recipient line */
  129. X
  130. Xchar    template[] = "pc.XXXXXX";    /* template lock file */
  131. X
  132. X/* local functions */
  133. X
  134. Xvoid    sender();
  135. Xvoid    message();
  136. Xvoid    error();
  137. X
  138. Xchar   *progname;
  139. X
  140. Xmain(argc, argv)
  141. Xint     argc;
  142. Xchar  **argv;
  143. X{
  144. X    struct passwd *pwd;
  145. X    static char userdir[BUFSIZ];
  146. X    int     seqno;
  147. X
  148. X    progname = argv[0];
  149. X
  150. X    /* Garbage in, garbage out */
  151. X
  152. X    if (argc != 2)
  153. X    error(EX_USAGE, "usage: %s user", *argv);
  154. X
  155. X#ifndef DEBUG
  156. X    if (geteuid() != 0)
  157. X    error(EX_USAGE, "must run with root privileges");
  158. X
  159. X    /* need this for SYSVR2 or mkdir(1) fails */
  160. X#ifdef SYSV
  161. X    if (setuid(0) != 0)
  162. X    error(EX_OSERR, "cannot setuid(0)");
  163. X#endif
  164. X#endif
  165. X
  166. X    if ((pwd = getpwnam(argv[1])) == 0)
  167. X    error(EX_NOUSER, "unknown user: %s", argv[1]);
  168. X
  169. X    /* Setup a somewhat safe environment */
  170. X
  171. X    if (putenv("PATH=/bin:/usr/bin:/usr/ucb")
  172. X    || putenv("IFS= \t\n"))
  173. X    error(EX_TEMPFAIL, "putenv() failed");
  174. X
  175. X    /* Check the necessary directories exist */
  176. X
  177. X    (void) sprintf(userdir, "%s/%s", MAILDIR, argv[1]);
  178. X    checkdir(userdir, pwd->pw_uid, pwd->pw_gid, 0700);
  179. X
  180. X    /* Now with that out of the way, try to deliver the message */
  181. X
  182. X    if (setgid(pwd->pw_gid))
  183. X    error(EX_USAGE, "setgid(%s) failed: %m", argv[1]);
  184. X    if (setuid(pwd->pw_uid))
  185. X    error(EX_USAGE, "setuid(%s) failed: %m", argv[1]);
  186. X
  187. X    /* make sure the user mail directory is accessible */
  188. X
  189. X    if (chdir(userdir))
  190. X    error(EX_TEMPFAIL, "can't access mail directory %s", userdir);
  191. X
  192. X    /* deliver mail */
  193. X
  194. X    seqno = newseqno(userdir);            /* Allocate sequence number */
  195. X    message(pwd, seqno);            /* Create message file */
  196. X    sender(seqno);                /* Create metafile (sender) */
  197. X    exit(EX_OK);                /* Done. */
  198. X    /* NOTREACHED */
  199. X}
  200. X
  201. X/* message - write message file */
  202. X
  203. Xvoid    message(pwd, seqno)
  204. Xstruct passwd *pwd;
  205. Xint     seqno;
  206. X{
  207. X    static char buf[BUFSIZ];
  208. X    register FILE *fp;
  209. X
  210. X    /* Create the message file */
  211. X
  212. X    (void) sprintf(buf, MSGFIL_FMT, seqno);
  213. X    if ((fp = fopen(buf, "w")) == 0)
  214. X    error(EX_CANTCREAT, "create error for file %s/%s: %m",
  215. X          pwd->pw_name, buf);
  216. X    if (unix2dos(stdin, fp)) {
  217. X    (void) unlink(buf);
  218. X    error(EX_CANTCREAT, "write error for file %s/%s: %m",
  219. X          pwd->pw_name, buf);
  220. X    }
  221. X    (void) fclose(fp);
  222. X    (void) chmod(buf, 0400);            /* Avoid tampering */
  223. X}
  224. X
  225. X/* sender - extract sender from message */
  226. X
  227. Xvoid    sender(seqno)
  228. Xint     seqno;
  229. X{
  230. X    register FILE *ifp;
  231. X    register FILE *ofp;
  232. X    static char fname[BUFSIZ];        /* file names */
  233. X    static char line[MAXLINE];        /* read buffer */
  234. X    static char from[MAXLINE] = "Unknown";    /* sender */
  235. X    static char subject[MAXLINE] = "";    /* subject */
  236. X    register int context = MS_UUCP;
  237. X
  238. X    /*
  239. X     * Try to open the message file; if that fails, let the pc software scan
  240. X     * for the sender at a later time.
  241. X     * 
  242. X     * We recognize the following From line formats:
  243. X     * 
  244. X     * From name stuff            use name
  245. X     * 
  246. X     * >From name stuff            use name
  247. X     * 
  248. X     * From: address (full_name)    use full_name
  249. X     * 
  250. X     * From: full_name <address>    use full_name
  251. X     * 
  252. X     * From: full_name            use full_name
  253. X     */
  254. X
  255. X    (void) sprintf(fname, MSGFIL_FMT, seqno);
  256. X    if ((ifp = fopen(fname, "r")) == 0)
  257. X    return;
  258. X
  259. X    /* Extract sender and subject from message */
  260. X
  261. X    while (dosgets(line, sizeof(line), ifp) != 0
  262. X    && (context = ms_parse(context, line)) != MS_BODY) {
  263. X    switch (context) {
  264. X    case MS_UUCP:
  265. X        if (sscanf(line, "%*[>] From %s", from) != 1)
  266. X        (void) sscanf(line, "From %s", from);
  267. X        break;
  268. X    case MS_HEADER:
  269. X        if (hscanf(line, "Subject:", " %[^\n]", subject) == 0
  270. X        && hscanf(line, "From:", " %*s ( %[^)] )", from) == 0)
  271. X        (void) hscanf(line, "From:", " %[^<]", from);
  272. X        break;
  273. X    }
  274. X    }
  275. X    (void) fclose(ifp);
  276. X
  277. X    /*
  278. X     * Try to create the meta file; if that fails, let the pc software try
  279. X     * again at a later time.
  280. X     */
  281. X
  282. X    (void) sprintf(fname, SNDFIL_FMT, seqno);
  283. X    if (ofp = fopen(fname, "w")) {
  284. X    (void) fprintf(ofp, "%s\r\n%s\r\n", from, subject);
  285. X    if (fflush(ofp) || ferror(ofp) || feof(ofp) || fclose(ofp)) {
  286. X        (void) unlink(fname);
  287. X    } else {
  288. X        (void) chmod(fname, 0400);        /* avoid tampering */
  289. X    }
  290. X    }
  291. X}
  292. X
  293. X/* newseqno - allocate new message sequence number */
  294. X
  295. Xint     newseqno(userdir)
  296. Xchar   *userdir;
  297. X{
  298. X    register DIR *dd;
  299. X    register struct direct *p;
  300. X    struct stat st;
  301. X    register int seqno = 0;
  302. X    int     tmp = 0;
  303. X    int     i;
  304. X    char    junk;
  305. X
  306. X    /*
  307. X     * When the pc adds a file to the "mail data base", the file name is
  308. X     * composed of a single letter and a unique sequence number. The pc
  309. X     * chooses a new sequence number by adding one to the highest existing
  310. X     * sequence number.
  311. X     * 
  312. X     * Now that the pc mounts its mail directory from the nfs server we must
  313. X     * avoid possible concurrency conflicts when both pc and file server try
  314. X     * to update the "mail data base".
  315. X     * 
  316. X     * Since the pc does not know about concurrent access from the nfs server,
  317. X     * the server has to add 2 to the highest existing message sequence
  318. X     * number, in order to avoid conflicts. Fortunately, only one pc at a
  319. X     * time will be accessing a mail directory of a particular user.
  320. X     * 
  321. X     * Further concurrency conflicts are be avoided on the server side by using
  322. X     * lock files.
  323. X     * 
  324. X     * If we cannot create a lock file right now, we back off and let sendmail
  325. X     * try again later.
  326. X     */
  327. X
  328. X    /* Get rid of stale lock files */
  329. X
  330. X    if (stat(LOCK, &st) == 0 && st.st_mtime < time((long *) 0) - STALE)
  331. X    (void) unlink(LOCK);
  332. X
  333. X    /* Wait until we can create the lock file */
  334. X
  335. X    if (creat(mktemp(template), 0400) < 0)
  336. X    error(EX_TEMPFAIL, "cannot set lock in directory %s: check ownership",
  337. X          userdir);
  338. X    for (i = 0; link(template, LOCK) && i < MAXTRY; i++)
  339. X    (void) sleep(1);
  340. X    (void) unlink(template);
  341. X    if (i >= MAXTRY)
  342. X    error(EX_TEMPFAIL, "locked: %s", userdir);
  343. X
  344. X    /* Scan the user mail directory for the highest existing message number */
  345. X
  346. X    if ((dd = opendir(userdir)) == 0) {
  347. X    (void) unlink(LOCK);
  348. X    error(EX_TEMPFAIL, "opendir(\"%s\") failed: %m", userdir);
  349. X    }
  350. X    while (p = readdir(dd)) {
  351. X    if (strlen(p->d_name) == 6
  352. X    && sscanf(p->d_name + 1, "%d%c", &tmp, &junk) == 1 && tmp > seqno)
  353. X        seqno = tmp;
  354. X    }
  355. X
  356. X    /* clean up and terminate */
  357. X
  358. X    closedir(dd);
  359. X    (void) unlink(LOCK);
  360. X    return (seqno + 2);
  361. X}
  362. X
  363. X/* checkdir - check/update presence/ownership/protection of directory */
  364. X
  365. Xcheckdir(path, uid, gid, mode)
  366. Xchar   *path;
  367. Xint     uid;
  368. Xint     gid;
  369. Xint     mode;
  370. X{
  371. X    struct stat st;
  372. X
  373. X    /*
  374. X     * If a user mail directory does not exist, try to create it. Otherwise,
  375. X     * make sure it has sane permissions
  376. X     */
  377. X
  378. X    if (stat(path, &st) == -1) {        /* no directory */
  379. X    if (mkdir(path, mode))            /* try to create it */
  380. X        error(EX_TEMPFAIL, "cannot create directory %s: %m", path);
  381. X    if (chown(path, uid, gid))        /* set owner, group */
  382. X        error(EX_TEMPFAIL, "cannot chown directory %s: %m", path);
  383. X    } else {                    /* directory exists */
  384. X    if ((st.st_mode & S_IFMT) != S_IFDIR)    /* must be directory! */
  385. X        error(EX_TEMPFAIL, "%s should be a directory", path);
  386. X    if ((st.st_uid != uid || st.st_gid != gid)    /* check owner/group */
  387. X        &&chown(path, uid, gid))        /* correct owner, group */
  388. X        error(EX_TEMPFAIL, "cannot chown directory %s: %m", path);
  389. X    if ((st.st_mode & 0777) != mode        /* check permissions */
  390. X        && chmod(path, mode))        /* correct permissions */
  391. X        error(EX_TEMPFAIL, "cannot chmod %o directory %s: %m", mode, path);
  392. X    }
  393. X}
  394. X
  395. X/* error - print diagnostic and terminate */
  396. X
  397. X/* VARARGS */
  398. X
  399. Xvoid    error(va_alist) va_dcl
  400. X{
  401. X    va_list ap;
  402. X    register int exstat;
  403. X    register char *fmt;
  404. X    char    buf[BUFSIZ];
  405. X    int     err = errno;
  406. X
  407. X    /* Format the error message */
  408. X
  409. X    va_start(ap);
  410. X    exstat = va_arg(ap, int);            /* exit status */
  411. X    fmt = va_arg(ap, char *);            /* format string */
  412. X    (void) vsprintf(buf, percentm(fmt, err), ap);
  413. X    va_end(ap);
  414. X
  415. X    /* Write message to standard error stream */
  416. X
  417. X    (void) fprintf(stderr, "%s: %s\n", progname, buf);
  418. X
  419. X    /* Append the same message to system log */
  420. X
  421. X    (void) openlog("pc-mail", LOG_PID, LOG_MAIL);
  422. X    (void) syslog(LOG_WARNING, "%s", buf);
  423. X    (void) closelog();
  424. X
  425. X    /* Notify sendmail of the nature of the problem */
  426. X
  427. X    exit(exstat);
  428. X}
  429. X
  430. X#ifdef SYSV
  431. X
  432. X/* mkdir - create directory */
  433. X
  434. Xint     mkdir(dir, mode)
  435. Xchar   *dir;
  436. Xint     mode;
  437. X{
  438. X    char    cmd[BUFSIZ];
  439. X
  440. X    (void) sprintf(cmd, "mkdir %s 2>&1 >/dev/null 2>&1 && chmod %o %s",
  441. X        dir, mode, dir);
  442. X    return (system(cmd));            /* does not set errno */
  443. X}
  444. X
  445. X#endif
  446. END_OF_daemon/pc-mail.c
  447. if test 11399 -ne `wc -c <daemon/pc-mail.c`; then
  448.     echo shar: \"daemon/pc-mail.c\" unpacked with wrong size!
  449. fi
  450. # end of overwriting check
  451. fi
  452. if test -f main/DEFAULT.ins -a "${1}" != "-c" ; then 
  453.   echo shar: Will not over-write existing file \"main/DEFAULT.ins\"
  454. else
  455. echo shar: Extracting \"main/DEFAULT.ins\" \(11543 characters\)
  456. sed "s/^X//" >main/DEFAULT.ins <<'END_OF_main/DEFAULT.ins'
  457. X@(#) DEFAULT.ins 2.1 90/01/22 13:01:05
  458. X
  459. XThis document briefly describes how to set up  a  PC-mail  system
  460. Xthat uses the default PC-mail UUCP software to exchange mail with
  461. Xits UNIX host. The examples given apply to `old' UNIX UUCP;  your
  462. Xfile names and formats may vary.
  463. X
  464. XTHE UNIX SIDE OF THE CONNECTION
  465. X
  466. XThe PC-mail programs will need a UNIX host to  exchange  messages
  467. Xwith.  This  automatically gives access to other networks. I sug-
  468. Xgest the following strategy:
  469. X
  470. X - Get a UUCP login, say, `uuxyz' on a  UNIX  system.  This  will
  471. Xalso  become  the  UUCP-node  name of the PC. Sometimes, the UNIX
  472. XUUCP software will refuse  to  work  with  arbitrary  (uid,  gid)
  473. Xvalues; during the initial handshake messages, it will reply with
  474. XRLOGIN instead of ROK. On these systems, the (uid, gid) values of
  475. Xyour  UUCP  login  should be small numbers, probably in the range
  476. X1-100.
  477. X
  478. XThe UUCP systems file (/usr/lib/uucp/L.sys)  should  be  extended
  479. Xwith an entry for the `uuxyz' host, e.g.
  480. X
  481. X    uuxyz Passive
  482. X
  483. XOn some systems one has to specify `Never' instead of  `Passive'.
  484. XIt  may  also  be  necessary  to update the UUCP permissions file
  485. X(/usr/lib/uucp/USERFILE).
  486. X
  487. X - Have all mail for user `uuxyz' forwarded to  `uuxyz!somebody'.
  488. XWith Berkeley UNIX, this can be achieved by placing  the  address
  489. X`uuxyz!somebody'  in the file ~uuxyz/.forward; with System-V UNIX
  490. Xone may have to put the text `Forward to uuxyz!somebody'  in  the
  491. Xfile  /usr/mail/uuxyz,  which  should  be read/writeable by group
  492. Xmail. Alternatively, you can ask the UNIX system administrator to
  493. Xdefine  an  alias  that  maps  `uuxyz' to `uuxyz!somebody' if the
  494. Xlocal mailer supports aliases.
  495. X
  496. XIn the above examples, `somebody' is a name that  can  be  freely
  497. Xchosen;  it  will  not  be used by the PC. Of course, mail can be
  498. Xforwarded to `uuxyz!somebody' from other accounts as well.
  499. X
  500. XThe result of all this is that you can send mail to any  user  on
  501. Xthe  UNIX  host by just specifying her login name; your mail will
  502. Xappear to come from the user `uuxyz' on the UNIX host. The  UUCP-
  503. Xnode  name of your PC will not appear in mail headers. People can
  504. Xsend mail to you by specifying `uuxyz' on the  UNIX  host.  Since
  505. Xthe  host name of the PC does not appear in mail headers there is
  506. Xno need to register the PC in, e.g., the UUCP maps.
  507. X
  508. XAs a minimum, the UNIX host should support the standard UUCP  `g'
  509. Xprotocol.  This  protocol  was developed for eight-bit data paths
  510. Xacross  dial-up  links  (modems).  Unfortunately,  more  advanced
  511. Xnetworks eat up XON/XOFF and other control characters. This  must
  512. Xbe the price of progress.
  513. X
  514. XTo handle  non-transparent  networks  I  have  written  a  simple
  515. Xstart/stop  `k'  protocol.  It  has  been in use on the Eindhoven
  516. XUniversity Sytek network since 1986 and is part  of  the  PC-mail
  517. Xdistribution.  If  you're really desperate (and have UNIX source)
  518. Xbuild this  protocol  into  the  uucico  program  by  adding  the
  519. Xfollowing line to struct Proto Ptbl[] in the file cntrl.c:
  520. X
  521. X    'k', kturnon, krdmsg, kwrmsg, krddata, kwrdata, kturnoff,
  522. X
  523. Xand linking the uucico objects with kio.c  kp.h  kphys.c  kpres.c
  524. Xand ktrans.c.
  525. X
  526. XTHE PC SIDE OF THE CONNECTION
  527. X
  528. XA warning for MS-DOS users: TSR programs may interfere  with  the
  529. Xoperation of the dial-up and file transfer program.
  530. X
  531. XTo up bring PC-mail, copy the appropriate makefile.whatever  file
  532. Xto  makefile  and  edit it (there are template makefiles for UNIX
  533. Xand MS-DOS).  The MS-DOS makefile is for a  UNIX-compatible  make
  534. Xutility  posted  to usenet near the end of 1986. There is a batch
  535. Xcommand file (DEFAULT.bat) for those who  do  not  have  a  unix-
  536. Xcompatible make utility. The make  utility  provided  with  early
  537. Xreleases of MicroSoft C is definitely not UNIX compatible.
  538. X
  539. XSaying `make' should produce five programs:
  540. X
  541. X - mail, the menu-driven user interface
  542. X - cmail, a program that checks if there is new mail
  543. X - smail, a program that queues messages for  transmission  after
  544. X          doing alias substitution on mail addresses
  545. X - nmail, extracts "From" and "Subject" info from new mail
  546. X - cico, the program that performs dialups and file transfers
  547. X
  548. XUnder MS-DOS, the cico program has to be compiled with the  small
  549. Xmemory  model; in order to handle mail messages larger than about
  550. X10 kbyte, the mail user interface program should be compiled with
  551. Xthe large memory model.
  552. X
  553. XThe programs access a common data base in the  form  of  a  spool
  554. Xdirectory with setup file, logfile and message files.  Optionally
  555. Xthere may be  header  and  trailer  files  to  generate  template
  556. Xmessages.  There should be no other files in the spool directory,
  557. Xto avoid confusion. The spool  directory  should  be  created  by
  558. Xhand; the PC-mail programs will not do that.
  559. X
  560. XYou will have to set some environment  variables  before  running
  561. Xthe mail program.
  562. X
  563. X - MAILDIR, the location of your mail data base directory
  564. X - EDITOR, the name of your favourite editor program
  565. X - PATH, in order locate the PC-mail executables, and your editor
  566. X
  567. XIt is advised to use absolute path names that include  the  drive
  568. Xname.  The  editor  command  may be an MS-DOS batch file; in that
  569. Xcase you should include the '.bat' suffix in the command name.
  570. X
  571. XThe following two environment variables are optional.
  572. X
  573. X - MAILPRN, the name of a file, if printer output should not go to 
  574. X    the default printer.
  575. X - MAILCMD, a command that is executed  on  exit  from  the  mail
  576. X        program. If this is an MS-DOS batch file you should include
  577. X    the '.bat' suffix in the command name.
  578. X
  579. XAt our site, these two variables  are  used  to  collect  printer
  580. Xoutput  in  one  file, and to send it to a networked printer upon
  581. Xexit from the program.
  582. X
  583. XMake sure that the limit on the number of  open  files  is  large
  584. Xenough  (20  or  so).  On  MS-DOS, this is handled by a line with
  585. X`files=20' in the CONFIG.SYS file.
  586. X
  587. XOn MS-DOS, the mail user interface requires the ANSI.SYS  driver.
  588. XThe CONFIG.SYS file should specify a line with "device=ansi.sys".
  589. X
  590. XRun the interactive mail program and choose  the  setup  command.
  591. X
  592. XAll entries must be filled or the cico program (for  dial-up  and
  593. Xfile  transfer)  will  complain. On MS-DOS systems, only the com1
  594. Xport is supported (see the file comport.asm). I am not interested
  595. Xin  80*86  assembly-language programming; you will have hack your
  596. Xown com2 support.
  597. X
  598. XThe first item in the setup is not used by the dial-up and  file-
  599. Xtransfer program.
  600. X
  601. XHere  is  my  setup  (some names changed) for getting through the
  602. Xhorrible port selector of our local university network (it  needs
  603. Xto  receive  up  to  nine carriage returns in order to detect the
  604. Xbaud rate of a dial-up call, can you believe it):
  605. X
  606. X    ignore_header_lines: received message-id
  607. X    communications_port: com1
  608. X    baud_rate:           2400
  609. X    remote_host_name:    eutwc1
  610. X    login_name:          uutest
  611. X    dialup_sequence:     atz\r OK atdt0,455215\r CONNECT \r \0 \r \0 \r 
  612. X    \0 \r \0 \0 \r \0 \r \0 \r \0 \r \0 \r \0 \r ease: abc\r ease: def\r 
  613. X    hoice:-\0-hoice: 1\r lled: b076\r CLOSED \r
  614. X    disconnect_sequence: \0
  615. X
  616. XThe dial-up sequence requires some explanation. Basically it is a
  617. Xlist of words separated by whitespace:
  618. X
  619. X    send1 expect1 send2 expect2 (and so on)
  620. X
  621. XThe first word is sent to the comm. port. The program  will  wait
  622. Xuntil  it receives the second word. Then it sends the third word.
  623. XAnd so on. There is a retry facility, similar to the one in  real
  624. XUUCP or later versions of Kermit, that works as follows:  instead
  625. Xof an expect string you can  specify  an  alternate  sequence  as
  626. Xwords separated by hyphen characters:
  627. X
  628. X    expect-altsend1-altexpect1-altsend2-altexpect2 (and so on)
  629. X
  630. XIf the "expect" string  is  not  received,  the  first  alternate
  631. X"send"  string  is  sent, and the program waits until it receives
  632. Xthe first alternate "expect" string. If that  fails,  the  second
  633. Xalternate "send" string is sent and so on. The alternate sequence
  634. Xis terminated until an (alternate) expect succeeds or  until  the
  635. Xalternate sequence is exhausted, or due to time out.
  636. X
  637. XNote  that  carriage-return  characters  are  not   automatically
  638. Xappended  to  "send" strings. In order to specify these and other
  639. Xcontrol characters in send/expect  strings  I  have  stolen  some
  640. Xescape sequences from the C programming language, and added some:
  641. X
  642. X    \f          formfeed
  643. X    \n          linefeed
  644. X    \r          carriage return
  645. X    \s          blank
  646. X    \t          tab
  647. X    \\          backslash
  648. X    \nnn        octal code for a character
  649. X
  650. XIn  order to send or expect an empty string, use the \0 sequence.
  651. XEmpty "send" strings introduce brief delays.  An  empty  "expect"
  652. Xstring always succeeds.
  653. X
  654. XThe following "send" strings are given special treatment:
  655. X
  656. X    BREAK    causes a null character to be sent
  657. X    EOT        causes a Control-D character to be sent
  658. X
  659. XThe dial-up sequence specified in the setup file should terminate
  660. Xwhen  the  UNIX host is about to display its "login:" prompt; the
  661. Xremainder of the dialup sequence  is  wired  into  the  software.
  662. XThis, and having (PC node name) equal to (UUCP login name) are to
  663. Xprevent fraud. If You  have  problems  with  this  approach,  you
  664. Xshould edit the file "connect.c".
  665. X
  666. XThus, assuming a Hayes-compatible  modem,  your  dialup  sequence
  667. Xcould be as simple as:
  668. X
  669. X    atz\r OK atdt123456\r CONNECT
  670. X
  671. XWhen this dialup sequence succeeds, the  program  continues  with
  672. Xits built-in sequence:
  673. X
  674. X    ogin: your_uucp_login\r ssword: your_uucp_password\r
  675. X
  676. XThe disconnect sequence uses the same  escape  sequences  as  the
  677. Xdial-up  sequence,  but does not use the send/expect protocol. In
  678. Xthe example above, the disconnect  sequence  is  a  null  string,
  679. Xwhich happens to be the default.
  680. X
  681. XIn order to test your dial-up  sequence  you  can  run  the  cico
  682. Xprogram by hand, for example:
  683. X
  684. X    cico -d 9 -p your_uucp_password
  685. X
  686. XThis  will  produce  a  lot  of  debugging  output.  Setting  the
  687. Xdebugging  level  to  less  than  9 reduces verbosity. Level 4 is
  688. Xsufficient to monitor the dial-up and login sequence.
  689. X
  690. XALIAS DATABASE
  691. X
  692. XThe user can define aliases for (groups of) mail  addresses.  The
  693. Xalias data base is a text file with on each line:
  694. X
  695. X    alias replacement_part
  696. X
  697. XThe alias should be a single word; words are separated by blanks,
  698. Xtabs  or  commas.  The replacement part may be one or more words.
  699. XWhenever the smail (mail spooler) program recognizes an alias, it
  700. Xis  replaced by the `replacement part'. Aliases may be defined in
  701. Xterms of other aliases; the order in which  they  appear  in  the
  702. Xalias data base is not important (except when an alias is defined
  703. Xmore than once; the program remembers only the last definition of
  704. Xan alias). The alias expansion software is smart enough to detect
  705. Xinfinite loops and to suppress multiple occurrances of  the  same
  706. Xrecipient. Alias substitution is not case-sensitive.
  707. X
  708. XBATCH-MODE OPERATION
  709. X
  710. XThe cmail program can be run from a batch file  (say,  each  time
  711. Xthe  PC is turned on), to contact the UNIX host, and to report if
  712. Xthere is new mail. Also, you may want to auto-execute  the  cmail
  713. Xcommand  when  exiting from the interactive mail shell (using the
  714. XMAILCMD environment variable described  above).  See  the  manual
  715. Xpage in the cmail.c source.
  716. X
  717. XTEMPLATE FILES
  718. X
  719. XThe user can provide message templates with standard  header  and
  720. Xtrailer  lines.  If  the  file  "header"  is  present in the mail
  721. Xdirectory, its contents will be  included  at  the  beginning  of
  722. Xevery  mail  message created by the user. Similarly, the contents
  723. Xof a file "trailer" will be included at the end of mail messages.
  724. XThe "header" and "trailer" files should be ordinary text files.
  725. END_OF_main/DEFAULT.ins
  726. if test 11543 -ne `wc -c <main/DEFAULT.ins`; then
  727.     echo shar: \"main/DEFAULT.ins\" unpacked with wrong size!
  728. fi
  729. # end of overwriting check
  730. fi
  731. if test -f main/desk.c -a "${1}" != "-c" ; then 
  732.   echo shar: Will not over-write existing file \"main/desk.c\"
  733. else
  734. echo shar: Extracting \"main/desk.c\" \(12439 characters\)
  735. sed "s/^X//" >main/desk.c <<'END_OF_main/desk.c'
  736. X/*++
  737. X/* NAME
  738. X/*    desk 3
  739. X/* CATEGORY
  740. X/*    mail box display
  741. X/* PROJECT
  742. X/*    pc-mail
  743. X/* PACKAGE
  744. X/*    mail
  745. X/* SYNOPSIS
  746. X/*    #include "mail.h"
  747. X/*
  748. X/*    void init()
  749. X/*
  750. X/*    int junk_desk()
  751. X/*
  752. X/*    char message[];
  753. X/*    char comment[];
  754. X/* DESCRIPTION
  755. X/*      Most functions in this module are invoked by the keyboard interpreter
  756. X/*      and are responsible for the mail box view of message summary lines.
  757. X/*
  758. X/*    init() is the main entry point. It presents the user with a display
  759. X/*    of message categories (create, unread, already seen, unsent, sent,
  760. X/*    in preparation), and the number of messages in each category. After 
  761. X/*    the user has chosen a category, an editor is invoked, or a sorted 
  762. X/*    display of all messages in the respective category is displayed.
  763. X/*
  764. X/*    junk_desk() should be invoked when the number of files in the mail box
  765. X/*    may have changed. Always returns a zero value. This function
  766. X/*    should be called when a message is added to, or deleted from, the
  767. X/*    spool directory.
  768. X/*
  769. X/*    The strings "message" and "comment" hold path names of the currently
  770. X/*    selected message file, and its associated meta file (with message
  771. X/*    destination, origin or comments). These names are used by functions
  772. X/*    that read, delete or otherwise manipulate message files.
  773. X/* FILES
  774. X/*      mail header files in the spool directory
  775. X/* SEE ALSO
  776. X/*      pager(3), pager(5), kbdinp(3)
  777. X/* DIAGNOSTICS
  778. X/*      If a selected mail message could not be found an error message
  779. X/*      is displayed instead.
  780. X/* BUGS
  781. X/*      Since a message can be accessed only if its metafile exists,
  782. X/*    a message is "lost" when for some reason the metafile is
  783. X/*    not available.
  784. X/* AUTHOR(S)
  785. X/*      W.Z. Venema
  786. X/*      Eindhoven University of Technology
  787. X/*      Department of Mathematics and Computer Science
  788. X/*      Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
  789. X/* CREATION DATE
  790. X/*    Tue May 12 15:35:20 GMT+1:00 1987
  791. X/* LAST MODIFICATION
  792. X/*    90/01/22 13:01:29
  793. X/* VERSION/RELEASE
  794. X/*    2.1
  795. X/*--*/
  796. X
  797. X#include <stdio.h>
  798. X#include <ctype.h>
  799. X#include <errno.h>
  800. X#include <sys/types.h>
  801. X#include <sys/stat.h>
  802. X#include <time.h>
  803. X
  804. X#include "defs.h"
  805. X#include "mail.h"
  806. X#include "path.h"
  807. X#include "ndir.h"
  808. X#include "pager.h"
  809. X#include "screen.h"
  810. X#include "status.h"
  811. X#include "window.h"
  812. X#include "ascf.h"
  813. X#include "snapshot.h"
  814. X
  815. Xhidden void make_desk();        /* forward declarations */
  816. Xhidden int pick_desk();
  817. Xhidden int show_desk();
  818. Xhidden void desk();
  819. Xhidden void make_init();
  820. Xhidden int pick_init();
  821. Xhidden int show_init();
  822. Xhidden char *singular();
  823. X
  824. Xhidden File *deskfile = 0;        /* mail box pager file */
  825. Xhidden File *initfile = 0;        /* initial screen */
  826. Xpublic char message[BUFSIZ];        /* path to message file */
  827. Xpublic char comment[BUFSIZ];        /* path to meta file */
  828. X
  829. X/* Definitions of the various message categories */
  830. X
  831. Xtypedef struct {
  832. X    char   *type;            /* work, incoming, outgoing, ... */
  833. X    char    mesg;            /* message-file prefix */
  834. X    char    meta;            /* meta-file prefix */
  835. X    int   (*access) ();            /* message access function */
  836. X    int     flags;            /* see below */
  837. X    char   *whatsit;            /* explanation */
  838. X} CATEGORY;
  839. X
  840. X#define    MULT    1            /* multi-message category */
  841. X#define    DMON    2            /* subject to daemon activity */
  842. X
  843. Xhidden CATEGORY categories[] = {
  844. X    "Create", WORK_MESG, WORK_META, create,0,       "Create a new message",
  845. X    "Work",   WORK_MESG, WORK_META, work, MULT,     "Message%S in preparation",
  846. X    "New",    NEW_MESG,  NEW_META,  mbox, MULT|DMON,"Unread message%S",
  847. X    "In",     OLD_MESG,  OLD_META,  mbox, MULT,     "Message%S already read",
  848. X    "Out",    OUT_MESG,  OUT_META,  mbox, MULT|DMON,"Message%S not-yet sent",
  849. X    "Sent",   SENT_MESG, SENT_META, mbox, MULT|DMON,"Message%S already sent",
  850. X    0,                    /* terminator */
  851. X};
  852. X
  853. Xhidden CATEGORY *category;        /* selected message category */
  854. X
  855. X/* table with all meta-file name prefixes */
  856. X
  857. Xhidden char metalist[] = {
  858. X    WORK_META, NEW_META, OLD_META, OUT_META, SENT_META, 0,
  859. X};
  860. X
  861. Xhidden char initsingle[] = "%-6s %5s %s";
  862. Xhidden char initmulti[] = "%-6s %5u %s";
  863. Xhidden char scanmulti[] = "%s %u";
  864. X
  865. Xhidden char dispfmt[] = "%5u  %.16s  %.53s";
  866. Xhidden char dispfmts[] = "%5u  %.16s  %.40s \"%s\"";
  867. Xhidden char scanfmt[] = "%u";
  868. X
  869. X/* init - main entry point for message manipulations */
  870. X
  871. Xpublic void init()
  872. X{
  873. X    static Screen screen[] = {
  874. X    'C',    "Close",    0,    "Terminate the program",
  875. X    'F',    "File",        file,    "Mail a copy of an ordinary file",
  876. X#ifndef    DAEMON
  877. X    'N',    "Network",    call,    "Exchange mail with the network",
  878. X#endif
  879. X    'S',    "Setup",    setup,    "Set communications parameters",
  880. X    'A',    "Alias",    alias,    "Display the alias data base",
  881. X    'P',    "Print",    print,    "Print contents of this display",
  882. X    UP,    "Up",        up_pager,    csrup,
  883. X    DOWN,    "Down",        dn_pager,    csrdn,
  884. X    ENTER,    "Enter",    pick_init,    "Select message category",
  885. X    0,    0,        show_init,
  886. X    "Select a message category with cursor keys and press ENTER\n\
  887. Xor select one of the commands in the top line."
  888. X    };
  889. X
  890. X    kbdinp(screen);                /* and there they go... */
  891. X}
  892. X
  893. X/* show_init - create or refresh the initial screen */
  894. X
  895. Xhidden int show_init()
  896. X{
  897. X    if (initfile == 0) {            /* no initial screen file */
  898. X    patience();                /* one moment please */
  899. X    make_init(initfile = open_pager());    /* build initial screen */
  900. X    } else {                    /* pager file exists */
  901. X    set_pager(initfile);            /* select pager file */
  902. X    }
  903. X    ds_pager();                    /* display it */
  904. X    return (0);                    /* screen is ok */
  905. X}
  906. X
  907. X/* junk_init - force re-scan of mail directory and re-build of initial screen */
  908. X
  909. Xhidden void junk_init()
  910. X{
  911. X    if (initfile) {
  912. X    close_pager(initfile);
  913. X    initfile = 0;
  914. X    }
  915. X    snap_junk();                /* re-scan mail directory */
  916. X}
  917. X
  918. X/* make_init - build initial screen */
  919. X
  920. Xhidden void make_init(pp)
  921. XFile   *pp;
  922. X{
  923. X    register SNAP_SHOT *s;
  924. X    register unsigned count;
  925. X    register CATEGORY *c;
  926. X
  927. X    /*
  928. X     * In case of multi-message categories, show the number of messages in
  929. X     * that category.
  930. X     */
  931. X
  932. X    for (c = categories; c->type; c++) {
  933. X    if (c->flags & MULT) {            /* multi-message category */
  934. X        for (count = 0, s = snap_shot(metalist); s->prefix; s++)
  935. X        if (c->meta == s->prefix)
  936. X            count++;
  937. X        app_pager(pp, strcons(initmulti, c->type, count,
  938. X                  singular(count, c->whatsit)));
  939. X    } else {                /* single-message category */
  940. X        app_pager(pp, strcons(initsingle, c->type, "", c->whatsit));
  941. X    }
  942. X    }
  943. X    pp->opts |= PG_NOEND;            /* suppress 'end-of-display' */
  944. X}
  945. X
  946. X/* exec_msg - execute access function for a particular message */
  947. X
  948. Xhidden int exec_msg(msgno)
  949. Xunsigned msgno;
  950. X{
  951. X    (void) strcpy(message, mesg_file(category->mesg, msgno));
  952. X    (void) strcpy(comment, meta_file(category->meta, msgno));
  953. X    return (CALL(category->access) (category->meta, msgno));
  954. X}
  955. X
  956. X/* pick_init - user selected a message category */
  957. X
  958. Xhidden int pick_init()
  959. X{
  960. X    char    type[BUFSIZ];
  961. X    register CATEGORY *c;
  962. X    unsigned count;
  963. X
  964. X    /*
  965. X     * Read the message type (in, out, work etc) from the summary line in the
  966. X     * initial display.
  967. X     * 
  968. X     * On systems that do not use daemons for message delivery, disallow
  969. X     * selection an empty message category.
  970. X     */
  971. X
  972. X    count = type[0] = 0;            /* initialize */
  973. X    (void) sscanf(gets_pager(), scanmulti, type, &count);
  974. X
  975. X    for (c = categories; c->type; c++) {    /* try to recognize the */
  976. X    if (strcmp(c->type, type) == 0) {    /* message type */
  977. X        category = c;            /* GLOBAL! */
  978. X        if ((c->flags & MULT) == 0) {    /* create-message category */
  979. X        return (exec_msg(newseqno()));
  980. X#ifndef DAEMON
  981. X        } else if (count == 0) {        /* multi-message, empty */
  982. X        break;
  983. X#endif
  984. X        } else {                /* multi-message */
  985. X        desk();
  986. X        return (S_REDRAW);
  987. X        }
  988. X    }
  989. X    }
  990. X    beep();                    /* error */
  991. X    return (0);                    /* nothing happened */
  992. X}
  993. X
  994. X/* desk - manipulate one category of messages */
  995. X
  996. Xhidden void desk()
  997. X{
  998. X    static Screen screen[] = {
  999. X    'C',    "Close",    0,    initscreen,
  1000. X    'F',    "File",        file,    "Mail a copy of an ordinary file",
  1001. X#ifndef    DAEMON
  1002. X    'N',    "Network",    call,    "Exchange mail with the network",
  1003. X#endif
  1004. X    'S',    "Setup",    setup,    "Set communications parameters",
  1005. X    'A',    "Alias",    alias,    "Display the alias data base",
  1006. X    'P',    "Print",    print,    "Print contents of this display",
  1007. X    PGUP,    PgUp,        pu_pager,pageup,
  1008. X    PGDN,    PgDn,        pd_pager,pagedn,
  1009. X    UP,    "Up",        up_pager,csrup,
  1010. X    DOWN,    "Down",        dn_pager,csrdn,
  1011. X    ENTER,    "Enter",    pick_desk,"Select message",
  1012. X    0,    0,        show_desk,
  1013. X    "Select a message with the cursor keys and press ENTER\n\
  1014. Xor select one of the commands in the top line."
  1015. X    };
  1016. X
  1017. X    /* 
  1018. X     * On systems where daemon processes take care of message delivery, we
  1019. X     * re-scan the mail directory if the user selects a message category that
  1020. X     * can be affected by daemon activity, and force a re-scan of the mail
  1021. X     * directory upon return to the initial screen (unless that just happened
  1022. X     * as a result of some user action).
  1023. X     */
  1024. X
  1025. X#ifdef    DAEMON
  1026. X    if (category->flags & DMON)            /* if affected by daemons */
  1027. X    junk_init();                /* re-scan mail directory */
  1028. X#endif
  1029. X    kbdinp(screen);
  1030. X#ifdef    DAEMON
  1031. X    junk_init();                /* re-scan mail directory */
  1032. X#endif
  1033. X    close_pager(deskfile);
  1034. X    deskfile = 0;
  1035. X}
  1036. X
  1037. X/* show_desk - create or refresh a display of a mail box selection */
  1038. X
  1039. Xhidden int show_desk()
  1040. X{
  1041. X    if (deskfile == 0) {            /* no mail box pager file */
  1042. X    patience();            /* one moment please... */
  1043. X    make_desk(deskfile = open_pager());    /* build mail box display */
  1044. X    } else {                    /* pager file exists */
  1045. X    set_pager(deskfile);            /* select pager file */
  1046. X    }
  1047. X    ds_pager();                    /* display it */
  1048. X    return (0);                    /* screen is ok */
  1049. X}
  1050. X
  1051. X/* make_desk - build display of summary lines of selected message type */
  1052. X
  1053. Xhidden void make_desk(pp)
  1054. XFile   *pp;
  1055. X{
  1056. X    FILE   *fp;                /* used to read meta info */
  1057. X    char    ident[MAXLINE];        /* usually person\'s name or address */
  1058. X    char    subj[MAXLINE];        /* subject info */
  1059. X    char   *line;
  1060. X    register SNAP_SHOT *s;        /* snapshot table */
  1061. X    struct stat st;
  1062. X
  1063. X    /*
  1064. X     * The message sequence number and type are already known; we just have
  1065. X     * to retrieve from the meta file: a person\'s name or address (first
  1066. X     * line) and an optional subject (second line).
  1067. X     * 
  1068. X     * If Subject: information is present we truncate the person\'s name or
  1069. X     * address to (screen width - 40) columns. Any text that extends beyond
  1070. X     * the width of the screen is truncated as well.
  1071. X     */
  1072. X
  1073. X    for (s = snap_shot(metalist); s->prefix; s++) {
  1074. X    if ((s->prefix == category->meta)
  1075. X    && (fp = ascopen(meta_file(s->prefix, s->msgno), "r"))) {
  1076. X        if ((ascgets(ident, sizeof(ident), fp) != 0)
  1077. X        && (fstat(fileno(fp), &st) == 0)) {
  1078. X        if (ascgets(subj, sizeof(subj), fp) && subj[0]) {
  1079. X            /* subject found; truncate person\'s name or address */
  1080. X            if (strlen(ident) > CO - 40)
  1081. X            (void) strcpy(ident + CO - 42, "..");
  1082. X            line = strcons(dispfmts, s->msgno,
  1083. X                   tstamp(&(st.st_mtime)), ident, subj);
  1084. X        } else {
  1085. X            /* no subject info */
  1086. X            line = strcons(dispfmt, s->msgno,
  1087. X                   tstamp(&(st.st_mtime)), ident);
  1088. X        }
  1089. X        /* truncate final result anyway */ if (strlen(line) >= CO - 1)
  1090. X            (void) strcpy(line + CO - 3, "..");
  1091. X        app_pager(pp, line);
  1092. X        }
  1093. X        ascclose(fp);
  1094. X    }
  1095. X    }
  1096. X
  1097. X/* sort summary lines in reverse order, i.e. newest comes first */
  1098. X
  1099. X    sort_pager(pp, BACK_SORT);
  1100. X}
  1101. X
  1102. X/* pick_desk - user selected a message */
  1103. X
  1104. Xhidden int pick_desk()
  1105. X{
  1106. X    unsigned msgno;
  1107. X
  1108. X    /*
  1109. X     * Read message sequence number from summary line in the mail box
  1110. X     * display. Build actual message file and meta file names. Then call the
  1111. X     * appropriate function to access that message.
  1112. X     */
  1113. X
  1114. X    msgno = 0;                    /* initialize */
  1115. X    (void) sscanf(gets_pager(), scanfmt, &msgno);
  1116. X
  1117. X    if (msgno) {
  1118. X    return (exec_msg(msgno));
  1119. X    } else {
  1120. X    beep();                    /* unrecognized message id */
  1121. X    return (0);                /* nothing happened */
  1122. X    }
  1123. X}
  1124. X
  1125. X/* junk_desk - force rebuilding of mail box display */
  1126. X
  1127. Xpublic int junk_desk()
  1128. X{
  1129. X    if (deskfile) {
  1130. X    close_pager(deskfile);        /* delete pager file */
  1131. X    deskfile = 0;                /* say it's gone */
  1132. X    }
  1133. X    junk_init();                /* and re-scan mail directory */
  1134. X    return (0);                    /* in case one wants it */
  1135. X}
  1136. X
  1137. X/* singular - replace %S depending on whether a count is 1 */
  1138. X
  1139. Xhidden char *singular(c, s)
  1140. Xint     c;
  1141. Xregister char *s;
  1142. X{
  1143. X    static char buf[BUFSIZ];
  1144. X    register char *bp = buf;
  1145. X
  1146. X    while (*s) {
  1147. X    if (*s == '%') {
  1148. X        if (s[1] == 'S') {            /* expand %S */
  1149. X        if (c != 1)
  1150. X            *bp++ = 's';
  1151. X        s += 2;
  1152. X        } else if (s[1] == '\0') {        /* don\'t fall off end */
  1153. X        *bp++ = *s++;
  1154. X        } else {                /* leave %<any> alone */
  1155. X        *bp++ = *s++, *bp++ = *s++;
  1156. X        }
  1157. X    } else {
  1158. X        *bp++ = *s++;
  1159. X    }
  1160. X    }
  1161. X    *bp = '\0';
  1162. X    return (buf);
  1163. X}
  1164. END_OF_main/desk.c
  1165. if test 12439 -ne `wc -c <main/desk.c`; then
  1166.     echo shar: \"main/desk.c\" unpacked with wrong size!
  1167. fi
  1168. # end of overwriting check
  1169. fi
  1170. if test -f main/gtrans.c -a "${1}" != "-c" ; then 
  1171.   echo shar: Will not over-write existing file \"main/gtrans.c\"
  1172. else
  1173. echo shar: Extracting \"main/gtrans.c\" \(10252 characters\)
  1174. sed "s/^X//" >main/gtrans.c <<'END_OF_main/gtrans.c'
  1175. X/*++
  1176. X/* NAME
  1177. X/*    gtrans 3
  1178. X/* SUMMARY
  1179. X/*    g protocol strategy functions
  1180. X/* PROJECT
  1181. X/*    pc-mail
  1182. X/* PACKAGE
  1183. X/*    cico
  1184. X/* SYNOPSIS
  1185. X/*    #include "gp.h"
  1186. X/*
  1187. X/*    int ginit(fd)
  1188. X/*    int fd;
  1189. X/*
  1190. X/*    Packet *galloc()
  1191. X/*
  1192. X/*    void gsproto(fd,pk)
  1193. X/*    int fd;
  1194. X/*    Packet *pk;
  1195. X/*
  1196. X/*    Packet *grproto(fd)
  1197. X/*    int fd;
  1198. X/*
  1199. X/*    void gfree(pk)
  1200. X/*    Packet *pk;
  1201. X/*
  1202. X/*    int gfinit(fd)
  1203. X/*    int fd;
  1204. X/* DESCRIPTION
  1205. X/*    ginit() exchanges the initial g protocol messages and allocates
  1206. X/*    memory for packet buffers.
  1207. X/*
  1208. X/*    galloc() returns a pointer to a free packet, after filling
  1209. X/*    in its k and len fields. This packet is supposed to be filled
  1210. X/*    with data, and to be subsequently queued with gsproto().
  1211. X/*
  1212. X/*    grproto() extracts the next packet from the input queue.
  1213. X/*    The packet should be returned to the free pool with gfree().
  1214. X/*
  1215. X/*    gfinit() sends protocol termination messages until it receives one
  1216. X/*    or until it gets bored.
  1217. X/* FUNCTIONS AND MACROS
  1218. X/*    gsctrl(), gsdata(), grpack(), gfail()
  1219. X/* DIAGNOSTICS
  1220. X/*    ginit(), gfinit() return a nonzero value if there was a problem.
  1221. X/*
  1222. X/*    The other functions return through a call of gfail() in case of
  1223. X/*    unrecoverable problems.
  1224. X/* BUGS
  1225. X/*    Window size is equal to one. This implies that the program 
  1226. X/*    only sends new data when the previous packet was acknowledged.
  1227. X/*    However, only the functions in *this* module need to be adapted 
  1228. X/*    to accomodate larger transmission window sizes.
  1229. X/* AUTHOR(S)
  1230. X/*    W.Z. Venema
  1231. X/*    Eindhoven University of Technology
  1232. X/*    Department of Mathematics and Computer Science
  1233. X/*    Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
  1234. X/* CREATION DATE
  1235. X/*    Sun Apr 19 17:30:08 GMT+1:00 1987
  1236. X/* LAST MODIFICATION
  1237. X/*    90/01/22 13:01:44
  1238. X/* VERSION/RELEASE
  1239. X/*    2.1
  1240. X/*--*/
  1241. X
  1242. X#include "gp.h"
  1243. X
  1244. X/*
  1245. X* "The protocol is defined in terms of message transmissions of 8-bit bytes."
  1246. X* "Each message includes one control byte plus a data segment of zero or more"
  1247. X* "information bytes. The allowed data segment sizes range between 32 and"
  1248. X* "4096 as determined by the formula 32*2^k where k is a 3-bit number."
  1249. X*/
  1250. X
  1251. Xint seglen[] = {             /* data segment sizes */
  1252. X    1,32,64,128,256,512,1024,2048,4096,
  1253. X};
  1254. X
  1255. Xstatic int sndseg;            /* data segment k-value they want */
  1256. Xstatic int sndlen;            /* data segment length they want */
  1257. Xstatic int sndwin;            /* transmission window size they want */
  1258. X
  1259. X#define ourseg    2            /* data segment k-value we want */
  1260. X#define ourlen    64            /* data segment length we want */
  1261. X#define ourwin    1            /* transmission window size we want */
  1262. X
  1263. Xstatic Packet *inpk = 0;        /* receive packet "pool" */
  1264. Xstatic Packet *outpk = 0;        /* send packet "pool" */
  1265. X
  1266. Xstatic int rval = 0;            /* our R value */
  1267. Xstatic int sval = 1;            /* our S value */
  1268. X
  1269. X/*
  1270. X* "Initial synchronization is accomplished with two 3-way handshakes:"
  1271. X* "two each of INITA/INITB/INITC. Each sender transmits INITA messages"
  1272. X* "repeatedly. When an INITA message is received, INITB is sent in return."
  1273. X* "When an INITB message is received *and* an INITB message has been sent,"
  1274. X* "an INITC message is sent. The INITA and INITB messages carry with them"
  1275. X* "the packet and window size that each receiver wants to use, and the"
  1276. X* "senders are supposed to comply. When a receiver has seen all three INIT"
  1277. X* "messages, the channel is considered to be open. (...) the INIT messages"
  1278. X* "are ignored elsewhere. (...)"
  1279. X* "After initial synchronization each receiver sets a modulo-8"
  1280. X* "incrementing counter R to 0; each sender sets a similar counter S to 1."
  1281. X* "The value of R is always the number of the most recent correctly received"
  1282. X* "packet. The value of S is always the first sequence number in the output"
  1283. X* "window."
  1284. X*
  1285. X* Since INIT messages are ignored once the channel has been opened, we
  1286. X* set the initial values of R and S at compile time.
  1287. X*/
  1288. X
  1289. X/* ginit - g-protocol start-up */
  1290. X
  1291. Xint ginit(fd)
  1292. Xint fd;
  1293. X{
  1294. X    register int state = 0;
  1295. X    register int next = 0;
  1296. X    int count = 0;
  1297. X
  1298. X    /* set up receive packet buffers */
  1299. X
  1300. X    if ((inpk = (Packet *) malloc((unsigned)sizeof(Packet)+ourlen)) == 0) {
  1301. X    DEBUG(7,"gopen: malloc failed\n","");
  1302. X    return(FAIL);
  1303. X    }
  1304. X
  1305. X    /* 
  1306. X    * Very simple automaton for initial message exchanges.
  1307. X    * We send a packet, receive a packet and so on. The
  1308. X    * automaton terminates when it reaches its accepting state,
  1309. X    * when a time-out error occurs, or when it seems to get
  1310. X    * stuck in one state.
  1311. X    */
  1312. X
  1313. X    while (state != INITC) {
  1314. X
  1315. X    /* select action to be done in this state */
  1316. X
  1317. X    switch (state) {
  1318. X    case 0:                    /* initial state */
  1319. X        gsctrl(fd,INITA|IFLD(ourwin));    /* send INITA message */
  1320. X        break;
  1321. X    case INITA:                /* we received INITA */
  1322. X        gsctrl(fd,INITB|IFLD(ourseg-1));    /* send INITB in response */
  1323. X        break;
  1324. X    case INITB:                /* we received INITB */
  1325. X        gsctrl(fd,INITC|IFLD(ourwin));    /* assume we sent INITB */
  1326. X        break;
  1327. X    }
  1328. X
  1329. X    /*
  1330. X    * Transition part of the automaton. Receive a packet and process
  1331. X    * its contents. Depending on the packet and the current state
  1332. X    * select a new state. Stay in the current state when a corrupted 
  1333. X    * packet is received or when we receive an unexpected packet.
  1334. X    * If no packet is received assume we have lost contact and terminate.
  1335. X    */
  1336. X
  1337. X    switch (next = grpack(fd,inpk)) {    /* see what we get */
  1338. X    case INITA:
  1339. X        sndwin = IVAL(inpk->c);        /* transmission window size */
  1340. X        state = next;
  1341. X        break;
  1342. X    case INITB:
  1343. X        sndseg = IVAL(inpk->c)+1;        /* send-segment type */
  1344. X        sndlen = seglen[sndseg];        /* send-segment length */
  1345. X        state = (state == INITA ? next : state);
  1346. X        break;
  1347. X    case INITC:
  1348. X        state = (state == INITB ? next : state);
  1349. X        break;
  1350. X    case FAIL:                /* corrupted message received */
  1351. X        break;
  1352. X    case TIME:                /* no message received */
  1353. X        return(FAIL);
  1354. X    }
  1355. X
  1356. X    /* check we don't stay in the same state forever */
  1357. X
  1358. X    if (state == next) {
  1359. X        count = 0;
  1360. X    } else if (count++ > MAXTRY) {
  1361. X        return(FAIL);
  1362. X    }
  1363. X    }
  1364. X
  1365. X    /* set up transmission buffer "pool" */
  1366. X
  1367. X    if ((outpk = (Packet *) malloc((unsigned)sizeof(Packet)+sndlen)) == 0) {
  1368. X    DEBUG(7,"gopen: malloc failed\n","");
  1369. X    return(FAIL);
  1370. X    }
  1371. X    return(0);
  1372. X}
  1373. X
  1374. X/*
  1375. X* The current version used a window size of 1, i.e. no further data
  1376. X* transmissions until the last transmitted data have been acknowledged.
  1377. X* The following routines anticipate on future versions with a real pool of
  1378. X* transmit and receive buffers.
  1379. X*/
  1380. X
  1381. X/* galloc - allocate send packet, fill in size info */
  1382. X
  1383. XPacket *galloc()
  1384. X{
  1385. X    register Packet *pk = outpk;
  1386. X
  1387. X    pk->k = sndseg;                /* data segment type */
  1388. X    pk->len = sndlen;                /* data segment size */
  1389. X    return(pk);
  1390. X}
  1391. X
  1392. X/* gfree - release receive packet */
  1393. X
  1394. Xvoid gfree(pk)
  1395. Xregister Packet *pk;
  1396. X{
  1397. X    /* this function intentionally left blank */
  1398. X}
  1399. X
  1400. X/*
  1401. X* The central part of the protocol is in the routines gsproto() and
  1402. X* grproto(). These are the functions that negotiate with the other
  1403. X* host about what data to (re)transmit and to (n)ack.
  1404. X* Major changes are to be expected here when larger transmission
  1405. X* window sizes are to be supported.
  1406. X*/
  1407. X
  1408. X/* gsproto - queue one packet for transmission */
  1409. X
  1410. Xvoid gsproto(fd,pk)
  1411. Xint fd;
  1412. XPacket *pk;
  1413. X{
  1414. X    int numtry = 0;                /* retry count */
  1415. X
  1416. X    gsdata(fd,pk,SFLD(sval)|RFLD(rval));    /* send data packet */
  1417. X
  1418. X    inpk->k = ourseg;                /* "allocate" receive packet */
  1419. X    inpk->len = ourlen;
  1420. X
  1421. X    while (numtry < MAXTRY) {
  1422. X    switch (grpack(fd,inpk)) {        /* what is the reply */
  1423. X    case SHORT:                /* SHORT DATA */
  1424. X    case DATA:                /* LONG DATA */
  1425. X        gsctrl(fd,RJ|RFLD(rval));        /* not now please */
  1426. X    case RJ:                               /* REJECT */
  1427. X    case RR:                               /* RECEIVER READY */
  1428. X        if (RVAL(inpk->c) == sval) {    /* check their R value */
  1429. X        sval = (sval+1)&07;        /* update our S value */
  1430. X        return;
  1431. X        }
  1432. X    case FAIL:                /* bad packet received */
  1433. X    case TIME:                /* no packet received */
  1434. X        gsdata(fd,pk,SFLD(sval)|RFLD(rval));/* send data packet again */
  1435. X        numtry++;                /* but not forever */
  1436. X        break;
  1437. X    case CLOSE:
  1438. X        gfail();                /* surprise! */
  1439. X        /* NOTREACHED */
  1440. X    }
  1441. X    }
  1442. X    gfail();                        /* too may retries, abort */
  1443. X    /* NOTREACHED */
  1444. X}
  1445. X
  1446. X/* grproto - take one packet from input queue */
  1447. X
  1448. XPacket *grproto(fd)
  1449. Xint fd;
  1450. X{
  1451. X    int numtry = 0;                /* retry count */
  1452. X    int xpct = (rval+1)&07;            /* expected sequence nr */
  1453. X    register Packet *pk = inpk;            /* take one from the "pool" */
  1454. X
  1455. X    pk->k = ourseg;                /* initialize receive packet */
  1456. X    pk->len = ourlen;
  1457. X
  1458. X    while (numtry < MAXTRY) {            /* don't loop forever */
  1459. X    switch (grpack(fd,pk)) {        /* see what we got */
  1460. X    case DATA:                /* LONG DATA */
  1461. X    case SHORT:                /* SHORT DATA */
  1462. X        if (SVAL(pk->c) == xpct) {        /* you're the 1 that I want */
  1463. X        gsctrl(fd,RR|RFLD(rval = xpct));/* update R and acknowledge */
  1464. X        return(pk);            /* we are done here */
  1465. X        }                    /* else ignore the packet */
  1466. X    case FAIL:                /* bad packet */
  1467. X        gsctrl(fd,RJ|RFLD(rval));        /* reset their S value */
  1468. X    case TIME:                /* no packet, no nak */
  1469. X        numtry++;                /* don't loop forever */
  1470. X        break;                /* read another packet */
  1471. X    case RR:                /* RECEIVER READY */
  1472. X    case RJ:                /* REJECT */
  1473. X        break;                /* ignore */
  1474. X    case CLOSE:                /* surprise! */
  1475. X        gfail();                /* boy, am I confused */
  1476. X        /* NOTREACHED */
  1477. X    }
  1478. X    }
  1479. X    gfail();                        /* too may retries, abort */
  1480. X    /* NOTREACHED */
  1481. X}
  1482. X
  1483. X/*
  1484. X* "The CLOSE message is used to terminate communications. Software on"
  1485. X* "either or both ends of the communication channel may initiate"
  1486. X* "termination. In any case when one end wants to terminate it sends"
  1487. X* "CLOSE messages until one is received from the other end or until a"
  1488. X* "programmable limit on the number of CLOSE messages is reached. Receipt"
  1489. X* "of a CLOSE message causes a CLOSE message to be sent."
  1490. X*
  1491. X* Normally systems decide together when to turn off the protocol so
  1492. X* that each system will start sending CLOSE messages at the same time.
  1493. X*
  1494. X* When a CLOSE message is received in the middle of a conversation
  1495. X* a protocol error is generated in grproto() or gsproto(). Then
  1496. X* gfinit() is called, so that the other system still sees a few CLOSE
  1497. X* messages.
  1498. X*/
  1499. X
  1500. X/* gfinit - shut down the g protocol */
  1501. X
  1502. Xint gfinit(fd)
  1503. Xint fd;
  1504. X{
  1505. X    register int numtry;
  1506. X
  1507. X    for (numtry = 0; numtry < MAXTRY; numtry++) {    /* programmable limit */
  1508. X    gsctrl(fd,CLOSE);                /* send CLOSE message */
  1509. X    if (grpack(fd,inpk) == CLOSE)            /* hope for same */
  1510. X        return(0);                    /* got it */
  1511. X    }
  1512. X    return(FAIL);                    /* no CLOSE received */
  1513. X}
  1514. END_OF_main/gtrans.c
  1515. if test 10252 -ne `wc -c <main/gtrans.c`; then
  1516.     echo shar: \"main/gtrans.c\" unpacked with wrong size!
  1517. fi
  1518. # end of overwriting check
  1519. fi
  1520. if test -f main/window.c -a "${1}" != "-c" ; then 
  1521.   echo shar: Will not over-write existing file \"main/window.c\"
  1522. else
  1523. echo shar: Extracting \"main/window.c\" \(10368 characters\)
  1524. sed "s/^X//" >main/window.c <<'END_OF_main/window.c'
  1525. X/*++
  1526. X/* NAME
  1527. X/*      window 3
  1528. X/* SUMMARY
  1529. X/*      screen manipulator
  1530. X/* PROJECT
  1531. X/*      pc-mail
  1532. X/* PACKAGE
  1533. X/*      mail
  1534. X/* SYNOPSIS
  1535. X/*      #include "window.h"
  1536. X/*
  1537. X/*      int printcl(wp,line,s)
  1538. X/*      WIN *wp;
  1539. X/*      int line;
  1540. X/*      char *s;
  1541. X/*
  1542. X/*      int printat(wp,line,s)
  1543. X/*      WIN *wp;
  1544. X/*      int line;
  1545. X/*      char *s;
  1546. X/*
  1547. X/*    void setwin(wp)
  1548. X/*    WIN *wp;
  1549. X/*
  1550. X/*      int wputc(c)
  1551. X/*      int c;
  1552. X/*
  1553. X/*      int wputs(s)
  1554. X/*      char *s;
  1555. X/*
  1556. X/*      void clrtoeol()
  1557. X/*
  1558. X/*      void clrtobot()
  1559. X/*
  1560. X/*      void clrscreen()
  1561. X/*
  1562. X/*      void beep()
  1563. X/*
  1564. X/*    int fputchar(c)
  1565. X/*    int c;
  1566. X/*
  1567. X/*    void wininit()
  1568. X/* DESCRIPTION
  1569. X/*      The window manipulator is responsable for three screen windows:
  1570. X/*      the top window for key labels, the middle window for
  1571. X/*      information, and the lower window for messages and dialogue.
  1572. X/*    These can be manipulated via the window handles topwin, midwin
  1573. X/*    and botwin, respectively.
  1574. X/*      Use is made of the terminal capability database termcap, or terminfo,
  1575. X/*    or something else, depending on the OS we are dealing with.
  1576. X/*
  1577. X/*    For MS-DOS systems, there is a termcap facility that generates
  1578. X/*    escape sequences for the ANSI.SYS terminal driver.
  1579. X/*
  1580. X/*      Appropriate macros for window selection are given in window.h.
  1581. X/*      Needless to say, all screen output should proceed through
  1582. X/*      functions in this module.
  1583. X/*
  1584. X/*      All character output functions return the number of screen lines
  1585. X/*      used for outputting the text (at least 1). All routines that
  1586. X/*    have a window agrument set the current window.
  1587. X/*
  1588. X/*      printat() writes the specified line in the specified window,
  1589. X/*      starting at the left margin.
  1590. X/*
  1591. X/*      printcl() performs the same functions as printat() and erases to
  1592. X/*      the end of the line.
  1593. X/*
  1594. X/*    setwin() sets the current window. The cursor is moved to the
  1595. X/*    current (row, column) of that window.
  1596. X/*
  1597. X/*      wputs() writes a character string to the current cursor location
  1598. X/*    in the current window.
  1599. X/*
  1600. X/*      wputc() does the same for characters. 
  1601. X/*
  1602. X/*      cltroeol(), clrtobot() erase the screen from the cursor to the
  1603. X/*      end of the line and screen respectively. beep() makes some noise.
  1604. X/*
  1605. X/*    fputchar() outputs a character to stdout, just as putchar,
  1606. X/*    but it is not a macro.
  1607. X/*
  1608. X/*      wininit() initializes the window manipulator. It reads the
  1609. X/*    terminal capabilities from the termcap database.
  1610. X/* FILES
  1611. X/*      /etc/termcap, $TERMCAP         on V7 or BSD UNIX
  1612. X/*    /usr/lib/terminfo, $TERMINFO    on System-V UNIX
  1613. X/* SEE ALSO
  1614. X/*      window(5)       window manipulator definitions
  1615. X/* DIAGNOSTICS
  1616. X/*      The program is terminated with an error message if no terminal
  1617. X/*      descriptions could be found, if the terminal lacks some
  1618. X/*      essential features or if an attempt is made to write outside
  1619. X/*      a window.
  1620. X/* BUGS
  1621. X/*    All functions that do not take a "window" argument should not be
  1622. X/*    called before anything has appeared on the screen.
  1623. X/*
  1624. X/*    This module should be replaced by a PD curses/termcap library.
  1625. X/* AUTHOR(S)
  1626. X/*      W.Z. Venema
  1627. X/*      Eindhoven University of Technology
  1628. X/*      Department of Mathematics and Computer Science
  1629. X/*      Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
  1630. X/* CREATION DATE
  1631. X/*      Wed Apr  1 21:14:53 GMT+1:00 1987
  1632. X/* LAST MODIFICATION
  1633. X/*    90/01/22 13:02:55
  1634. X/* VERSION/RELEASE
  1635. X/*    2.1
  1636. X/*--*/
  1637. X
  1638. X#include <stdio.h>
  1639. X#include <ctype.h>
  1640. X
  1641. X#include "defs.h"
  1642. X#include "window.h"
  1643. X
  1644. X#define BUFFERSIZE      1024        /* max. length of termcap entry */
  1645. X
  1646. Xhidden char outbuf[BUFSIZ];        /* for stdio */
  1647. X
  1648. Xhidden char tcapent[BUFFERSIZE];    /* storage for termcap entry */
  1649. Xhidden char capst[BUFFERSIZE];        /* storage for tgetstr */
  1650. Xhidden char *capptr = capst;        /* pointer to next tgetstr output */
  1651. X
  1652. Xextern char *tgetstr();            /* returns capability string */
  1653. Xextern char *getenv();
  1654. Xextern char *tgoto();            /* returns cursor addressing code */
  1655. X
  1656. X/* function-key strings, keypad control */
  1657. X
  1658. Xpublic char *KU, *KD, *KL, *KR, *PU, *PD;
  1659. Xpublic char *KS, *KE;
  1660. X
  1661. X/* screen control capabilities */
  1662. X
  1663. Xhidden char *CL, *CD, *CM, *CE, *SO, *SE;
  1664. X
  1665. Xpublic int CO, LI;            /* screen size */
  1666. X
  1667. X/* Storage for convenient capability lookup */
  1668. X
  1669. Xstruct strcaps {
  1670. X    char  **dest;            /* pointer to storage */
  1671. X    char   *name;            /* capability name */
  1672. X};
  1673. X
  1674. Xstruct intcaps {
  1675. X    int    *dest;            /* pointer to storage */
  1676. X    char   *name;            /* capability name */
  1677. X};
  1678. X
  1679. X/* Required string-valued termcap capabilities */
  1680. X
  1681. Xhidden struct strcaps reqd_str[] = {
  1682. X    &CE, "ce",                /* clear to end of line */
  1683. X    &CD, "cd",                /* clear to end of screen */
  1684. X    &CL, "cl",                /* clear to end of screen */
  1685. X#ifdef  someday
  1686. X    &SO, "so",                /* stand-out on */
  1687. X    &SE, "se",                /* stand-out off */
  1688. X#endif
  1689. X    &CM, "cm",                /* cursor movement */
  1690. X    &KU, "ku",                /* up-arrow */
  1691. X    &KD, "kd",                /* down_arrow */
  1692. X    &KL, "kl",                /* left-arrow */
  1693. X    &KR, "kr",                /* right-arrow */
  1694. X#ifdef  unix
  1695. X    &PU, "k1",                /* page-up (F1) */
  1696. X    &PD, "k2",                /* page down (F2) */
  1697. X#endif
  1698. X#ifdef MSDOS
  1699. X    &PU, "PU",                /* really PgUp */
  1700. X    &PD, "PD",                /* really PgDn */
  1701. X#endif
  1702. X    0, 0,
  1703. X};
  1704. X
  1705. X/* Required integer-valued terminal capabilities */
  1706. X
  1707. Xhidden struct intcaps reqd_int[] = {
  1708. X    &CO, "co",                /* number of columns */
  1709. X    &LI, "li",                /* number of lines */
  1710. X    0, 0,
  1711. X};
  1712. X
  1713. X/* Optional string-valued terminal capabilities */
  1714. X
  1715. Xhidden struct strcaps opt_str[] = {
  1716. X    &KS, "ks",                /* keypad on */
  1717. X    &KE, "ke",                /* keypad off */
  1718. X    0, 0,
  1719. X};
  1720. X
  1721. X/* Optional integer-valued terminal capabilities */
  1722. X
  1723. Xhidden struct intcaps opt_int[] = {
  1724. X    0, 0,
  1725. X};
  1726. X
  1727. X/* Window bases and sizes */
  1728. X
  1729. XWIN     wins[3] = {
  1730. X    {0, 2, 0, 0},            /* top */
  1731. X    {2, 0, 0, 0},            /* middle */
  1732. X    {0, 5, 0, 0},            /* bottom */
  1733. X};
  1734. X
  1735. X/* Stuff related to where we are on the screen */
  1736. X
  1737. Xhidden WIN *currwin = 0;        /* what window we are in */
  1738. X
  1739. X/* convenient macros to update and set cursor location */
  1740. X
  1741. X#define    moveto(wp)    tputs(tgoto(CM,wp->x,wp->y+wp->base),1,fputchar)
  1742. X#define    moveset(wp,c,l)    { wp->x = c; wp->y = l; moveto(wp); }
  1743. X
  1744. Xhidden void winout();
  1745. X
  1746. X/* checkline - validate line number */
  1747. X
  1748. Xhidden WIN *checkline(wp, line)
  1749. XWIN    *wp;
  1750. Xint     line;
  1751. X{
  1752. X    if (line < 0 || line >= wp->size)
  1753. X    fatal("line %d not in window %d", line, wp - wins);
  1754. X}
  1755. X
  1756. X/* printcl - print one line in a window, then clear to end of line */
  1757. X
  1758. Xpublic int printcl(wp, line, s)
  1759. XWIN    *wp;
  1760. Xint     line;
  1761. Xchar   *s;
  1762. X{
  1763. X    checkline(currwin = wp, line);
  1764. X
  1765. X    moveset(wp, 0, line);
  1766. X    winout(s);
  1767. X    if (wp->y < wp->size)
  1768. X    tputs(CE, 1, fputchar);
  1769. X    (void) fflush(stdout);
  1770. X    return (wp->y - line + 1);
  1771. X}
  1772. X
  1773. X/* printat - print one line in a window */
  1774. X
  1775. Xpublic int printat(wp, line, s)
  1776. XWIN    *wp;
  1777. Xint     line;
  1778. Xchar   *s;
  1779. X{
  1780. X    checkline(currwin = wp, line);
  1781. X
  1782. X    moveset(wp, 0, line);
  1783. X    winout(s);
  1784. X    (void) fflush(stdout);
  1785. X    return (wp->y - line + 1);
  1786. X}
  1787. X
  1788. X/* setwin - set focus and cursor */
  1789. X
  1790. Xpublic void setwin(wp)
  1791. Xregister WIN *wp;
  1792. X{
  1793. X    currwin = wp;
  1794. X    moveto(wp);
  1795. X}
  1796. X
  1797. X/* wputc - put character at current location in current window */
  1798. X
  1799. Xpublic int wputc(c)
  1800. Xint     c;
  1801. X{
  1802. X    register int line = currwin->y;
  1803. X    static char buf[] = "?";
  1804. X
  1805. X    buf[0] = c;
  1806. X    winout(buf);
  1807. X    (void) fflush(stdout);
  1808. X    return (currwin->y - line + 1);
  1809. X}
  1810. X
  1811. X/* wputs - print string at current location in current window */
  1812. X
  1813. Xpublic int wputs(s)
  1814. Xchar   *s;
  1815. X{
  1816. X    register int line = currwin->y;
  1817. X
  1818. X    winout(s);
  1819. X    (void) fflush(stdout);
  1820. X    return (currwin->y - line + 1);
  1821. X}
  1822. X
  1823. X/* winout - update current window and keep track of where we are */
  1824. X
  1825. Xhidden void winout(s)
  1826. Xregister char *s;
  1827. X{
  1828. X    register int ch;
  1829. X    register WIN *wp;
  1830. X    static char dectrl[] = "^?";
  1831. X
  1832. X    for (wp = currwin; (ch = (*s & 0177)) && wp->y < wp->size; s++) {
  1833. X    if (isprint(ch) || ch == ' ') {        /* if printable */
  1834. X        putchar(ch), wp->x++;        /* leave it alone */
  1835. X    } else if (ch == '\t') {
  1836. X        do {
  1837. X        winout(" ");            /* expand it */
  1838. X        } while ((wp->x & 7) && wp->y < wp->size);
  1839. X    } else if (ch == '\b') {
  1840. X        if (wp->x > 0 || wp->y > 0) {    /* don\'t leave the window */
  1841. X        if (wp->x-- == 0) {        /* at beginning of line */
  1842. X            wp->x = CO - 1;
  1843. X            wp->y--;
  1844. X            moveto(wp);
  1845. X        } else {
  1846. X            putchar(ch);
  1847. X        }
  1848. X        }
  1849. X    } else if (ch == '\n') {
  1850. X        tputs(CE, 1, fputchar);        /* erase rest of line */
  1851. X        moveset(wp, 0, wp->y + 1);        /* advance */
  1852. X    } else if (ch == '\r') {
  1853. X        (wp->x = 0), moveto(wp);        /* back to left margin */
  1854. X    } else if (ch == '\07') {
  1855. X        putchar(ch);            /* make them sound */
  1856. X    } else {
  1857. X        dectrl[1] = ch ^ 0100;        /* uncontrollify */
  1858. X        winout(dectrl);            /* and output */
  1859. X    }
  1860. X    if (wp->x >= CO)            /* wrap at end of line */
  1861. X        moveset(wp, 0, wp->y + 1);
  1862. X    }
  1863. X}
  1864. X
  1865. X#ifdef unix
  1866. X/* fputchar - output a character on stdout */
  1867. X
  1868. Xpublic int fputchar(c)
  1869. Xint     c;
  1870. X{
  1871. X    return (putchar(c));
  1872. X}
  1873. X
  1874. X#endif                    /* unix */
  1875. X
  1876. X/* clrtoeol - clear to end of line */
  1877. X
  1878. Xpublic void clrtoeol()
  1879. X{
  1880. X    tputs(CE, 1, fputchar);
  1881. X}
  1882. X
  1883. X/* clrtobot - clear to end of screen */
  1884. X
  1885. Xpublic void clrtobot()
  1886. X{
  1887. X    tputs(CD, 1, fputchar);
  1888. X}
  1889. X
  1890. X/* clrscreen - clear screen */
  1891. X
  1892. Xpublic void clrscreen()
  1893. X{
  1894. X    tputs(CL, 1, fputchar);
  1895. X}
  1896. X
  1897. X/* beep - ring the bell */
  1898. X
  1899. Xpublic void beep()
  1900. X{
  1901. X    (void) putchar('\07');
  1902. X    (void) fflush(stdout);
  1903. X}
  1904. X
  1905. X/* wininit - extract terminal info and initialize window routines */
  1906. X
  1907. Xpublic void wininit()
  1908. X{
  1909. X    char   *term;
  1910. X    struct strcaps *cp;
  1911. X    struct intcaps *ip;
  1912. X
  1913. X    /* selected buffered standard output */
  1914. X
  1915. X    setbuf(stdout, outbuf);
  1916. X
  1917. X    /* make sure our terminal is known */
  1918. X
  1919. X#ifdef unix
  1920. X    if ((term = getenv("TERM")) == 0)
  1921. X    fatal("TERM not set");
  1922. X#endif
  1923. X
  1924. X    switch (tgetent(tcapent, term)) {
  1925. X    case -1:
  1926. X    fatal("no terminal database\n");
  1927. X    /* NOTREACHED */
  1928. X    case 0:
  1929. X    fatal("unknown terminal: %s\n", term);
  1930. X    /* NOTREACHED */
  1931. X    }
  1932. X    /* extract required terminal capabilities */
  1933. X
  1934. X    for (cp = reqd_str; cp->name; cp++)
  1935. X    if ((*cp->dest = tgetstr(cp->name, &capptr)) == 0)
  1936. X        fatal("Your terminal is too dumb");
  1937. X    for (ip = reqd_int; ip->name; ip++)
  1938. X    if ((*ip->dest = tgetnum(ip->name)) == 0)
  1939. X        fatal("Your terminal is too dumb");
  1940. X
  1941. X    /* set up per-window base and size */
  1942. X
  1943. X    if (CO < 80)
  1944. X    fatal("Terminal screen is to narrow");
  1945. X    botwin->base = LI - botwin->size;
  1946. X    if ((midwin->size = botwin->base - midwin->base) < 10)
  1947. X    fatal("Not enough lines on this terminal");
  1948. X
  1949. X    /* extract optional terminal capabilities */
  1950. X
  1951. X    for (cp = opt_str; cp->name; cp++)
  1952. X    *cp->dest = tgetstr(cp->name, &capptr);
  1953. X    for (ip = opt_int; ip->name; ip++)
  1954. X    *ip->dest = tgetnum(ip->name);
  1955. X}
  1956. END_OF_main/window.c
  1957. if test 10368 -ne `wc -c <main/window.c`; then
  1958.     echo shar: \"main/window.c\" unpacked with wrong size!
  1959. fi
  1960. # end of overwriting check
  1961. fi
  1962. echo shar: End of archive 8 \(of 11\).
  1963. cp /dev/null ark8isdone
  1964. MISSING=""
  1965. for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
  1966.     if test ! -f ark${I}isdone ; then
  1967.     MISSING="${MISSING} ${I}"
  1968.     fi
  1969. done
  1970. if test "${MISSING}" = "" ; then
  1971.     echo You have unpacked all 11 archives.
  1972.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1973. else
  1974.     echo You still need to unpack the following archives:
  1975.     echo "        " ${MISSING}
  1976. fi
  1977. ##  End of shell archive.
  1978. exit 0
  1979.  
  1980.  
  1981.