home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume10 / pcmail2 / part08 < prev    next >
Text File  |  1990-01-24  |  61KB  |  1,988 lines

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