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

  1. Newsgroups: comp.sources.misc
  2. subject: v10i041: PC-MAIL release 2, 9/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 41
  7. Submitted-by: wswietse@lso.win.tue.nl (Wietse Venema)
  8. Archive-name: pcmail2/part09
  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 9 (of 11)."
  17. # Contents:  daemon/pc-maild.c main/hsearch.c main/pager.c
  18. # Wrapped by wswietse@tuewsa on Mon Jan 22 17:27:21 1990
  19. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  20. if test -f daemon/pc-maild.c -a "${1}" != "-c" ; then 
  21.   echo shar: Will not over-write existing file \"daemon/pc-maild.c\"
  22. else
  23. echo shar: Extracting \"daemon/pc-maild.c\" \(14808 characters\)
  24. sed "s/^X//" >daemon/pc-maild.c <<'END_OF_daemon/pc-maild.c'
  25. X/*++
  26. X/* NAME
  27. X/*    pc-maild 8
  28. X/* SUMMARY
  29. X/*    deliver unsent mail
  30. X/* PROJECT
  31. X/*    pc-mail
  32. X/* PACKAGE
  33. X/*    nfs
  34. X/* SYNOPSIS
  35. X/*    pc-maild [delay]
  36. X/* DESCRIPTION
  37. X/*    This program should be run on the nfs file server that exports
  38. X/*    mail directories to MS-DOS pc-mail users. It replaces the
  39. X/*    (MS-DOS -> UNIX) transmission function of the MS-DOS \fIcico\fR
  40. X/*    program.
  41. X/*
  42. X/*    The per-user mail directories (default: /usr/spool/pc-mail/\fIuser\fR)
  43. X/*    are scanned for outgoing mail every \fIdelay\fR seconds (default: 300).
  44. X/*    When outgoing mail is found, it is sent through the UNIX rmail program
  45. X/*    (uucp mail interface) and the corresponding files are removed from the
  46. X/*    user\'s mail directory.
  47. X/*
  48. X/*    The program should run with root privileges. It will assume
  49. X/*    the (uid, gid) of the sending user before accessing mail files.
  50. X/* COMMANDS
  51. X/*    rmail(1), uucp mail interface program
  52. X/* FILES
  53. X/*    /usr/spool/pc-mail/\fIuser\fR/dNNNNN, mail message (unsent mail)
  54. X/*    /usr/spool/pc-mail/\fIuser\fR/xNNNNN, recipients, subject (unsent mail)
  55. X/*    /usr/spool/pc-mail/\fIuser\fR/qNNNNN, mail message (sent mail)
  56. X/*    /usr/spool/pc-mail/\fIuser\fR/rNNNNN, recipients, subject (sent mail)
  57. X/*    (NNNNN is the pc-mail "message id").
  58. X/* SEE ALSO
  59. X/*    pc-mail(1)
  60. X/* DIAGNOSTICS
  61. X/*    Errors found during initialization cause the program to
  62. X/*    terminate with a diagnostic on the standard error stream.
  63. X/*    All other errors are considered transient, i.e. if something
  64. X/*    fails, it is tried again at a later time.  Where possible,
  65. X/*    diagnostics are logged through the syslog facility.
  66. X/* BUGS
  67. X/*    Scanning mail directories is an inefficient way to detect
  68. X/*    unsent mail.
  69. X/* AUTHOR(S)
  70. X/*    W.Z. Venema
  71. X/*    Eindhoven University of Technology
  72. X/*    Department of Mathematics and Computer Science
  73. X/*    Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
  74. X/* CREATION DATE
  75. X/*    Sun Oct 22 22:12:15 MED 1989
  76. X/* LAST MODIFICATION
  77. X/*    1/6/90 19:45:05
  78. X/* VERSION/RELEASE
  79. X/*    1.6
  80. X/*--*/
  81. X
  82. X#ifndef lint
  83. Xstatic char sccsid[] = "@(#) pc-maild.c 1.6 1/6/90 19:45:05";
  84. X
  85. X#endif
  86. X
  87. X /*
  88. X  * General return-value conventions:
  89. X  * 
  90. X  * int func():        0 means OK
  91. X  * 
  92. X  * stuff *func():    0 means error
  93. X  * 
  94. X  */
  95. X
  96. X#include <stdio.h>
  97. X#include <pwd.h>
  98. X#include <time.h>
  99. X#include <signal.h>
  100. X#include <sys/types.h>
  101. X#include <sys/stat.h>
  102. X
  103. X#ifdef SYSLOG
  104. X#include <syslog.h>
  105. X#else
  106. X#include "syslog.h"
  107. X#endif
  108. X
  109. X#ifdef SYSV
  110. X#include <sys/utsname.h>
  111. X#include <ndir.h>
  112. X#else
  113. X#include <sys/types.h>
  114. X#include <sys/dir.h>
  115. X#include <sgtty.h>
  116. X#endif
  117. X
  118. X#include "dosunix.h"
  119. X#include "util.h"
  120. X#include "mtime.h"
  121. X
  122. X/* Library functions */
  123. X
  124. Xextern char *strtok();
  125. Xextern char *strncpy();
  126. Xextern struct passwd *getpwnam();
  127. Xextern unsigned sleep();
  128. Xextern void exit();
  129. Xextern void _exit();
  130. Xextern struct tm *localtime();
  131. Xextern char *asctime();
  132. Xextern long time();
  133. X
  134. X/* Local defines, declarations */
  135. X
  136. X#ifndef    DELAY
  137. X#define    DELAY    300            /* default: scan every 5 minutes */
  138. X#endif
  139. X
  140. X#ifndef    MAILDIR
  141. X#define    MAILDIR    "/usr/spool/pc-mail"    /* default pc-mail directory tree */
  142. X#endif
  143. X
  144. X#define    MAXLINE    1024            /* max length of recipients line */
  145. X
  146. Xvoid    catchsig();
  147. Xvoid    finduser();
  148. Xchar   *getrcpt();
  149. Xchar   *fullname();
  150. X
  151. Xint     delay = DELAY;            /* the default delay */
  152. Xchar   *progname;            /* my process name */
  153. X
  154. X
  155. Xint     main(argc, argv)
  156. Xint     argc;
  157. Xchar  **argv;
  158. X{
  159. X    progname = *argv;
  160. X
  161. X    /* Sanity checks */
  162. X
  163. X#ifndef DEBUG
  164. X
  165. X    if (geteuid() != 0) {
  166. X    (void) fprintf(stderr, "%s: must be run as root\n", progname);
  167. X    exit(1);
  168. X    }
  169. X#endif
  170. X
  171. X    /* Check for command-line delay argument */
  172. X
  173. X    if (argc > 1 && (delay = atoi(argv[1])) < 1)
  174. X    delay = DELAY;
  175. X
  176. X    /* Become a daemon process */
  177. X
  178. X#ifndef DEBUG
  179. X    disconnect();
  180. X#endif
  181. X
  182. X    /* Set up signal handling */
  183. X
  184. X    catchsig();
  185. X
  186. X    /* Enable syslogging */
  187. X
  188. X    (void) openlog(progname, LOG_PID, LOG_MAIL);
  189. X    syslog(LOG_WARNING, "daemon restarted");
  190. X
  191. X    /* Setup a decent environment */
  192. X
  193. X    if (putenv("PATH=/bin:/usr/bin:/usr/ucb") || putenv("IFS= \t\n")) {
  194. X    syslog(LOG_WARNING, "initialization failed (insufficient resources)");
  195. X    exit(1);
  196. X    }
  197. X    /* Enter the main loop */
  198. X
  199. X    finduser();
  200. X    /* NOTREACHED */
  201. X}
  202. X
  203. X/* finduser - repeatedly iterate over all pc-mail user directories */
  204. X
  205. Xvoid    finduser()
  206. X{
  207. X    register DIR *maildp;
  208. X    register struct direct *dp;
  209. X    register struct passwd *uinfo;
  210. X    MTIME  *dirtime;
  211. X    char    userdir[BUFSIZ];
  212. X    struct stat st;
  213. X
  214. X    /*
  215. X     * Ignore names that start with a period.
  216. X     *
  217. X     * Ignore names that do not correspond to a directory.
  218. X     * 
  219. X     * Ignore directories that did not change since the last time they were
  220. X     * known to hold no unsent mail.
  221. X     * 
  222. X     * Ignore directories that do not have a name equal to the login name of a
  223. X     * user.
  224. X     */
  225. X
  226. X    for (;;) {
  227. X    if ((e_chdir(MAILDIR) == 0) && (maildp = e_opendir(MAILDIR))) {
  228. X        while (dp = readdir(maildp)) {
  229. X        if ((dp->d_name[0] != '.')
  230. X        && (stat(dp->d_name, &st) == 0)
  231. X        && ((st.st_mode & S_IFMT) == S_IFDIR)
  232. X        && (st.st_mtime > (dirtime = mtime(dp->d_name))->time)
  233. X        && ((uinfo = getpwnam(dp->d_name)) != 0)) {
  234. X            (void) sprintf(userdir, "%s/%s", MAILDIR, dp->d_name);
  235. X            if (findmail(uinfo, userdir) == 0)
  236. X            dirtime->time = st.st_mtime;    /* say it was empty */
  237. X        }
  238. X        }
  239. X        closedir(maildp);            /* done with this user */
  240. X        (void) chdir("/");            /* be friendly */
  241. X    }
  242. X    (void) sleep((unsigned) delay);        /* try again later */
  243. X    }
  244. X}
  245. X/* findmail - enter a user\'s mail directory and scan for unsent mail */
  246. X
  247. Xint     findmail(uinfo, userdir)
  248. Xstruct passwd *uinfo;
  249. Xchar   *userdir;
  250. X{
  251. X    register DIR *dd;
  252. X    register struct direct *p;
  253. X    int     seqno;
  254. X    static char xfile[BUFSIZ];        /* file with recipients (unsent mail) */
  255. X    static char rfile[BUFSIZ];        /* same, but with "Sent" status */
  256. X    static char dfile[BUFSIZ];        /* file with message (unsent mail) */
  257. X    static char qfile[BUFSIZ];        /* same, but with "Sent" status */
  258. X    int     found = 0;            /* no mail found yet */
  259. X
  260. X    /*
  261. X     * Use the fact that 'x' files (recipient addresses) are created later
  262. X     * than 'd' files (message body) when a pc user generates a message.
  263. X     * Extract the pc-mail message id from the file name and try to pipe the
  264. X     * message through the UNIX rmail command. All knowledge about pc-mail
  265. X     * file names resides in this function. Return zero if no unsent mail was
  266. X     * found.
  267. X     */
  268. X
  269. X    if ((e_chdir(userdir) == 0) && (dd = e_opendir(userdir))) {
  270. X    while (p = readdir(dd)) {
  271. X        if (*p->d_name == 'x' && sscanf(p->d_name + 1, "%d", &seqno) == 1) {
  272. X        (void) sprintf(xfile, "x%05d", seqno);    /* recipients */
  273. X        if (strcmp(p->d_name, xfile) == 0) {    /* ignore junk */
  274. X            (void) sprintf(dfile, "d%05d", seqno);
  275. X            (void) sprintf(rfile, "r%05d", seqno);
  276. X            (void) sprintf(qfile, "q%05d", seqno);
  277. X            pickup(uinfo, xfile, dfile, rfile, qfile);
  278. X            found = 1;            /* found unsent mail */
  279. X        }
  280. X        }
  281. X    }
  282. X    closedir(dd);
  283. X    (void) chdir(MAILDIR);
  284. X    }
  285. X    return (found);
  286. X}
  287. X
  288. X/* pickup - pick up one message from a user\'s mail directory */
  289. X
  290. Xpickup(uinfo, xfile, dfile, rfile, qfile)
  291. Xstruct passwd *uinfo;
  292. Xchar   *xfile;
  293. Xchar   *dfile;
  294. Xchar   *rfile;
  295. Xchar   *qfile;
  296. X{
  297. X
  298. X    /*
  299. X     * Actual delivery must be done with the (uid, gid) of the sender, or the
  300. X     * From: lines will not be correct. Therefore, we do delivery in a child
  301. X     * process. This also avoid all kinds of nasty security problems. All
  302. X     * errors are considered non-fatal.
  303. X     */
  304. X
  305. X#ifdef DEBUG
  306. X    sendasuser(uinfo, xfile, dfile, rfile, qfile);    /* don't fork */
  307. X#else
  308. X    switch (e_fork()) {
  309. X    case -1:                    /* failure */
  310. X    break;
  311. X    case 0:                    /* child */
  312. X    sendasuser(uinfo, xfile, dfile, rfile, qfile);
  313. X    _exit(0);
  314. X    /* NOTREACHED */
  315. X    default:                    /* parent */
  316. X    (void) wait((int *) 0);
  317. X    break;
  318. X    }
  319. X#endif
  320. X}
  321. X
  322. X/* sendasuser - send mail through rmail(1), having (uid, gid) of sender */
  323. X
  324. Xsendasuser(uinfo, xfile, dfile, rfile, qfile)
  325. Xstruct passwd *uinfo;
  326. Xchar   *xfile;
  327. Xchar   *dfile;
  328. Xchar   *rfile;
  329. Xchar   *qfile;
  330. X{
  331. X    char   *dest;            /* recipient address(es) */
  332. X
  333. X    /*
  334. X     * User-specific mail files must be opened AFTER we have assumed the
  335. X     * (uid, gid) of the pc-mail user; this in order to avoid nasty security
  336. X     * holes. When delivery succeeds, we rename the spool files so they
  337. X     * reflect the "Sent" status.
  338. X     */
  339. X
  340. X    if ((setugid(uinfo) == 0)            /* assume proper (uid, gid) */
  341. X    && (dest = getrcpt(uinfo, xfile))        /* extract recipients */
  342. X    && (rmail(uinfo, dfile, dest) == 0)) {    /* pipe message through rmail */
  343. X    (void) unlink(rfile);            /* just in case */
  344. X    (void) unlink(qfile);            /* just in case */
  345. X    (void) u_link(uinfo, xfile, rfile);    /* change status to "Sent" */
  346. X    (void) u_link(uinfo, dfile, qfile);    /* change status to "Sent" */
  347. X    (void) u_unlink(uinfo, xfile);        /* recipients file */
  348. X    (void) u_unlink(uinfo, dfile);        /* message body file */
  349. X    }
  350. X}
  351. X
  352. X/* setugid - assume (uid, gid) of user */
  353. X
  354. Xint     setugid(uinfo)
  355. Xstruct passwd *uinfo;
  356. X{
  357. X    if (setgid(uinfo->pw_gid)) {
  358. X    syslog(LOG_WARNING, "setgid(%s) failed: %m", uinfo->pw_name);
  359. X    return (1);
  360. X    }
  361. X    if (setuid(uinfo->pw_uid)) {
  362. X    syslog(LOG_WARNING, "setuid(%s) failed: %m", uinfo->pw_name);
  363. X    return (1);
  364. X    } else {
  365. X    return (0);
  366. X    }
  367. X}
  368. X
  369. X/* getrcpt - extract recipient from user mail file */
  370. X
  371. Xchar   *getrcpt(uinfo, xfile)
  372. Xstruct passwd *uinfo;
  373. Xchar   *xfile;
  374. X{
  375. X    FILE   *xfp;            /* recipient file pointer */
  376. X    static char dest[MAXLINE];        /* recipient names */
  377. X    register char *ret;
  378. X
  379. X    if ((xfp = u_fopen(uinfo, xfile, "r")) == 0) {
  380. X    return (0);
  381. X    } else {
  382. X    pc_wait(fileno(xfp));        /* let pc finish writing */
  383. X    ret = dosgets(dest, sizeof(dest), xfp);
  384. X    (void) fclose(xfp);
  385. X    if (ret == 0)
  386. X        syslog(LOG_WARNING, "no recipients specified in %s/%s",
  387. X           uinfo->pw_name, xfile);
  388. X    return (ret);
  389. X    }
  390. X}
  391. X
  392. X/* rmail - pipe a pc mail message through the UNIX rmail program */
  393. X
  394. Xint     rmail(uinfo, dfile, dest)
  395. Xstruct passwd *uinfo;            /* originator */
  396. Xchar   *dfile;                /* message file */
  397. Xchar   *dest;                /* recipients */
  398. X{
  399. X    static char cmd[MAXLINE+20];    /* command + arguments */
  400. X    FILE   *dfp;            /* source stream */
  401. X    FILE   *pfp;            /* output stream */
  402. X    int     ret;            /* return value, 0 if OK */
  403. X    long    secs;            /* absolute UNIX time */
  404. X    static char hostname[BUFSIZ];    /* our host name */
  405. X
  406. X    /*
  407. X     * The UNIX rmail command needs a UUCP-style From_ line.
  408. X     * 
  409. X     * The To: and From: lines can be added for esthetical porposes.
  410. X     * 
  411. X     * Report communication failures with the rmail command. Error returns from
  412. X     * rmail are ignored; they should be handled in sendmail.
  413. X     */
  414. X
  415. X    if (dfp = u_fopen(uinfo, dfile, "r")) {    /* open message file */
  416. X    (void) sprintf(cmd, "rmail %s", dest);
  417. X    if ((pfp = popen(cmd, "w")) == 0) {    /* invoke rmail... */
  418. X        syslog(LOG_WARNING, "cannot invoke %.20s...: %m", rmail);
  419. X        ret = 1;
  420. X    } else {
  421. X        secs = time((long *) 0);
  422. X        (void) gethostname(hostname, sizeof(hostname));
  423. X        (void) fprintf(pfp, "From %s %.24s remote from %s\n", 
  424. X               uinfo->pw_name,
  425. X               asctime(localtime(&secs)),
  426. X               hostname);        /* add UUCP From_ line */
  427. X#ifdef    RFC822
  428. X        rfc822hdr(uinfo, dest, pfp);    /* do RFC822 stuff */
  429. X#endif
  430. X        if (ret = dos2unix(dfp, pfp))    /* append message body */
  431. X        syslog(LOG_WARNING, "write to rmail failed: %m");
  432. X        (void) pclose(pfp);
  433. X    }
  434. X    (void) fclose(dfp);
  435. X    }
  436. X    return (ret);
  437. X}
  438. X
  439. X/* rfc822hdr - generate subset of RFC822 header lines */
  440. X
  441. Xrfc822hdr(uinfo, dest, pfp)
  442. Xregister struct passwd *uinfo;
  443. Xchar   *dest;
  444. Xregister FILE *pfp;
  445. X{
  446. X    char   *sep = " ,\t\r\n";
  447. X    char   *name;
  448. X    int     n = 0;
  449. X
  450. X    /*
  451. X     * There are a few problems with this function. First of all, it destroys
  452. X     * the dest argument. In the second place, putting each recipient on a
  453. X     * separate header line is ugly; fortunately, sendmail will fix this.
  454. X     */
  455. X
  456. X    (void) fprintf(pfp, "From: %s (%s)\n", uinfo->pw_name,
  457. X        fullname(uinfo));            /* add From: header line */
  458. X    for (name = strtok(dest, sep); name; name = strtok((char *) 0, sep))
  459. X    (void) fprintf(pfp, "%s%s", n == 0 ? "To: " : ",\n    ", name);
  460. X    if (n)
  461. X    (void) putc('\n', pfp);
  462. X}
  463. X
  464. X/* fullname - extract full name from gecos field */
  465. X
  466. Xchar   *fullname(uinfo)
  467. Xstruct passwd *uinfo;
  468. X{
  469. X    static char name[BUFSIZ];
  470. X
  471. X    /* This code assumes BSD-style gecos fields (name,stuff,stuff...) */
  472. X
  473. X    if (sscanf(uinfo->pw_gecos, "%[^,]", name) == 0)
  474. X    name[0] = '\0';
  475. X    return (name);
  476. X}
  477. X
  478. X/* gotsig - caught a signal; terminate with diagnostic */
  479. X
  480. Xvoid    gotsig(sig)
  481. Xint     sig;
  482. X{
  483. X    syslog(LOG_WARNING, "going down on signal %d", sig);
  484. X    closelog();
  485. X    exit(sig);
  486. X}
  487. X
  488. X/* catchsig - catch some signals */
  489. X
  490. Xvoid    catchsig()
  491. X{
  492. X#ifdef    DEBUG
  493. X    (void) signal(SIGHUP, gotsig);
  494. X    (void) signal(SIGINT, gotsig);
  495. X    (void) signal(SIGQUIT, gotsig);
  496. X#else
  497. X    (void) signal(SIGHUP, SIG_IGN);
  498. X    (void) signal(SIGINT, SIG_IGN);
  499. X    (void) signal(SIGQUIT, SIG_IGN);
  500. X#endif
  501. X    (void) signal(SIGBUS, gotsig);
  502. X    (void) signal(SIGSEGV, gotsig);
  503. X    (void) signal(SIGTERM, gotsig);
  504. X}
  505. X
  506. X/* disconnect - become a daemon process */
  507. X
  508. Xdisconnect()
  509. X{
  510. X#ifndef    SYSV
  511. X    int     fd;
  512. X
  513. X#endif
  514. X
  515. X    /* Get rid of the parent process */
  516. X
  517. X    switch (e_fork()) {
  518. X    case -1:                    /* failure */
  519. X    exit(1);
  520. X    /* NOTREACHED */
  521. X    default:
  522. X    _exit(0);                /* parent */
  523. X    /* NOTREACHED */
  524. X    case 0:                    /* child */
  525. X    break;
  526. X    }
  527. X
  528. X    /* Get rid of the controlling terminal */
  529. X
  530. X    (void) close(0);
  531. X    (void) close(1);
  532. X    (void) close(2);
  533. X#ifdef SYSV
  534. X    (void) setpgrp();
  535. X#else
  536. X    if ((fd = open("/dev/tty", 0)) >= 0) {
  537. X    (void) ioctl(fd, TIOCNOTTY, 0);
  538. X    (void) close(fd);
  539. X    }
  540. X#endif
  541. X}
  542. X
  543. X/* pc_wait - wait till the pc has finished writing a file */
  544. X
  545. Xpc_wait(fd)
  546. Xint     fd;
  547. X{
  548. X    struct stat st;
  549. X    long    oldsize = 0;
  550. X
  551. X    /*
  552. X     * Repeatedly sleep one second until the file size does not change
  553. X     * anymore.
  554. X     * 
  555. X     * At first sight, this does not seem to be a very robust algorithm. It is,
  556. X     * however, sufficient. The pc sofware will first create a message file,
  557. X     * then the file with recipient addresses. The pc-maild program, on the
  558. X     * other hand, will read the recipient-address file first. If that file
  559. X     * turns out to be empty, it will try again at a later time. So, the only
  560. X     * time we may produce an incorrect result is under the following
  561. X     * conditions:
  562. X     * 
  563. X     * (1) the file with recipient names is longer than the PC/NFS packet size
  564. X     * or the pc\'s stdio buffer size.
  565. X     * 
  566. X     * (2) the network connection goes down for > 1 second after part of the
  567. X     * data has arrived in the file with recipient addresses.
  568. X     */
  569. X
  570. X    while (fstat(fd, &st) == 0 && oldsize != st.st_size) {
  571. X    oldsize = st.st_size;
  572. X    (void) sleep(1);
  573. X    }
  574. X}
  575. X
  576. X#ifdef SYSV
  577. X
  578. X/* gethostname - BSD compatibility routine */
  579. X
  580. Xgethostname(name, len)
  581. Xchar   *name;
  582. Xint     len;
  583. X{
  584. X    struct utsname ut;
  585. X
  586. X    (void) uname(&ut);
  587. X    (void) strncpy(name, ut.nodename, len);
  588. X    return (0);
  589. X}
  590. X
  591. X#endif
  592. END_OF_daemon/pc-maild.c
  593. if test 14808 -ne `wc -c <daemon/pc-maild.c`; then
  594.     echo shar: \"daemon/pc-maild.c\" unpacked with wrong size!
  595. fi
  596. # end of overwriting check
  597. fi
  598. if test -f main/hsearch.c -a "${1}" != "-c" ; then 
  599.   echo shar: Will not over-write existing file \"main/hsearch.c\"
  600. else
  601. echo shar: Extracting \"main/hsearch.c\" \(15461 characters\)
  602. sed "s/^X//" >main/hsearch.c <<'END_OF_main/hsearch.c'
  603. X/* Original source written by Arnold Robbins */
  604. X
  605. X#include <ctype.h>        /* for case-insensitive version */
  606. X#include "hsearch.h"        /* join the parts that were broken up */
  607. X
  608. X#define    strcmp    istrcmp        /* for case-insensitive version */
  609. X
  610. Xstatic ELEMENT **Table = NULL;    /* pointer to dynamicly allocated table */
  611. Xstatic int Num_elem = -1;    /* number of elements */
  612. X
  613. Xextern char *calloc();
  614. X
  615. Xextern void hdestroy();
  616. Xextern int hcreate();
  617. Xextern ENTRY *hsearch();
  618. X
  619. Xstatic int hashit();
  620. X
  621. X/*
  622. X * table of first 1900 or so primes, for use in finding the right prime
  623. X * number to be the table size.  this table may be generally useful...
  624. X */
  625. X
  626. Xstatic unsigned short primetab[] = {
  627. X/*
  628. X * comment these out, so that table will always have a minimal size...
  629. X1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
  630. X*/
  631. X73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
  632. X157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233,
  633. X239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317,
  634. X331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
  635. X421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503,
  636. X509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607,
  637. X613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
  638. X709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811,
  639. X821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911,
  640. X919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019,
  641. X1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097,
  642. X1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201,
  643. X1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291,
  644. X1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409,
  645. X1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487,
  646. X1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579,
  647. X1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667,
  648. X1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777,
  649. X1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877,
  650. X1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993,
  651. X1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083,
  652. X2087, 2089, 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179,
  653. X2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287,
  654. X2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381,
  655. X2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473,
  656. X2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609,
  657. X2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693,
  658. X2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789,
  659. X2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887,
  660. X2897, 2903, 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001,
  661. X3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, 3119,
  662. X3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229,
  663. X3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331,
  664. X3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, 3457,
  665. X3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539, 3541,
  666. X3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637,
  667. X3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, 3739,
  668. X3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853,
  669. X3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947,
  670. X3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, 4073,
  671. X4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177,
  672. X4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273,
  673. X4283, 4289, 4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409,
  674. X4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517,
  675. X4519, 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, 4639,
  676. X4643, 4649, 4651, 4657, 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733,
  677. X4751, 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, 4861, 4871,
  678. X4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951, 4957, 4967, 4969,
  679. X4973, 4987, 4993, 4999, 5003, 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077,
  680. X5081, 5087, 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, 5189,
  681. X5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, 5281, 5297, 5303, 5309,
  682. X5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, 5413, 5417, 5419, 5431,
  683. X5437, 5441, 5443, 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521,
  684. X5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, 5641, 5647, 5651,
  685. X5653, 5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743,
  686. X5749, 5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851,
  687. X5857, 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981,
  688. X5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091,
  689. X6101, 6113, 6121, 6131, 6133, 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211,
  690. X6217, 6221, 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, 6311,
  691. X6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, 6373, 6379, 6389, 6397,
  692. X6421, 6427, 6449, 6451, 6469, 6473, 6481, 6491, 6521, 6529, 6547, 6551, 6553,
  693. X6563, 6569, 6571, 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673,
  694. X6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761, 6763, 6779, 6781,
  695. X6791, 6793, 6803, 6823, 6827, 6829, 6833, 6841, 6857, 6863, 6869, 6871, 6883,
  696. X6899, 6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991,
  697. X6997, 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, 7109, 7121,
  698. X7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, 7219, 7229, 7237,
  699. X7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369,
  700. X7393, 7411, 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, 7507,
  701. X7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589,
  702. X7591, 7603, 7607, 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699,
  703. X7703, 7717, 7723, 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829,
  704. X7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919, 7927, 7933, 7937,
  705. X7949, 7951, 7963, 7993, 8009, 8011, 8017, 8039, 8053, 8059, 8069, 8081, 8087,
  706. X8089, 8093, 8101, 8111, 8117, 8123, 8147, 8161, 8167, 8171, 8179, 8191, 8209,
  707. X8219, 8221, 8231, 8233, 8237, 8243, 8263, 8269, 8273, 8287, 8291, 8293, 8297,
  708. X8311, 8317, 8329, 8353, 8363, 8369, 8377, 8387, 8389, 8419, 8423, 8429, 8431,
  709. X8443, 8447, 8461, 8467, 8501, 8513, 8521, 8527, 8537, 8539, 8543, 8563, 8573,
  710. X8581, 8597, 8599, 8609, 8623, 8627, 8629, 8641, 8647, 8663, 8669, 8677, 8681,
  711. X8689, 8693, 8699, 8707, 8713, 8719, 8731, 8737, 8741, 8747, 8753, 8761, 8779,
  712. X8783, 8803, 8807, 8819, 8821, 8831, 8837, 8839, 8849, 8861, 8863, 8867, 8887,
  713. X8893, 8923, 8929, 8933, 8941, 8951, 8963, 8969, 8971, 8999, 9001, 9007, 9011,
  714. X9013, 9029, 9041, 9043, 9049, 9059, 9067, 9091, 9103, 9109, 9127, 9133, 9137,
  715. X9151, 9157, 9161, 9173, 9181, 9187, 9199, 9203, 9209, 9221, 9227, 9239, 9241,
  716. X9257, 9277, 9281, 9283, 9293, 9311, 9319, 9323, 9337, 9341, 9343, 9349, 9371,
  717. X9377, 9391, 9397, 9403, 9413, 9419, 9421, 9431, 9433, 9437, 9439, 9461, 9463,
  718. X9467, 9473, 9479, 9491, 9497, 9511, 9521, 9533, 9539, 9547, 9551, 9587, 9601,
  719. X9613, 9619, 9623, 9629, 9631, 9643, 9649, 9661, 9677, 9679, 9689, 9697, 9719,
  720. X9721, 9733, 9739, 9743, 9749, 9767, 9769, 9781, 9787, 9791, 9803, 9811, 9817,
  721. X9829, 9833, 9839, 9851, 9857, 9859, 9871, 9883, 9887, 9901, 9907, 9923, 9929,
  722. X9931, 9941, 9949, 9967, 9973, 10007, 10009, 10037, 10039, 10061, 10067, 10069,
  723. X10079, 10091, 10093, 10099, 10103, 10111, 10133, 10139, 10141, 10151, 10159,
  724. X10163, 10169, 10177, 10181, 10193, 10211, 10223, 10243, 10247, 10253, 10259,
  725. X10267, 10271, 10273, 10289, 10301, 10303, 10313, 10321, 10331, 10333, 10337,
  726. X10343, 10357, 10369, 10391, 10399, 10427, 10429, 10433, 10453, 10457, 10459,
  727. X10463, 10477, 10487, 10499, 10501, 10513, 10529, 10531, 10559, 10567, 10589,
  728. X10597, 10601, 10607, 10613, 10627, 10631, 10639, 10651, 10657, 10663, 10667,
  729. X10687, 10691, 10709, 10711, 10723, 10729, 10733, 10739, 10753, 10771, 10781,
  730. X10789, 10799, 10831, 10837, 10847, 10853, 10859, 10861, 10867, 10883, 10889,
  731. X10891, 10903, 10909, 10937, 10939, 10949, 10957, 10973, 10979, 10987, 10993,
  732. X11003, 11027, 11047, 11057, 11059, 11069, 11071, 11083, 11087, 11093, 11113,
  733. X11117, 11119, 11131, 11149, 11159, 11161, 11171, 11173, 11177, 11197, 11213,
  734. X11239, 11243, 11251, 11257, 11261, 11273, 11279, 11287, 11299, 11311, 11317,
  735. X11321, 11329, 11351, 11353, 11369, 11383, 11393, 11399, 11411, 11423, 11437,
  736. X11443, 11447, 11467, 11471, 11483, 11489, 11491, 11497, 11503, 11519, 11527,
  737. X11549, 11551, 11579, 11587, 11593, 11597, 11617, 11621, 11633, 11657, 11677,
  738. X11681, 11689, 11699, 11701, 11717, 11719, 11731, 11743, 11777, 11779, 11783,
  739. X11789, 11801, 11807, 11813, 11821, 11827, 11831, 11833, 11839, 11863, 11867,
  740. X11887, 11897, 11903, 11909, 11923, 11927, 11933, 11939, 11941, 11953, 11959,
  741. X11969, 11971, 11981, 11987, 12007, 12011, 12037, 12041, 12043, 12049, 12071,
  742. X12073, 12097, 12101, 12107, 12109, 12113, 12119, 12143, 12149, 12157, 12161,
  743. X12163, 12197, 12203, 12211, 12227, 12239, 12241, 12251, 12253, 12263, 12269,
  744. X12277, 12281, 12289, 12301, 12323, 12329, 12343, 12347, 12373, 12377, 12379,
  745. X12391, 12401, 12409, 12413, 12421, 12433, 12437, 12451, 12457, 12473, 12479,
  746. X12487, 12491, 12497, 12503, 12511, 12517, 12527, 12539, 12541, 12547, 12553,
  747. X12569, 12577, 12583, 12589, 12601, 12611, 12613, 12619, 12637, 12641, 12647,
  748. X12653, 12659, 12671, 12689, 12697, 12703, 12713, 12721, 12739, 12743, 12757,
  749. X12763, 12781, 12791, 12799, 12809, 12821, 12823, 12829, 12841, 12853, 12889,
  750. X12893, 12899, 12907, 12911, 12917, 12919, 12923, 12941, 12953, 12959, 12967,
  751. X12973, 12979, 12983, 13001, 13003, 13007, 13009, 13033, 13037, 13043, 13049,
  752. X13063, 13093, 13099, 13103, 13109, 13121, 13127, 13147, 13151, 13159, 13163,
  753. X13171, 13177, 13183, 13187, 13217, 13219, 13229, 13241, 13249, 13259, 13267,
  754. X13291, 13297, 13309, 13313, 13327, 13331, 13337, 13339, 13367, 13381, 13397,
  755. X13399, 13411, 13417, 13421, 13441, 13451, 13457, 13463, 13469, 13477, 13487,
  756. X13499, 13513, 13523, 13537, 13553, 13567, 13577, 13591, 13597, 13613, 13619,
  757. X13627, 13633, 13649, 13669, 13679, 13681, 13687, 13691, 13693, 13697, 13709,
  758. X13711, 13721, 13723, 13729, 13751, 13757, 13759, 13763, 13781, 13789, 13799,
  759. X13807, 13829, 13831, 13841, 13859, 13873, 13877, 13879, 13883, 13901, 13903,
  760. X13907, 13913, 13921, 13931, 13933, 13963, 13967, 13997, 13999, 14009, 14011,
  761. X14029, 14033, 14051, 14057, 14071, 14081, 14083, 14087, 14107, 14143, 14149,
  762. X14153, 14159, 14173, 14177, 14197, 14207, 14221, 14243, 14249, 14251, 14281,
  763. X14293, 14303, 14321, 14323, 14327, 14341, 14347, 14369, 14387, 14389, 14401,
  764. X14407, 14411, 14419, 14423, 14431, 14437, 14447, 14449, 14461, 14479, 14489,
  765. X14503, 14519, 14533, 14537, 14543, 14549, 14551, 14557, 14561, 14563, 14591,
  766. X14593, 14621, 14627, 14629, 14633, 14639, 14653, 14657, 14669, 14683, 14699,
  767. X14713, 14717, 14723, 14731, 14737, 14741, 14747, 14753, 14759, 14767, 14771,
  768. X14779, 14783, 14797, 14813, 14821, 14827, 14831, 14843, 14851, 14867, 14869,
  769. X14879, 14887, 14891, 14897, 14923, 14929, 14939, 14947, 14951, 14957, 14969,
  770. X14983, 15013, 15017, 15031, 15053, 15061, 15073, 15077, 15083, 15091, 15101,
  771. X15107, 15121, 15131, 15137, 15139, 15149, 15161, 15173, 15187, 15193, 15199,
  772. X15217, 15227, 15233, 15241, 15259, 15263, 15269, 15271, 15277, 15287, 15289,
  773. X15299, 15307, 15313, 15319, 15329, 15331, 15349, 15359, 15361, 15373, 15377,
  774. X15383, 15391, 15401, 15413, 15427, 15439, 15443, 15451, 15461, 15467, 15473,
  775. X15493, 15497, 15511, 15527, 15541, 15551, 15559, 15569, 15581, 15583, 15601,
  776. X15607, 15619, 15629, 15641, 15643, 15647, 15649, 15661, 15667, 15671, 15679,
  777. X15683, 15727, 15731, 15733, 15737, 15739, 15749, 15761, 15767, 15773, 15787,
  778. X15791, 15797, 15803, 15809, 15817, 15823, 15859, 15877, 15881, 15887, 15889,
  779. X15901, 15907, 15913, 15919, 15923, 15937, 15959, 15971, 15973, 15991, 16001,
  780. X16007, 16033, 16057, 16061, 16063, 16067, 16069, 16073, 16087, 16091, 16097,
  781. X16103, 16111, 16127, 16139, 16141, 16183, 16187, 16189, 16193, 16217, 16223,
  782. X16229, 16231, 16249, 16253, 16267, 16273, 16301, 16319, 16333, 16339, 16349,
  783. X16361, 16363, 16369, 16381
  784. X};
  785. X
  786. X/* hcreate --- create a hash table at least howmany big */
  787. X
  788. Xint hcreate (howmany)
  789. Xregister unsigned int howmany;
  790. X{
  791. X    register int i, j;
  792. X
  793. X    /*
  794. X     * find first prime number >= howmany, and use it for table size
  795. X     */
  796. X
  797. X    if (Num_elem != -1)    /* already a table out there */
  798. X        hdestroy();    /* remove it */
  799. X
  800. X    j = sizeof (primetab) / sizeof (primetab[0]);
  801. X    for (i = 0; i < j; i++)
  802. X        if (primetab[i] >= howmany)
  803. X            break;
  804. X
  805. X    if (i >= j)    /* howmany bigger than any prime we have, use it */
  806. X        Num_elem = howmany;
  807. X    else
  808. X        Num_elem = primetab[i];
  809. X
  810. X    if ((Table = (ELEMENT **) calloc (Num_elem, sizeof (ELEMENT *))) == NULL)
  811. X        return (0);
  812. X    else
  813. X        return (1);
  814. X}
  815. X
  816. X/* idestroy --- destroy a single element on a chain */
  817. X
  818. Xstatic void idestroy (elem)
  819. XELEMENT *elem;
  820. X{
  821. X    if (elem != NULL)
  822. X    {
  823. X        idestroy (elem->next);
  824. X        free ((char *) elem);
  825. X    }
  826. X}
  827. X
  828. X/* hdestroy --- nuke the existing hash table */
  829. X
  830. Xvoid hdestroy()
  831. X{
  832. X    register unsigned int i;
  833. X
  834. X    if (Table != NULL)
  835. X    {
  836. X        /* free all the chains */
  837. X        for (i = 0; i < Num_elem; i++)
  838. X            idestroy (Table[i]);
  839. X
  840. X        /* now the table itself */
  841. X        free ((char *) Table);
  842. X        Num_elem = -1;
  843. X        Table = NULL;
  844. X    }
  845. X}
  846. X
  847. X/* hsearch --- lookup or enter an item in the hash table */
  848. X
  849. XENTRY *hsearch (entry, action)
  850. XENTRY entry;
  851. XACTION action;
  852. X{
  853. X    ELEMENT e;
  854. X    ELEMENT *ep = NULL;
  855. X    ELEMENT *ep2 = NULL;
  856. X    int index;
  857. X
  858. X    if (Table == NULL)
  859. X        return (NULL);
  860. X
  861. X    index = hashit (entry.key);
  862. X    if (Table[index] == NULL)    /* nothing there */
  863. X    {
  864. X        if (action == FIND)
  865. X            return (NULL);
  866. X        else
  867. X        {
  868. X            /* add it to the table */
  869. X            e.item = entry;
  870. X            e.next = NULL;
  871. X            if ((Table[index] = (ELEMENT *) calloc (1, sizeof (ELEMENT))) == NULL)
  872. X                return (NULL);
  873. X            *Table[index] = e;
  874. X            return (& Table[index]->item);
  875. X        }
  876. X    }
  877. X    else
  878. X    {
  879. X        /* something in bucket, see if already on chain */
  880. X        for (ep = Table[index]; ep != NULL; ep = ep->next)
  881. X        {
  882. X            if (strcmp (ep->item.key, entry.key) == 0)
  883. X            {
  884. X                if (action == ENTER)
  885. X                    ep->item.data = entry.data;
  886. X                    /* already there, just change data */
  887. X                /* or action was just find it */
  888. X                return (& ep->item);
  889. X            }
  890. X            else
  891. X                ep2 = ep;
  892. X        }
  893. X        /* at this point, item was not in table */
  894. X        /* ep2 points at last element on the list */
  895. X        if (action == ENTER)
  896. X        {
  897. X            if ((ep2->next = (ELEMENT *) calloc (1, sizeof (ELEMENT))) == NULL)
  898. X                return (NULL);
  899. X            ep2->next->item = entry;
  900. X            ep2->next->next = NULL;
  901. X            return (& ep2->next->item);
  902. X        }
  903. X        else
  904. X            return (NULL);
  905. X    }
  906. X    /*NOTREACHED*/
  907. X}
  908. X
  909. X/* hashit --- do the hashing algorithm */
  910. X
  911. X/*
  912. X * algorithm is sum of string elements, plus string length
  913. X * mod table size.
  914. X *
  915. X * made case insensitive for hyphenation program
  916. X */
  917. X
  918. Xstatic int hashit (text)
  919. Xregister char *text;
  920. X{
  921. X    register long int sum = 0;
  922. X    register int i;
  923. X    register int kludge;
  924. X
  925. X    for (i = 0; (kludge = text[i]) != '\0'; i++)
  926. X        sum += (isupper(kludge) ? tolower(kludge) : kludge);
  927. X    sum += i;
  928. X
  929. X    return (sum % Num_elem);
  930. X}
  931. X
  932. END_OF_main/hsearch.c
  933. if test 15461 -ne `wc -c <main/hsearch.c`; then
  934.     echo shar: \"main/hsearch.c\" unpacked with wrong size!
  935. fi
  936. # end of overwriting check
  937. fi
  938. if test -f main/pager.c -a "${1}" != "-c" ; then 
  939.   echo shar: Will not over-write existing file \"main/pager.c\"
  940. else
  941. echo shar: Extracting \"main/pager.c\" \(13541 characters\)
  942. sed "s/^X//" >main/pager.c <<'END_OF_main/pager.c'
  943. X/*++
  944. X/* NAME
  945. X/*      pager 3
  946. X/* SUMMARY
  947. X/*      pager for text files
  948. X/* PROJECT
  949. X/*      pc-mail
  950. X/* PACKAGE
  951. X/*      mail
  952. X/* SYNOPSIS
  953. X/*      #include "pager.h"
  954. X/*
  955. X/*      File *open_pager()
  956. X/*
  957. X/*      void close_pager(p)
  958. X/*      File *p;
  959. X/*
  960. X/*      void set_pager(p)
  961. X/*      File *p;
  962. X/*
  963. X/*      void app_pager(p,s)
  964. X/*      File *p;
  965. X/*      char *s;
  966. X/*
  967. X/*      void del_pager(p)
  968. X/*      File *p;
  969. X/*
  970. X/*      void mesg_pager(p,m)
  971. X/*      File *p;
  972. X/*      char *m[];
  973. X/*
  974. X/*      void sort_pager(p,dir)
  975. X/*      File *p;
  976. X/*
  977. X/*      int cp_pager(path)
  978. X/*      char *path;
  979. X/*
  980. X/*      int pr_pager()
  981. X/*
  982. X/*      int rd_pager(p,path)
  983. X/*      File *p;
  984. X/*      char *path;
  985. X/*
  986. X/*      char *gets_pager();
  987. X/*      File *p;
  988. X/*
  989. X/*      void puts_pager(s);
  990. X/*      char *s;
  991. X/*
  992. X/*      int ds_pager()
  993. X/*
  994. X/*      int pr_pager()
  995. X/*
  996. X/*      int up_pager()
  997. X/*
  998. X/*      int dn_pager()
  999. X/*
  1000. X/*      int pu_pager()
  1001. X/*
  1002. X/*      int pd_pager()
  1003. X/* DESCRIPTION
  1004. X/*      The pager provides acces to a pager file which is displayed
  1005. X/*      on the screen in the middle window. Some functions operate
  1006. X/*    on what is called the "current" pager file. All functions
  1007. X/*    have access to the contents of the middle screen window only.
  1008. X/*
  1009. X/*      open_pager() creates a new (empty) pager file. The return value
  1010. X/*      should be used in subsequent accesses to the file. Sets the
  1011. X/*      current file.
  1012. X/*
  1013. X/*      close_pager() releases storage for a pager file. Sets the
  1014. X/*      current file to none if that is the one being deleted.
  1015. X/*
  1016. X/*      app_pager() appends a new line of text to the end of a pager file.
  1017. X/*      Sets the current file.
  1018. X/*
  1019. X/*      del_pager() deletes the line at the current cursor position. Sets the
  1020. X/*      current file.
  1021. X/*
  1022. X/*      mesg_pager() invokes app_pager() to copy a null-terminated array of
  1023. X/*    strings to a pager file. Since it invokes app_pager(), the current
  1024. X/*    pager file is set as well. Pager files filled by mesg-pager()
  1025. X/*    will not be displayed with an '-- end of display --' line at their end.
  1026. X/*
  1027. X/*      ins_pager() inserts a line at the current cursor position. Sets the
  1028. X/*      current file.
  1029. X/*
  1030. X/*      scan_pager() takes similar arguments as scanf(3), reads from the
  1031. X/*      line at the current cursor position and returns the number of
  1032. X/*      successfull conversions done. Does not set the current file.
  1033. X/*
  1034. X/*      sort_pager() sorts a file alphabetically. Sets the current file.
  1035. X/*      The dir argument selects the direction of sort (FORW_SORT, BACK_SORT).
  1036. X/*
  1037. X/*      set_pager() sets the current file.
  1038. X/*
  1039. X/*      gets_pager() returns a pointer to the current line in the
  1040. X/*      current file, or a null pointer is there is none.
  1041. X/*
  1042. X/*      puts_pager() replaces the current line in the current file.
  1043. X/*
  1044. X/*      cp_pager() copies the contents of current pager file
  1045. X/*      to a normal (external) file. It returns a nonzero status if
  1046. X/*      an error occurred (bad path, write error,...).
  1047. X/*
  1048. X/*      pr_pager() copies the current pager file to the printer.
  1049. X/*
  1050. X/*      rd_pager() appends a permanent file to the current pager file.
  1051. X/*      It returns a nonzero status if an error occurred. Sets the current file.
  1052. X/*    rd_pager() reads through the ascf(3) ascii filter, so that it is
  1053. X/*    suitable for viewing word-processor files.
  1054. X/*
  1055. X/*      up_pager() moves the cursor one line up, if there is one. The
  1056. X/*    screen is scrolled when the cursor was at the top of the screen.
  1057. X/*
  1058. X/*      dn_pager moves the cursor one line down, if there is one. The
  1059. X/*    screen is scrolled when the cursor was at the bottom of the screen.
  1060. X/*
  1061. X/*      pu_pager() displays a page of text that precedes the one on the
  1062. X/*      screen, or as much as there is.
  1063. X/*
  1064. X/*      pd_pager() displays the next page of text, or as much as there is.
  1065. X/* FUNCTIONS AND MACROS
  1066. X/*      printcl(), printat(), beep(), propen(), prclose(),ascopen(),
  1067. X/*    ascclose(), ascget()
  1068. X/* SEE ALSO
  1069. X/*      path(3), window(3), window(5), asc(3)
  1070. X/* DIAGNOSTICS
  1071. X/*      The buzzer makes noise when attempt is made to move the
  1072. X/*      cursor beyond the beginning or end of the pager file.
  1073. X/*      The program aborts with an error message if references are made
  1074. X/*      to the "current file" or "current line" if there is none.
  1075. X/* BUGS
  1076. X/*      It looks a lot like an editor, but it isn't.
  1077. X/*
  1078. X/*    No optimization. It just overwrites the whole middle window.
  1079. X/* AUTHOR(S)
  1080. X/*      W.Z. Venema
  1081. X/*      Eindhoven University of Technology
  1082. X/*      Department of Mathematics and Computer Science
  1083. X/*      Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
  1084. X/* CREATION DATE
  1085. X/*      Fri Apr  3 22:06:00 GMT+1:00 1987
  1086. X/* LAST MODIFICATION
  1087. X/*    90/01/22 13:02:21
  1088. X/* VERSION/RELEASE
  1089. X/*    2.1
  1090. X/*--*/
  1091. X
  1092. X#include <stdio.h>
  1093. X
  1094. X#include "defs.h"
  1095. X#include "window.h"
  1096. X#include "pager.h"
  1097. X#include "path.h"
  1098. X#include "ascf.h"
  1099. X
  1100. Xhidden File *curfile = NULL;        /* head of the current file */
  1101. X
  1102. X/* open_pager - create pager file and set current file */
  1103. X
  1104. Xpublic File *open_pager()
  1105. X{
  1106. X    register File *p = curfile = (File *) myalloc(sizeof(File));
  1107. X
  1108. X    p->top = p->curr = p->head = p->last = NULL;
  1109. X    p->opts = 0;
  1110. X    return (p);
  1111. X}
  1112. X
  1113. X/* close_pager - release memory in a pager file */
  1114. X
  1115. Xpublic void close_pager(p)
  1116. Xregister File *p;
  1117. X{
  1118. X    register Line *q;
  1119. X
  1120. X    if (p) {
  1121. X    for (q = p->head; q; q = q->next)    /* release lines */
  1122. X        free((char *) q);
  1123. X    if (curfile == p)            /* unset current file */
  1124. X        curfile = 0;
  1125. X    free((char *) p);            /* release header */
  1126. X    }
  1127. X}
  1128. X
  1129. X/* app_pager - append line at end of file and set current file */
  1130. X
  1131. Xpublic  app_pager(p, s)
  1132. Xregister File *p;
  1133. Xchar   *s;
  1134. X{
  1135. X    register Line *l = (Line *) myalloc(sizeof(Line) + strlen(s));
  1136. X
  1137. X    if (p->head == 0) {                /* first line in the file */
  1138. X    p->head = p->top = p->curr = l;
  1139. X    } else {                    /* real append */
  1140. X    p->last->next = l;
  1141. X    }
  1142. X    l->next = NULL;                /* since it is last */
  1143. X    l->prev = p->last;                /* since it is last */
  1144. X    p->last = l;                /* since it is last */
  1145. X
  1146. X    strcpy(l->line, s);                /* copy the line */
  1147. X
  1148. X    curfile = p;                /* set current file */
  1149. X}
  1150. X
  1151. X/* del_pager - delete line at cursor and set current file (untested!) */
  1152. X
  1153. Xpublic void del_pager(p)
  1154. Xregister File *p;
  1155. X{
  1156. X    register Line *l = p->curr;
  1157. X
  1158. X    if (l) {
  1159. X    if (l->prev)
  1160. X        l->prev->next = l->next;
  1161. X    if (l->next)
  1162. X        l->next->prev = l->prev;
  1163. X    if (l == p->head)
  1164. X        p->curr = p->head = l->next;
  1165. X    if (l == p->top)
  1166. X        p->curr = p->top = l->next ? l->next : l->prev;
  1167. X    if (l == p->last)
  1168. X        p->curr = p->last = l->prev;
  1169. X    if (l == p->curr)
  1170. X        p->curr = l->next;
  1171. X    free((char *) l);
  1172. X    }
  1173. X    curfile = p;
  1174. X}
  1175. X
  1176. X/* set_pager - set the current file; use with care */
  1177. X
  1178. Xpublic void set_pager(p)
  1179. XFile   *p;
  1180. X{
  1181. X    curfile = p;
  1182. X}
  1183. X
  1184. X /*
  1185. X  * The following functions provide an easy interface to the keyboard
  1186. X  * interpreter routines. The keyboard interpreter just associates a key
  1187. X  * stroke with a function call, does not care what a function does and has
  1188. X  * almost no facility for passing function arguments. Although the keyboard
  1189. X  * interpreter manipulates cursor and page coordinates when the some keys
  1190. X  * are hit, it never knows which keys affect what the user sees on the
  1191. X  * screen. That explains why the following functions rely on the above ones
  1192. X  * for setting the "current file". It may also explain why the above
  1193. X  * routines do not immediately update the terminal screen, whereas the
  1194. X  * routines below do.
  1195. X  */
  1196. X
  1197. X/* ds_pager - display a page of the current pager file */
  1198. X
  1199. Xpublic int ds_pager()
  1200. X{
  1201. X    static char endline[] = "-- end of display --";
  1202. X
  1203. X    if (curfile && curfile->curr) {
  1204. X    register Line *p;
  1205. X    register int k;
  1206. X
  1207. X    for (p = curfile->top, k = 0; p && k < midwin->size; p = p->next)
  1208. X        k += p->llen = printcl(midwin, p->lineno = k, p->line);
  1209. X    if (k < midwin->size)
  1210. X        printcl(midwin, k++, (curfile->opts & PG_NOEND) ? "" : endline);
  1211. X    while (k < midwin->size)
  1212. X        printcl(midwin, k++, "");
  1213. X    printat(midwin, curfile->curr->lineno, "");
  1214. X    } else {
  1215. X    register int k;
  1216. X
  1217. X    printcl(midwin, 0, (curfile->opts & PG_NOEND) ? "" : endline);
  1218. X    for (k = 1; k < midwin->size; k++)
  1219. X        printcl(midwin, k, "");
  1220. X    printat(midwin, 0, "");
  1221. X    }
  1222. X    return (0);                    /* screen up-to-date */
  1223. X}
  1224. X
  1225. X/* up_pager - up-arrow key hit. check cursor position */
  1226. X
  1227. Xpublic int up_pager()
  1228. X{
  1229. X    register Line *p = curfile ? curfile->curr : 0;
  1230. X
  1231. X    if (p == 0 || p->prev == 0) {
  1232. X    beep();
  1233. X    } else {
  1234. X    if (p->lineno == 0)
  1235. X        pu_pager();
  1236. X    printat(midwin, (curfile->curr = p->prev)->lineno, "");
  1237. X    }
  1238. X    return (0);
  1239. X}
  1240. X
  1241. X/* dn_pager - down-arrow key hit. check cursor position */
  1242. X
  1243. Xpublic int dn_pager()
  1244. X{
  1245. X    register Line *p = curfile ? curfile->curr : 0;
  1246. X
  1247. X    if (p == 0 || p->next == 0) {
  1248. X    beep();
  1249. X    } else {
  1250. X    if (p->lineno + p->llen >= midwin->size)
  1251. X        pd_pager();
  1252. X    printat(midwin, (curfile->curr = p->next)->lineno, "");
  1253. X    }
  1254. X    return (0);
  1255. X}
  1256. X
  1257. X/* pu_pager - display preceding page of info */
  1258. X
  1259. Xpublic int pu_pager()
  1260. X{
  1261. X    register Line *p;
  1262. X    register int k;
  1263. X
  1264. X    if (curfile && (p = curfile->top) && curfile->top->prev) {
  1265. X    for (k = 0; k < midwin->size && p; k += p->llen, p = p->prev)
  1266. X        curfile->top = p;
  1267. X    curfile->curr = curfile->top;
  1268. X    ds_pager();
  1269. X    } else {
  1270. X    beep();
  1271. X    }
  1272. X    return (0);
  1273. X}
  1274. X
  1275. X/* pd_pager - display next page of info */
  1276. X
  1277. Xpublic int pd_pager()
  1278. X{
  1279. X    register Line *p = curfile ? curfile->top : 0;
  1280. X    register int k;
  1281. X    register Line *dummy;
  1282. X
  1283. X    for (k = 0; k < midwin->size && p; k += p->llen, p = p->next)
  1284. X    dummy = p;
  1285. X    if (p) {
  1286. X    curfile->curr = curfile->top = dummy;
  1287. X    ds_pager();
  1288. X    } else {
  1289. X    beep();
  1290. X    }
  1291. X    return (0);
  1292. X}
  1293. X
  1294. X /*
  1295. X  * The following functions copy external files to pager file and vice-versa.
  1296. X  * There is a limited error detection facility in the form of nonzero return
  1297. X  * values.
  1298. X  */
  1299. X
  1300. X/* cp_pager - copy current pager file to permanent file */
  1301. X
  1302. Xpublic int cp_pager(path)
  1303. Xchar   *path;
  1304. X{
  1305. X    register FILE *fp;
  1306. X
  1307. X    if (curfile && (fp = fopen(path, "w"))) {
  1308. X    register Line *pp;
  1309. X    int     err;
  1310. X
  1311. X    for (pp = curfile->head; pp; pp = pp->next)
  1312. X        fputs(pp->line, fp), putc('\n', fp);
  1313. X    err = (fflush(fp) || ferror(fp));
  1314. X    fclose(fp);
  1315. X    return (err);
  1316. X    } else {
  1317. X    return (-1);
  1318. X    }
  1319. X}
  1320. X
  1321. X/* pr_pager - print pager file on default printer */
  1322. X
  1323. Xpublic int pr_pager()
  1324. X{
  1325. X    register FILE *fp;
  1326. X
  1327. X    if (curfile && (fp = propen())) {
  1328. X    register Line *pp;
  1329. X    int     err;
  1330. X
  1331. X    for (pp = curfile->head; pp; pp = pp->next)
  1332. X        fputs(pp->line, fp), putc('\n', fp);
  1333. X    err = (fflush(fp) || ferror(fp));
  1334. X    prclose(fp);
  1335. X    return (err);
  1336. X    } else {
  1337. X    return (-1);
  1338. X    }
  1339. X}
  1340. X
  1341. X/* rd_pager - copy ordinary file via filter to pager file */
  1342. X
  1343. Xpublic int rd_pager(p, path)
  1344. XFile   *p;
  1345. Xchar   *path;
  1346. X{
  1347. X    register FILE *fp;
  1348. X
  1349. X    if (p && (fp = ascopen(path, "r"))) {    /* init the filter */
  1350. X    char    buf[BUFSIZ];
  1351. X    int     err;
  1352. X
  1353. X    while (ascgets(buf, sizeof(buf), fp))    /* copy to pager file */
  1354. X        app_pager(p, buf);        /* line by line */
  1355. X
  1356. X    err = ferror(fp);            /* check for errors */
  1357. X    ascclose(fp);
  1358. X    return (err);
  1359. X    } else {
  1360. X    return (-1);
  1361. X    }
  1362. X}
  1363. X
  1364. X/* fwdcmp, revcmp - compare lexical order of lines */
  1365. X
  1366. Xhidden int fwdcmp(l1, l2)
  1367. XLine  **l1,
  1368. X      **l2;
  1369. X{
  1370. X    return (strcmp((*l1)->line, (*l2)->line));
  1371. X}
  1372. X
  1373. Xhidden int revcmp(l1, l2)
  1374. XLine  **l1,
  1375. X      **l2;
  1376. X{
  1377. X    return (strcmp((*l2)->line, (*l1)->line));
  1378. X}
  1379. X
  1380. X/* sort_pager - sort a pager file */
  1381. X
  1382. Xpublic void sort_pager(pp, dir)
  1383. XFile   *pp;
  1384. Xint     dir;
  1385. X{
  1386. X    register Line *l;
  1387. X    register int i;
  1388. X    int     lines;
  1389. X    Line  **lvec;
  1390. X
  1391. X    /* Build a vector with pointers to line structures. */
  1392. X
  1393. X    for (i = 0, l = pp->head; l; l = l->next)    /* count nbr of lines */
  1394. X    i++;
  1395. X    if (i <= 1)                    /* no work */
  1396. X    return;
  1397. X
  1398. X    lvec = (Line **) myalloc((lines = i) * sizeof(*lvec));
  1399. X
  1400. X    for (i = 0, l = pp->head; l; l = l->next)    /* fill vector */
  1401. X    lvec[i++] = l;
  1402. X
  1403. X    /* Sort the vector with pointers to line structures. */
  1404. X
  1405. X    qsort((char *) lvec, lines, sizeof(*lvec),
  1406. X      dir == FORW_SORT ? fwdcmp : revcmp);
  1407. X
  1408. X    /* Restore links between line structures and destroy the sorted vector */
  1409. X
  1410. X    for (i = 0; i < lines - 1; i++)        /* fix forward links */
  1411. X    lvec[i]->next = lvec[i + 1];
  1412. X    lvec[i]->next = NULL;
  1413. X
  1414. X    lvec[0]->prev = NULL;            /* fix backward links */
  1415. X    for (i = 1; i < lines; i++)
  1416. X    lvec[i]->prev = lvec[i - 1];
  1417. X
  1418. X    pp->head = pp->top = pp->curr = lvec[0];    /* fix file header */
  1419. X    pp->last = lvec[lines - 1];
  1420. X
  1421. X    free((char *) lvec);            /* release vector */
  1422. X
  1423. X    curfile = pp;                /* set current file */
  1424. X}
  1425. X
  1426. X/* gets_pager - return current line in current file */
  1427. X
  1428. Xpublic char *gets_pager()
  1429. X{
  1430. X    return (curfile && curfile->curr ? curfile->curr->line : 0);
  1431. X}
  1432. X
  1433. X/* puts_pager - replace line (cleanup this mess) */
  1434. X
  1435. Xpublic void puts_pager(s)
  1436. Xchar   *s;
  1437. X{
  1438. X    if (curfile == 0 || curfile->curr == 0) {    /* no-no if there is no line */
  1439. X    fatal("puts_pager: no current file");
  1440. X    } else {
  1441. X    register Line *old = curfile->curr;    /* get current line */
  1442. X    register Line *new = (Line *) myalloc(sizeof(Line) + strlen(s));
  1443. X
  1444. X    new->prev = old->prev;            /* fill it in */
  1445. X    new->next = old->next;
  1446. X    new->lineno = old->lineno;
  1447. X    strcpy(new->line, s);
  1448. X    if (new->next)                /* check next line */
  1449. X        new->next->prev = new;
  1450. X    if (new->prev)                /* check previous line */
  1451. X        new->prev->next = new;
  1452. X    if (old == curfile->head)        /* check file head */
  1453. X        curfile->head = new;
  1454. X    if (old == curfile->top)        /* check file display */
  1455. X        curfile->top = new;
  1456. X    if (old == curfile->last)        /* check file tail */
  1457. X        curfile->last = new;
  1458. X    free((char *) curfile->curr);        /* release old line */
  1459. X    curfile->curr = new;            /* set current line */
  1460. X    }
  1461. X}
  1462. X
  1463. X/* mesg_pager - copy null-terminated array of strings to pager file */
  1464. X
  1465. Xpublic void mesg_pager(pp, msg)
  1466. Xregister File *pp;
  1467. Xregister char **msg;
  1468. X{
  1469. X    pp->opts |= PG_NOEND;            /* suppress end marker */
  1470. X
  1471. X    while (*msg)
  1472. X    app_pager(pp, *msg++);
  1473. X}
  1474. END_OF_main/pager.c
  1475. if test 13541 -ne `wc -c <main/pager.c`; then
  1476.     echo shar: \"main/pager.c\" unpacked with wrong size!
  1477. fi
  1478. # end of overwriting check
  1479. fi
  1480. echo shar: End of archive 9 \(of 11\).
  1481. cp /dev/null ark9isdone
  1482. MISSING=""
  1483. for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
  1484.     if test ! -f ark${I}isdone ; then
  1485.     MISSING="${MISSING} ${I}"
  1486.     fi
  1487. done
  1488. if test "${MISSING}" = "" ; then
  1489.     echo You have unpacked all 11 archives.
  1490.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1491. else
  1492.     echo You still need to unpack the following archives:
  1493.     echo "        " ${MISSING}
  1494. fi
  1495. ##  End of shell archive.
  1496. exit 0
  1497.  
  1498.