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

  1. From: lee@sq.sq.com (Liam R. E. Quin)
  2. Newsgroups: alt.sources
  3. Subject: lq-text Full Text Retrieval Database Part 05/13
  4. Message-ID: <1991Mar4.020405.16387@sq.sq.com>
  5. Date: 4 Mar 91 02:04:05 GMT
  6.  
  7. : cut here --- cut here --
  8. : To unbundle, sh this file
  9. #! /bin/sh
  10. : part 05
  11. echo x - lq-text/src/liblqtext/asciitrace.c 1>&2
  12. sed 's/^X//' >lq-text/src/liblqtext/asciitrace.c <<'@@@End of lq-text/src/liblqtext/asciitrace.c'
  13. X/* asciitrace.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 * This file simply declares AsciiTrace, which is used for verbose (-v)
  17. X * output (when set to 1), and for debugging/tracing at higher levels.
  18. X */
  19. X
  20. Xint AsciiTrace = 0;
  21. X
  22. X/* $Id: asciitrace.c,v 1.3 90/10/06 00:21:12 lee Rel1-10 $
  23. X *
  24. X */
  25. @@@End of lq-text/src/liblqtext/asciitrace.c
  26. echo x - lq-text/src/liblqtext/bcopy.c 1>&2
  27. sed 's/^X//' >lq-text/src/liblqtext/bcopy.c <<'@@@End of lq-text/src/liblqtext/bcopy.c'
  28. X/* bcopy.c -- Copyright 1989 Liam R. Quin.  All Rights Reserved.
  29. X * This code is NOT in the public domain.
  30. X * See the file COPYRIGHT for full details.
  31. X */
  32. X
  33. X#ifdef BCOPYTEST
  34. X# include <stdio.h>
  35. X#endif
  36. X
  37. X/* this is a simple replacement for bcopy() where the native bcopy()
  38. X * does not handle overlapping blocks.
  39. X * do
  40. X *    cc -DBCOPYTEST -o bcopy bcopy.c
  41. X * and run "./bcopy" for a simple test.  You should get three
  42. X * identical lines of output.
  43. X *
  44. X * $Id: bcopy.c,v 1.3 90/10/06 00:21:24 lee Rel1-10 $
  45. X */
  46. X
  47. Xbcopy(src, dest, nbytes)
  48. X    char *dest;
  49. X    char *src;
  50. X    int nbytes;
  51. X{
  52. X    /* We have to be clever about this...
  53. X     * If src < dest then we copy from the top down
  54. X     * otherwise, copy from the bottom up...
  55. X     */
  56. X    
  57. X    register char *p, *q;
  58. X
  59. X    if (src < dest) {
  60. X    for (p = &src[nbytes - 1], q =&dest[nbytes - 1]; nbytes--; q--, p--) {
  61. X        *q = *p;
  62. X    }
  63. X    } else {
  64. X    for (p = src, q = dest; nbytes--; p++, q++) {
  65. X        *q = *p;
  66. X    }
  67. X    }
  68. X}
  69. X
  70. X#ifdef BCOPYTEST
  71. Xmain()
  72. X{
  73. X    char buffer[4096];
  74. X    char *s = "The naked children hugged each other";
  75. X
  76. X    puts(s); /* first line */
  77. X    (void) sprintf(&buffer[12], "%s", s);
  78. X    bcopy(&buffer[12], buffer, strlen(s) + 1);
  79. X    printf("[%s]\n", buffer); /* 2nd line */
  80. X    bcopy(buffer, &buffer[12], strlen(s) + 1);
  81. X    printf("[%s]\n", &buffer[12]); /* 3rd line */
  82. X}
  83. X#endif
  84. X
  85. X/* $Log:    bcopy.c,v $
  86. X * Revision 1.3  90/10/06  00:21:24  lee
  87. X * Prepared for first beta release.
  88. X * 
  89. X *
  90. X */
  91. @@@End of lq-text/src/liblqtext/bcopy.c
  92. echo x - lq-text/src/liblqtext/cmdname.c 1>&2
  93. sed 's/^X//' >lq-text/src/liblqtext/cmdname.c <<'@@@End of lq-text/src/liblqtext/cmdname.c'
  94. X/* cmdname.c -- Copyright 1989 Liam R. Quin.  All Rights Reserved.
  95. X * This code is NOT in the public domain.
  96. X * See the file COPYRIGHT for full details.
  97. X */
  98. X
  99. Xchar *cmdname = (char *) 0;
  100. X
  101. X/* $Id: cmdname.c,v 1.2 90/10/06 00:12:08 lee Rel1-10 $
  102. X *
  103. X * $Log:    cmdname.c,v $
  104. X * Revision 1.2  90/10/06  00:12:08  lee
  105. X * Prepared for first beta release.
  106. X * 
  107. X * Revision 1.1  90/03/24  17:08:19  lee
  108. X * Initial revision
  109. X * 
  110. X *
  111. X */
  112. @@@End of lq-text/src/liblqtext/cmdname.c
  113. echo x - lq-text/src/liblqtext/malloc.c 1>&2
  114. sed 's/^X//' >lq-text/src/liblqtext/malloc.c <<'@@@End of lq-text/src/liblqtext/malloc.c'
  115. X/* malloc.c -- Copyright 1989 Liam R. Quin.  All Rights Reserved.
  116. X * This code is NOT in the public domain.
  117. X * See the file COPYRIGHT for full details.
  118. X */
  119. X
  120. X/*
  121. X * malloc.c -- wrapper routines for free/malloc, that do checking and
  122. X * print errors.
  123. X *
  124. X * $Id: malloc.c,v 1.6 91/03/03 00:14:27 lee Rel1-10 $
  125. X *
  126. X * $Log:    malloc.c,v $
  127. X * Revision 1.6  91/03/03  00:14:27  lee
  128. X * Simpler interface if MALLOCTRACE undefined.
  129. X * 
  130. X * Revision 1.5  90/10/06  00:12:11  lee
  131. X * Prepared for first beta release.
  132. X * 
  133. X * Revision 1.4  90/09/29  23:51:31  lee
  134. X * Added MALLTRACE to detect memory leaks.
  135. X * 
  136. X * Revision 1.3  90/08/29  21:46:55  lee
  137. X * Alpha release.
  138. X * 
  139. X * Revision 1.2  90/08/09  19:16:44  lee
  140. X * BSD lint and fixes...
  141. X * 
  142. X * Revision 2.2  89/10/08  20:46:10  lee
  143. X * Working version of nx-text engine.  Addfile and wordinfo work OK.
  144. X * 
  145. X *
  146. X */
  147. X
  148. X#include "globals.h"
  149. X
  150. X#include <malloc.h>
  151. X#include <stdio.h>
  152. X#include <ctype.h>
  153. X
  154. Xextern char *progname;
  155. X
  156. Xextern void exit();
  157. X
  158. Xint _LiamIsInCurses = 0;
  159. X    /* This should be in error.c */
  160. X
  161. XINLINE char *
  162. X_emalloc(nbytes
  163. X#ifdef MALLOCTRACE
  164. X, FileName, Line
  165. X#endif
  166. X)
  167. X    unsigned nbytes;
  168. X#ifdef MALLOCTRACE
  169. X    char *FileName;
  170. X    int Line;
  171. X#endif
  172. X{
  173. X    char *Result;
  174. X
  175. X    if ((Result = malloc(nbytes)) == (char *) 0) {
  176. X#ifdef MALLOCTRACE
  177. X    fprintf(stderr, "%s: %s: %d: emalloc(%u) failed.\n",
  178. X                    progname, FileName, Line, nbytes);
  179. X#else
  180. X    fprintf(stderr, "%s: emalloc(%u) failed.\n", progname, nbytes);
  181. X#endif
  182. X    exit(1);
  183. X    }
  184. X
  185. X#ifdef MALLOCTRACE
  186. X    (void) fprintf(stderr, "malloc %u > 0x%x %s %d\n", nbytes, Result, FileName, Line);
  187. X#endif
  188. X    return Result;
  189. X}
  190. X
  191. XINLINE char *
  192. X_ecalloc(Number, Size
  193. X#ifdef MALLOCTRACE
  194. X            , FileName, Line
  195. X#endif
  196. X)
  197. X    unsigned Number;
  198. X    unsigned Size;
  199. X#ifdef MALLOCTRACE
  200. X    char *FileName;
  201. X    int Line;
  202. X#endif
  203. X{
  204. X    char *Result;
  205. X    extern char *calloc();
  206. X
  207. X    if ((Result = calloc(Number, Size)) == (char *) 0) {
  208. X#ifdef MALLOCTRACE
  209. X    fprintf(stderr, "%s: %s: %d: ecalloc(%u x %u) failed.\n",
  210. X            progname, FileName, Line, Number, Size);
  211. X#else
  212. X    fprintf(stderr, "%s: ecalloc(%u x %u) failed.\n",
  213. X            progname, Number, Size);
  214. X#endif
  215. X    exit(1);
  216. X    }
  217. X
  218. X    return Result;
  219. X}
  220. X
  221. XINLINE char *
  222. X_erealloc(OldString, NewSize
  223. X#ifdef MALLOCTRACE
  224. X                , FileName, Line
  225. X#endif
  226. X)
  227. X    char *OldString;
  228. X    unsigned NewSize;
  229. X#ifdef MALLOCTRACE
  230. X    char *FileName;
  231. X    int Line;
  232. X#endif
  233. X{
  234. X    char *Result;
  235. X
  236. X    if ((Result = realloc(OldString, NewSize)) == (char *) 0) {
  237. X#ifdef MALLOCTRACE
  238. X    fprintf(stderr, "%s: %s: %d: erealloc(0x%x, %u) failed.\n",
  239. X                progname, FileName, Line, OldString, NewSize);
  240. X#else
  241. X    fprintf(stderr, "%s: erealloc(0x%x, %u) failed.\n",
  242. X                progname, OldString, NewSize);
  243. X#endif
  244. X    exit(1);
  245. X    }
  246. X
  247. X#ifdef MALLOCTRACE
  248. X    (void) fprintf(stderr, "realloc 0x%x %u > 0x%x %s %d\n", OldString, NewSize, Result, FileName, Line);
  249. X#endif
  250. X    return Result;
  251. X}
  252. X
  253. XINLINE void
  254. X_efree(String
  255. X#ifdef MALLOCTRACE
  256. X        , FileName, Line
  257. X#endif
  258. X)
  259. X    char *String;
  260. X#ifdef MALLOCTRACE
  261. X    char *FileName;
  262. X    int Line;
  263. X#endif
  264. X{
  265. X    if (!String) {
  266. X#ifdef MALLOCTRACE
  267. X    (void) fprintf(stderr, "%s: %s: %d: Warning: free(0) ignored\n",
  268. X            progname, FileName, Line);
  269. X#else
  270. X    (void) fprintf(stderr, "%s: Warning: free(0) ignored\n", progname);
  271. X#endif
  272. X    return;
  273. X    }
  274. X#ifdef MALLOCTRACE
  275. X    (void) fprintf(stderr, "free 0x%x\n", String);
  276. X#endif
  277. X
  278. X    (void) free(String);
  279. X}
  280. @@@End of lq-text/src/liblqtext/malloc.c
  281. echo x - lq-text/src/liblqtext/numbers.c 1>&2
  282. sed 's/^X//' >lq-text/src/liblqtext/numbers.c <<'@@@End of lq-text/src/liblqtext/numbers.c'
  283. X/* numbers.c -- Copyright 1989 Liam R. Quin.  All Rights Reserved.
  284. X * This code is NOT in the public domain.
  285. X * See the file COPYRIGHT for full details.
  286. X */
  287. X
  288. X/* Routines to read and write numbers in a compressed format, preserving
  289. X * block boundaries.
  290. X * The actual compression seems to be about 50%, as most numbers turn
  291. X * out to fit in 16 bits.  Interestingly, there is room for another one
  292. X * or two bits, I think, that could be used for something else, in the
  293. X * main pblock index.  For example, it could mark whether words were
  294. X * plural/-"ing"/"un"-/ with 2 bits.
  295. X *
  296. X * $Id: numbers.c,v 1.4 90/10/06 00:12:16 lee Rel1-10 $
  297. X *
  298. X * $Log:    numbers.c,v $
  299. X * Revision 1.4  90/10/06  00:12:16  lee
  300. X * Prepared for first beta release.
  301. X * 
  302. X * Revision 1.3  90/08/09  19:16:49  lee
  303. X * BSD lint and fixes...
  304. X * 
  305. X * Revision 1.2  90/04/18  19:47:13  lee
  306. X * More flexible (and slightly more compact) number format.
  307. X * 
  308. X * Revision 2.2  89/10/08  20:46:36  lee
  309. X * Working version of nx-text engine.  Addfile and wordinfo work OK.
  310. X * 
  311. X * Revision 2.1  89/10/02  01:15:15  lee
  312. X * New index format, with Block/WordInBlock/Flags/BytesSkipped info.
  313. X * 
  314. X * Revision 1.2  89/09/16  21:16:26  lee
  315. X * First demonstratable version.
  316. X * 
  317. X * Revision 1.1  89/09/07  21:06:01  lee
  318. X * Initial revision
  319. X * 
  320. X *
  321. X */
  322. X
  323. X#include "globals.h"
  324. X
  325. X#ifdef SYSV
  326. X    extern int _filbuf(), _flsbuf();
  327. X#endif
  328. X#include <stdio.h>
  329. X#include "numbers.h"
  330. X
  331. X/* ReadNumber and WriteNumber take/return a long, using a compression
  332. X * algorithm to reduce the amount of data taken.
  333. X * The current algorithm is simply like internet addresses:
  334. X * a 0 in the top bit followed by a 0 means it's one byte
  335. X * a 0 followed by a 1 means it's 2 bytes
  336. X * a 1 followed by a 0 means it's 3 bytes, and
  337. X * a 1 followed by a 1 means it's 4 bytes.
  338. X * A better alternative might simply use a 1 in the top bit, hence fitting
  339. X * 7 bits into each bytes.  The advantages of considering more than
  340. X * one number at a time and using compress-style LS packing are not clear.
  341. X * In particular, speed of recovery is an issue too.
  342. X *
  343. X * The routines use (char *) pointers instead of files prefixes with an s.
  344. X * see numbers.h for some related macros.
  345. X *
  346. X */
  347. X
  348. X
  349. X#ifdef TESTNUMBERS
  350. Xchar *progname;
  351. X
  352. Xint
  353. Xmain(ac, av)
  354. X    int ac;
  355. X    char *av[];
  356. X{
  357. X    extern long atol();
  358. X    FILE *f;
  359. X    extern FILE *fopen();
  360. X
  361. X    progname = av[0];
  362. X
  363. X    while (--ac) {
  364. X    unsigned long L = atol(*++av);
  365. X    unsigned long L2;
  366. X
  367. X    f = fopen("/tmp/boy", "w");
  368. X    printf("Write %u\n", L);
  369. X    fWriteNumber(f, L);
  370. X    fclose(f);
  371. X    f = fopen("/tmp/boy", "r");
  372. X    L2 = fReadNumber(f);
  373. X    printf("Read %u\n", L2);
  374. X    if (L != L2) {
  375. X        printf("**** ERROR **** %ld != %ld\n", L, L2);
  376. X    }
  377. X    fclose(f);
  378. X    }
  379. X    return 0;
  380. X}
  381. X#endif /*TESTNUMBERS*/
  382. X
  383. XINLINE void
  384. XfWriteNumber(f, Number)
  385. X    FILE *f;
  386. X    unsigned long Number;
  387. X{
  388. X    /* Compressed numbers:
  389. X     * 7 bit numbers --> single byte;
  390. X     * 8...14 bits --> 2 bytes
  391. X     * 15...21 bits --> 3 bytes
  392. X     * 22..28 bits --> 4 bytes
  393. X     * 29..32 bits --> 5 bytes
  394. X     */
  395. X    while (Number > 0177) {
  396. X    putc((Number & 0177) | 0200, f);
  397. X    Number >>= 7;
  398. X    }
  399. X    putc(Number & 0177, f);
  400. X}
  401. X
  402. X#define PutC(ch, S)  (*((*S)++) = (char) (ch))
  403. X
  404. XINLINE void
  405. XsWriteNumber(s, Number)
  406. X    char **s;
  407. X    unsigned long Number;
  408. X{
  409. X    /* Compressed numbers:
  410. X     * 7 bit numbers --> single byte;
  411. X     * 8...14 bits --> 2 bytes
  412. X     * 15...21 bits --> 3 bytes
  413. X     * 22..28 bits --> 4 bytes
  414. X     * 29..32 bits --> 5 bytes
  415. X     */
  416. X    while (Number > 0177) {
  417. X    PutC((Number & 0177) | 0200, s);
  418. X    Number >>= 7;
  419. X    }
  420. X    PutC(Number & 0177, s);
  421. X}
  422. X
  423. XINLINE unsigned long
  424. XfReadNumber(f)
  425. X    FILE *f;
  426. X{
  427. X    unsigned long Result = 0L;
  428. X    int ThereIsMore;
  429. X    int Shift = 0;
  430. X
  431. X    /* Read a number, 7 bits at a time, lsb first, until there is
  432. X     * a byte without the top bit set -- that's the most significant
  433. X     * byte, and there is no more of this number.
  434. X     */
  435. X    do {
  436. X    Result |= ((ThereIsMore = getc(f)) & 0177) << Shift;
  437. X    ThereIsMore &= 0200;
  438. X    Shift += 7;
  439. X    } while (ThereIsMore);
  440. X    return Result;
  441. X}
  442. X
  443. X#define GetC(S) \
  444. X    ( (unsigned int) * (unsigned char *) ((* (unsigned char **)S)++) )
  445. X
  446. XINLINE unsigned long
  447. XsReadNumber(s)
  448. X    char **s;
  449. X{
  450. X    unsigned long Result = 0L;
  451. X    int ThereIsMore;
  452. X    int Shift = 0;
  453. X
  454. X    /* Read a number, 7 bits at a time, lsb first, until there is
  455. X     * a byte without the top bit set -- that's the most significant
  456. X     * byte, and there is no more of this number.
  457. X     */
  458. X    do {
  459. X    Result |= ((ThereIsMore = GetC(s)) & 0177) << Shift;
  460. X    ThereIsMore &= 0200;
  461. X    Shift += 7;
  462. X    } while (ThereIsMore);
  463. X    return Result;
  464. X}
  465. @@@End of lq-text/src/liblqtext/numbers.c
  466. echo x - lq-text/src/liblqtext/pblock.c 1>&2
  467. sed 's/^X//' >lq-text/src/liblqtext/pblock.c <<'@@@End of lq-text/src/liblqtext/pblock.c'
  468. X/* pblock.c -- Copyright 1989 Liam R. Quin.  All Rights Reserved.
  469. X * This code is NOT in the public domain.
  470. X * See the file COPYRIGHT for full details.
  471. X */
  472. X
  473. X/* The physical Word Database for NX-Text...
  474. X *
  475. X * Interface by Liam Quin, 1989
  476. X *
  477. X * The main database is a mesh of linked lists, rather like tagliatelli.
  478. X *
  479. X * $Id: pblock.c,v 1.10 90/10/13 03:08:36 lee Rel1-10 $
  480. X *
  481. X * $Log:    pblock.c,v $
  482. X * Revision 1.10  90/10/13  03:08:36  lee
  483. X * deleted an illegal dereference!
  484. X * 
  485. X * Revision 1.9  90/10/06  00:12:17  lee
  486. X * Prepared for first beta release.
  487. X * 
  488. X * Revision 1.8  90/09/29  23:49:05  lee
  489. X * commented out (with #if 0) some code that called WID2WordInfo needlessly.
  490. X * 
  491. X * Revision 1.7  90/08/29  21:47:04  lee
  492. X * Alpha release.
  493. X * 
  494. X * Revision 1.6  90/08/09  19:16:52  lee
  495. X * BSD lint and fixes...
  496. X * 
  497. X * Revision 1.5  90/08/08  22:26:17  lee
  498. X * Many major lint and gcc -Wall fixes...
  499. X * 
  500. X * Revision 1.4  90/03/22  14:26:54  lee
  501. X * Fixed the test for Sorting monotonicity (?)..., which had not been
  502. X * checking that the FIDS were the same before checking the blocks...
  503. X * 
  504. X * Revision 1.3  90/03/21  19:42:22  lee
  505. X * new calls to efree();
  506. X * removed WID from the data blocks.
  507. X * doesn't work yet, though, sorry.
  508. X * 
  509. X * Revision 2.2  89/10/08  20:46:53  lee
  510. X * Working version of nx-text engine.  Addfile and wordinfo work OK.
  511. X * 
  512. X * Revision 2.1  89/10/02  01:15:24  lee
  513. X * New index format, with Block/WordInBlock/Flags/BytesSkipped info.
  514. X * 
  515. X * Revision 1.3  89/09/17  23:03:12  lee
  516. X * Various fixes; NumberInBlock now a short...
  517. X * 
  518. X * Revision 1.2  89/09/16  21:16:30  lee
  519. X * First demonstratable version.
  520. X * 
  521. X * Revision 1.1  89/09/07  21:06:05  lee
  522. X * Initial revision
  523. X * 
  524. X *
  525. X */
  526. X
  527. X#include "globals.h" /* defines and declarations for database filenames */
  528. X
  529. X#include <stdio.h> /* stderr, also for fileinfo.h */
  530. X#include <fcntl.h>
  531. X#include <malloc.h>
  532. X#include <sys/types.h>
  533. X#include "fileinfo.h" /* for wordinfo.h */
  534. X#include "wordinfo.h"
  535. X#include "pblock.h"
  536. X#include "numbers.h"
  537. X#include "wordrules.h"
  538. X#include "emalloc.h"
  539. X
  540. X#ifndef STREQ
  541. X# define STREQ(boy,girl) ((*(boy) == *(girl)) && (!strcmp((boy),(girl))))
  542. X#endif
  543. X
  544. X#define new(type) ( ((type) *) emalloc(sizeof(type)) )
  545. X
  546. Xextern t_WordInfo *WID2WordInfo();
  547. Xextern t_WID Word2WID();
  548. X
  549. X/** Unix system calls that need to be declared: **/
  550. Xextern void exit();
  551. Xextern int open();
  552. Xextern int read(), write();
  553. X
  554. X/** C library functions that need to be declared: **/
  555. Xextern char *memcpy();
  556. Xextern void perror();
  557. Xextern void qsort();
  558. Xextern int strcmp();
  559. Xextern char *strcpy();
  560. Xextern int strlen();
  561. X
  562. X/** lqtext library functions that need to be declared: **/
  563. Xextern int MkWIB();
  564. Xextern void MkWIBH();
  565. Xextern int PutWordInfoIntoIndex();
  566. Xextern void SortWordPlaces();
  567. Xextern t_WordInfo *MakeWordInfo();
  568. Xextern void SlayWordInfo();
  569. X
  570. X/** Functions within this file that need to be declared: **/
  571. Xvoid DeleteWordPlaces();
  572. Xstatic void FlushBlock();
  573. X
  574. Xt_WordPlace *GetWordPlaces();
  575. Xt_pblock *Getpblock();
  576. Xunsigned short PutWordPlaces();
  577. Xint _PutByte(), PutLong();
  578. Xunsigned char _GetByte();
  579. Xunsigned long GetLong();
  580. Xunsigned long FindFreeBlock();
  581. Xvoid FillWordPlaces();
  582. X/** **/
  583. X
  584. Xextern char *progname;
  585. X
  586. X/* If you find this macro confusing, see "The C Programming Language",
  587. X * Kernighan & Ritchie, 1st. Edition, for a good introduction to
  588. X * programming in C.
  589. X * :-( :-)
  590. X */
  591. X#define PutByte(Byte, WID, sp, Blockp, BlockLength, LastStart, NextBlock) \
  592. X    ( (*(sp) - (unsigned char *) *(Blockp) >= *(BlockLength)) ? \
  593. X       _PutByte(Byte, WID, sp, Blockp, BlockLength, LastStart, NextBlock) : \
  594. X      ((*((*(sp))++) = (Byte)), 0))
  595. X
  596. X#define GetByte(WID, sp, Blockp, BlockLength, NextBlock) \
  597. X    ( (*(sp) - (unsigned char *) *(Blockp) >= *(BlockLength)) ? \
  598. X       _GetByte(WID, sp, Blockp, BlockLength, NextBlock) : *((*(sp))++) )
  599. X
  600. Xextern long lseek();
  601. X
  602. Xstatic int DataFile = -1;
  603. X
  604. X#ifdef ASCIITRACE
  605. Xextern int AsciiTrace;
  606. X#endif
  607. X
  608. Xchar *ReadBlock();
  609. Xvoid WriteBlock();
  610. Xunsigned long Putpblock();
  611. X
  612. X/* Layout of the physical index database (OUT OF DATE)
  613. X * =====================================
  614. X *
  615. X * This file is the only interface to the database of FID/Offset pairs.
  616. X *
  617. X * The db is organised in blocks arranged in Tagliatelli format: a linked
  618. X * list of blocks for each WID; there is a list for each WID in the Word
  619. X * Index.  The Word Index contains the block number of the start of the
  620. X * chain.
  621. X * Block 0 is the start of the free list (but is never itself free!)
  622. X *
  623. X * block 0... Free list header; otherwise currently unused.
  624. X * block 1... first data block:
  625. X * +---------------------------
  626. X * | bytes 0...3: Offset of next block in this chain
  627. X * |       4...7: Number of valid pairs in this block (0-->unused block)
  628. X * |       8..11: WID to which this block refers (for checking)
  629. X * |      12..15: Total number of WIDS in the chain
  630. X * |          (only the first block in each chain has this)
  631. X * | The (FID, Offset) pairs follow, in compressed (Internet-style) format.
  632. X * |
  633. X * block 2... next data block (either the start of a new chain, or a
  634. X * continuation of some other chain.  Or maybe unused, especially if files
  635. X * have been deleted).
  636. X *
  637. X * The block header is described by t_BlockHeader.
  638. X *
  639. X */
  640. X
  641. X#include "blkheader.h"
  642. X
  643. X/* Look up a word in the database...
  644. X * and return a list of all the WordPlaces where it's found
  645. X * BUG: should be called WordInfo2pblock().
  646. X */
  647. Xt_pblock *
  648. XGetpblock(WordInfo)
  649. X    t_WordInfo *WordInfo;
  650. X{
  651. X    t_pblock *pblock = 0;
  652. X    unsigned long HowManyToGet = 0L;
  653. X    t_WordPlace *WordPlaces;
  654. X    unsigned long CurrentPair = 0L;
  655. X
  656. X#if 0
  657. X    /* This code allows one to call GetPblock with a very minimal
  658. X     * wordinfo entry:
  659. X     */
  660. X    if (WordInfo->Offset == 0L) {
  661. X    /* No database entry found yet... */
  662. X    if (!WordInfo->WID && WordInfo->Length) {
  663. X        WordInfo->WID = Word2WID(WordInfo->Word, WordInfo->Length);
  664. X    }
  665. X    if (WordInfo->WID) {
  666. X        WordInfo = WID2WordInfo(WordInfo->WID);
  667. X    }
  668. X    }
  669. X#endif
  670. X
  671. X    if (!WordInfo->NumberOfWordPlaces) {
  672. X    return (t_pblock *) 0; /* nothing to write */
  673. X    }
  674. X
  675. X    if (!WordInfo->Offset && !WordInfo->NumberOfWordPlaces) {
  676. X    /* no pblock offset, so no pblock! */
  677. X    return (t_pblock *) 0;
  678. X    }
  679. X
  680. X    HowManyToGet = WordInfo->NumberOfWordPlaces;
  681. X
  682. X    pblock = (t_pblock *) emalloc( sizeof(t_pblock) +
  683. X            (unsigned) (HowManyToGet + 1) * sizeof(t_WordPlace));
  684. X
  685. X    WordPlaces = pblock->WordPlaces;
  686. X    pblock->WID = WordInfo->WID;
  687. X    pblock->ChainStart = WordInfo->Offset;
  688. X    pblock->NumberOfWordPlaces = WordInfo->NumberOfWordPlaces;
  689. X
  690. X    /* First, the pairs in the WordInfo might suffice: */
  691. X    if (WordInfo->WordPlacesInHere >= HowManyToGet) {
  692. X    for (; CurrentPair < WordInfo->WordPlacesInHere; CurrentPair++) {
  693. X        WordPlaces[CurrentPair] = WordInfo->WordPlaces[CurrentPair];
  694. X    }
  695. X    }
  696. X
  697. X    /* If they all fitted in the WordInfo block, well, that was a big win! */
  698. X    if (CurrentPair >= HowManyToGet) {
  699. X    pblock->ChainStart = 0L;
  700. X    return pblock;
  701. X    }
  702. X
  703. X    /* So we need to read the entire list of WordPlaces from the database.
  704. X     * Although we may have already done the first few, I'm going to do them
  705. X     * all again because that ensures that the last few bytes in the
  706. X     * WordInfo data block can get used!
  707. X     */
  708. X    
  709. X    WordPlaces = GetWordPlaces(
  710. X        WordInfo->WID,
  711. X        WordInfo->WordPlaceStart,
  712. X        (unsigned) WIDBLOCKSIZE -
  713. X                (WordInfo->WordPlaceStart - WordInfo->DataBlock),
  714. X        WordInfo->Offset,
  715. X        HowManyToGet);
  716. X
  717. X  
  718. X  
  719. X    if (WordPlaces == (t_WordPlace *) 0) {
  720. X    fprintf(stderr, "%s: SNAFU: no wordplaces for WID %ld, wanted %ld\n",
  721. X            progname, WordInfo->WID, HowManyToGet);
  722. X    exit(1);
  723. X    }
  724. X
  725. X    /* copy the result... */
  726. X    (void) memcpy((char *) pblock->WordPlaces, (char *) WordPlaces,
  727. X                (int) (sizeof(t_WordPlace) * HowManyToGet));
  728. X    (void) efree((char *) WordPlaces);
  729. X    return pblock;
  730. X}
  731. X
  732. X/* This is how many WordPlaces one could write into a block in WIDFILE:
  733. X * The Header takes up 1 for the word length, MinWordLength or more for
  734. X * the actual word, and another 1 for the number of places.
  735. X * (32 - 5) / 3 is 27/3 is 9, but this is rather rare in practice!
  736. X * See the comment just before PutPairs().
  737. X * If this is not kept up to date, space may be wasted in the database,
  738. X * or you will slow down lqaddfile.
  739. X */
  740. X#define MaxWordPlacesInAWordBlock ((WIDBLOCKSIZE - (MinWordLength + 2)/3))
  741. X
  742. X/* This should in fact take a (t_WordPlaceList *) or something.
  743. X * It returns the number of words written, for checking in
  744. X * DumpCache (wordtable.c)
  745. X */
  746. Xint
  747. XWriteWordChain(WordPlaceList)
  748. X    t_WordPlaceList *WordPlaceList;
  749. X{
  750. X    extern t_WID GetNextWID();
  751. X    extern t_WordInfo *FindWordInfoFromIndex();
  752. X
  753. X    t_WID WID;
  754. X    int Length;
  755. X    t_pblock *pblock = (t_pblock *) 0;
  756. X    int NumberOfWordPlacesToAdd = 0;
  757. X    t_WordPlaceList *WP;
  758. X    t_WordInfo *IndexEntry = 0;
  759. X    char *FirstWord;
  760. X    register int i;
  761. X    int TotalWordsWritten = 0;
  762. X
  763. X    if (!WordPlaceList) return 0; /* nothing to do */
  764. X
  765. X    /* Sanity check: */
  766. X    if (!(WordPlaceList->Word) || !*(WordPlaceList->Word)) {
  767. X    fprintf(stderr, "Warning: WriteWordChain() asked to write null word\n");
  768. X    return 0;
  769. X    }
  770. X
  771. X    Length = strlen(WordPlaceList->Word);
  772. X
  773. X    /* This is undoubtedly a bad way to do things:
  774. X     * if IndexEntry is big, we don't want to fetch it when we could
  775. X     * simply stuff things on the end!
  776. X     */
  777. X    if ((WID = Word2WID(WordPlaceList->Word, Length)) != (t_WID) 0) {
  778. X    if ((IndexEntry = WID2WordInfo(WID)) != (t_WordInfo *) 0) {
  779. X        pblock = Getpblock(IndexEntry);
  780. X    }
  781. X    }
  782. X
  783. X    if (!WID) {
  784. X    WID = GetNextWID();
  785. X    }
  786. X
  787. X#ifdef ASCIITRACE
  788. X    if (AsciiTrace >= 3) {
  789. X    fprintf(stderr, "Entry %lu for \"%s\"", WID, WordPlaceList->Word);
  790. X    if (IndexEntry) {
  791. X        fprintf(stderr, ", had %d pairs\n", IndexEntry->NumberOfWordPlaces);
  792. X    } else {
  793. X        fprintf(stderr, " (new entry)\n");
  794. X    }
  795. X    }
  796. X#endif
  797. X
  798. X    /* Count the number of entries we are going to add.
  799. X     * There may be several different words in the chain, but they are
  800. X     * sorted in word order.
  801. X     */
  802. X    FirstWord = WordPlaceList->Word;
  803. X
  804. X    for (WP = WordPlaceList; WP; WP = WP->Next) {
  805. X    if (!STREQ(WP->Word, FirstWord)) break;
  806. X    ++NumberOfWordPlacesToAdd;
  807. X    if (WP != WordPlaceList) {
  808. X        WP->Word = (char *) 0; /* so we don't free it; see AddWord() */
  809. X    }
  810. X    }
  811. X
  812. X    if (pblock == (t_pblock *) 0) {
  813. X    int oldnp = (IndexEntry) ? IndexEntry->NumberOfWordPlaces : 0;
  814. X
  815. X    pblock = (t_pblock *) emalloc(sizeof(t_pblock) +
  816. X        (NumberOfWordPlacesToAdd + oldnp) * sizeof(t_WordPlace));
  817. X    pblock->NumberOfWordPlaces = 0;
  818. X    if (IndexEntry && IndexEntry->NumberOfWordPlaces) {
  819. X        register t_WordPlace *W, *Q;
  820. X        register unsigned long boy = 0L;
  821. X  
  822. X        if (IndexEntry->WordPlacesInHere < IndexEntry->NumberOfWordPlaces) {
  823. X        FillWordPlaces(IndexEntry);
  824. X        }
  825. X
  826. X        for (W = IndexEntry->WordPlaces, Q = pblock->WordPlaces;
  827. X                boy < IndexEntry->NumberOfWordPlaces; boy++) {
  828. X        *Q++ = *W++;
  829. X        }
  830. X        pblock->NumberOfWordPlaces = IndexEntry->NumberOfWordPlaces;
  831. X    }
  832. X    } else {
  833. X    /* Remove the old information from disk.
  834. X     * This isn't as bad as it sounds, as it will be at the start
  835. X     * of the freelist, so when we write it out again it will be
  836. X     * in the buffer cache...
  837. X     * Although, it would be faster simply to append.
  838. X     */
  839. X    if (IndexEntry->Offset) {
  840. X        DeleteWordPlaces(IndexEntry->Offset, IndexEntry->WID);
  841. X    }
  842. X    /*NOSTRICT*/
  843. X    pblock = (t_pblock *) erealloc((char *) pblock, sizeof(t_pblock) +
  844. X        (unsigned) (NumberOfWordPlacesToAdd +
  845. X            pblock->NumberOfWordPlaces) * sizeof(t_WordPlace));
  846. X    }
  847. X
  848. X    pblock->WID = WID;
  849. X    pblock->ChainStart = 0L; /* certainly it is now invalid! */
  850. X    i = pblock->NumberOfWordPlaces;
  851. X    pblock->NumberOfWordPlaces += NumberOfWordPlacesToAdd;
  852. X
  853. X    for (WP = WordPlaceList; WP && NumberOfWordPlacesToAdd--; WP = WP->Next) {
  854. X    /* As we ignore the rest of these WordInfo entries, something
  855. X     * has to change here for more efficiency!
  856. X     * Probably we should get passed a linked list of WordPlaces
  857. X     * instead of WordInfo structures.
  858. X     */
  859. X    pblock->WordPlaces[i] = WP->WordPlace;
  860. X    i++;
  861. X    }
  862. X
  863. X    if (WP && STREQ(WP->Word, FirstWord)) {
  864. X    fprintf(stderr, "Internal error counting pairs to add\n");
  865. X    exit(1);
  866. X    }
  867. X
  868. X    if (NumberOfWordPlacesToAdd > 0) {
  869. X    fprintf(stderr, "I've got some pairs left over, \"%s\" line %d\n",
  870. X            __FILE__, __LINE__ - 1);
  871. X    exit(1);
  872. X    }
  873. X
  874. X    /* We are going to need a WordInfo here... */
  875. X    if (IndexEntry == (t_WordInfo *) 0) {
  876. X    IndexEntry = MakeWordInfo(WID, Length, WordPlaceList->Word);
  877. X    }
  878. X    IndexEntry->NumberOfWordPlaces = pblock->NumberOfWordPlaces;
  879. X
  880. X    /* Now, see if they all fit into the WordInfo itself! */
  881. X
  882. X    pblock->ChainStart = IndexEntry->Offset = 0L;
  883. X
  884. X    if (pblock->NumberOfWordPlaces <= MaxWordPlacesInAWordBlock) {
  885. X    (void) MkWIB(IndexEntry, pblock);
  886. X    }
  887. X
  888. X    if (IndexEntry->WordPlacesInHere == pblock->NumberOfWordPlaces) { 
  889. X    /* no pblock needed! */
  890. X    if (PutWordInfoIntoIndex(IndexEntry, (unsigned long) 0L) < 0) {
  891. X        extern int errno;
  892. X        int e = errno;
  893. X        fprintf(stderr, "%s: Couldn't put \"%s\" into the ",
  894. X                        progname, IndexEntry->Word);
  895. X        errno = e;
  896. X        perror("index");
  897. X        exit(1);
  898. X    }
  899. X    } else {
  900. X    (void) Putpblock(IndexEntry, pblock); /* (this alters *WordInfo) */
  901. X
  902. X    if (PutWordInfoIntoIndex(IndexEntry, pblock->ChainStart) < 0) {
  903. X        extern int errno;
  904. X        int e = errno;
  905. X        fprintf(stderr, "%s: Couldn't add word \"%s\" to the ",
  906. X                        progname, IndexEntry->Word);
  907. X        errno = e;
  908. X        perror("word index");
  909. X        exit(1);
  910. X    }
  911. X    }
  912. X
  913. X    if (pblock) {
  914. X    efree((char *) pblock);
  915. X    pblock = (t_pblock *) 0;
  916. X    }
  917. X
  918. X    if (IndexEntry) {
  919. X    SlayWordInfo(IndexEntry);
  920. X    }
  921. X
  922. X    TotalWordsWritten += NumberOfWordPlacesToAdd;
  923. X
  924. X    /* Now see if there are any more words in the chain: */
  925. X    if (WP) {
  926. X    TotalWordsWritten += WriteWordChain(WP);
  927. X    }
  928. X
  929. X    return TotalWordsWritten;
  930. X}
  931. X
  932. Xstatic unsigned char pblockBuffer[BLOCKSIZE + 5];
  933. X
  934. X/* Write out an entire (presumably new) data entry, and
  935. X * return a disk pointer to the start of the chain
  936. X */
  937. Xunsigned long
  938. XPutpblock(WordInfo, pblock)
  939. X    t_WordInfo *WordInfo;
  940. X    t_pblock *pblock;
  941. X{
  942. X    /* Assume that we can discard the PairBlock in WordInfo --
  943. X     * it was a pointer to a static buffer anyway.
  944. X     */
  945. X
  946. X    if (WordInfo->DataBlock) {
  947. X    WordInfo->DataBlock = (char *) 0;
  948. X    }
  949. X
  950. X    WordInfo->Offset = pblock->ChainStart = FindFreeBlock(WordInfo->WID);
  951. X    (void) MkWIBH(WordInfo, pblock);
  952. X    PutWordPlaces(
  953. X        pblock->WordPlaces,
  954. X        WordInfo->WID,
  955. X        (unsigned char *) WordInfo->WordPlaceStart,
  956. X        (unsigned)
  957. X        WIDBLOCKSIZE - (WordInfo->WordPlaceStart - WordInfo->DataBlock),
  958. X        WordInfo->Offset,
  959. X        pblock->NumberOfWordPlaces);
  960. X
  961. X    return WordInfo->Offset;
  962. X}
  963. X
  964. X/** WordPlaces are now stored as sequences, as follows:
  965. X **  FID*2 -- 1, 2, 3 (usually) or 4 bytes                1-4
  966. X **  (very, very occasionaly a variable-size number may be 5 bytes.)
  967. X **   . the bottom bit in the stored number determines whether there
  968. X **     is more than one FID to follow
  969. X **  number of following places (only if prev. bit was 1) -- 1 byte    0-1
  970. X ** For each following entry:-
  971. X **   . for each of the following places:
  972. X **     Block In File (long, 1, 2, 3 or 4 bytes, usually 1)        1-4
  973. X **     Word In Block -- always 1 byte                    1-1
  974. X **        the bottom bit of this says if there are flags
  975. X **     Flags -- always 1 byte, if present                0-1
  976. X **        Stuff Before -- 1 byte                    0-1
  977. X **        (if there are no flags, there's no Stuff Before byte, and
  978. X **     we use the default value of 1)
  979. X **
  980. X ** Hence:    each sub-place takes from 2 to 7 bytes [was: 0 to 4]
  981. X **        each Place sequence takes from 3 [min was 2 with old format]
  982. X **        to (4 + 1) + 255 * (2..7) bytes.
  983. X **        In most (I guess > 7/10) cases, flags will be 0, and
  984. X **        StuffBefore be the default of 1.
  985. X **
  986. X **    I am hoping, of course, that the extra information stored is
  987. X ** worth while!
  988. X **    It might be possible to coalesce WordInBlock and BlockInFile using
  989. X ** delta modulation -- i.e., storing the increment from the previous.  In
  990. X ** this case, a flag bit could mean that those two values each occupy a
  991. X ** nibble in a single byte.  Or, I could use a single byte, like this:
  992. X **    [a b c d e f g h]
  993. X **    a == 1 --> (efgh) is word in block inc., (bcd is block in file inc)
  994. X ** but I need to do some real measurements to figure out how best to save
  995. X ** space.  It really is worth while keeping the format as simple as I can,
  996. X ** as this speeds retrieval.
  997. X **
  998. X **/
  999. X
  1000. Xunsigned short
  1001. XPutWordPlaces(WordPlaces, WID, Block, BlockLength, NextOffset, NumberToWrite)
  1002. X    t_WordPlace *WordPlaces;
  1003. X    t_WID WID;
  1004. X    unsigned char *Block;
  1005. X    unsigned BlockLength;
  1006. X    unsigned long NextOffset;
  1007. X    unsigned long NumberToWrite;
  1008. X{
  1009. X    unsigned char *q = Block;
  1010. X    unsigned long L;
  1011. X    int CurrentPlace = 0;
  1012. X    unsigned long LastStart = 0L;
  1013. X    t_FID LastFID = 0;
  1014. X    unsigned long LastBlock = 0L;
  1015. X
  1016. X    /** IMPORTANT NOTICE:  keep the definition of MaxWordPlacesInAWordBlock
  1017. X     ** up to date (it's #defined above).
  1018. X     **/
  1019. X
  1020. X    /* sort the pblock to simplify subsequent accesses */
  1021. X    if (NumberToWrite > 1) {
  1022. X    SortWordPlaces(NumberToWrite, WordPlaces);
  1023. X    }
  1024. X
  1025. X    while (CurrentPlace < 0 || CurrentPlace < NumberToWrite) {
  1026. X    unsigned short NumberOfRepeats;
  1027. X    unsigned char U;
  1028. X    t_FID FID = WordPlaces[CurrentPlace].FID;
  1029. X    int LastPlace;
  1030. X
  1031. X    /* Determine the number of Places in the same file;
  1032. X     * note that we can write at most 255 in the same place, so
  1033. X     * longer stretches are broken up into clumps of 255.
  1034. X     * This is a reasonable tradeoff, I think.  The alternative would
  1035. X     * be to write NumberOfRepeats as a long, and lose if there were
  1036. X     * (say) between 64 (old, 127 new) and 255 of them.  This case only
  1037. X     * occurs once in the New Testament anyway, and presumably is
  1038. X     * generally quite rare.
  1039. X     */
  1040. X    NumberOfRepeats = 0;
  1041. X    LastPlace = CurrentPlace;
  1042. X    while (NumberOfRepeats < 255) {
  1043. X        if (LastPlace >= NumberToWrite) {
  1044. X        break;
  1045. X        } else if (WordPlaces[LastPlace].FID != FID) {
  1046. X        break;
  1047. X        }
  1048. X        ++NumberOfRepeats;
  1049. X        ++LastPlace;
  1050. X    }
  1051. X
  1052. X    L = (FID - LastFID) << 1;
  1053. X    LastFID = FID;
  1054. X    if (NumberOfRepeats > 1) L |= 01L;
  1055. X    if (PutLong(L, WID, &q, &Block, &BlockLength,
  1056. X                        &NextOffset, &LastStart) < 0) {
  1057. X        return CurrentPlace;
  1058. X    }
  1059. X    if (L & 01L) {
  1060. X        if (PutByte(NumberOfRepeats, WID, &q, &Block, &BlockLength,
  1061. X                        &LastStart, &NextOffset) < 0) {
  1062. X        return CurrentPlace;
  1063. X        }
  1064. X    }
  1065. X
  1066. X    LastBlock = 0;
  1067. X
  1068. X    for (; NumberOfRepeats != 0; --NumberOfRepeats) {
  1069. X        if (CurrentPlace > NumberToWrite) {
  1070. X        fprintf(stderr,
  1071. X        "Entry for file %lu (WID %ld) has more matches than expected\n",
  1072. X                                    FID, WID);
  1073. X        exit(1);
  1074. X        /* This would represent a rather serious bug, I think! */
  1075. X        }
  1076. X        /* block number */
  1077. X        if (WordPlaces[CurrentPlace].BlockInFile < LastBlock) {
  1078. X        fprintf(stderr, "Sort WID %ld failed, non-monatonic blocks\n",
  1079. X                                    WID);
  1080. X        exit(1); /* can't cope with this one */
  1081. X        } else if (CurrentPlace &&
  1082. X            (WordPlaces[CurrentPlace].FID ==
  1083. X                WordPlaces[CurrentPlace - 1].FID) &&
  1084. X            (WordPlaces[CurrentPlace].BlockInFile == LastBlock) &&
  1085. X            (WordPlaces[CurrentPlace].WordInBlock <=
  1086. X                WordPlaces[CurrentPlace - 1].WordInBlock)) {
  1087. X        fprintf(stderr,
  1088. X            "Sort WID %ld failed, FID %ld: Blk %d: WIB %d <= %d\n",
  1089. X            WID, FID, LastBlock, WordPlaces[CurrentPlace].WordInBlock,
  1090. X            WordPlaces[CurrentPlace - 1].WordInBlock
  1091. X        );
  1092. X        }
  1093. X        L = WordPlaces[CurrentPlace].BlockInFile - LastBlock;
  1094. X        LastBlock += L;
  1095. X
  1096. X        if (PutLong(L, WID, &q, &Block, &BlockLength, &NextOffset,
  1097. X                                &LastStart) < 0) {
  1098. X        return CurrentPlace;
  1099. X        }
  1100. X        U = (WordPlaces[CurrentPlace].WordInBlock << 1);
  1101. X        if (WordPlaces[CurrentPlace].StuffBefore != 1) {
  1102. X        WordPlaces[CurrentPlace].Flags |= WPF_HASSTUFFBEFORE;
  1103. X        }
  1104. X        if (WordPlaces[CurrentPlace].Flags != WPF_HASSTUFFBEFORE) {
  1105. X        U |= 01;
  1106. X        }
  1107. X        if (U > 255) {
  1108. X        fprintf(stderr,
  1109. X        "WID %lu: WordInBlock (0%o) from FID %lu too big\n",
  1110. X            WID, U, FID);
  1111. X        exit(1);
  1112. X        }
  1113. X
  1114. X        if (PutByte(U, WID, &q, &Block, &BlockLength, &LastStart,
  1115. X                            &NextOffset) < 0) {
  1116. X        return CurrentPlace;
  1117. X        }
  1118. X        if (U & 01) {
  1119. X        if (PutByte(WordPlaces[CurrentPlace].Flags, WID, &q, &Block,
  1120. X                &BlockLength, &LastStart, &NextOffset) < 0) {
  1121. X            return CurrentPlace;
  1122. X        }
  1123. X        }
  1124. X
  1125. X        /* Even if there are flags, there still might not be a separate
  1126. X         * entry for the number of preceding skipped bytes.
  1127. X         */
  1128. X        if ((U & 01) &&
  1129. X            (WordPlaces[CurrentPlace].Flags & WPF_HASSTUFFBEFORE)) {
  1130. X        if (PutByte(WordPlaces[CurrentPlace].StuffBefore, WID, &q,
  1131. X            &Block, &BlockLength, &LastStart, &NextOffset) < 0) {
  1132. X            return CurrentPlace;
  1133. X        }
  1134. X        }
  1135. X        ++CurrentPlace;
  1136. X    }
  1137. X    if (CurrentPlace > LastPlace) {
  1138. X        fprintf(stderr, "oops- I went wrong and wrote %ld > %ld\n",
  1139. X                            CurrentPlace, LastPlace);
  1140. X    }
  1141. X    }
  1142. X    if (LastStart) {
  1143. X    /* NextStart had better not be non-zero, but FlushBlock will
  1144. X     * take care of it (we have wasted a block in that case!).
  1145. X     * LastStart is zero if we fitted it all inside the WordInfo
  1146. X     * block, although this is currently unlikely as we don't get
  1147. X     * called in that case!
  1148. X     * Oh, maybe we do.
  1149. X     */
  1150. X    FlushBlock((unsigned char *) Block, &NextOffset, &LastStart, WID);
  1151. X    }
  1152. X    return NumberToWrite;
  1153. X}
  1154. X
  1155. Xt_WordPlace *
  1156. XGetWordPlaces(WID, Block, BlockLength, NextOffset, NumberExpected)
  1157. X    t_WID WID;
  1158. X    char *Block;
  1159. X    unsigned BlockLength;
  1160. X    unsigned long NextOffset;
  1161. X    unsigned long NumberExpected;
  1162. X{
  1163. X    unsigned char *q = (unsigned char *) Block;
  1164. X    unsigned long L;
  1165. X    t_WordPlace *Places = (t_WordPlace *) 0;
  1166. X    long CurrentPlace = 0;
  1167. X    t_FID LastFID = (t_FID) 0;
  1168. X    unsigned LastBlock = 0L;
  1169. X
  1170. X#ifdef ASCIITRACE
  1171. X    if (AsciiTrace > 10) {
  1172. X    fprintf(stderr,
  1173. X        "GetWordPlaces WID %ld Blk %ld len %d next %ld No. %ld\n",
  1174. X            WID, Block, BlockLength, NextOffset, NumberExpected);
  1175. X            
  1176. X    }
  1177. X#endif
  1178. X
  1179. X    if (Block == (char *) 0) {
  1180. X    fprintf(stderr, "GetWordPlaces Error %lu\n", WID);
  1181. X    }
  1182. X
  1183. X    /*NOSTRICT*/
  1184. X    Places = (t_WordPlace *) emalloc(sizeof(t_WordPlace) * NumberExpected);
  1185. X
  1186. X    while (CurrentPlace < NumberExpected) {
  1187. X    unsigned short NumberOfRepeats;
  1188. X    unsigned char Uchar;
  1189. X    t_FID FID;
  1190. X
  1191. X    /** First get the FID.  The bottom bit of the number stored
  1192. X     ** actually determines whether there are multiple Places
  1193. X     ** stored here for the same FID.
  1194. X     **/
  1195. X    L = GetLong(WID, &q, &Block, &BlockLength, &NextOffset);
  1196. X    FID = (L >> 1) + LastFID; /* Get rid of flag bit */
  1197. X    LastFID = FID;
  1198. X    NumberOfRepeats = (L & 01L) ? 
  1199. X        GetByte(WID, &q, &Block, &BlockLength, &NextOffset) : 1;
  1200. X
  1201. X    /* Quick Sanity check */
  1202. X    switch (NumberOfRepeats) {
  1203. X    case 0L:
  1204. X        fprintf(stderr, "Warning: no entries! for FID %lu\n", FID);
  1205. X    case 1L:
  1206. X        if (L & 01L) {
  1207. X        fprintf(stderr, "Warning: FID %lu repeated 1 times!\n", FID);
  1208. X        }
  1209. X    }
  1210. X
  1211. X    LastBlock = 0L;
  1212. X    if (CurrentPlace + NumberOfRepeats > NumberExpected) {
  1213. X        fprintf(stderr,
  1214. X        "Entry for file %lu WID %ld has %lu matches, not %lu\n",
  1215. X        FID, WID, CurrentPlace + NumberOfRepeats + 1, NumberExpected);
  1216. X        NumberOfRepeats = NumberExpected - CurrentPlace;
  1217. X    }
  1218. X    for (; NumberOfRepeats != 0; --NumberOfRepeats) {
  1219. X        Places[CurrentPlace].FID = FID;
  1220. X        /* block number */
  1221. X        L = GetLong(WID, &q, &Block, &BlockLength, &NextOffset);
  1222. X        LastBlock += L;
  1223. X        Places[CurrentPlace].BlockInFile = LastBlock;
  1224. X        Uchar = GetByte(WID, &q, &Block, &BlockLength, &NextOffset);
  1225. X        Places[CurrentPlace].WordInBlock = (Uchar >> 1);
  1226. X
  1227. X        /* Sanity check: */
  1228. X        if (CurrentPlace > 0 && Places[CurrentPlace].FID ==
  1229. X                    Places[CurrentPlace - 1].FID) {
  1230. X        if (Places[CurrentPlace - 1].BlockInFile ==
  1231. X                    Places[CurrentPlace].BlockInFile) {
  1232. X            if ( Places[CurrentPlace - 1].WordInBlock >=
  1233. X                Places[CurrentPlace].WordInBlock) {
  1234. X            fprintf(stderr,
  1235. X    "%s: warning: %dth match for word %ld (FID %ld) WIB goes backwards!\n",
  1236. X                    progname, CurrentPlace, WID, FID);
  1237. X            }
  1238. X        } else if (Places[CurrentPlace - 1].BlockInFile >
  1239. X                Places[CurrentPlace].BlockInFile) {
  1240. X            fprintf(stderr,
  1241. X    "%s: warning: %dth match for word %ld (FID %ld) BIF goes backwards!\n",
  1242. X                    progname, CurrentPlace, WID, FID);
  1243. X        }
  1244. X        }
  1245. X        /* end of sanity test */
  1246. X
  1247. X        if (Uchar & 01) { /* use if, not ?:, for profiler */
  1248. X        Places[CurrentPlace].Flags = 
  1249. X            GetByte(WID, &q, &Block, &BlockLength, &NextOffset);
  1250. X        } else {
  1251. X        Places[CurrentPlace].Flags = 0;
  1252. X        }
  1253. X
  1254. X        /* If there are flags, there still might not be a separate
  1255. X         * entry for the number of preceding skipped bytes.
  1256. X         */
  1257. X        if (Places[CurrentPlace].Flags & WPF_HASSTUFFBEFORE) {
  1258. X        Places[CurrentPlace].StuffBefore = 
  1259. X            GetByte(WID, &q, &Block, &BlockLength, &NextOffset);
  1260. X        } else {
  1261. X        Places[CurrentPlace].StuffBefore = 1;
  1262. X        }
  1263. X        ++CurrentPlace;
  1264. X    }
  1265. X    }
  1266. X    return Places;
  1267. X}
  1268. X
  1269. Xvoid
  1270. XFillWordPlaces(WordInfo)
  1271. X    t_WordInfo *WordInfo;
  1272. X{
  1273. X    WordInfo->WordPlaces = GetWordPlaces(
  1274. X        WordInfo->WID,
  1275. X        WordInfo->WordPlaceStart,
  1276. X        WIDBLOCKSIZE - (WordInfo->WordPlaceStart - WordInfo->DataBlock),
  1277. X        WordInfo->Offset,
  1278. X        WordInfo->NumberOfWordPlaces
  1279. X    );
  1280. X}
  1281. X
  1282. Xstatic void
  1283. XFlushBlock(Block, NextOffset, LastStart, WID)
  1284. X    char *Block;
  1285. X    unsigned long *NextOffset, *LastStart;
  1286. X    t_WID WID;
  1287. X{
  1288. X    if (*LastStart && Block) {
  1289. X    /*NOSTRICT*/
  1290. X    t_BlockHeader *BH = (t_BlockHeader *) Block;
  1291. X
  1292. X    BH->NextOffset = ((*NextOffset) / BLOCKSIZE) << 1;
  1293. X    WriteBlock(*LastStart, Block);
  1294. X    }
  1295. X    *LastStart = *NextOffset = 0L;
  1296. X}
  1297. X
  1298. X/* This is simply to help keep the source lines getting too long! */
  1299. Xtypedef unsigned char *UCP;
  1300. X
  1301. Xint
  1302. X_PutByte(Byte, WID, sp, Blockp, BlockLength, LastStart, NextBlock)
  1303. X    unsigned char Byte;
  1304. X    t_WID WID;
  1305. X    unsigned char **sp;
  1306. X    unsigned char **Blockp;
  1307. X    unsigned *BlockLength;
  1308. X    unsigned long *NextBlock;
  1309. X    unsigned long *LastStart; /* for writing the linked list */
  1310. X{
  1311. X    t_BlockHeader *BH;
  1312. X
  1313. X    if (*sp - (*Blockp) >= (*BlockLength)) {
  1314. X    if (!*NextBlock && !*LastStart) return -1; /* only do the 1st block */
  1315. X    if (*NextBlock == (unsigned long) 0) {
  1316. X        *NextBlock = FindFreeBlock(WID);
  1317. X    }
  1318. X    /* Complete the information in the previous block, if required */
  1319. X    if (*LastStart) {
  1320. X        BH = (t_BlockHeader *) (*Blockp);
  1321. X        BH->NextOffset = ((*NextBlock) / BLOCKSIZE) << 1;
  1322. X        /* Write the old block */
  1323. X        WriteBlock(*LastStart, *Blockp);
  1324. X        *LastStart = 0L;
  1325. X    }
  1326. X    *LastStart = (*NextBlock);
  1327. X    *BlockLength = BLOCKSIZE;
  1328. X    (*NextBlock) = 0L;
  1329. X    *Blockp = pblockBuffer; /* Use static (to this file) data buffer */
  1330. X    /*NOSTRICT*/
  1331. X    BH = (t_BlockHeader *) (*Blockp);
  1332. X    (*sp) = (UCP) BH->Data;
  1333. X    }
  1334. X    **sp = Byte;
  1335. X    (*sp)++;
  1336. X    return 0;
  1337. X}
  1338. X
  1339. Xunsigned char
  1340. X_GetByte(WID, sp, Blockp, BlockLength, NextBlock)
  1341. X    t_WID WID;
  1342. X    unsigned char **sp;
  1343. X    unsigned char **Blockp;
  1344. X    unsigned long *BlockLength;
  1345. X    unsigned long *NextBlock;
  1346. X{
  1347. X    t_BlockHeader *BH;
  1348. X
  1349. X    if (*sp - (*Blockp) >= (*BlockLength)) {
  1350. X    if (*NextBlock == (unsigned long) 0) {
  1351. X        (*Blockp) = (*sp) = (UCP) 0;
  1352. X        fprintf(stderr, "Database Corrupt, Next is zero\n");
  1353. X        return 0;
  1354. X    } else {
  1355. X        (*sp) = (*Blockp) = (UCP) ReadBlock(*NextBlock);
  1356. X    }
  1357. X    /* Check the new block */
  1358. X    if ((*Blockp) == (UCP) 0) {
  1359. X        fprintf(stderr, "Database corrupt, %lu, sigh.\n", *NextBlock);
  1360. X        exit(1);
  1361. X    }
  1362. X    /*NOSTRICT*/
  1363. X    BH = (t_BlockHeader *) (*Blockp);
  1364. X    *NextBlock = (BH->NextOffset >> 1) * BLOCKSIZE;
  1365. X    *BlockLength = BLOCKSIZE;
  1366. X    (*sp) = (UCP) BH->Data;
  1367. X    }
  1368. X    return *((*sp)++);
  1369. X}
  1370. X
  1371. X/* PutLong -- write a long number in compressed/abbreviated form into a
  1372. X * string.  If this moves the string pointer beyond the block, write out
  1373. X * the block and start a new one.  In that case, the number written may well
  1374. X * span the gap between the blocks.  We use an overflow buffer to copy
  1375. X * the bytes (if any) that overflowed into it.
  1376. X * Then we write them at the start of the next block.
  1377. X *
  1378. X * This routine returns -1 and writes a partial number (no allocated block)
  1379. X * if *LastBlock and *NextBlock are zero.  This allows PutwOrdPlaces to be
  1380. X * called to put the WordPlaces into the WIDFILE block without writing out
  1381. X * an entire chain.
  1382. X */
  1383. Xint
  1384. XPutLong(Long, WID, sp, Blockp, BlockLength, NextBlock, LastStart)
  1385. X    unsigned long Long;
  1386. X    t_WID WID;
  1387. X    unsigned char **sp;
  1388. X    unsigned char **Blockp;
  1389. X    unsigned *BlockLength;
  1390. X    unsigned long *NextBlock;
  1391. X    unsigned long *LastStart; /* for writing the linked list */
  1392. X{
  1393. X    t_BlockHeader *BH;
  1394. X    unsigned char Buffer[sizeof(unsigned long) + 1];
  1395. X    unsigned char *Bufp = Buffer;
  1396. X    unsigned char *p;
  1397. X
  1398. X    sWriteNumber((char **) sp, Long);
  1399. X
  1400. X    if ((*sp) - (*Blockp) > (*BlockLength)) { /* gone too far! */
  1401. X    if (!*NextBlock && !*LastStart) return -1;
  1402. X    /* Save the overflow in Buffer:
  1403. X     * the 1st 1 or more characters will fitted into the old block,
  1404. X     * but we need them all in a lump for readnumber().
  1405. X     * When we write the next block, we need to put the overflow
  1406. X     * characters into the start of the next block.
  1407. X     */
  1408. X    for (p = &(*Blockp)[*BlockLength]; p < (*sp); p++) {
  1409. X        *Bufp++ = *p;
  1410. X    }
  1411. X    if (*NextBlock == (unsigned long) 0) {
  1412. X        *NextBlock = FindFreeBlock(WID);
  1413. X    }
  1414. X    /* Complete the information in the previous block, if required */
  1415. X    if (*LastStart) {
  1416. X        BH = (t_BlockHeader *) (*Blockp);
  1417. X        BH->NextOffset = ((*NextBlock) / BLOCKSIZE) << 1;
  1418. X
  1419. X        /* Write the old block */
  1420. X        WriteBlock(*LastStart, *Blockp);
  1421. X    }
  1422. X    *LastStart = (*NextBlock);
  1423. X    (*NextBlock) = 0L;
  1424. X    *Blockp = pblockBuffer;
  1425. X    BH = (t_BlockHeader *) (*Blockp);
  1426. X    *BlockLength = BLOCKSIZE;
  1427. X    (*sp) = (UCP) BH->Data;
  1428. X    /* Now write the stuff from Buffer into the new block */
  1429. X    if (Bufp > Buffer) {
  1430. X        for (p = Buffer; p < Bufp; p++) {
  1431. X        *((*sp)++) = (*p);
  1432. X        }
  1433. X    }
  1434. X    }
  1435. X    return 0;
  1436. X}
  1437. X
  1438. X/* This is the reverse of PutLong.
  1439. X * Things are slightly complicated by the need to provide sReadNumber
  1440. X * with a contiguous copy of all of the bytes in a number that spanned
  1441. X * a gap between data blocks.
  1442. X */
  1443. Xunsigned long
  1444. XGetLong(WID, sp, Blockp, BlockLength, NextBlock)
  1445. X    t_WID WID;
  1446. X    unsigned char **sp;
  1447. X    unsigned char **Blockp;
  1448. X    unsigned *BlockLength;
  1449. X    unsigned long *NextBlock;
  1450. X{
  1451. X    unsigned char Buffer[sizeof(unsigned long) + 1];
  1452. X    long Result;
  1453. X    t_BlockHeader *BH;
  1454. X    unsigned char *NumberStart = (*sp);
  1455. X    unsigned char *p;
  1456. X
  1457. X    Result = sReadNumber(sp);
  1458. X
  1459. X    /* Now, have we fallen off the end of the block? */
  1460. X    if ((*sp) - (*Blockp) > (*BlockLength)) {
  1461. X    unsigned char *bp = Buffer;
  1462. X
  1463. X    if (*NextBlock == (unsigned long) 0) {
  1464. X        return 0L;
  1465. X    }
  1466. X
  1467. X    /* Copy the first half of the number into the overflow buffer */
  1468. X    for (p = NumberStart; p < &(*Blockp)[*BlockLength]; p++) {
  1469. X        *bp++ = *p;
  1470. X    }
  1471. X
  1472. X    /** Now:
  1473. X     ** . sp is garbage, as is NumberStart, as they point at the old
  1474. X     **   data block
  1475. X     ** . Buffer contains the first few bytes of the number
  1476. X     ** . we need some more bytes, but don't yet know how many, as
  1477. X     **   this depends on the number representation
  1478. X     **   NOTE that we must have, however, that we know that there
  1479. X     **   are more bytes, so that we know if we need the next block.
  1480. X     ** . bp points 1 beyond the end of the 1st half of the number.
  1481. X     **/
  1482. X
  1483. X    (*sp) = *Blockp = (UCP) ReadBlock(*NextBlock);
  1484. X    /* Check the new block */
  1485. X    if ((*Blockp) == (UCP) 0) {
  1486. X        fprintf(stderr, "Database corrupt, %lu, oh dear!\n", *NextBlock);
  1487. X        exit(1);
  1488. X    }
  1489. X    BH = (t_BlockHeader *) *Blockp;
  1490. X    *NextBlock = (BH->NextOffset >> 1) * BLOCKSIZE;
  1491. X    *BlockLength = BLOCKSIZE;
  1492. X    (*sp) = (UCP) BH->Data;
  1493. X    /* Fill up the buffer from the new block */
  1494. X    for (p = bp; p - Buffer < sizeof(Buffer) - 1; p++) {
  1495. X        *p = *(*sp)++;
  1496. X    }
  1497. X    /* read the number from the buffer */
  1498. X    (*sp) = Buffer;
  1499. X    /* Try that number again... */
  1500. X    Result = sReadNumber(sp);
  1501. X    /* Now put sp where it should be.  Part of the buffer was
  1502. X     * from the old block...
  1503. X     */
  1504. X    (*sp) = (UCP) BH->Data + ((*sp) - bp);
  1505. X    }
  1506. X    return Result;
  1507. X}
  1508. X
  1509. Xvoid
  1510. XWriteBlock(Block, Data)
  1511. X    unsigned long Block;
  1512. X    char *Data;
  1513. X{
  1514. X    if (DataFile < 0) {
  1515. X    if ((DataFile = open(DataBase, O_RDWR|O_CREAT, 0666)) < 0) {
  1516. X        fprintf(stderr, "Can't open database");
  1517. X        perror(DataBase);
  1518. X        exit(1);
  1519. X    }
  1520. X    }
  1521. X
  1522. X    if (lseek(DataFile, (long) Block, 0) < 0) {
  1523. X    perror("lseek");
  1524. X    exit(1);
  1525. X    }
  1526. X
  1527. X    if (write(DataFile, Data, BLOCKSIZE) != BLOCKSIZE) {
  1528. X    extern int errno;
  1529. X    int e = errno;
  1530. X    fprintf(stderr,
  1531. X    "Warning: %s: E%d -- couldn't write %ld bytes at block %lu: ",
  1532. X                        DataBase, e, BLOCKSIZE, Block);
  1533. X    errno = e;
  1534. X    perror("write");
  1535. X    }
  1536. X    return;
  1537. X}
  1538. X
  1539. Xunsigned long
  1540. XFindFreeBlock(WID)
  1541. X    t_WID WID;
  1542. X{
  1543. X    char Data[BLOCKSIZE];
  1544. X    t_BlockHeader *HP;
  1545. X    unsigned long Here = 0L;
  1546. X    unsigned long There = 0L;
  1547. X
  1548. X    if (DataFile < 0) {
  1549. X    if ((DataFile = open(DataBase, O_RDWR|O_CREAT, 0666)) < 0) {
  1550. X        fprintf(stderr, "Can't open database");
  1551. X        perror(DataBase);
  1552. X        exit(1);
  1553. X    }
  1554. X    }
  1555. X
  1556. X    do {
  1557. X    (void) lseek(DataFile, (long) Here, 0);
  1558. X
  1559. X    if (read(DataFile, Data, BLOCKSIZE) < BLOCKSIZE) {
  1560. X        HP = (t_BlockHeader *) 0;
  1561. X        There = 0L;
  1562. X        Here = BLOCKSIZE;
  1563. X    } else {
  1564. X        HP = (t_BlockHeader *) Data;
  1565. X    }
  1566. X
  1567. X    /* It is free if it has no pairs (or if the block has
  1568. X     * never been written to, of course)
  1569. X     * Freed blocks are given a NextOffset or'd with 1.
  1570. X     */
  1571. X    if ((!HP || (HP->NextOffset & 01L)) && Here != 0L) {
  1572. X        unsigned long NextFree = HP ? HP->NextOffset : 0L;
  1573. X
  1574. X        if (NextFree) {
  1575. X        /* make it suitable for use with lseek() */
  1576. X        NextFree = (NextFree >> 1) * BLOCKSIZE;
  1577. X        }
  1578. X
  1579. X        /* Make the previous block in the chain point to the one
  1580. X         * after this one in the free list, instead of this one.
  1581. X         */
  1582. X
  1583. X        if (HP) {
  1584. X        (void) lseek(DataFile, (long) There, 0);
  1585. X        (void) read(DataFile, Data, BLOCKSIZE);
  1586. X                        /* Well, we read it a second ago!*/
  1587. X        }
  1588. X        HP = (t_BlockHeader *) Data;
  1589. X        HP->NextOffset = (NextFree / BLOCKSIZE) << 1;
  1590. X        (void) lseek(DataFile, (long) There, 0);
  1591. X        (void) write(DataFile, Data, BLOCKSIZE);
  1592. X
  1593. X        goto GotOne;
  1594. X    }
  1595. X
  1596. X    There = Here;
  1597. X    Here = (HP->NextOffset >> 1) * BLOCKSIZE;
  1598. X
  1599. X    } while (Here != 0L);
  1600. X
  1601. X    if ((Here = lseek(DataFile, 0L, 2)) == 0L) {
  1602. X    Here = BLOCKSIZE;
  1603. X    }
  1604. X
  1605. XGotOne:
  1606. X    /* Mark it in use */
  1607. X    HP->NextOffset = 0L;
  1608. X    (void) lseek(DataFile, (long) Here, 0);
  1609. X    (void) write(DataFile, Data, BLOCKSIZE);
  1610. X
  1611. X#ifdef ASCIITRACE
  1612. X    if (AsciiTrace > 10) {
  1613. X    fprintf(stderr, "\tFindFree --> %lu\n", (Here == 0L) ? BLOCKSIZE : Here);
  1614. X    }
  1615. X#endif
  1616. X    return (Here == 0L) ? BLOCKSIZE : Here;
  1617. X}
  1618. X
  1619. X/* Get a single disk block from the database */
  1620. Xchar *
  1621. XReadBlock(Offset)
  1622. X    unsigned long Offset;
  1623. X{
  1624. X    static char Buffer0[BLOCKSIZE];
  1625. X
  1626. X    if (DataFile < 0) {
  1627. X    if ((DataFile = open(DataBase, O_RDWR|O_CREAT, 0664)) < 0) {
  1628. X        return (char *) 0;
  1629. X    }
  1630. X    }
  1631. X
  1632. X#ifdef ASCIITRACE
  1633. X    if (AsciiTrace > 15) {
  1634. X    fprintf(stderr, "===ReadBlock(%ld)\n", Offset);
  1635. X    }
  1636. X#endif
  1637. X
  1638. X    if (lseek(DataFile, (long) Offset, 0) < 0) {
  1639. X    return (char *) 0;
  1640. X    }
  1641. X
  1642. X    /* Now we are in the right place */
  1643. X
  1644. X    if (read(DataFile, Buffer0, BLOCKSIZE) != BLOCKSIZE) {
  1645. X    return (char *) 0;
  1646. X    }
  1647. X
  1648. X    /* Now that we have the 1st block of the data.. */
  1649. X    return Buffer0;
  1650. X}
  1651. X
  1652. Xint
  1653. XWordPlaceCompare(F1, F2)
  1654. X    t_WordPlace *F1, *F2;
  1655. X{
  1656. X    if (F1->FID != F2->FID) return F1->FID - F2->FID;
  1657. X    if (F1->BlockInFile != F2->BlockInFile) {
  1658. X    return F1->BlockInFile - F2->BlockInFile;
  1659. X    }
  1660. X    return F1->WordInBlock - F2->WordInBlock;
  1661. X}
  1662. X
  1663. Xint
  1664. XRemoveFIDFrompblock(FID, pblock)
  1665. X    t_FID FID;
  1666. X    t_pblock *pblock;
  1667. X{
  1668. X    unsigned int ThisPair;
  1669. X    int TotalWordPlacesToMove = 0;
  1670. X
  1671. X    /* Mark unwanted pairs as deleted */
  1672. X    for (ThisPair = 0; ThisPair < pblock->NumberOfWordPlaces; ThisPair++) {
  1673. X    if (pblock->WordPlaces[ThisPair].FID == FID) {
  1674. X        pblock->WordPlaces[ThisPair].FID = 0L;
  1675. X        ++TotalWordPlacesToMove;
  1676. X    } 
  1677. X    }
  1678. X    if (!TotalWordPlacesToMove) return 0;
  1679. X
  1680. X    if (pblock->NumberOfWordPlaces > 1) {
  1681. X    SortWordPlaces(pblock->NumberOfWordPlaces, pblock->WordPlaces);
  1682. X    }
  1683. X
  1684. X    /* shuffle up any FID=0 pairs */
  1685. X    {
  1686. X    register t_WordPlace *Src, *Dest;
  1687. X    register int n = TotalWordPlacesToMove;
  1688. X
  1689. X    Src = pblock->WordPlaces;
  1690. X    Dest = &pblock->WordPlaces[TotalWordPlacesToMove];
  1691. X
  1692. X    while (n-- > 0) {
  1693. X        *Src++ = *Dest++;
  1694. X    }
  1695. X    }
  1696. X
  1697. X    return TotalWordPlacesToMove;
  1698. X}
  1699. X
  1700. Xvoid
  1701. XSortWordPlaces(NumberOfWordPlaces, WordPlaces)
  1702. X    unsigned long NumberOfWordPlaces;
  1703. X    t_WordPlace *WordPlaces;
  1704. X{
  1705. X    /* sort the list */
  1706. X    if (NumberOfWordPlaces > 2) {
  1707. X    qsort(WordPlaces, (int) NumberOfWordPlaces,
  1708. X        sizeof(t_WordPlace), WordPlaceCompare);
  1709. X    } else {
  1710. X    /* don't call qsort in the trivial cases... */
  1711. X    if (NumberOfWordPlaces == 2) {
  1712. X        if (WordPlaceCompare(&WordPlaces[0], &WordPlaces[1]) > 0) {
  1713. X        t_WordPlace tmp;
  1714. X        tmp = WordPlaces[0]; /* structure copy! */
  1715. X        WordPlaces[0] = WordPlaces[1];
  1716. X        WordPlaces[1] = tmp;
  1717. X        }
  1718. X    }
  1719. X    }
  1720. X}
  1721. X
  1722. Xvoid
  1723. XDeleteWordPlaces(FirstBlock, WID)
  1724. X    unsigned long FirstBlock;
  1725. X    t_WID WID;
  1726. X{
  1727. X    char Data[BLOCKSIZE];
  1728. X    char BlockZero[BLOCKSIZE];
  1729. X    t_BlockHeader *H;
  1730. X    t_BlockHeader *HZERO; /* for the free list*/
  1731. X    unsigned long ThisBlock;
  1732. X    unsigned long Here;
  1733. X
  1734. X    if (!FirstBlock) {
  1735. X    return;
  1736. X    }
  1737. X
  1738. X    if (DataFile < 0) {
  1739. X    if ((DataFile = open(DataBase, O_RDWR|O_CREAT, 0666)) < 0) {
  1740. X        fprintf(stderr, "Can't open database");
  1741. X        perror(DataBase);
  1742. X        exit(1);
  1743. X    }
  1744. X    }
  1745. X
  1746. X    (void) lseek(DataFile, 0L, 0);
  1747. X
  1748. X    if (read(DataFile, BlockZero, BLOCKSIZE) <= 0) {
  1749. X    return; /* nothing to delete */
  1750. X    }
  1751. X
  1752. X    /* Block zero is always the head of the free list */
  1753. X    HZERO = (t_BlockHeader *) BlockZero;
  1754. X
  1755. X    Here = FirstBlock;
  1756. X
  1757. X    do {
  1758. X    if (lseek(DataFile, (long) Here, 0) < 0) {
  1759. X        fprintf(stderr, "Aaaargh:");
  1760. X        perror("lseek");
  1761. X        exit(1);
  1762. X    }
  1763. X
  1764. X    if (read(DataFile, Data, BLOCKSIZE) <= 0) {
  1765. X        return; /* it's not there to delete */
  1766. X    }
  1767. X
  1768. X    H = (t_BlockHeader *) Data;
  1769. X
  1770. X    ThisBlock = Here; /* so we can write the changed block */
  1771. X    Here = (H->NextOffset >> 1) * BLOCKSIZE;
  1772. X    if (Here == 0L) { /* next block... */
  1773. X        H->NextOffset = HZERO->NextOffset;
  1774. X    }
  1775. X    H->NextOffset |= 01L; /* mark it as free */
  1776. X
  1777. X    /* Write out the newly freed block, having saved its Next pointer
  1778. X     */
  1779. X    WriteBlock(ThisBlock, Data);
  1780. X    } while (Here);
  1781. X
  1782. X    /* Now make the free list point to the start of the new chain;
  1783. X     * the end of the newly added chain points to the start of the
  1784. X     * original chain.  This means that the new blocks are still in the
  1785. X     * Unix buffer cache, so this helps performance.  I hope.
  1786. X     */
  1787. X    HZERO->NextOffset = (FirstBlock / BLOCKSIZE) << 1;
  1788. X    (void) lseek(DataFile, 0L, 0);
  1789. X    (void) write(DataFile, BlockZero, BLOCKSIZE);
  1790. X
  1791. X    return;
  1792. X}
  1793. X
  1794. Xvoid
  1795. XDeletepblock(pblock)
  1796. X    t_pblock *pblock;
  1797. X{
  1798. X    char Data[BLOCKSIZE];
  1799. X    char BlockZero[BLOCKSIZE];
  1800. X    t_BlockHeader *H;
  1801. X    t_BlockHeader *HZERO; /* for the free list*/
  1802. X    unsigned long ThisBlock;
  1803. X    unsigned long Here;
  1804. X
  1805. X    if (!pblock || !pblock->ChainStart) {
  1806. X    return;
  1807. X    }
  1808. X
  1809. X    if (DataFile < 0) {
  1810. X    if ((DataFile = open(DataBase, O_RDWR|O_CREAT, 0666)) < 0) {
  1811. X        fprintf(stderr, "Can't open database");
  1812. X        perror(DataBase);
  1813. X        exit(1);
  1814. X    }
  1815. X    }
  1816. X
  1817. X    if (read(DataFile, BlockZero, BLOCKSIZE) <= 0) {
  1818. X    return; /* nothing to delete */
  1819. X    }
  1820. X
  1821. X    /* Block zero is always the head of the free list */
  1822. X    HZERO = (t_BlockHeader *) BlockZero;
  1823. X
  1824. X    Here = pblock->ChainStart;
  1825. X
  1826. X    do {
  1827. X    if (lseek(DataFile, (long) Here, 0) < 0) {
  1828. X        fprintf(stderr, "Aaaargh:");
  1829. X        perror("lseek");
  1830. X        exit(1);
  1831. X    }
  1832. X
  1833. X    if (read(DataFile, Data, BLOCKSIZE) <= 0) {
  1834. X        return; /* it's not there to delete */
  1835. X    }
  1836. X
  1837. X    H = (t_BlockHeader *) Data;
  1838. X
  1839. X    ThisBlock = Here; /* so we can write the changed block later */
  1840. X    if ((Here = ((H->NextOffset >> 1) * BLOCKSIZE)) == 0L) {
  1841. X        H->NextOffset = HZERO->NextOffset;
  1842. X    }
  1843. X    H->NextOffset |= 01L; /* mark it as free */
  1844. X
  1845. X    /* Write out the newly freed block, having saved its Next pointer
  1846. X     */
  1847. X    WriteBlock(ThisBlock, Data);
  1848. X    } while (Here);
  1849. X
  1850. X    /* Now make the free list point to the start of the new chain;
  1851. X     * the end of the newly added chain points to the start of the
  1852. X     * original chain.  This means that the new blocks are still in the
  1853. X     * Unix buffer cache, so this helps performance.  I hope.
  1854. X     */
  1855. X    HZERO->NextOffset = (pblock->ChainStart / BLOCKSIZE) << 1;
  1856. X    (void) lseek(DataFile, 0L, 0);
  1857. X    write(DataFile, BlockZero, BLOCKSIZE);
  1858. X
  1859. X    return;
  1860. X}
  1861. @@@End of lq-text/src/liblqtext/pblock.c
  1862. echo end of part 05
  1863. -- 
  1864. Liam R. E. Quin,  lee@sq.com, SoftQuad Inc., Toronto, +1 (416) 963-8337
  1865.