home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2956 < prev    next >
Internet Message Format  |  1991-03-03  |  52KB

  1. From: lee@sq.sq.com (Liam R. E. Quin)
  2. Newsgroups: alt.sources
  3. Subject: lq-text Full Text Retrieval Database Part 08/13
  4. Message-ID: <1991Mar4.020723.16680@sq.sq.com>
  5. Date: 4 Mar 91 02:07:23 GMT
  6.  
  7. : cut here --- cut here --
  8. : To unbundle, sh this file
  9. #! /bin/sh
  10. : part 08
  11. echo x - lq-text/src/lqtext/lqword.c 1>&2
  12. sed 's/^X//' >lq-text/src/lqtext/lqword.c <<'@@@End of lq-text/src/lqtext/lqword.c'
  13. X/* lqword.c -- Copyright 1989 Liam R. Quin.  All Rights Reserved.
  14. X * This code is NOT in the public domain.
  15. X * See the file COPYRIGHT for full details.
  16. X */
  17. X
  18. X/* lqword -- simple program to print information about individual words.
  19. X *
  20. X * $Id: lqword.c,v 2.8 90/10/06 00:51:00 lee Rel1-10 $
  21. X */
  22. X
  23. X#include "globals.h" /* defines and declarations for database filenames */
  24. X
  25. X#include <stdio.h>
  26. X#include <sys/types.h>
  27. X#include <malloc.h>
  28. X#include <fcntl.h> /* for fileinfo.h */
  29. X#include <ctype.h>
  30. X
  31. X#ifdef BSD
  32. X# define USI_MAX ((unsigned int) -1)
  33. X#else
  34. X# include <limits.h>
  35. X  /* for USI_MAX, the largest unsigned integer.
  36. X   * 4.3 BSD doesn't seem to have this.  I don't know how to get this
  37. X   * on BSD systems.
  38. X   */
  39. X#endif
  40. X
  41. X#include "fileinfo.h"
  42. X#include "wordinfo.h"
  43. X#include "smalldb.h"
  44. X#include "pblock.h"
  45. X#include "wordrules.h"
  46. X#include "emalloc.h"
  47. X
  48. X/*** Declarations: ***/
  49. X/** System calls and library routines: **/
  50. Xextern void exit();
  51. X
  52. X/** System calls: **/
  53. X
  54. X/** Unix Library Functions: **/
  55. Xextern char *strncpy();
  56. X#ifndef tolower
  57. X extern int tolower();
  58. X#endif
  59. X
  60. X/** lqtext library functions: **/
  61. Xextern char *UnFlag();
  62. Xextern t_WordInfo *WID2WordInfo();
  63. Xextern int TooCommon();
  64. Xextern void cleanupdb();
  65. Xextern void SetDefaults();
  66. Xextern void DefaultUsage();
  67. Xextern void DeleteWord();
  68. X
  69. X/** functions defined within this file: */
  70. Xvoid PrintWordInfo(), AllWordInfo();
  71. Xvoid Display(), ShowWordList();
  72. Xvoid dbmmarch();
  73. X
  74. X/** Macros and variable definitions **/
  75. X
  76. X#define DISPLAY_ALL 1
  77. X#define DISPLAY_NAME 2
  78. X    /* These are the possible DisplayMode values -- see main() */
  79. X
  80. Xchar *progname = 0;
  81. X    /* Used for error messages */
  82. X
  83. Xint SilentMode = 0;
  84. X    /* Set if we were invoked with the -s option.  In this mode, we behave
  85. X     * like grep -s, and exit with a zero exit status if one or more of
  86. X     * the words were found in the database.
  87. X     */
  88. X
  89. Xint ListMode = 0;
  90. X    /* Set if we are to provide a terser output format suitable for use
  91. X     * with lqshow(1L).
  92. X     */
  93. X
  94. Xint AsciiTrace = 0;
  95. X    /* If this is non-zero, we provide debugging information.  The lqtext
  96. X     * library also uses this variable.  Setting it to values greater
  97. X     * than 1 or 2 will generally provide large amounts of debugging
  98. X     * information.  If the library was compiled with -UASCIITRACE,
  99. X     * however, there will be much less diagnostic output at higher
  100. X     * levels.
  101. X     */
  102. X
  103. Xstatic char *Revision = "lqword 2.2";
  104. X
  105. X/** end of declarations... **/
  106. X
  107. X
  108. Xint
  109. Xmain(argc, argv)
  110. X    int argc;
  111. X    char *argv[];
  112. X{
  113. X    extern int optind, getopt();  /* For getopt(3) */
  114. X    extern char *optarg;      /* For getopt(3) */
  115. X    int ch;              /* For getopt(3) */
  116. X    int ErrorFlag = 0;          /* For getopt(3) */
  117. X    int DisplayMode = 0;
  118. X    /* DisplayMode indicates what kind of information we are to
  119. X     * print in response to queries.  The values understood are
  120. X     * the DISPLAY_* constants.  Perhaps this should be an enum.
  121. X     */
  122. X
  123. X    progname = argv[0];
  124. X    /* I see this as a library program, so I am leaving the full
  125. X     * path.  lqaddfile(1L) and lqphrase(1L) set progname to be
  126. X     * the filename of the command, rather than the full pathname.
  127. X     */
  128. X
  129. X    SetDefaults(argc, argv);
  130. X    /* Deal with any arguments that are understood by all lqtext
  131. X     * programs.
  132. X     */
  133. X
  134. X    while ((ch = getopt(argc, argv, "aAD:lsVxZz:")) != EOF) {
  135. X    switch (ch) {
  136. X    case 'a':
  137. X        DisplayMode = DISPLAY_NAME;
  138. X        break;
  139. X    case 'A':
  140. X        DisplayMode = DISPLAY_ALL;
  141. X        break;
  142. X    case 'D':
  143. X        DeleteWord(optarg); /* MISFEATURE */
  144. X        /* This actually removes all entries for the given word
  145. X         * from the database.  You need write permission, of
  146. X         * course.
  147. X         */
  148. X        break;
  149. X    case 'l':
  150. X        ListMode = 1;
  151. X        break;
  152. X    case 's':
  153. X        SilentMode = 1;
  154. X        break;
  155. X    case 'V':
  156. X        fprintf(stderr, "%s version %s\n", progname, Revision);
  157. X        break;
  158. X    case 'x':
  159. X        ErrorFlag++;
  160. X        break;
  161. X    case '?':
  162. X        ErrorFlag++;
  163. X        break;
  164. X    case 'z':
  165. X    case 'Z':
  166. X        break; /* done by SetDefaults(); */
  167. X    }
  168. X    }
  169. X
  170. X    /* Normally put call to lrqError here to give a helpful message,
  171. X     * but not yet ready to ship the error handling package, sorry
  172. X     */
  173. X    if (ErrorFlag) {
  174. X    fprintf(stderr, "%s: options are:\n", progname);
  175. X    fputs("\
  176. X    -D Word -- delete the named word (DANGEROUS!)\n\
  177. X    -l    -- list mode, for use with lqshow\n\
  178. X    -s    -- silent mode (like grep -s)\n", stderr);
  179. X    DefaultUsage();
  180. X        /* DefaultUsage() prints the list of the standard options. */
  181. X    fputs("\n\
  182. XIn addition, if no words are given, the following are understood:\n\
  183. X    -a    -- print all words\n\
  184. X    -A    -- print all matches to all words\n", stderr);
  185. X    exit(1);
  186. X    }
  187. X
  188. X    if (optind >= argc) {
  189. X    if (SilentMode) exit(1);
  190. X        /* if there were no words given, none of them matched.
  191. X         * It could be argued that this case should be an error.
  192. X         */
  193. X    if (DisplayMode) {
  194. X        AllWordInfo(DisplayMode);
  195. X    } else {
  196. X        /* In this case, there were no command-line options and no
  197. X         * display-mode flags, so we do the default thing.
  198. X         * This happens to be to print every word in the database.
  199. X         * This is probably bogus behaviour -- there should be a better
  200. X         * way of finding words that match a given pattern than using
  201. X         * lqword | grep
  202. X         * which is what this allows.
  203. X         */
  204. X        dbmmarch();
  205. X    }
  206. X    } else {
  207. X    if (!SilentMode && !ListMode) {
  208. X        /* Print some pretty headers */
  209. X        printf("       WID | Where   | Total   | Word\n");
  210. X        puts(
  211. X"===========|=========|=========|============================================");
  212. X    }
  213. X
  214. X    while (optind < argc) {
  215. X        PrintWordInfo(argv[optind++]);
  216. X    }
  217. X    }
  218. X    cleanupdb();
  219. X    /* close database files.  This is particularly important if we are
  220. X     * updating the database -- the horrible -D option -- but should
  221. X     * probably be done by liblqtext itself.
  222. X     */
  223. X    exit(SilentMode); /* 0 or 1 (this is a little devious) */
  224. X#ifdef lint
  225. X    /*NOTREACHED*/
  226. X    return 1;
  227. X    /* this is for versions of lint and gcc that don't understand
  228. X     * that exit() doesn't return -- or, if it douse, that there is
  229. X     * nothing that can be done about it!
  230. X     */
  231. X#endif
  232. X}
  233. X
  234. Xvoid
  235. XPrintWordInfo(Word)
  236. X    char *Word;
  237. X{
  238. X    extern t_WordInfo *FindWordInfoFromIndex();
  239. X    extern long atol();
  240. X    extern t_WID Word2WID();
  241. X    extern char *WordRoot();
  242. X
  243. X    register char *p;
  244. X    t_WordInfo *WordInfo;
  245. X    t_WID WID;
  246. X    t_WordInfo Root;
  247. X
  248. X    Root.WordPlace.Flags = 0;
  249. X
  250. X    /** Find the canonical form of the word, with plurals reduced to the
  251. X     ** singular and letters folded into lower case.
  252. X     **/
  253. X
  254. X    /* First, remember if the word originally started with an upper case
  255. X     * letter:
  256. X     */
  257. X    if (isupper(*Word)) {
  258. X    Root.WordPlace.Flags |= WPF_UPPERCASE;
  259. X    }
  260. X
  261. X    /* now convert to lower case and measure its length at the same time: */
  262. X    for (p = Word; *p; p++) {
  263. X    if (isupper(*p)) *p = tolower(*p);
  264. X    }
  265. X
  266. X    Root.Length = p - Word;
  267. X    Root.Word = Word;
  268. X
  269. X    /* Now call WordRoot() to find the canonical form: */
  270. X    Word = WordRoot(&Root);
  271. X
  272. X    /** Now see if the canonical word is too common to list: **/
  273. X
  274. X    if (TooCommon(&Root)) {
  275. X    /* It is listed in the common word list, so don't bother looking
  276. X     * it up at all
  277. X     */
  278. X    if (!SilentMode) {
  279. X        fprintf(stderr, "No index information for: %s (too common)\n",
  280. X                                    Word);
  281. X    }
  282. X    return;
  283. X    }
  284. X
  285. X    /** It is not too common, so look it up: **/
  286. X
  287. X    if (((WID = Word2WID(Word, Root.Length)) == (t_WID) 0) ||
  288. X    (WordInfo = WID2WordInfo(WID)) == (t_WordInfo *) 0) {
  289. X    if (!SilentMode) {
  290. X        if (WID) {
  291. X        /* In this case the word is in the database (since it has
  292. X         * a non-zero WID), but not in the word index.  This might
  293. X         * happen if the word is being deleted (or added) by someone
  294. X         * else at this very moment, or if the database is corrupt.
  295. X         */
  296. X        fprintf(stderr, "No index information for: %s (WID %lu)\n",
  297. X                                Word, WID);
  298. X        } else {
  299. X        /* In this case the word is neither listed as common nor
  300. X         * found in the database.  Either it was spelt differently
  301. X         * there or it isn't there at all.
  302. X         */
  303. X        fprintf(stderr, "No index information for: %s\n", Word);
  304. X        }
  305. X    }
  306. X    return;
  307. X    }
  308. X    if (SilentMode && WordInfo->NumberOfWordPlaces > 0) {
  309. X    /* We found something, so there is no point looking further --
  310. X     * we already know enough to exit.  If a lot of words are given,
  311. X     * this could be a big efficiency win.
  312. X     */
  313. X    exit(0);
  314. X    }
  315. X
  316. X    /** Now we have the database entry for the word, so let's print it!
  317. X     **/
  318. X    Display(WordInfo, DISPLAY_ALL);
  319. X
  320. X    /** Now return the storage used...
  321. X     **/
  322. X    if (WordInfo) {
  323. X    SlayWordInfo(WordInfo);
  324. X    }
  325. X
  326. X    /** All done for this word.
  327. X     **/
  328. X}
  329. X
  330. X/* Display() -- print information about a single word */
  331. Xvoid
  332. XDisplay(WordInfo, Verbose)
  333. X    t_WordInfo *WordInfo;
  334. X    int Verbose;
  335. X{
  336. X    char *Buf = emalloc(WordInfo->Length + 1);
  337. X
  338. X    /* Words in a t_WordInfo might not be null terminated, since the
  339. X     * storage overhead and the work of putting the nulls there might
  340. X     * be significant...
  341. X     */
  342. X    (void) strncpy(Buf, WordInfo->Word, WordInfo->Length);
  343. X    Buf[WordInfo->Length] = '\0';
  344. X
  345. X    if (!ListMode) {
  346. X    /* Print a little header for the word, unless we were asked not to */
  347. X    printf("%10lu | %7lu | %7lu | %s\n", WordInfo->WID,
  348. X                         WordInfo->Offset,
  349. X                         WordInfo->NumberOfWordPlaces,
  350. X                         WordInfo->Word
  351. X    );
  352. X                
  353. X    }
  354. X    if ((ListMode || Verbose == DISPLAY_ALL) && WordInfo->NumberOfWordPlaces) {
  355. X    /* If  there are occurrences in the database (there might not be if
  356. X     *     the word has been deleted, or has only just been added),
  357. X     * and  we want all the matches,
  358. X     * then  print the list of matches in the appropriate format:
  359. X     */
  360. X    ShowWordList(WordInfo);
  361. X    }
  362. X
  363. X    (void) efree(Buf);
  364. X    /* reclaim storage */
  365. X}
  366. X
  367. Xvoid
  368. XShowWordList(WordInfo)
  369. X    t_WordInfo *WordInfo;
  370. X{
  371. X    extern t_pblock *Getpblock();
  372. X    t_FileInfo *GetFileInfo();
  373. X
  374. X    t_FileInfo *FileInfo = (t_FileInfo *) 0;
  375. X    t_pblock *pblock = (t_pblock *) 0;
  376. X    t_WordPlace *PP = (t_WordPlace *) 0;
  377. X    int Place;
  378. X    char *LastRoot = "[internal error lqword.c 392]";
  379. X    /* the message is in case I make a coding error!.  The number
  380. X     * was once the line number of the message, but it only needs to
  381. X     * be a distinct enough message to search for.
  382. X     */
  383. X
  384. X    if (WordInfo->WordPlacesInHere >= WordInfo->NumberOfWordPlaces) {
  385. X    /* In this case, the match info all fits in the index, so it
  386. X     * does not matter if automatic pre-fetching from the overflow
  387. X     * file "data" happens or not (i.e. if we are using Lazy Evaluation,
  388. X     * it doesn't happen, but it makes no difference in this case).
  389. X     */
  390. X    PP = WordInfo->WordPlaces;
  391. X    } else if ((pblock = Getpblock(WordInfo)) != (t_pblock *) 0) {
  392. X    PP = pblock->WordPlaces;
  393. X    /* If Lazy Evaluation is enabled, liblqtext might not have fetched
  394. X     * all of the match information from the overflow database, in
  395. X     * which case we must do it now.
  396. X     */
  397. X    }
  398. X
  399. X    if (PP) {
  400. X    t_FID LastFID = USI_MAX;
  401. X        /* This is not a plausible FID (File IDentifier), so it
  402. X         * will force a call to GetFileInfo() in the loop below.
  403. X         */
  404. X    unsigned int LastFlags = 256 * 2;
  405. X        /* Similarly, this is an impossible flag value, since the
  406. X         * flags are constrained to fit in a single byte.
  407. X         */
  408. X
  409. X    /* cycle through the Place... */
  410. X    for (Place = 0; Place < WordInfo->NumberOfWordPlaces; Place++) {
  411. X
  412. X        char BIF[100]; char WIB[100];
  413. X        register char *p;
  414. X        char *Bp, *Wp;
  415. X        long l;
  416. X
  417. X        if (LastFlags != PP[Place].Flags) {
  418. X        LastFlags = PP[Place].Flags;
  419. X        LastRoot = UnFlag(WordInfo, LastFlags);
  420. X            /* UnFlag() takes a canonical (singular, lower-case)
  421. X             * word and a set of flags, and reverses the
  422. X             * transformations implied by the flags.  For example,
  423. X             * if WordInfo->Word is "boy" and flags contain the
  424. X             * Plural flag, you should get "boys" returned.
  425. X             * Since we don't remember whether a word was in all
  426. X             * caps or had only the first letter capitalised (at
  427. X             * the moment, anyway), the routine will return Boys
  428. X             * even if the input was BOYS or BoYs.
  429. X             * Possessives (the boy's books) may also be indicated.
  430. X             */
  431. X        }
  432. X
  433. X        if (LastFID != PP[Place].FID || FileInfo == (t_FileInfo *) 0) {
  434. X        /* The first part of the test means we don't call the
  435. X         * function to retrieve the file name lots of times if
  436. X         * there are multiple matches in the same data file. 
  437. X         * This turns out to be a common case.
  438. X         */
  439. X
  440. X        /* Reclaim storage */
  441. X        if (FileInfo) {
  442. X            if (FileInfo->Name) {
  443. X            (void) efree(FileInfo->Name);
  444. X            }
  445. X            (void) efree(FileInfo);
  446. X        }
  447. X
  448. X        /* Find the file name from the FID.  This routine should
  449. X         * be called FID2FileName(), and may in fact be renamed
  450. X         * in the future.
  451. X         */
  452. X        if ((FileInfo = GetFileInfo(LastFID = PP[Place].FID)) ==
  453. X                        (t_FileInfo *) 0) {
  454. X            /* No filename information available.  This sometimes
  455. X             * happens if you rin lqword diring an lqaddfile
  456. X             * session and match a word in one of the new files.
  457. X             * Note that if the output is for reuse, we don't
  458. X             * want to include references to files whose names
  459. X             * we don't have!
  460. X             */
  461. X            if (!ListMode) {
  462. X            printf("%20s | %-.5lu/%-.3lu | [FID %d]\n",
  463. X                LastRoot,
  464. X                PP[Place].BlockInFile,
  465. X                PP[Place].WordInBlock,
  466. X                PP[Place].FID);
  467. X            }
  468. X            continue;
  469. X        }
  470. X        }
  471. X
  472. X        /* This is an inline printf, because otherwise this call
  473. X         * to printf takes over 20% of the execution time, and nearly
  474. X         * 40% for a frequent word (e.g. over 1000 places) !!
  475. X         */
  476. X        p = &BIF[sizeof(BIF) - 1];
  477. X        *p = '\0';
  478. X        if (PP[Place].BlockInFile == 0) {
  479. X        *--p = '0';
  480. X        } else for (l = PP[Place].BlockInFile; l; l /= 10) {
  481. X        *--p = "0123456789"[l % 10];
  482. X        }
  483. X        Bp = p;
  484. X
  485. X        p = &WIB[sizeof(WIB) - 1];
  486. X        *p = '\0';
  487. X        {
  488. X        register int i = PP[Place].WordInBlock;
  489. X        if (i == 0) {
  490. X            *--p = '0';
  491. X        } else for (; i; i /= 10) {
  492. X            *--p = "0123456789"[i % 10];
  493. X        }
  494. X        Wp = p;
  495. X        }
  496. X
  497. X          if (ListMode) {
  498. X        while (*Bp) {
  499. X            putchar(*Bp);
  500. X            Bp++;
  501. X        }
  502. X        putchar(' ');
  503. X        while (*Wp) {
  504. X            putchar(*Wp);
  505. X            Wp++;
  506. X        }
  507. X        putchar(' ');
  508. X        puts(FileInfo->Name);
  509. X          } else {
  510. X        /* Well, if we are not reusing the output, maybe the speed
  511. X         * is not quite so critical...
  512. X         */
  513. X          printf("%20s | %5lu/%3lu F=%3u S=%3u | %s\n",
  514. X            LastRoot,
  515. X            PP[Place].BlockInFile,
  516. X            PP[Place].WordInBlock,
  517. X            PP[Place].Flags, /* XXX */
  518. X            PP[Place].StuffBefore,
  519. X            FileInfo->Name);
  520. X        }
  521. X    }
  522. X    }
  523. X
  524. X    if (pblock) {
  525. X    /* If we had to go and get the matches ourselves, we had better
  526. X     * release the storage.
  527. X     * Actually we should also be freeing the FileInfo and possibly
  528. X     * the WordInfo as well, but the pblock is the biggest... and I
  529. X     * am only adding comments today, not fixing code (I hope)...
  530. X     * NOTDONE FIXME
  531. X     */
  532. X    (void) efree(pblock);
  533. X    }
  534. X}
  535. X
  536. Xvoid
  537. XAllWordInfo(Verbose)
  538. X    int Verbose;
  539. X{
  540. X    extern char *WID2Word();
  541. X    extern t_WID GetMaxWID();
  542. X
  543. X    t_WID i;
  544. X    t_WID MaxWid = GetMaxWID();
  545. X    t_WordInfo *WordInfo;
  546. X    char *Name;
  547. X
  548. X    /* Loop over all possible WID numbers and print information
  549. X     * for each of them.
  550. X     */
  551. X    for (i = (t_WID) 1; i <= MaxWid; i++) {
  552. X    if ((Name = WID2Word(i)) != (char *) 0) {
  553. X
  554. X        /* If Name is zero, that WID is unused.  There might be gaps
  555. X         * if a word was deleted.
  556. X         */
  557. X
  558. X        if ((WordInfo = WID2WordInfo(i)) != (t_WordInfo *) 0) {
  559. X        Display(WordInfo, Verbose);
  560. X        SlayWordInfo(WordInfo);
  561. X        } else {
  562. X        /* In this case the word is known, but there is no further
  563. X         * information about it.  In the current inplementation,
  564. X         * this cannot happen unless someone else is updating the
  565. X         * database and replacing a WID whose word had been deleted.
  566. X         */
  567. X        if (!ListMode) {
  568. X            /* If we are in list mode, it is probably because the
  569. X             * output is wanted by another prpgram, so we had
  570. X             * better not print out this (useless) entry.
  571. X             */
  572. X            printf("%10lu | %7lu |           | ?? %s\n",
  573. X            i, 0L, Name);
  574. X        }
  575. X        }
  576. X
  577. X        /* Reclaim the storage used... */
  578. X        (void) efree(Name);
  579. X    } /* end if */
  580. X    } /* for each WID */
  581. X
  582. X    if (!ListMode) {
  583. X    printf("Maximum WID is %lu\n", MaxWid);
  584. X    }
  585. X}
  586. X
  587. X/* dbmmarch -- print every value in a dbm database.  This might go
  588. X * wrong (omitting some values) if the database is being concurrently
  589. X * updated.
  590. X */
  591. Xvoid
  592. Xdbmmarch()
  593. X{
  594. X    DBM *db;
  595. X    datum d;
  596. X
  597. X    if ((db = startdb(WordIndex)) == (DBM *) 0) {
  598. X    /* WordIndex is the list of words, defined in "globals.h".
  599. X     * If we didn't open it, the user probably has not set
  600. X     * $LQTEXTDIR, or didn't use the -d database-dir option that
  601. X     * is handled bu SetDefaults() called from main().
  602. X     */
  603. X    fprintf(stderr, "Can't open database file \"%s\"\n", WordIndex);
  604. X    exit(1);
  605. X    }
  606. X
  607. X    /* The word database contains WID-->word matches, that look like
  608. X     * (key = "Word", content = WID)
  609. X     */
  610. X    for (d = dbm_firstkey(db); d.dsize != 0; d = dbm_nextkey(db)) {
  611. X    register char *s;
  612. X
  613. X    /* IMPORTANT NOTE:
  614. X     * The words are not nul-terminated in the database.  It is
  615. X     * therefore not safe to use printf() or puts() unless we make
  616. X     * a copy or are careful...
  617. X     */
  618. X    for (s = d.dptr; s - d.dptr < d.dsize; s++) {
  619. X        putchar(*s);
  620. X    }
  621. X    putchar('\n');
  622. X    }
  623. X    enddb(db);
  624. X}
  625. X
  626. X/*
  627. X * $Log:    lqword.c,v $
  628. X * Revision 2.8  90/10/06  00:51:00  lee
  629. X * Prepared for first beta release.
  630. X * 
  631. X * Revision 2.7  90/08/29  21:45:37  lee
  632. X * Alpha release
  633. X * 
  634. X * Revision 2.6  90/08/08  22:22:53  lee
  635. X * Added heavy comments.  Cleaned up dbmmarch() and made some other
  636. X * minor fixes.
  637. X * 
  638. X * Revision 2.5  90/08/08  21:06:21  lee
  639. X * Added -x option; removed rude message about getpts bugs.
  640. X * 
  641. X * Revision 2.4  90/04/21  18:50:38  lee
  642. X * fixed a serious bug in the -l mode -- now prints the entire match!
  643. X * 
  644. X * Revision 2.3  90/03/27  13:20:57  lee
  645. X * now passes gcc -Wall
  646. X * 
  647. X * Revision 2.2  89/10/08  20:47:23  lee
  648. X * Working version of nx-text engine.  Addfile and wordinfo work OK.
  649. X * 
  650. X * Revision 2.1  89/10/02  01:16:10  lee
  651. X * New index format, with Block/WordInBlock/Flags/BytesSkipped info.
  652. X * 
  653. X * Revision 1.3  89/09/17  23:04:42  lee
  654. X * Various fixes; NumberInBlock now a short...
  655. X * 
  656. X * Revision 1.2  89/09/16  21:18:50  lee
  657. X * First demonstratable version.
  658. X * 
  659. X * Revision 1.1  89/09/07  21:06:14  lee
  660. X * Initial revision
  661. X * 
  662. X */
  663. @@@End of lq-text/src/lqtext/lqword.c
  664. echo x - lq-text/src/lqtext/matchword.sh 1>&2
  665. sed 's/^X//' >lq-text/src/lqtext/matchword.sh <<'@@@End of lq-text/src/lqtext/matchword.sh'
  666. X:
  667. X# matchword pattern [...] -- grep for words in the database
  668. X#
  669. X# matchword -- Copyright 1990 Liam R. Quin.  All Rights Reserved.
  670. X# This code is NOT in the public domain.
  671. X# See the file ../COPYRIGHT for full details.
  672. X#
  673. X# $Id: matchword.sh,v 1.2 90/10/06 00:51:02 lee Rel1-10 $
  674. X#
  675. X
  676. X# "echo" portability test:
  677. XN=; C='\c'; if [ x"`echo -n hello`" = x"hello" ]; then N=-n;C=; fi
  678. Xexport N C
  679. X
  680. Xans=no
  681. Xwhile [ x"$ans" != x"q" ]
  682. Xdo
  683. X    echo $N "Enter a word or pattern: $C"
  684. X    read pattern
  685. X    if [ x"$pattern" = x"q" ]
  686. X    then
  687. X    break
  688. X    fi
  689. X    WORDS=`lqword | grep "^${pattern}\$"`
  690. X    if [ "$WORDS" = "" ]
  691. X    then echo "(no match in the database for ${pattern})"
  692. X    else echo `echo "$WORDS" | wc -l` words found:
  693. X     echo "$WORDS" | sort -d | rs | ${PAGER-more}
  694. X     # If you don't have rs, you could use cat instead.
  695. X     # PAGER could also be "pg -nse", or "less -q".
  696. X    fi
  697. Xdone
  698. X
  699. @@@End of lq-text/src/lqtext/matchword.sh
  700. echo x - lq-text/src/lqtext/sizes.c 1>&2
  701. sed 's/^X//' >lq-text/src/lqtext/sizes.c <<'@@@End of lq-text/src/lqtext/sizes.c'
  702. X/* sizes.c -- Copyright 1990 Liam R. Quin.  All Rights Reserved.
  703. X * This code is NOT in the public domain.
  704. X * See the file COPYRIGHT for full details.
  705. X */
  706. X
  707. X#ifndef lint
  708. X static char *Rcs = "$Id: sizes.c,v 1.3 90/10/06 00:51:03 lee Rel1-10 $";
  709. X#endif
  710. X
  711. X#include "globals.h" /* defines and declarations for database filenames */
  712. X
  713. X#include <stdio.h>
  714. X#include <sys/types.h>
  715. X#include "fileinfo.h"
  716. X#include "wordinfo.h"
  717. X#include "pblock.h"
  718. X#include "wordrules.h"
  719. X#include "wordindex.h"
  720. X
  721. Xmain()
  722. X{
  723. X    printf("FileInfo  %u bytes\n", sizeof(t_FileInfo));
  724. X    printf("WordInfo  %u bytes\n", sizeof(t_WordInfo));
  725. X    printf("WordPlace %u bytes\n", sizeof(t_WordPlace));
  726. X    printf("pblock    %u bytes\n", sizeof(t_pblock));
  727. X}
  728. @@@End of lq-text/src/lqtext/sizes.c
  729. echo x - lq-text/src/lqtext/wordtable.c 1>&2
  730. sed 's/^X//' >lq-text/src/lqtext/wordtable.c <<'@@@End of lq-text/src/lqtext/wordtable.c'
  731. X/* wordtable.c -- Copyright 1989, 1990 Liam R. Quin.  All Rights Reserved.
  732. X * This code is NOT in the public domain.
  733. X * See the file ../COPYRIGHT for full details.
  734. X */
  735. X
  736. X/* Symbol Table Interface to text retrieval database.
  737. X * Handles both the internal and external indexes.
  738. X *
  739. X * This originally used a linked list.  Converting to a hash table reduced
  740. X * the time to index comp.os.vms from nearly an hour to one and a half
  741. X * minutes...
  742. X *
  743. X * Liam Quin, 1989
  744. X */
  745. X
  746. X/* 
  747. X * $Id: wordtable.c,v 2.11 91/02/20 19:07:37 lee Rel1-10 $
  748. X */
  749. X
  750. X#ifndef lint
  751. X static char *Rcs = "$Id: wordtable.c,v 2.11 91/02/20 19:07:37 lee Rel1-10 $";
  752. X#endif
  753. X
  754. X#include "globals.h" /* defines and declarations for database filenames */
  755. X
  756. X#ifdef SYSV
  757. Xextern int _filbuf();
  758. X#endif
  759. X#include <stdio.h>
  760. X#include <malloc.h>
  761. X#include <ctype.h>
  762. X#include <sys/types.h>
  763. X#include <fcntl.h> /* for O_RDWR wtc */
  764. X#include "smalldb.h"
  765. X#include "fileinfo.h"
  766. X#include "wordinfo.h"
  767. X#include "pblock.h"
  768. X#include "wordrules.h"
  769. X#include "emalloc.h"
  770. X
  771. X#define HASHSIZ 32768 /* MUST be a power of two */
  772. X
  773. X#ifndef MAXWORDSINCACHE
  774. X# define MAXWORDSINCACHE  (HASHSIZ * 10)
  775. X#endif
  776. Xint MaxWordsInCache = MAXWORDSINCACHE;
  777. X
  778. Xextern int AsciiTrace;
  779. X
  780. X/* useful macros */
  781. X#define NumberOfElements(array, type) (sizeof(array)/sizeof(type))
  782. X#define STRCMP(a,b) ((*(a) > *(b)) ? 1 : ((*(a) < *(b)) ? -1 : strcmp(a,b)) )
  783. X/* #define Hash(WordInfo) \
  784. X *    (dbm_hash(WordInfo->Word, WordInfo->Length) % HashSize)
  785. X */
  786. X
  787. X/** System calls and library functions used in this file: **/
  788. X
  789. X/** Lqtext calls */
  790. Xextern unsigned int Putpblock();
  791. Xextern void DeleteWordPlaces();
  792. X
  793. X/** System calls: */
  794. X
  795. X/** Library Functions: */
  796. Xextern char *strncpy();
  797. Xextern int strcmp();
  798. Xextern void perror();
  799. Xextern void exit();
  800. X/**/
  801. X
  802. X#define enew(var, type) (var = (type *) emalloc(sizeof (type)))
  803. X
  804. Xextern char *progname;
  805. Xstatic int HashSize = HASHSIZ; /* MUST be a power of two */
  806. X
  807. X#ifdef NEWSYM
  808. X
  809. X#define NPLACES 7
  810. X/* THis is small to optimise the common case -- by far the majority of
  811. X * words are used less than 10 times.  In the cases where we've gone
  812. X * wrong, well, there'll be a few thousand.
  813. X */
  814. X
  815. Xtypedef struct s_HashEl {
  816. X    char *Word;
  817. X    t_WID WID;
  818. X    int PlacesUsed;
  819. X    t_WordPlace Places[NPLACES];
  820. X    struct s_HashEl *Next;
  821. X} t_HashEl;
  822. X
  823. Xstatic t_HashEl *SymbolTable;
  824. Xstatic t_HashEl *LastEl;
  825. Xstatic int WordsInCache = 0;
  826. X
  827. XStartHash()
  828. X{
  829. X    if (MaxWordsInCache) HashSize = MaxWordsInCache / 16;
  830. X    SymbolTable = (t_HashEl *) emalloc(sizeof(t_HashEl) * HashSize);
  831. X    /* Note that we only need to initialise the Word pointers... */
  832. X    for (LastEl = SymbolTable; LastEl != &SymbolTable[HashSize]; LastEl++) {
  833. X    LastEl->Word = (char *) 0;
  834. X    }
  835. X    /* ASSERT: LastEl == &SymbolTable[HashSize] */
  836. X    MaxWordsInCache = HashSize;
  837. X}
  838. X
  839. XSetElEmpty(El)    /* Initialisation function for Hash Elements */
  840. X    t_HashEl *El;
  841. X{
  842. X    El->Word = (char *) 0;
  843. X    El->WID = (t_WID) -1;
  844. X        /* NOT zero, so we can distinguish between unknown and
  845. X         * "haven't looked"
  846. X         */
  847. X    El->PlacesUsed = 0;
  848. X    El->Next = (t_HashEl *) 0;
  849. X}
  850. X
  851. Xvoid DumpCache();
  852. X
  853. Xvoid
  854. XAddWord(WordInfo)
  855. X    t_WordInfo *WordInfo;
  856. X{
  857. X    register t_HashEl *HashEl;
  858. X    int Slot;
  859. X    t_HashEl *FirstEl;
  860. X
  861. X    if (!WordInfo || !WordInfo->Word || !WordInfo->Word[0]) {
  862. X    (void) fprintf(stderr, "%s: warning: Null Word in AddWord\n", progname);
  863. X    return;
  864. X    }
  865. X
  866. X    if (!LastEl) {
  867. X    StartHash();
  868. X    } else if (MaxWordsInCache && ++WordsInCache > MaxWordsInCache) {
  869. X    DumpCache(1);
  870. X    }
  871. X
  872. X    if (WordInfo->Word[0] == 'q') {
  873. X    register char *xp;
  874. X
  875. X    for (xp = &WordInfo->Word[1]; *xp && *xp == 'x'; xp++) {
  876. X        /*NULLBODY*/
  877. X    }
  878. X    if (!*xp) {
  879. X        if (AsciiTrace >= 10) {
  880. X        (void) fprintf(stderr, "Discard %d\n", WordInfo->Word);
  881. X        }
  882. X        return;
  883. X    }
  884. X    }
  885. X
  886. X    Slot = Hash(WordInfo);
  887. X    FirstEl = HashEl = &SymbolTable[Slot];
  888. X
  889. X
  890. X    for (;;) {
  891. X    if (!HashEl->Word) {
  892. X        extern char *strcpy();
  893. X        extern t_WID Word2WID();
  894. X
  895. X        if (AsciiTrace > 9) {
  896. X        (void) fprintf(stderr, "New ", WordInfo->Word);
  897. X        }
  898. X        /* make a new element */
  899. X        SetElEmpty(HashEl);
  900. X        HashEl->Word = emalloc(WordInfo->Length + 1);
  901. X        (void) strcpy(HashEl->Word, WordInfo->Word);
  902. X        /**
  903. X        HashEl->WID = (t_WID) -1;
  904. X        **/
  905. X        HashEl->WID = Word2WID(HashEl->Word, WordInfo->Length);
  906. X        /** **/
  907. X        break;
  908. X    } else if (STREQ(HashEl->Word, WordInfo->Word)) {
  909. X        break;
  910. X    }
  911. X
  912. X    if (++HashEl == LastEl) HashEl = SymbolTable;
  913. X
  914. X    if (HashEl == FirstEl) {
  915. X        /* We need to dump the cache and start again */
  916. X        DumpCache(1);
  917. X        AddWord(WordInfo);
  918. X        return;
  919. X    }
  920. X    }
  921. X    /* If we get here, all we need to do is add the WordPlace */
  922. X    if (AsciiTrace > 9) {
  923. X    (void) fprintf(stderr, "AddWord %s\n", WordInfo->Word);
  924. X    }
  925. X    FirstEl = HashEl;
  926. X
  927. X    while (HashEl->PlacesUsed >= NPLACES && HashEl->Next != (t_HashEl *) 0) {
  928. X    HashEl = HashEl->Next;
  929. X    }
  930. X
  931. X    if (HashEl->PlacesUsed >= NPLACES) {
  932. X    t_HashEl *New;
  933. X
  934. X    New = (t_HashEl *) malloc(sizeof(t_HashEl));
  935. X    SetElEmpty(New);
  936. X
  937. X    New->Next = FirstEl->Next;
  938. X    FirstEl->Next = HashEl = New;
  939. X    }
  940. X    HashEl->Places[HashEl->PlacesUsed] = WordInfo->WordPlace; /* structure copy */
  941. X    HashEl->PlacesUsed++;
  942. X    return;
  943. X}
  944. X
  945. Xvoid
  946. XDumpCache(CallFree)
  947. X    int CallFree;
  948. X{
  949. X    register t_HashEl *HashEl, *MeNext;
  950. X    int Progress = 0;
  951. X
  952. X    for (HashEl = SymbolTable; HashEl != LastEl; HashEl++) {
  953. X    if (HashEl->Word) {
  954. X        extern t_WordInfo *MakeWordInfo();
  955. X        unsigned len;
  956. X        t_WordInfo *WP;
  957. X
  958. X        /* We are going to make a new index entry for the word.
  959. X         * There are two cases -- depending on whether the word
  960. X         * is already indexed or not.
  961. X         * In the former case we must merge the new information.
  962. X         * In the latter case we don't have to read the old info,
  963. X         * but we must make a new entry in the WID Index.
  964. X         */
  965. X
  966. X        len = strlen(HashEl->Word);
  967. X        if (HashEl->WID == (t_WID) -1) {
  968. X        HashEl->WID = Word2WID(HashEl->Word, len);
  969. X        }
  970. X        WP = MakeWordInfo(HashEl->WID, len, HashEl->Word);
  971. X
  972. X        if (HashEl->WID == (t_WID) 0) {
  973. X        NewEntry(HashEl, WP);
  974. X        } else {
  975. X        UpdateEntry(HashEl, WP);
  976. X        }
  977. X        /* Reclaim storage */
  978. X        if (CallFree) {
  979. X        extern void SlayWordInfo();
  980. X        register t_HashEl *FreeMe = HashEl;
  981. X
  982. X        (void) SlayWordInfo(WP);
  983. X
  984. X        efree(HashEl->Word);
  985. X        FreeMe->Word = (char *) 0;
  986. X        FreeMe = FreeMe->Next; /* don't do the first one */
  987. X        while (FreeMe) {
  988. X            MeNext = FreeMe->Next;
  989. X            (void) efree((char *) FreeMe);
  990. X            FreeMe = MeNext;
  991. X        }
  992. X        }
  993. X    }
  994. X    if (AsciiTrace > 1) {
  995. X        if (HashEl - SymbolTable >= Progress * (HashSize / 16)) {
  996. X        fputc(" 01234567890ABCDEFGHIJKL"[Progress], stderr);
  997. X        ++Progress;
  998. X        }
  999. X    }
  1000. X    }
  1001. X    WordsInCache = 0;
  1002. X}
  1003. X
  1004. XNewEntry(HashEl, WP)
  1005. X    t_HashEl *HashEl;
  1006. X    t_WordInfo *WP;
  1007. X{
  1008. X    extern t_WID GetNextWID();
  1009. X    t_pblock *pblock;
  1010. X    long MatchCount;
  1011. X    t_HashEl *Ep;
  1012. X
  1013. X    /** Assign a new WID */
  1014. X    WP->WID = GetNextWID();
  1015. X
  1016. X    /** make a WIDIndex entry and mark it as invalid (NOTDONE) */
  1017. X
  1018. X    /* In order to do this, we must make a "pblock", a structure that
  1019. X     * reflects the physical database.  This is fairly low-level stuff
  1020. X     * for efficiency's sake...
  1021. X     */
  1022. X
  1023. X    /* count the total number of entries we're adding: */
  1024. X    for (Ep = HashEl, MatchCount = 0; Ep; Ep = Ep->Next) {
  1025. X    MatchCount += Ep->PlacesUsed;
  1026. X    }
  1027. X
  1028. X    /* allocate a pblock structure.  These are rather devious things, a
  1029. X     * structure with an array tacked onto the end.
  1030. X     */
  1031. X    pblock = (t_pblock *) emalloc(sizeof(t_pblock) +
  1032. X                MatchCount * sizeof(t_WordPlace));
  1033. X    
  1034. X    pblock->WID = WP->WID;
  1035. X    pblock->ChainStart = 0L; /* address on disk -- not there yet, so 0! */
  1036. X    pblock->NumberOfWordPlaces = WP->NumberOfWordPlaces = MatchCount;
  1037. X
  1038. X    /* fill in the WordPlaces */
  1039. X    for (Ep = HashEl, MatchCount = 0; Ep; Ep = Ep->Next) {
  1040. X    register int i;
  1041. X
  1042. X    for (i = 0; i < Ep->PlacesUsed; i++) {
  1043. X        pblock->WordPlaces[MatchCount++] = Ep->Places[i]; /* struct copy */
  1044. X    }
  1045. X    }
  1046. X
  1047. X    /* Now fill in enough of WP to let us use the low-level routines: */
  1048. X    WP->FID = (t_FID) 0;
  1049. X    WP->Next = (t_WordInfo *) 0;
  1050. X    WP->DataBlock = (char *) 0;
  1051. X    WP->WordPlaceStart = (char *) 0;
  1052. X    WP->WordPlaces = (t_WordPlace *) 0;
  1053. X    WP->WordPlacesInHere = 0;
  1054. X    WP->WordPlace.FID = 0;
  1055. X    WP->WordPlace.Flags = 0;
  1056. X    WP->Offset = 0;
  1057. X
  1058. X    /* First, let's make an index entry: */
  1059. X#ifndef MaxWordPlacesInAWordBlock
  1060. X# define MaxWordPlacesInAWordBlock ((WIDBLOCKSIZE-(WP->Length+2)/3))
  1061. X#endif
  1062. X    if (pblock->NumberOfWordPlaces <= MaxWordPlacesInAWordBlock) {
  1063. X    (void) MkWIB(WP, pblock);
  1064. X    }
  1065. X
  1066. X    /** write out the new entry */
  1067. X    if (WP->WordPlacesInHere == pblock->NumberOfWordPlaces) {
  1068. X    /* In this case it all fits into the main index */
  1069. X    if (PutWordInfoIntoIndex(WP, (unsigned long) 0L) < 0) {
  1070. X        extern int errno;
  1071. X        int e = errno;
  1072. X        fprintf(stderr, "%s: Couldn't insert word \"%s\" into the index",
  1073. X                progname, WP->Word);
  1074. X        perror("");
  1075. X        exit(1);
  1076. X    }
  1077. X    } else {
  1078. X    (void) Putpblock(WP, pblock);
  1079. X    if (PutWordInfoIntoIndex(WP, pblock->ChainStart) < 0) {
  1080. X        extern int errno;
  1081. X        int e = errno;
  1082. X        fprintf(stderr, "%s: Couldn't re-insert word \"%s\" into the index",
  1083. X                progname, WP->Word);
  1084. X        perror("");
  1085. X        exit(1);
  1086. X    }
  1087. X    }
  1088. X
  1089. X    /** mark it as valid (NOTDONE) */
  1090. X
  1091. X    /** reclaim storage */
  1092. X    (void) efree((char *) pblock);
  1093. X    /* the caller *must* do SlayWordInfo(WP) */
  1094. X}
  1095. X
  1096. XUpdateEntry(HashEl, WP)
  1097. X    t_HashEl *HashEl;
  1098. X    t_WordInfo *WP;
  1099. X{
  1100. X    extern t_pblock *Getpblock();
  1101. X    extern t_WordInfo *WID2WordInfo();
  1102. X    t_pblock *pblock;
  1103. X    long MatchCount;
  1104. X    t_HashEl *Ep;
  1105. X    t_WordInfo *Wpp;
  1106. X
  1107. X    /** Mark the old entry as invalid (NOTDONE) */
  1108. X
  1109. X    /** get the old entry */
  1110. X    if ((Wpp = WID2WordInfo(WP->WID)) == (t_WordInfo *) 0) {
  1111. X    /* someone else has just deleted it! */
  1112. X    NewEntry(HashEl, WP);
  1113. X    return;
  1114. X    }
  1115. X    /* It would be best if we could append to the old entry... which is what
  1116. X     * I had in mind when I designed the disk storage stuff... but you can't.
  1117. X     */
  1118. X    pblock = Getpblock(Wpp);
  1119. X
  1120. X    /** merge the old and new entries */
  1121. X
  1122. X    /* count the total number of entries we're adding: */
  1123. X    for (Ep = HashEl, MatchCount = 0; Ep; Ep = Ep->Next) {
  1124. X    MatchCount += Ep->PlacesUsed;
  1125. X    }
  1126. X
  1127. X    pblock = (t_pblock *) erealloc((char *) pblock, sizeof(t_pblock) +
  1128. X         (Wpp->NumberOfWordPlaces + MatchCount) * sizeof(t_WordPlace));
  1129. X
  1130. X    /* delete the old entry from disk */
  1131. X    if (Wpp->Offset) {
  1132. X    DeleteWordPlaces(Wpp->Offset, Wpp->WID);
  1133. X    }
  1134. X
  1135. X    /* fill in the WordPlaces */
  1136. X    for (Ep = HashEl, MatchCount = 0; Ep; Ep = Ep->Next) {
  1137. X    register int i;
  1138. X
  1139. X    for (i = 0; i < Ep->PlacesUsed; i++) {
  1140. X        pblock->WordPlaces[pblock->NumberOfWordPlaces++] =
  1141. X                    Ep->Places[i]; /* struct copy */
  1142. X    }
  1143. X    }
  1144. X
  1145. X    Wpp->Offset = 0L; /* it's invalid now... */
  1146. X    Wpp->WordPlacesInHere = 0;
  1147. X
  1148. X    /* First, let's make an index entry: */
  1149. X    if (pblock->NumberOfWordPlaces <= MaxWordPlacesInAWordBlock) {
  1150. X    (void) MkWIB(WP, pblock);
  1151. X    }
  1152. X
  1153. X    /** write out the new entry */
  1154. X    if (Wpp->WordPlacesInHere == pblock->NumberOfWordPlaces) {
  1155. X    /* In this case it all fits into the main index */
  1156. X    if (PutWordInfoIntoIndex(Wpp, (unsigned long) 0L) < 0) {
  1157. X        extern int errno;
  1158. X        int e = errno;
  1159. X        fprintf(stderr, "%s: Couldn't insert word \"%s\" into the index",
  1160. X                progname, Wpp->Word);
  1161. X        perror("");
  1162. X        exit(1);
  1163. X    }
  1164. X    } else {
  1165. X    (void) Putpblock(Wpp, pblock);
  1166. X    if (PutWordInfoIntoIndex(Wpp, pblock->ChainStart) < 0) {
  1167. X        extern int errno;
  1168. X        int e = errno;
  1169. X        fprintf(stderr, "%s: Couldn't re-insert word \"%s\" into the index",
  1170. X                progname, Wpp->Word);
  1171. X        perror("");
  1172. X        exit(1);
  1173. X    }
  1174. X    }
  1175. X
  1176. X    /** mark it as valid (NOTDONE) */
  1177. X
  1178. X    /** reclaim storage */
  1179. X    (void) efree((char *)pblock);
  1180. X    /* the caller *must* do SlayWordInfo(WP) */
  1181. X    (void) SlayWordInfo(Wpp);
  1182. X}
  1183. X
  1184. X#else /* NEWSYM */
  1185. Xstatic t_WordPlaceList *SymbolTable[HASHSIZ]; /* static --> initialised to 0 */
  1186. X#endif /* NEWSYM */
  1187. X
  1188. X#ifdef __GNU__
  1189. Xinline
  1190. X#endif
  1191. X#ifndef Hash
  1192. Xint
  1193. XHash(WordInfo)
  1194. X    t_WordInfo *WordInfo;
  1195. X{
  1196. X    register unsigned long n = 0;
  1197. X    register int len = WordInfo->Length;
  1198. X    register char *str = WordInfo->Word;
  1199. X
  1200. X#ifdef DUFF /* clever stuff for speedup... dmr-approved!... */
  1201. X
  1202. X#define HASHC    n = *str++ + 65599 * n
  1203. X
  1204. X    if (len > 0) {
  1205. X    register int loop = (len + 8 - 1) >> 3;
  1206. X
  1207. X    switch(len & (8 - 1)) {
  1208. X    case 0:    do {
  1209. X        HASHC;    case 7:    HASHC;
  1210. X    case 6:    HASHC;    case 5:    HASHC;
  1211. X    case 4:    HASHC;    case 3:    HASHC;
  1212. X    case 2:    HASHC;    case 1:    HASHC;
  1213. X        } while (--loop);
  1214. X    }
  1215. X
  1216. X    }
  1217. X#else /* DUFF */
  1218. X    while (len--)
  1219. X    n = *str++ + 65599 * n;
  1220. X#endif /* DUFF */
  1221. X    /**
  1222. X    return n & (HashSize - 1);
  1223. X    **/
  1224. X    return n % HashSize;
  1225. X}
  1226. X#endif
  1227. X
  1228. Xstatic int HashOK = 0;
  1229. X
  1230. Xvoid
  1231. XInitHash()
  1232. X{
  1233. X    HashOK = 1;
  1234. X}
  1235. X
  1236. X#ifndef NEWSYM
  1237. Xstatic int WordsInCache = 0;
  1238. X
  1239. X/* FIXME: this ought to taks a WordInfo and a WordPlaceList instead.
  1240. X * Using a hash table means that we can end up with really pathalogical
  1241. X * paging pehaviour.  Nearly all of lqaddfile is resident when running
  1242. X * on a Sun.  Hence, I shall be replacing this code entirely soon with
  1243. X * something that has less memory fragmentation, perhaps by coalescing
  1244. X * list members or with a tree.
  1245. X * For now, MaxWordsInCache is a parameter that you can set to zero if
  1246. X * you want.
  1247. X *
  1248. X * Also, the cache structure should be cleaver enough to avoid writing
  1249. X * out the more common words if it can, so as to minimise the number
  1250. X * of data _fetches_ that have to be done.
  1251. X * You could also argue that it should be more efficient to add new data,
  1252. X * of course.  I couldn't disagree.
  1253. X *
  1254. X * Next change required is to make AddWord do a little more of the work --
  1255. X * in particular, to call Word2WID for each new word, in an attempt to
  1256. X * make cache dumping faster.
  1257. X */
  1258. X
  1259. XAddWord(WordInfo) /* old version */
  1260. X    t_WordInfo *WordInfo;
  1261. X{
  1262. X    int Slot;
  1263. X    int GreaterOrLess = 1;
  1264. X    t_WordPlaceList *SaveOldNext;
  1265. X    t_WordPlaceList **WPL;
  1266. X
  1267. X    if (!HashOK) InitHash();
  1268. X
  1269. X    /* The following are all awfully serious internal errors.
  1270. X     * They will only happen if I make a huge coding error, whereupon
  1271. X     * they tend to happen for every word in the input...
  1272. X     */
  1273. X    if (!WordInfo) {
  1274. X    fprintf(stderr, "AddWord(0)\n");
  1275. X    return;
  1276. X    } else if (!WordInfo->Word) {
  1277. X    fprintf(stderr, "AddWord(Word=0)\n");
  1278. X    return;
  1279. X    } else if (!WordInfo->Word[0]) {
  1280. X    fprintf(stderr, "AddWord(Word[0]=0)\n");
  1281. X    return;
  1282. X#ifdef ASCIITRACE
  1283. X    } else if (AsciiTrace > 20) {
  1284. X    fprintf(stderr, "[%s.len %d]\n", WordInfo->Word, WordInfo->Length);
  1285. X#endif
  1286. X    }
  1287. X
  1288. X    Slot = Hash(WordInfo);
  1289. X
  1290. X#ifdef ASCIITRACE
  1291. X    if (AsciiTrace > 10) {
  1292. X    fprintf(stderr, "H %d %s\n", Slot, WordInfo->Word);
  1293. X    }
  1294. X#endif
  1295. X
  1296. X    if (WordInfo->Word[0] == 'q') {
  1297. X    register char *p = WordInfo->Word;
  1298. X
  1299. X    /* Words of the form qxxxxx* are not indexed.  This is so the filters
  1300. X     * can preprocess the files without upsetting the word counts.
  1301. X     * If you can think of a better way to do this, well, tell me!
  1302. X     * Lee
  1303. X     */
  1304. X    
  1305. X    for (++p; p - WordInfo->Word < WordInfo->Length; p++) {
  1306. X        if (*p != 'x') break;
  1307. X    }
  1308. X
  1309. X    if (p - WordInfo->Word == WordInfo->Length) {
  1310. X#ifdef ASCIITRACE
  1311. X        if (AsciiTrace > 10) {
  1312. X        (void) fprintf(stderr, "rejected %s (too boring)\n",
  1313. X                    WordInfo->Word);
  1314. X        }
  1315. X#endif
  1316. X        return;
  1317. X    }
  1318. X    }
  1319. X
  1320. X    for (WPL = &SymbolTable[Slot]; *WPL; WPL = &((*WPL)->Next)) {
  1321. X    if ((GreaterOrLess = STRCMP((*WPL)->Word, WordInfo->Word)) <= 0) {
  1322. X        break;
  1323. X    }
  1324. X    }
  1325. X
  1326. X    /* Insert the new word at the head of the Word Chain,
  1327. X     * i.e. at the start of the group of similar words
  1328. X     */
  1329. X    SaveOldNext = *WPL;
  1330. X
  1331. X    enew(*WPL, t_WordPlaceList);
  1332. X    (*WPL)->WordPlace = WordInfo->WordPlace; /* structure copy */
  1333. X    (*WPL)->WordPlace.FID = WordInfo->WordPlace.FID;
  1334. X    (*WPL)->Next = SaveOldNext;
  1335. X
  1336. X    if (GreaterOrLess || !SaveOldNext) {
  1337. X    (*WPL)->Word = emalloc(WordInfo->Length + 1);
  1338. X    (void) strncpy((*WPL)->Word, WordInfo->Word, (int) WordInfo->Length);
  1339. X    (*WPL)->Word[WordInfo->Length] = '\0';
  1340. X    } else {
  1341. X    /* The word is already saved, so we only need to link to it */
  1342. X    (*WPL)->Word = SaveOldNext->Word;
  1343. X    }
  1344. X    if (MaxWordsInCache && ++WordsInCache > MaxWordsInCache) {
  1345. X    void DumpCache();
  1346. X
  1347. X    DumpCache(1);
  1348. X    WordsInCache = 0;
  1349. X    }
  1350. X}
  1351. X
  1352. Xvoid
  1353. XDumpCache(CallFree)
  1354. X    int CallFree; /* call efree() if non-zero */
  1355. X{
  1356. X    extern int WriteWordChain();
  1357. X
  1358. X    register int Slot;
  1359. X    register t_WordPlaceList *WordPlaceList;
  1360. X    int WordsLeft = WordsInCache;
  1361. X    int EmptySlots = 0, UsedSlots = 0;
  1362. X    int Progress = 0;
  1363. X
  1364. X    if (WordsInCache == 0) return; /* save some work maybe */
  1365. X
  1366. X    if (AsciiTrace) {
  1367. X    fprintf(stderr, "Writing%s%d words\n",
  1368. X            (CallFree) ? " and freeing " : " ", WordsInCache);
  1369. X    }
  1370. X
  1371. X    for (Slot = 0; WordsLeft > 0 && Slot < HASHSIZ; Slot++) {
  1372. X
  1373. X    if (AsciiTrace > 1) {
  1374. X        if (Slot >= Progress * (HASHSIZ / 16)) {
  1375. X        fputc(" 01234567890ABCDEFGHIJKL"[Progress], stderr);
  1376. X        ++Progress;
  1377. X        }
  1378. X    }
  1379. X    if (SymbolTable[Slot] == (t_WordPlaceList *) 0) {
  1380. X        ++EmptySlots;
  1381. X        continue;
  1382. X    } else {
  1383. X        char *LastFreed = (char *) 0;
  1384. X
  1385. X        ++UsedSlots;
  1386. X        WordPlaceList = SymbolTable[Slot];
  1387. X        WordsLeft -= WriteWordChain(WordPlaceList);
  1388. X
  1389. X        if (CallFree) {
  1390. X        while (WordPlaceList) {
  1391. X            register t_WordPlaceList *SavePointer;
  1392. X
  1393. X            if (WordPlaceList->Word &&
  1394. X                    WordPlaceList->Word != LastFreed) {
  1395. X            efree(WordPlaceList->Word);
  1396. X            LastFreed = WordPlaceList->Word;
  1397. X            }
  1398. X
  1399. X            SavePointer = WordPlaceList->Next;
  1400. X            efree((char *) WordPlaceList);
  1401. X            WordPlaceList = SavePointer;
  1402. X        }
  1403. X        SymbolTable[Slot] = (t_WordPlaceList *) 0;
  1404. X        }
  1405. X    }
  1406. X    }
  1407. X
  1408. X    if (AsciiTrace) {
  1409. X    double d = UsedSlots;
  1410. X    d /= (EmptySlots + UsedSlots);
  1411. X    d *= 100.0;
  1412. X
  1413. X    fprintf(stderr, "%4.3f%% cache used -- %d out of (%d <= %d)\n",
  1414. X            d, UsedSlots, UsedSlots + EmptySlots, HASHSIZ);
  1415. X#ifdef MALLOCTRACE
  1416. X    mallocmap();
  1417. X#endif
  1418. X    }
  1419. X
  1420. X    if (WordsInCache != 0 && CallFree) {
  1421. X    WordsInCache = 0;
  1422. X    }
  1423. X}
  1424. X
  1425. X#endif /*!NEWSYM*/
  1426. X
  1427. X/*
  1428. X * $Log:    wordtable.c,v $
  1429. X * Revision 2.11  91/02/20  19:07:37  lee
  1430. X * The qxxx fix only worked if ASCIITRACE was defined!
  1431. X * 
  1432. X * Revision 2.10  90/10/06  00:51:05  lee
  1433. X * Prepared for first beta release.
  1434. X * 
  1435. X * Revision 2.9  90/10/05  23:44:30  lee
  1436. X * Major experimentation with new symbol table failed...
  1437. X * 
  1438. X * Revision 2.8  90/09/26  19:45:02  lee
  1439. X * Added call to mallocmap() in ifdef MALLTRACE.
  1440. X * 
  1441. X * Revision 2.7  90/09/20  18:58:25  lee
  1442. X * Added some comments, and deleted a needless test.  Reorderered a loop
  1443. X * in the (probably vain) hope of a speed-up in the face of paging...
  1444. X * 
  1445. X * Revision 2.6  90/09/19  20:25:44  lee
  1446. X * Don't index "qxxxxxxxx" words (this is a hook for filters...)
  1447. X * 
  1448. X * Revision 2.5  90/08/29  21:46:11  lee
  1449. X * Alpha release
  1450. X * 
  1451. X * Revision 2.4  90/08/09  19:17:37  lee
  1452. X * BSD lint and Saber
  1453. X * 
  1454. X * Revision 2.3  90/03/21  17:32:31  lee
  1455. X * new hashing function, masses, masses better -- the old one only ever
  1456. X * used abuot 6% of the available values!
  1457. X * 
  1458. X * Revision 2.2  89/10/08  20:47:47  lee
  1459. X * Working version of nx-text engine.  Addfile and wordinfo work OK.
  1460. X * 
  1461. X * Revision 2.1  89/10/02  01:16:22  lee
  1462. X * New index format, with Block/WordInBlock/Flags/BytesSkipped info.
  1463. X * 
  1464. X * Revision 1.3  89/09/17  23:05:15  lee
  1465. X * Various fixes; NumberInBlock now a short...
  1466. X * 
  1467. X * Revision 1.2  89/09/16  21:18:55  lee
  1468. X * First demonstratable version.
  1469. X * 
  1470. X * Revision 1.1  89/09/07  21:06:20  lee
  1471. X * Initial revision
  1472. X * 
  1473. X */
  1474. @@@End of lq-text/src/lqtext/wordtable.c
  1475. echo x - lq-text/src/menu/Makefile 1>&2
  1476. sed 's/^X//' >lq-text/src/menu/Makefile <<'@@@End of lq-text/src/menu/Makefile'
  1477. X# Makefile for simple curses-based menu interface.
  1478. X#
  1479. X# $Id: Makefile,v 1.3 90/10/06 01:28:02 lee Rel1-10 $
  1480. X
  1481. XPWD=menu
  1482. X
  1483. X# PERFORMANCE makes curses go faster
  1484. XEXTRA=-I../h -DPERFORMANCE
  1485. XOPT=-O -g
  1486. XDEFS= -DASCIITRACE -UBSD -DSYSV
  1487. XWHICHDBM=sdbm
  1488. X# change the next three lines to be the same as the lq-text definitions.
  1489. XDBMLIBS=$(LIBDIR)/libsdbm.a
  1490. XBCOPY=bcopy.o
  1491. X# DBMLIBS=-lndbm -linet # 386/ix with hbtcpip provides a good bcopy()
  1492. X
  1493. XCFLAGS= $(OPT) $(DEFS) -UBSD -DSYSV $(GCCF) -D$(WHICHDBM) $(EXTRA)
  1494. XCC=gcc
  1495. XTERMCAP=-lcurses
  1496. XRANLIB=ranlib
  1497. X
  1498. XTEXT=lqtext
  1499. XPROG=m # a simple example of using the library...
  1500. XPROGOBJS=example.o
  1501. XPROGSRC=example.c
  1502. XLIBDIR=../lib
  1503. XBINDIR=../bin
  1504. XLIAMLIB=$(LIBDIR)/liblq.a
  1505. XLQTEXTLIB=$(LIBDIR)/liblqtext.a
  1506. XMENULIB=liblqmenu.a
  1507. X
  1508. XPROGS=$(PROG) $(TEXT) # removed by make clean
  1509. X
  1510. XOBJS=menu.o error.o stringbox.o OldCurses.o
  1511. XSRCS=menu.c error.c stringbox.c OldCurses.c
  1512. X
  1513. Xall: $(TEXT) m
  1514. X
  1515. Xinstall: $(MENULIB) $(TEXT)
  1516. X    cp $(MENULIB) $(LIBDIR)/$(MENULIB)
  1517. X    cp $(TEXT) $(BINDIR)/$(TEXT)
  1518. X    strip $(BINDIR)/$(TEXT)
  1519. X
  1520. X$(TEXT): text.o $(LQTEXTLIB) $(LIAMLIB) $(MENULIB) $(BCOPY)
  1521. X    $(CC) $(CFLAGS) -o $(TEXT) text.o $(BCOPY) \
  1522. X    $(MENULIB) $(LQTEXTLIB) $(DBMLIBS) $(LIAMLIB) $(TERMCAP)
  1523. X
  1524. X$(MENULIB): $(OBJS)
  1525. X    rm -f $(MENULIB)
  1526. X    ar rv $(MENULIB) $(OBJS)
  1527. X    $(RANLIB) $(MENULIB)
  1528. X
  1529. X$(PROG): $(PROGOBJS)
  1530. X    $(CC) $(CFLAGS) -o $(PROG) $(OBJS) $(PROGOBJS) $(BCOPY) $(TERMCAP)
  1531. X
  1532. Xlint$(PROG): $(OBJS) $(SRCS) $(PROGSRCS)
  1533. X    lint $(CFLAGS) $(SRCS) $(TERMCAP) 2>&1 | tee lint$(PROG)
  1534. X
  1535. X# Tidy should leave the final executables, but otherwise remove all
  1536. X# generated files
  1537. Xtidy:
  1538. X    /bin/rm -f *.o core make.log .mk m.log
  1539. X
  1540. X# Clean should revert to a distribution state as far as possible
  1541. Xclean:
  1542. X    /bin/rm -f *.o core *.a $(PROGS) $(CHARGEN) make.log .mk m.log
  1543. X
  1544. Xtext.o: text.c
  1545. X    $(CC) $(CFLAGS) $(TEXTINC) -c text.c
  1546. X
  1547. X
  1548. Xdepend:
  1549. X    mkdep $(CFLAGS) *.c
  1550. X
  1551. X#
  1552. X# $Log:    Makefile,v $
  1553. X# Revision 1.3  90/10/06  01:28:02  lee
  1554. X# deleted mkdep output.
  1555. X# 
  1556. X# Revision 1.2  90/10/01  20:33:09  lee
  1557. X# Added BSD compatibility hooks and improved "make clean".
  1558. X# 
  1559. X# Revision 1.1  90/08/29  21:48:48  lee
  1560. X# Initial revision
  1561. X# 
  1562. X# Revision 2.1  89/08/07  13:52:22  lee
  1563. X# First fully working release; this is the basis for all
  1564. X# future development.
  1565. X# 
  1566. X# Revision 1.2  89/08/04  17:59:23  lee
  1567. X# Fully working with Basic Functionality.
  1568. X# Scrolling menubar, scrolling menus, moveable Info windows.
  1569. X# 
  1570. X# Revision 1.1  89/07/27  11:41:39  lee
  1571. X# Initial revision
  1572. X#
  1573. X# DO NOT DELETE THIS LINE -- mkdep uses it.
  1574. X# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY.
  1575. X
  1576. X# IF YOU PUT ANYTHING HERE IT WILL GO AWAY
  1577. @@@End of lq-text/src/menu/Makefile
  1578. echo x - lq-text/src/menu/README 1>&2
  1579. sed 's/^X//' >lq-text/src/menu/README <<'@@@End of lq-text/src/menu/README'
  1580. XThis directory contains as much as necessary of my curses menu library
  1581. Xto demonstrate lq-text.  (and I just saw some more files I could remove...)
  1582. XThis is a fairly simple curses-based front end to the lq-text text retrieval
  1583. Xsoftware.
  1584. X
  1585. XIt has only been tested on System V Release 3.2, and almost certainly will
  1586. Xnot work on anything else without at least a little effort.
  1587. X
  1588. XSee the notes on porting below if you want to try...
  1589. X
  1590. XPlease do not ask me for the rest of the package.  If I get the time and the
  1591. Xnecessary facilities, I will make it available.  I am hoping to have a
  1592. Xversion which will run with no source changes under X windows as well as
  1593. Xunder Curses, but it is not trivial...  What you see here is a hacked version,
  1594. Xin order to minimise what I post.  Sorry.
  1595. X
  1596. X
  1597. XTo install:
  1598. X
  1599. X(1) You need to have lq-text already.
  1600. X    Make it and test that it works, for example by indexing the unix man pages.
  1601. X    In particular, check that lqphrase works with two-word phrases.  If it
  1602. X    doesn't, there is little point in proceeding.
  1603. X
  1604. X(2) You will need to edit Makefile in this directory to point to the directory
  1605. X    containing the lq-text source:
  1606. X    Change the defintion of $(NX) as appropriate -- for example,
  1607. X    NX=../../../src/lq-text/src
  1608. X    or something.
  1609. X
  1610. X(3) make text
  1611. X
  1612. X(4) ln text lqtext (if you want)
  1613. X
  1614. X(5) try it.  If you don't have working function keys, you can use ESC
  1615. X    followed by a digit (e.g. ESC 1 is the same as F1), and when you are
  1616. X    entering phrases, ^D at the start of the line will take you back to the
  1617. X    main menu just as F1 does.
  1618. X
  1619. X    **>>> You will need to have lqshow in your path for this to work. <<<**
  1620. X    **>>> You will need want to set $LQTEXTDIR, or use the -d option.
  1621. X    see the man page for lq-text for command-line options to "text".
  1622. X
  1623. X    Try
  1624. X    from the File menu, select "new words"
  1625. X    (you can type 'x' for an explanation at any point in the menus)
  1626. X
  1627. X    type some words or phrases
  1628. X
  1629. X    select "match all" from the "All Words" menu
  1630. X
  1631. X    the numbers by the phrases indicate the number of matches;
  1632. X    you can then do "browse all" from the "All Words" menu.
  1633. X
  1634. X    To exit, press "q" from the main menu, or select "Finish" from the
  1635. X    Main Menu.
  1636. X
  1637. X    When you have typed 'x', a box will appear containing an explanation.
  1638. X    You can type 'x' at this point for an explanation of what to do with
  1639. X    the box... for example, you can move the explain-box around the sceen
  1640. X    or resize it if you want.  I have no idea why you would want to do
  1641. X    this, but it can be a little fun for people who are bored... and is
  1642. X    a facility that came for free from my curses/menu package...
  1643. X
  1644. X(6) You might also like to try making "m", and running examples/vsh, which
  1645. X    is a simple shell-script.  It is not meant to be a useful shell -- just
  1646. X    a tiny demo I wrote at home in some spare time...
  1647. X
  1648. X
  1649. X(7) Now investigate internal.h and menu.h if you want to change things.
  1650. X    If you change these, go back to step (3)
  1651. X
  1652. XLee
  1653. Xsq.com
  1654. XThu Dec 14 21:06:34 EST 1989
  1655. @@@End of lq-text/src/menu/README
  1656. echo x - lq-text/src/menu/bcopy.c 1>&2
  1657. sed 's/^X//' >lq-text/src/menu/bcopy.c <<'@@@End of lq-text/src/menu/bcopy.c'
  1658. X#ifdef BCOPYTEST
  1659. X# include <stdio.h>
  1660. X#endif
  1661. X
  1662. X/* this is a simple replacement for bcopy() where the native bcopy()
  1663. X * does not handle overlapping blocks.
  1664. X * do
  1665. X *    cc -DBCOPYTEST -o bcopy bcopy.c
  1666. X * and run "./bcopy" for a simple test.  You should get three
  1667. X * identical lines of output.
  1668. X */
  1669. X
  1670. Xbcopy(src, dest, nbytes)
  1671. X    char *dest;
  1672. X    char *src;
  1673. X    int nbytes;
  1674. X{
  1675. X    /* We have to be clever about this...
  1676. X     * If src < dest then we copy from the top down
  1677. X     * otherwise, copy from the bottom up...
  1678. X     */
  1679. X    
  1680. X    register char *p, *q;
  1681. X
  1682. X    if (src < dest) {
  1683. X    for (p = &src[nbytes - 1], q = &dest[nbytes - 1]; nbytes--; q--, p--) {
  1684. X        *q = *p;
  1685. X    }
  1686. X    } else {
  1687. X    for (p = src, q = dest; nbytes--; p++, q++) {
  1688. X        *q = *p;
  1689. X    }
  1690. X    }
  1691. X}
  1692. X
  1693. X#ifdef BCOPYTEST
  1694. Xmain()
  1695. X{
  1696. X    char buffer[4096];
  1697. X    char *s = "The naked children hugged each other";
  1698. X
  1699. X    puts(s); /* first line */
  1700. X    (void) sprintf(&buffer[12], "%s", s);
  1701. X    bcopy(&buffer[12], buffer, strlen(s) + 1);
  1702. X    printf("[%s]\n", buffer); /* 2nd line */
  1703. X    bcopy(buffer, &buffer[12], strlen(s) + 1);
  1704. X    printf("[%s]\n", &buffer[12]); /* 3rd line */
  1705. X}
  1706. X#endif
  1707. @@@End of lq-text/src/menu/bcopy.c
  1708. echo x - lq-text/src/menu/OldCurses.c 1>&2
  1709. sed 's/^X//' >lq-text/src/menu/OldCurses.c <<'@@@End of lq-text/src/menu/OldCurses.c'
  1710. X/* Compatibility routines for older versions of curses...
  1711. X * $Id: OldCurses.c,v 1.2 90/10/04 16:27:58 lee Rel1-10 $
  1712. X *
  1713. X */
  1714. X
  1715. X#include <curses.h>
  1716. X#include <ctype.h>
  1717. X
  1718. X#ifndef A_STANDOUT
  1719. X#include "oldcurses.h"
  1720. X
  1721. X#undef CONTROL
  1722. X#define CONTROL(c) (c ^ 64)
  1723. X
  1724. X#undef wgetch
  1725. X
  1726. Xchtype
  1727. XLqwgetch(w)
  1728. X    WINDOW *w;
  1729. X{
  1730. X    int ch = wgetch(w);
  1731. X
  1732. X    if (isprint(ch)) return ch;
  1733. X
  1734. X    switch (ch) {
  1735. X    case CONTROL('^'): return KEY_HOME;
  1736. X    case CONTROL('P'): return KEY_UP;
  1737. X    case CONTROL('N'): return KEY_DOWN;
  1738. X    case CONTROL('B'): return KEY_LEFT;
  1739. X    case CONTROL('F'): return KEY_RIGHT;
  1740. X    case CONTROL('X'): return KEY_HELP; /* Xplain.... (groan) */
  1741. X    case '\033': /* Escape */
  1742. X    (void) fprintf(stderr, "ESC\007");
  1743. X    (void) fflush(stderr);
  1744. X
  1745. X    switch (ch = wgetch(w)) {
  1746. X    case 0:        return KEY_F0;
  1747. X    case 1:        return KEY_F(1);
  1748. X    case 2:        return KEY_F(2);
  1749. X    case 3:        return KEY_F(3);
  1750. X    case 4:        return KEY_F(4);
  1751. X    case 5:        return KEY_F(5);
  1752. X    case 6:        return KEY_F(6);
  1753. X    case 7:        return KEY_F(7);
  1754. X    case 8:        return KEY_F(8);
  1755. X    case 9:        return KEY_F(9);
  1756. X    case 'a': case 'A':    return KEY_F(10);
  1757. X    case 'b': case 'B':    return KEY_F(11);
  1758. X    case 'c': case 'C':    return KEY_F(12);
  1759. X    case 'd': case 'D':    return KEY_F(13);
  1760. X    case 'e': case 'E':    return KEY_F(14);
  1761. X    case 'f': case 'F':    return KEY_F(15);
  1762. X    case 'h':    return KEY_HELP;
  1763. X    }
  1764. X    break;
  1765. X    }
  1766. X    return ch;
  1767. X}
  1768. X
  1769. Xvoid
  1770. Xbeep()
  1771. X{
  1772. X    (void) putc('\b', stderr);
  1773. X    (void) fflush(stderr);
  1774. X}
  1775. X
  1776. Xvoid
  1777. Xbox(win, vert, hor)
  1778. X    WINDOW *win;
  1779. X    int vert;
  1780. X    int hor;
  1781. X{
  1782. X#undef box
  1783. X    if (hor == 0) hor = ACS_HLINE;
  1784. X    if (vert == 0) vert = ACS_VLINE;
  1785. X    box(win, vert, hor);
  1786. X}
  1787. X
  1788. Xvoid
  1789. XLqattrset(win, attr)
  1790. X    WINDOW *win;
  1791. X    int attr;
  1792. X{
  1793. X    if (attr) {
  1794. X    wstandout(win);
  1795. X    } else {
  1796. X    wstandend(win);
  1797. X    }
  1798. X}
  1799. X
  1800. Xwnoutrefresh(win)
  1801. X    WINDOW *win;
  1802. X{
  1803. X    touchwin(win);
  1804. X}
  1805. X#endif
  1806. X
  1807. X/* $Log:    OldCurses.c,v $
  1808. X * Revision 1.2  90/10/04  16:27:58  lee
  1809. X * SysV compat improved.
  1810. X * 
  1811. X * Revision 1.1  90/10/03  21:54:04  lee
  1812. X * Initial revision
  1813. X * 
  1814. X *
  1815. X */
  1816. @@@End of lq-text/src/menu/OldCurses.c
  1817. echo x - lq-text/src/menu/oldcurses.h 1>&2
  1818. sed 's/^X//' >lq-text/src/menu/oldcurses.h <<'@@@End of lq-text/src/menu/oldcurses.h'
  1819. X/* oldcurses.h -- compatibility with pre-System V.3 curses...
  1820. X * $Id: oldcurses.h,v 1.2 90/10/04 16:28:31 lee Rel1-10 $
  1821. X */
  1822. X
  1823. Xtypedef int chtype;
  1824. X
  1825. X#define ACS_LARROW    '>'
  1826. X#define ACS_RARROW    '<'
  1827. X#define ACS_HLINE    '='
  1828. X#define ACS_VLINE    '|'
  1829. X#define ACS_LRCORNER    '+'
  1830. X#define ACS_LLCORNER    '+'
  1831. X
  1832. X/* Line drawing: */
  1833. X#define ACS_BSSS    '+'    /* T-piece */
  1834. X#define ACS_SBSS    '+'    /* -| */
  1835. X#define ACS_SSBS    '+'    /* inverted T-piece */
  1836. X#define ACS_SSSB    '+'    /* |- */
  1837. X#define ACS_BBSS    '+'    /* top right corner */
  1838. X#define ACS_BSSB    '+'    /* bottom left corner */
  1839. X
  1840. X#define KEY_DOWN    257
  1841. X#define KEY_UP      258
  1842. X#define KEY_LEFT    259
  1843. X#define KEY_RIGHT    260
  1844. X#define KEY_HELP    261
  1845. X#define KEY_HOME    262
  1846. X#define KEY_F0        300
  1847. X#define KEY_F(n)    (KEY_F0+n)
  1848. X
  1849. X#undef getch
  1850. X#define getch()    Lqwgetch(stdscr)
  1851. X#define wgetch    Lqwgetch
  1852. X
  1853. X#undef box
  1854. X#define box LqBox
  1855. X
  1856. X#define A_STANDOUT 1
  1857. X#undef standout
  1858. X#undef standend
  1859. X#define wattrset Lqattrset
  1860. X#define attrset(a) Lqattrset(stdscr, a)
  1861. X#define keypad(win, bool)    1 /* ignore this one please */
  1862. X
  1863. X/* $Log:    oldcurses.h,v $
  1864. X * Revision 1.2  90/10/04  16:28:31  lee
  1865. X * SysV compat improved.
  1866. X * 
  1867. X * Revision 1.1  90/10/03  21:56:32  lee
  1868. X * Initial revision
  1869. X * 
  1870. X *
  1871. X */
  1872. @@@End of lq-text/src/menu/oldcurses.h
  1873. echo end of part 08
  1874. -- 
  1875. Liam R. E. Quin,  lee@sq.com, SoftQuad Inc., Toronto, +1 (416) 963-8337
  1876.