home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / MISC / vh_1.4.lzh / VH / vh.c < prev    next >
Text File  |  1994-04-22  |  35KB  |  1,535 lines

  1. /******************************************************************************
  2.  
  3. NAME
  4.    vh.c --- retrieval primitives for vh-format text
  5.  
  6. SYNOPSIS
  7.    vh [-icm] [textfile] [indexfile]
  8.  
  9. DESCRIPTION
  10.    Contains display-independent primitives for implementing a
  11. simple hypertext browser.
  12.  
  13. AUTHORS
  14.    Adapted by Eric S. Raymond <eric@snark.thyrsus.com> from the 1.1 version
  15. of Raymond Gardner's MS-DOS browser, October 1991.
  16.    Please see the READ.ME in this directory for license terms.
  17.  
  18. PORTING NOTES
  19.    Some effort has been made to make this code independent of whether the
  20. underlying OS uses \n or \r\n as a line terminator.  If CRLFSIZE is defined
  21. to 1, \n only is assumed; if it is 2, \r\n is assumed.
  22.  
  23. BUGS
  24.    Multi-file database support is only half implemented.
  25.  
  26. **************************************************************************/
  27. /*LINTLIBRARY*/
  28. #include <stdio.h>
  29. #include <string.h>
  30. #include <ctype.h>
  31. #include <assert.h>
  32. #include <time.h>
  33.  
  34. typedef int bool;
  35. #define FALSE       0
  36. #define TRUE        1
  37.  
  38. #include "vh.h"
  39.  
  40. #ifndef F_OK
  41. #define F_OK 0
  42. #endif /* F_OK */
  43.  
  44. #define VHPATHSIZ   128
  45.  
  46. #define TAB 0x09    /* tab */
  47. #define BS  0x08    /* backspace */
  48. #define CR  0x0D    /* carriage return */
  49. #define LF  0x0A    /* line feed */
  50. #define SP  0x20    /* space */
  51. #define DEL 0x7f    /* delete */
  52.  
  53. #define max(a,b)    ((a) > (b) ? (a) : (b))
  54.  
  55. #ifdef ATT
  56. #define srand(n)    srand48(n)
  57. #define rand()      lrand48()
  58. extern long lrand48();
  59. extern void srand48();
  60. /*
  61.  * Try this if ungetch() fails to resolve.
  62.  *
  63.  * #define ungetch ungetc
  64.  */
  65. #endif /* isxdigit */
  66.  
  67. extern char *malloc();
  68. extern long atol();
  69.  
  70. extern int LINES, COLS;     /* initialize screen size into these */
  71.  
  72. #ifdef UNIX
  73. #define CRLFSIZE    1   /* \n only */
  74. #endif /* UNIX */
  75.  
  76. #ifndef CRLFSIZE
  77. #define CRLFSIZE    2   /* \r\n as in MS-DOS, etc. */
  78. #endif /* CRLFSIZE */
  79.  
  80. #define WARNSIZE    75      /* warn of lines longer than this */
  81. #define ENTRYMARK   ':'
  82. #define SEPARATOR   (' ' - 2)
  83. #define STRSZ       80      /* for various strings */
  84.  
  85. #ifndef VHPATH
  86. #ifdef MSDOS
  87. #define VHPATH  ".;/usr/lib/vh"
  88. #else
  89. #ifndef AMIGA
  90. #ifndef OSK
  91. #define VHPATH  ".:/usr/lib/vh"
  92. #else
  93. char VHPATH[] = ".:/h0/vh";
  94. #endif
  95. #else
  96. #define VHPATH  "s:;jargon:"
  97. #endif
  98. #endif /* MSDOS */
  99. #endif /* VHPATH */
  100. #define TXT     ".txt"
  101. #define IDX     ".idx"
  102. #define TXTLEN      4
  103.  
  104. /*
  105.  * This was carefully tuned using prof(1) under UNIX SVr3.  Unless you have
  106.  * profiling tools at least as good, best not mess with it!
  107.  */
  108. #define DBBUF       4096        /* optimal buffer size */
  109.  
  110. #ifdef MSDOS
  111. #ifdef DBBUF
  112. #undef DBBUF
  113. #endif
  114. #define DBBUF       512     /* better size for MSDOS/Borland C -- rdg */
  115. #endif
  116.  
  117. #define IS_TAG(s, x)    (s[x] == LTAG || (x == 0 && s[0] == ENTRYMARK))
  118.  
  119. /*******************************************************************
  120.  *
  121.  * All message texts declared here for internationalization purposes
  122.  *
  123.  ******************************************************************/
  124.  
  125. #define FORMTYPE    "vh: format type is %d, "
  126. #define OUTASYNC    "vh: index is out of sync with text.\n"
  127. #define BADMAGIC    "vh: index file %s doesn't look like an index\n"
  128. #define CANTFIND    "vh: can't find database files for %s\n"
  129. #define CANTOPEN    "vh: can't open %s\n"
  130. #define NOMEMORY    "vh: out of memory.\n"
  131. #define TOOMANY     "vh: too many index items.\n"
  132. #define DANGLING    "\"%s%s\", line %ld: {%s} dangling reference\n"
  133. #define OUTERROR    "vh: error writing output -- disk full?\n"
  134. #define LENGTHZERO  "vh: line length 0 -- ignored.\n"
  135. #define TOOLONG     "vh: line too long\n"
  136. #define CONTROLZ    "vh: last line is Control-Z(s) -- ignored\n"
  137. #define NOCRLF      "vh: last line not terminated -- CR/LF added\n"
  138. #define NOINPUT     "vh: can't open input file %s\n"
  139. #define NOOUTPUT    "vh: can't open output file %s\n"
  140. #define FILLING     "filling index\n"
  141. #define LONGLINE    "\"%s%s\", line %ld: line too long (%d)\n"
  142. #define REREADING   "vh: rereading, "
  143. #define SORTING     "sorting, "
  144. #define WRITING     "writing, "
  145. #define BADREFS     "checking for bad references...\n"
  146. #define DONE        "done.\n"
  147.  
  148. /*******************************************************************
  149.  *
  150.  * Global data
  151.  *
  152.  ******************************************************************/
  153.  
  154. static FILEINFO textblk;
  155.  
  156. FILEINFO    vhi, *vht = &textblk;
  157.  
  158. static char *xp[ITEMSMAX];  /* index pointer table */
  159. static int  nitems;     /* xp item count */
  160.  
  161. #if defined(__TURBOC__) && defined(FFGETS)
  162. /*******************************************************************
  163.  *
  164.  * ffgets() -- faster replacement for fgets() (very Borland-specific!)
  165.  *      depends on Borland's FILE structure & usage
  166.  *      similar method may be useful on other systems
  167.  *
  168.  *   Note that implementing this doesn't require having any library
  169.  *   source.  All you need to know is in the getc() macro in stdio.h.
  170.  *
  171.  ******************************************************************/
  172. char *ffgets(s, n, f)
  173. char *s;
  174. int n;
  175. FILE *f;
  176. {
  177.     char *s0;
  178.     int c;
  179.  
  180.     s0 = s;
  181.     --n;
  182.     while ( n )
  183.     {
  184.     register int k;
  185.     register char *p;
  186.  
  187.     k = f->level;
  188.     if ( k > n )
  189.         k = n;
  190.     if ( k == 0 )
  191.     {
  192.         c = fgetc(f);
  193.         if ( c == EOF )
  194.         {
  195.         *s = '\0';
  196.         return NULL;
  197.         }
  198.         *s++ = c;
  199.         --n;
  200.         if ( c == '\n' )
  201.         break;
  202.     }
  203.     else
  204.     {
  205.         p = f->curp;
  206.         while ( *p++ != '\n' && --k )
  207.         ;
  208.         k = p - f->curp;
  209.         memmove(s, f->curp, k);
  210.         f->level -= k;
  211.         f->curp += k;
  212.         n -= k;
  213.         s += k;
  214.         if ( s[-1] == '\n' )
  215.         break;
  216.     }
  217.     }
  218.     *s = '\0';
  219.     return s0;
  220. }
  221. #if 00
  222. /* another approach ... */
  223. char *ffgets(s, n, f)
  224. char *s;
  225. int n;
  226. FILE *f;
  227. {
  228.     char *s0, *p;
  229.     int k, c;
  230.  
  231.     s0 = s;
  232.     n--;
  233.     while ( n )
  234.     {
  235.     k = f->level;
  236.     if ( k > n )
  237.         k = n;
  238.     if ( k < 1 )
  239.     {
  240.         c = fgetc(f);
  241.         if ( c == EOF )
  242.         {
  243.         *s = '\0';
  244.         return NULL;
  245.         }
  246.         *s++ = c;
  247.         --n;
  248.         if ( c == '\n' )
  249.         break;
  250.     }
  251.     else
  252.     {
  253.         memmove(s, f->curp, k);
  254.         s[k] = '\0';
  255.         p = strchr(s, '\n');
  256.         if ( p )
  257.         n = k = p - s + 1;
  258.         f->level -= k;
  259.         f->curp += k;
  260.         s += k;
  261.         n -= k;
  262.     }
  263.     }
  264.     *s = '\0';
  265.     return s0;
  266. }
  267. #endif
  268. #endif /* defined(__TURBOC__) && defined(FFGETS) */
  269.  
  270. #ifndef MSDOS
  271. /*******************************************************************
  272.  *
  273.  * Emulations of Borland C library functions
  274.  *
  275.  ******************************************************************/
  276.  
  277. bool strnicmp(s1, s2, n)
  278. /* case-insensitive length-limited string compare */
  279. register char *s1, *s2;
  280. register n;
  281. {
  282. #ifdef BSD
  283.     char    ls1;
  284.     char    ls2;
  285.  
  286.     while (--n >= 0) {
  287.     ls1 = *s1;
  288.     ls2 = *s2++;
  289.     if (isupper(ls1))
  290.         ls1 = tolower(*s1);
  291.     if (isupper(ls2))
  292.         ls2 = tolower(ls2);
  293.     if (ls1 != ls2)
  294.         break;
  295.     if (*s1++ == '\0')
  296.         return(0);
  297.     }
  298.     return((n < 0) ? 0 : (ls1-ls2));
  299. #else
  300.     while (--n >= 0 && tolower(*s1) == tolower(*s2++))
  301.     if (*s1++ == '\0')
  302.         return(0);
  303.     return((n < 0) ? 0 : (tolower(*s1) - tolower(*--s2)));
  304. #endif
  305. }
  306.  
  307. void strlwr(s)
  308. /* force string to lower case */
  309. char    *s;
  310. {
  311.     register char *cp;
  312.  
  313.     for (cp = s; *cp; cp++)
  314. #ifdef BSD
  315.     if (isupper(*cp))
  316. #endif
  317.         *cp = tolower(*cp);
  318. }
  319.  
  320. #ifndef linux
  321. char *strstr(t, s)
  322. /* find s in t */
  323. char    *s, *t;
  324. {
  325.     char    *cp;
  326.  
  327.     for (cp = t; *cp; cp++)
  328.     if (strncmp(cp, s, strlen(s)) == 0)
  329.         return(cp);
  330.     return((char *)NULL);
  331. }
  332. #endif /* !linux */
  333. #endif /* MSDOS */
  334.  
  335. /*******************************************************************
  336.  *
  337.  * All file-type dependent stuff
  338.  *
  339.  ******************************************************************/
  340.  
  341. static int format;  /* markup type, defaults to ORIGINAL296 */
  342. #define ORIGINAL296 0   /* Jargon File 2.9.6 version */
  343. #define COLON297    1   /* Jargon File 2.9.7+ with colons */
  344.  
  345. #define HDOFF(s)    (s[0] == ENTRYMARK)
  346.  
  347. void getformat(fp)
  348. /* figure out what format we're looking at */
  349. FILE    *fp;
  350. {
  351.     (void) fseek(fp, 0L, SEEK_SET);
  352.     if (fgetc(fp) != '=')
  353.     format = COLON297;
  354. }
  355.  
  356. char *headword(ln)
  357. /* is this line an entry? */
  358. char    *ln;
  359. {
  360.     char    *p;
  361.  
  362.     if (ln[0] == ' ' || (format > ORIGINAL296 && (ln[0] != ENTRYMARK)))
  363.     return((char *)NULL);
  364.  
  365.     for (p = ln + HDOFF(ln); p = strchr(p, ENTRYMARK); ++p)
  366.     if (isspace(p[1]) || (p[1] == ENTRYMARK && isspace(p[2])))
  367.         break;
  368.  
  369.     return(p);
  370. }
  371.  
  372. /*******************************************************************
  373.  *
  374.  * Sequential entry access
  375.  *
  376.  * This supports the following entry points:
  377.  *  getnextln() --- get line beginning at given position
  378.  *  getprevln() --- get *previous* line from given position
  379.  *
  380.  ******************************************************************/
  381.  
  382. void detab(s)
  383. /* expand tabs in s, in place; assumes tab stops every 8 chars */
  384. char *s;
  385. {
  386.     char *p;
  387.     int k, n, i;
  388.  
  389.     while ((p = strchr(s, TAB)) != NULL)
  390.     {
  391.     /* while any tabs */
  392.     k = p - s;      /* offset to tab */
  393.     n = (k + 8) / 8 * 8;    /* next tab stop */
  394.     for (i = strlen(s + k + 1); i >= 0; i--)
  395.         s[n + i] = s[k + 1 + i];
  396.     memset(&s[k], ' ', n - k); /* blank fill */
  397.     }
  398. }
  399.  
  400. /* daddr_t getnextln(FILE *fp, daddr_t pos, char *ln) -- get next line of file
  401. **  takes position where a line starts, gets line into buffer ln
  402. **  removes trailing CR/LF and expands tabs
  403. **  returns position of next byte after line (i.e. start of next line)
  404. **  returns NOWHERE at EOF
  405. */
  406. daddr_t getnextln(fp, pos, ln)
  407. FILE *fp;
  408. daddr_t pos;
  409. char *ln;
  410. {
  411.     int k;
  412.     char *p;
  413.     if (ftell(fp) != pos)   /* slight optimization for Borland & Zortech*/
  414.     (void) fseek(fp, pos, SEEK_SET);
  415.     if (fgets(ln, LNSZ, fp))
  416.     {
  417.     k = strlen(ln);
  418.     ln[k - 1] = '\0';
  419. #if CRLFSIZE > 1
  420.     if (ln[k - 2] == CR)
  421.         ln[k - 2] = '\0';   /* fixed bug here 11/28/91 rdg */
  422. #endif /* CRLFSIZE > 1 */
  423.     pos += k;
  424.     }
  425.     else
  426.     pos = NOWHERE;
  427.     detab(ln);
  428.     if (p = strchr(ln, SEPARATOR))
  429.     *p = '\0';
  430.     return(pos);
  431. }
  432.  
  433. /* daddr_t getprevln(FILE *fp, daddr_t pos, char *ln) -- get prev line in file
  434. **  takes position where a line starts, gets _previous_ line into buffer ln
  435. **  removes trailing CR/LF and expands tabs
  436. **  returns position of line returned
  437. **  returns NOWHERE at BOF
  438. */
  439. daddr_t getprevln(fp, pos, ln)
  440. FILE *fp;
  441. daddr_t pos;
  442. char *ln;
  443. {
  444.     int n;
  445.     char *p;
  446.  
  447.     assert(pos > 0);
  448.     n = LNSZ;
  449.     if (pos < LNSZ)
  450.     n = pos;
  451.     (void) fseek(fp, pos - n, SEEK_SET);
  452.     (void) fread(ln, 1, n, fp);
  453.     p = &ln[n-1];
  454.     while (p != ln)
  455.     {
  456.     int i;
  457.  
  458.     --p;
  459. #ifndef OSK
  460.     if (*p == LF)
  461. #else
  462.     if (*p == '\n')
  463. #endif
  464.     {
  465.         ++p;
  466.         n = &ln[n] - p;
  467.         for (i = 0; i < n; i++)
  468.         ln[i] = p[i];
  469. /*      memmove(ln, p, n);  */
  470.         break;
  471.     }
  472.     }
  473.     assert(p > ln || (p == ln && pos == n));
  474.     ln[n - 1] = '\0';
  475. #if CRLFSIZE > 1
  476.     if (ln[n - 2] == CR)
  477.     ln[n - 2] = '\0';
  478. #endif /* CRLFSIZE > 1 */
  479.     pos -= n;
  480.     detab(ln);
  481.     if (p = strchr(ln, SEPARATOR))
  482.     *p = '\0';
  483.     return(pos);
  484. }
  485.  
  486. /*******************************************************************
  487.  *
  488.  * Fortune-cookie mode
  489.  *
  490.  * This supports the following entry points:
  491.  *  jrandom()   -- return the offset of a random entry
  492.  *
  493.  ******************************************************************/
  494.  
  495. daddr_t jrandom()
  496. /* grab a fortune cookie */
  497. {
  498.     int r = rand() % nitems;
  499.  
  500.     return(atol(xp[r] + strlen(xp[r]) + 1));
  501. }
  502.  
  503. /*******************************************************************
  504.  *
  505.  * Entry-access by name
  506.  *
  507.  * This supports the following entry points:
  508.  *      xlocate() --- go to entry by name
  509.  *  ilocate() --- go to entry by name (incremental)
  510.  *  ffind() --- find string in file
  511.  *  ifind() --- find string in file (incremental)
  512.  *
  513.  ******************************************************************/
  514.  
  515. daddr_t ixlocate(s, isincrsearch)
  516. /* binary-search index for x; return offset if found, else -offset of next */
  517. char *s;
  518. bool isincrsearch;
  519. {
  520.     int lo, mid, hi, k, hit;
  521.     daddr_t pos;
  522.  
  523.     strlwr(s);
  524. #define NOTFOUND (-1)
  525.     lo = 0;
  526.     hi = nitems - 1;
  527.     hit = NOTFOUND;     /* your basic binary search */
  528.     while (hit == NOTFOUND && lo <= hi)
  529.     {
  530.     mid = (lo + hi) / 2;
  531.     if ((k = strcmp(s, xp[mid])) < 0)
  532.         hi = mid - 1;
  533.     else if (k > 0)
  534.         lo = mid + 1;
  535.     else
  536.         hit = mid;
  537.     }
  538.  
  539.     /*
  540.      * if not found, and item wanted exceeds item found,
  541.      * and there's room to move up, go to next higher item
  542.      */
  543.     if (hit==NOTFOUND && strcmp(s, xp[mid]) > 0 && mid < nitems-1)
  544.     {
  545.     ++mid;
  546.     assert(strcmp(s, xp[mid]) < 0);
  547.     }
  548.     /* the file position is stored right after the index string */
  549.     pos = atol(xp[mid] + strlen(xp[mid]) + 1);
  550.  
  551.     if (isincrsearch)
  552.     {
  553.     if (strncmp(s, xp[mid], strlen(s)))
  554.         pos = -pos;
  555.     }
  556.     else
  557.     {
  558.     /* if no hit, return negative pos */
  559.     if (hit == NOTFOUND)
  560.         pos = -pos;
  561.     }
  562.     return(pos);
  563. }
  564.  
  565. daddr_t xlocate(s)
  566. char *s;
  567. {
  568.     return(ixlocate(s, FALSE));
  569. }
  570.  
  571. daddr_t ilocate(c)
  572. /* incremental-lookup through entry key list */
  573. char c;
  574. {
  575.     static char key[LNSZ], *ep = key;
  576.     static int oldhit;
  577.  
  578.     if (c == BS)
  579.     {
  580.     if (ep > key)
  581.         *--ep = '\0';
  582.     }
  583.     else if (isprint(c))    /* incremental-search for given character */
  584.     {
  585.     *ep++ = tolower(c);
  586.     *ep = '\0';
  587.     return(ixlocate(key, TRUE));
  588.     }
  589.     else if (c == DEL)  /* reset from previous incremental lookup */
  590.     {
  591.     key[0] = '\0';
  592.     ep = key;
  593.     oldhit = 0;
  594.     }
  595.     return(NOWHERE);
  596. }
  597.  
  598. daddr_t ffind(fp, pos, ss)
  599. /* case-blind search forward for ss; return offset if found, else NOWHERE */
  600. FILE *fp;
  601. daddr_t pos;
  602. char *ss;
  603. {
  604.     char s[LNSZ], ln[LNSZ];
  605.  
  606.     (void) strcpy(s, ss);
  607.     strlwr(s);
  608.     (void) fseek(fp, pos, SEEK_SET);
  609.     while (fgets(ln, LNSZ, fp))
  610.     {
  611.     strlwr(ln);
  612.     if (strstr(ln, s))
  613.         return(pos);
  614.     pos = ftell(fp);    /* save pos before reading next line */
  615.     }
  616.     return(NOWHERE);
  617. }
  618.  
  619. daddr_t ifind(fp, pos, c)
  620. /* incremental-search forward */
  621. FILE *fp;
  622. daddr_t pos;
  623. char c;
  624. {
  625.     static char key[LNSZ], *ep = key;
  626.     static int oldhit;
  627.  
  628.     if (c == BS)
  629.     {
  630.     if (ep > key)
  631.         *--ep = '\0';
  632.     return(pos);
  633.     }
  634.     else if (isprint(c))    /* incremental-search for given character */
  635.     {
  636.     *ep++ = tolower(c);
  637.     *ep = '\0';
  638.     return(ffind(fp, pos, key));
  639.     }
  640.     else if (c == DEL)  /* reset from previous incremental search */
  641.     {
  642.     key[0] = '\0';
  643.     ep = key;
  644.     oldhit = 0;
  645.     return(pos);
  646.     }
  647.     return(NOWHERE);    /* should never reach here */
  648. }
  649.  
  650. /*******************************************************************
  651.  *
  652.  * Screen-fetch and line-retrieval code
  653.  *
  654.  * Entry points:
  655.  *  iflink() --- test for presence of a reference
  656.  *  findnextsel() --- find next link on current screen
  657.  *  findprevsel() --- find previous link on current screen
  658.  *
  659.  * These functions expect to be able to call:
  660.  *  readscr() --- get text from screen
  661.  *
  662.  ******************************************************************/
  663.  
  664. /* left tag and right tag of textual references */
  665. #define     LTAG        '{'
  666. #define     RTAG        '}'
  667.  
  668. /* daddr_t iflink() -- test for link at screen position, find right tag pos
  669. */
  670. daddr_t iflink(x, y, xrtag, yrtag, isindex)
  671. int x,y;
  672. int *xrtag, *yrtag;
  673. bool isindex;
  674. {
  675.     char *cp, s[MAXWIDTH];
  676.     char term[STRSZ];
  677.     int i;
  678.     daddr_t pos;
  679.  
  680.     readscr(0, y, COLS-1, y, s); /* get line on screen */
  681.  
  682.     if (isindex)        /* if index, x is col 0 */
  683.     {
  684.     (void) strcpy(term, s);
  685.     for (cp = term + strlen(term) - 1; *cp == ' '; cp--)
  686.         *cp = '\0';
  687.     x = strlen(term) - 1;
  688.     }
  689.     else if ((cp = headword(s)) && x < cp - s)  /* a headword? */
  690.     {
  691.     i = *cp;
  692.     *cp = '\0';
  693.     (void) strcpy(term, s + HDOFF(s));
  694.     *cp = i;
  695.     x = (cp - s) - 1;
  696.     }
  697.     else            /* not index or headword, find left tag */
  698.     {
  699.     int x0 = x;
  700.  
  701.     while (x >= 0 && s[x] != LTAG)
  702.         --x;
  703.     if (x < 0)      /* if no left tag, then no link */
  704.         return(0L);
  705.     assert(IS_TAG(s, x));
  706.  
  707.     /* find next non-LTAG char */
  708.     while (x < COLS && s[x] == LTAG)
  709.         ++x;
  710.     if (x >= COLS)      /* if none, no tag */
  711.         return(0L);
  712.  
  713.     /* scan the link text up to RTAG, put into term[] */
  714.     i = 0;
  715.     while (x < COLS-1 && s[x] != RTAG)
  716.         term[i++] = s[x++];
  717.     --x;
  718.     if (x < x0)     /* if we are left of start point, return 0 */
  719.         return(0L);     /* in case user clicks to right of link */
  720.     else if (x >= COLS-2)   /* in case link wraps across line */
  721.     {
  722.         ++y;
  723.         if (y > LASTLINE)
  724.         return(0L);
  725.         readscr(0, y, COLS-2, y, s);
  726.         while (i > 0 && term[i - 1] == ' ')
  727.         --i;
  728.         term[i++] = ' ';
  729.         x = 0;
  730.         while (x < COLS-1 && s[x] == ' ')
  731.         ++x;
  732.         while (x < COLS-1 && s[x] != RTAG)
  733.         term[i++] = s[x++];
  734.         --x;
  735.         if (x >= COLS)
  736.         return(0L);
  737.     }
  738.  
  739.     term[i] = '\0'; /* terminate the term */
  740.     }
  741.  
  742.     /* look up in index table */
  743.     pos = xlocate(term);
  744.     if (pos > 0)        /* if found, set the right tag x/y coords */
  745.     {
  746.     *yrtag = y;
  747.     *xrtag = x;
  748.     }
  749.     return(pos);    /* return position of link target, or 0L if none */
  750. }
  751.  
  752. /* region findnextsel() -- find next selection (i.e. link reference)
  753. **  given x/y coords of a link selection on the screen, find the next
  754. **  one onscreen searching left to right, then down, wrapping last line
  755. **  to top line, until back to original position if only one link onscreen;
  756. **  coordinates are placed in the result.
  757. */
  758. region findnextsel(x, y, isindex)
  759. int x, y;
  760. bool isindex;
  761. {
  762.     char s[MAXWIDTH];
  763.     int x0, y0, j;
  764.     daddr_t pos;
  765.     region res;
  766.  
  767.     if (isindex)        /* if index, just go to next row */
  768.     {
  769.     ++y;
  770.     if (y > LASTLINE)   /* wrap line to top if at end */
  771.         y = 0;
  772.     res.yl = res.yr = y;
  773.     res.xl = 0;
  774.     res.xr = COLS - 2;
  775.     return(res);
  776.     }
  777.  
  778.     if (y < 0)      /* if no selection currently, look for one */
  779.     y = 0;
  780.  
  781.     x0 = x;
  782.     y0 = y;
  783.     readscr(0, y, COLS-1, y, s);    /* get screen line */
  784.     while (x < COLS && IS_TAG(s, x))
  785.     ++x;
  786.     for (;;)
  787.     {
  788.     for (; x < COLS; ++x)
  789.     {
  790.         if (IS_TAG(s, x))
  791.         {
  792.         while (x < COLS && s[x] == LTAG || (x == 0 && s[0] == ENTRYMARK))
  793.             ++x;
  794.         /* look up link */
  795.         if (iflink(x, y, &res.xr, &res.yr, isindex) > 0)
  796.         {
  797.             res.xl = x; /* if found, set x/y and return */
  798.             res.yl = y;
  799.             return(res);
  800.         }
  801.         }
  802.     }
  803.     x = 0;          /* past end of row; reset x pos */
  804.     ++y;            /* step to next line, wrap around if at end */
  805.     if (y > LASTLINE)
  806.         y = 0;
  807.     if (y == y0 && x == x0) /* return if back at start point */
  808.     {
  809.         res.xl = res.yl = NOPLACE;
  810.         return(res);
  811.     }
  812.     readscr(0, y, COLS-1, y, s);    /* get screen line */
  813.     }
  814. }
  815.  
  816. /* void findprevsel() -- find prev selection (i.e. link reference)
  817. **  given x/y coords of a link selection on the screen, find the previous
  818. **  one onscreen searching right to left, then up, wrapping top line
  819. **  to last line, until back to original position if only one link onscreen;
  820. **  coordinates are placed in the result.
  821. */
  822. region findprevsel(x, y, isindex)
  823. int x, y;
  824. bool isindex;
  825. {
  826.     char s[MAXWIDTH];
  827.     int x0, y0, j;
  828.     daddr_t pos;
  829.     region res;
  830.  
  831.     if (isindex)        /* if index, get prev row */
  832.     {
  833.     --y;
  834.     if (y < 0)      /* wrap line to last if at top */
  835.         y = LASTLINE;
  836.     res.yl = res.yr = y;
  837.     res.xl = 0;
  838.     res.xr = COLS - 2;
  839.     return(res);
  840.     }
  841.  
  842.     x0 = x;
  843.     y0 = y;
  844.     readscr(0, y, COLS-1, y, s); /* get screen line */
  845.     while (x >= 0 && IS_TAG(s, x))
  846.     --x;
  847.     for (;;)
  848.     {
  849.     for (; x >= 0; --x)
  850.     {
  851.         if (IS_TAG(s, x))
  852.         {
  853.         while (x < COLS && IS_TAG(s, x))
  854.             ++x;
  855.         --x;        /* back up to LTAG; look up link */
  856.         if (iflink(x, y, &res.xr, &res.yr, isindex) > 0)
  857.         {
  858.             res.xl = ++x;   /* if found, set x/y and return */
  859.             res.yl = y;
  860.             return(res);
  861.         }
  862.         }
  863.     }
  864.     x = COLS - 1;   /* past beginning of row; reset x pos */
  865.     --y;            /* step to prev line, wrap around if at top */
  866.     if (y < 0)
  867.         y = LASTLINE;
  868.     if (y == y0 && x == x0) /* return if back at start point */
  869.     {
  870.         res.xl = res.yl = NOPLACE;
  871.         return(res);
  872.     }
  873.     readscr(0, y, COLS-1, y, s); /* get prev line, continue */
  874.     }
  875. }
  876.  
  877. /*******************************************************************
  878.  *
  879.  * Position stack management
  880.  *
  881.  * This supports the following entry points:
  882.  *  enqueue() --- push a placemark
  883.  *  dequeue() --- pop a placemark
  884.  *
  885.  ******************************************************************/
  886.  
  887. void enqueue(f)
  888. /* add file position & link info to backtrack stack */
  889. FILEINFO    *f;
  890. {
  891.     int k;
  892.  
  893.     k = f->btscnt;
  894.  
  895.     /* if full, move it up to make room */
  896.     if (k == BTSMAX)
  897.     {
  898.     int n;
  899.     for ( n = (BTSMAX-1) * sizeof(f->bts[0]); n--; )
  900.     {
  901.         memcpy(&f->bts[0], &f->bts[1], sizeof(f->bts[0]));
  902.         memcpy(&f->selbts[0], &f->selbts[1], sizeof(f->selbts[0]));
  903.     }
  904.     --k;
  905.     f->btscnt = k;      /* 9/27/91 rdg  fixed bts overflow bug */
  906.     }
  907.     f->bts[k] = f->toppos;
  908.     f->selbts[k].xl = f->sel.xl;
  909.     f->selbts[k].yl = f->sel.yl;
  910.     f->selbts[k].xr = f->sel.xr;
  911.     f->selbts[k].yr = f->sel.yr;
  912. #ifdef DEBUG
  913.     (void) fprintf(stderr, "enqueue[%d]: x = %d, y = %d, pos = %ld.\n",
  914.            k, f->sel.xl, f->sel.yl, f->toppos);
  915. #endif /* DEBUG */
  916.  
  917.     if (k == 0 || f->bts[k] != f->bts[k-1] || /* only enqueue if changed */
  918.         memcmp(&f->selbts[k], &f->selbts[k-1], sizeof(f->selbts[k])) != 0)
  919.     ++f->btscnt;
  920. #ifdef DEBUG
  921.     else
  922.     (void) fprintf(stderr, "enqueue[%d]: is duplicate, popped.\n", k);
  923. #endif /* DEBUG */
  924. }
  925.  
  926. void dequeue(f)
  927. /* pull file position & link info from stack */
  928. FILEINFO    *f;
  929. {
  930.     if (f->btscnt)  /* don't attempt pop if empty */
  931.     {
  932.     --f->btscnt;
  933.     f->toppos = f->bts[f->btscnt];
  934.     f->sel.xl = f->selbts[f->btscnt].xl;
  935.     f->sel.yl = f->selbts[f->btscnt].yl;
  936.     f->sel.xr = f->selbts[f->btscnt].xr;
  937.     f->sel.yr = f->selbts[f->btscnt].yr;
  938.  
  939. #ifdef DEBUG
  940.     (void) fprintf(stderr, "dequeue[%d]: x = %d, y = %d, pos = %ld.\n",
  941.                f->btscnt, f->sel.xl, f->sel.yl, f->toppos);
  942. #endif /* DEBUG */
  943.     }
  944. #ifdef DEBUG
  945.     else
  946.     (void) fprintf(stderr, "dequeue: failed, no stack space.\n");
  947. #endif /* DEBUG */
  948. }
  949.  
  950. /*******************************************************************
  951.  *
  952.  * Browse support
  953.  *
  954.  * Entry points:
  955.  *  initbrowse() --- set up in-core structures for given file pair
  956.  *  setlastpage() --- set lastpagetoppos members
  957.  *
  958.  ******************************************************************/
  959.  
  960. /* validate_synchronization(FILE *fp, char *ln)
  961. ** check file against index table, to try to be sure they match up
  962. ** will sample 10 index entries, evenly spaced, including first and last
  963. ** for each entry, looks at text file to see if text matches index
  964. */
  965. void validate_synchronization(fp, ln)
  966. FILE *fp;
  967. char *ln;
  968. {
  969. #define     nsamples        10
  970.     int i, k;
  971.     daddr_t pos;
  972.  
  973.     for (i = 0; i < nsamples; ++i)
  974.     {
  975.     k = (i * (nitems - 1)) / (nsamples - 1); /* sample index */
  976.     pos = xlocate(xp[k]); /* look up pos */
  977.     assert(pos > 0);
  978.     if (fseek(fp, pos, SEEK_SET) < 0) /* seek in file */
  979.         break;
  980.     getnextln(fp, pos, ln); /* get text line */
  981.     if (strnicmp(ln + HDOFF(ln), xp[k], strlen(xp[k])) != 0) /* match it */
  982.         break;
  983.     }
  984.     /* exit if any mismatch */
  985.     if (i < nsamples)
  986.     {
  987.     (void) fprintf(stderr, OUTASYNC);
  988.     exit(1);
  989.     }
  990. }
  991.  
  992. bool idxsearch(name, path)
  993. /* look for database along defined search path */
  994. char    *name, *path;
  995. {
  996.     extern char *getenv();
  997.     char    *srch, *cp;
  998.  
  999.     if ((srch = getenv("VHPATH")) == (char *)NULL)
  1000.     srch = VHPATH;
  1001.  
  1002. #ifdef MSDOS
  1003. #define LISTSEP ";"
  1004. #define PATHSEP "\\"
  1005. #else
  1006. #ifndef AMIGA
  1007. #define LISTSEP ":"
  1008. #else
  1009. #define LISTSEP ";"
  1010. #endif /* AMIGA */
  1011. #define PATHSEP "/"
  1012. #endif /* MSDOS */
  1013.  
  1014.     cp = strtok(srch, LISTSEP);
  1015.     do {
  1016.     (void) strcpy(path, cp);
  1017. #ifdef AMIGA
  1018. if (path[strlen(path) - 1] != ':') { /* e.g. "DH0:" */
  1019. #endif
  1020.     if (path[strlen(path) - 1] != PATHSEP[0])
  1021.         (void) strcat(path, PATHSEP);
  1022. #ifdef AMIGA
  1023. }
  1024. #endif
  1025.     (void) strcat(path, name);
  1026.     (void) strcat(path, IDX);
  1027.     if (access(path, F_OK) == 0)
  1028.         return(TRUE);
  1029.     } while
  1030.     (cp = strtok((char *)NULL, LISTSEP));
  1031.     return(FALSE);
  1032. }
  1033.  
  1034. bool initbrowse(name)
  1035. char    *name;
  1036. {
  1037.     char ln[LNSZ + 1], path[PATHLEN], *cp;
  1038. #ifdef VHHDR
  1039.     vhhdr hdr;
  1040. #endif /* VHHDR */
  1041.     int k, fno;
  1042.  
  1043.     /* look for the database files */
  1044.     if (!idxsearch(name, path))
  1045.     {
  1046.     (void) fprintf(stderr, CANTFIND, name);
  1047.     return(FALSE);
  1048.     }
  1049.  
  1050. #ifndef OSK
  1051.     if ((vhi.fp = fopen(path, "rb"))==(FILE*)NULL)   /* open index file */
  1052. #else
  1053.     if ((vhi.fp = fopen(path, "r"))==(FILE*)NULL)   /* open index file */
  1054. #endif
  1055.     {
  1056.     (void) fprintf(stderr, CANTOPEN, path);
  1057.     return(FALSE);
  1058.     }
  1059. #ifndef BSD
  1060.     (void) setvbuf(vhi.fp, (char *)NULL, _IOFBF, DBBUF);
  1061. #endif /* BSD */
  1062.  
  1063.     vhi.btscnt = 0;
  1064.     vhi.sel.xl = NOPLACE;
  1065.  
  1066. #ifdef VHHDR
  1067.     (void) fread(&hdr, sizeof(vhhdr), 1, vhi.fp);
  1068.     if (hdr.magic != VHMAGIC)
  1069.     {
  1070.     (void) fprintf(stderr, BADMAGIC, path);
  1071.     return(FALSE);
  1072.     }
  1073.  
  1074.     /* read in text file names */
  1075.     for (fno = 0 ; fno < hdr.nfiles; fno++)
  1076.     (void) fread(ln, 1, VHPATHSIZ, vhi.fp);
  1077. #endif /* VHHDR */
  1078.  
  1079.     /* fill index table */
  1080.     for (nitems = 0; nitems < ITEMSMAX; ++nitems)
  1081.     {
  1082.     if (fgets(ln, LNSZ, vhi.fp) == NULL)
  1083.         break;
  1084.     k = strlen(ln) - 1;
  1085.     ln[k] = '\0';
  1086. #if CRLFSIZE > 1
  1087.     if (ln[k-1] == '\r')
  1088.         ln[--k] = '\0';
  1089. #endif /* CRLFSIZE > 1 */
  1090.     if ((xp[nitems] = malloc(k + 1)) == NULL)
  1091.     {
  1092.         (void) fprintf(stderr, NOMEMORY);
  1093.         return(TRUE);
  1094.     }
  1095.     strcpy(xp[nitems], ln);
  1096.     assert(strchr(ln, SEPARATOR)); /* better have separator */
  1097.     *strchr(xp[nitems], SEPARATOR) = '\0'; /* replace it w/ null byte */
  1098.     strlwr(xp[nitems]); /* force items in table to lowercase */
  1099.     }
  1100.  
  1101.     /* did we overflow? */
  1102.     if (nitems == ITEMSMAX)
  1103.     {
  1104.     (void) fprintf(stderr, TOOMANY);
  1105.     return(FALSE);
  1106.     }
  1107.  
  1108.     vhi.toppos = 0;
  1109.  
  1110.     /* this will go inside a loop */
  1111.  
  1112.     vht->btscnt = 0;
  1113.     vht->sel.xl = NOPLACE;
  1114.  
  1115.     (void) strcpy(ln, path);
  1116.     (void) strcpy(ln + strlen(ln) - TXTLEN, TXT);
  1117. #ifndef OSK
  1118.     if ((vht->fp = fopen(ln, "rb"))==(FILE*)NULL)    /* open text file */
  1119. #else
  1120.     if ((vht->fp = fopen(ln, "r"))==(FILE*)NULL)    /* open text file */
  1121. #endif
  1122.     {
  1123.     (void) fprintf(stderr, CANTOPEN, path);
  1124.     return(FALSE);
  1125.     }
  1126. #ifndef BSD
  1127.     (void) setvbuf(vht->fp, (char *)NULL, _IOFBF, DBBUF);
  1128. #endif /* BSD */
  1129.  
  1130.     getformat(vht->fp); /* what file format are we looking at? */
  1131.     vht->toppos = 0;
  1132.  
  1133.     /* set random-number seed, in case we're after a fortune cookie */
  1134.     srand(time((time_t *)0));
  1135.  
  1136.     return(TRUE);
  1137. }
  1138.  
  1139. void setlastpage()
  1140. /* this needs to be called *after* initscr() */
  1141. {
  1142.     char ln[LNSZ + 1];
  1143.     int i;
  1144.  
  1145.     /* set up last page top pos for index */
  1146.     (void) fseek(vhi.fp, 0L, SEEK_END); /* seek end of index file */
  1147.     vhi.dsptoppos = vhi.lastpagetoppos = vhi.dspnextpos = vhi.endpos =ftell(vhi.fp);
  1148.     for (i = 0; i <= LASTLINE; ++i)
  1149.     if (vhi.lastpagetoppos)
  1150.         vhi.lastpagetoppos = getprevln(vhi.fp, vhi.lastpagetoppos, ln);
  1151.     if (vhi.lastpagetoppos == 0L)   /* avoid divide-by-zero error */
  1152.     vhi.lastpagetoppos = 1L;
  1153.     (void) fseek(vhi.fp, 0L, SEEK_SET); /* seek start of index file */
  1154.  
  1155.     (void) fseek(vht->fp, 0L, SEEK_END); /* seek eof */
  1156.     vht->dsptoppos = vht->lastpagetoppos = vht->dspnextpos = vht->endpos =ftell(vht->fp);
  1157.     for (i = 0; i <= LASTLINE; ++i)
  1158.     if (vht->lastpagetoppos)
  1159.         vht->lastpagetoppos = getprevln(vht->fp, vht->lastpagetoppos, ln);
  1160.     if (vht->lastpagetoppos == 0L)  /* avoid divide-by-zero error */
  1161.     vht->lastpagetoppos = 1L;
  1162.     (void) fseek(vht->fp, 0L, SEEK_SET);    /* back to top of file */
  1163. }
  1164.  
  1165. /*******************************************************************
  1166.  *
  1167.  * Consistency checking
  1168.  *
  1169.  ******************************************************************/
  1170.  
  1171. void chkindex(name)
  1172. /* look for over-long lines, dangling references, self-references */
  1173. char    *name;
  1174. {
  1175.     long lnum = 1;
  1176.     char refbuf[LNSZ + 1], *refpt = refbuf;
  1177.     int c, len = 0, depth = 0;
  1178.  
  1179.     /* time for consistency check */
  1180.     initbrowse(name);
  1181.  
  1182.     (void) fseek(vht->fp, 0L, SEEK_SET);
  1183.     while ((c = fgetc(vht->fp)) != EOF)
  1184.     if (c == '\n')
  1185.     {
  1186.         if (len > WARNSIZE + CRLFSIZE)
  1187.         (void) printf(LONGLINE, name, TXT, lnum, len);
  1188.         len = 0;
  1189.  
  1190.         ++lnum;
  1191.         if (depth != 0 && refpt[-1] != ' ')
  1192.         *refpt++ = ' ';
  1193.     }
  1194.     else if (c == LTAG)
  1195.     {
  1196.         ++len;
  1197.         ungetc(c = fgetc(vht->fp), vht->fp);
  1198.         if (isprint(c))
  1199.         ++depth;
  1200.     }
  1201.     else if (depth > 0 && c == RTAG)
  1202.     {
  1203.         ++len;
  1204.         --depth;
  1205.         if (depth == 0 && refpt != refbuf)
  1206.         {
  1207.         *refpt = '\0';
  1208.         if (xlocate(refbuf) < 0)
  1209.             (void) printf(DANGLING, name, TXT, lnum, refbuf);
  1210.         refpt = refbuf;
  1211.         }
  1212.     }
  1213.     else if (depth != 0)
  1214.     {
  1215.         ++len;
  1216.         if (!isspace(c) || refpt[-1] != ' ')
  1217.         *refpt++ = c;
  1218.     }
  1219.  
  1220.     validate_synchronization(vht->fp, refbuf); /* validate */
  1221. }
  1222.  
  1223. /*******************************************************************
  1224.  *
  1225.  * File indexing code
  1226.  *
  1227.  * Entry points:
  1228.  *  mkindex(name) -- generate index file from text
  1229.  *
  1230.  ******************************************************************/
  1231.  
  1232. static char ln[LNSZ + 1];
  1233.  
  1234. typedef struct list_struct
  1235. {
  1236.     struct list_struct  *next;
  1237.     unsigned int    len;
  1238.     char        ln[1];
  1239. }
  1240. list;
  1241.  
  1242. static list *listp = NULL;
  1243.  
  1244. char *mmalloc(n)
  1245. /* malloc(3) with error message and abort */
  1246. unsigned    n;
  1247. {
  1248.     char *p;
  1249.  
  1250.     if ((p = malloc(n)) == NULL)
  1251.     {
  1252.     (void) fprintf(stderr, NOMEMORY);
  1253.     exit(1);
  1254.     }
  1255.     return(p);
  1256. }
  1257.  
  1258. #define UNSCH(cp)       ((int)(*((unsigned char *)(cp))))
  1259.  
  1260. int strlcmp(a, b)
  1261. /* compare, smashing case */
  1262. char *a, *b;
  1263. {
  1264. #ifdef BSD
  1265.     return(strnicmp(a,b,max(strlen(a),strlen(b))));
  1266. #else
  1267.     char *lim;
  1268.     int v;
  1269.  
  1270.     for (; *a && *b; a++, b++)
  1271.     {
  1272.     v = tolower(UNSCH(a)) - tolower(UNSCH(b));
  1273.     if (v)
  1274.         return v;
  1275.     }
  1276.     return(tolower(UNSCH(a)) - tolower(UNSCH(b)));
  1277. #endif
  1278. }
  1279.  
  1280. int keycmp(p, q)
  1281. /* compare keys for equality */
  1282. list *p, *q;
  1283. {
  1284.     return(strlcmp(p->ln, q->ln));
  1285. }
  1286.  
  1287. list *lmerge (p, q)
  1288. /* merge 2 lists under dummy head item */
  1289. list *p, *q;
  1290. {
  1291.     list *r, head;
  1292.  
  1293.     for (r = &head; p && q;)
  1294.     {
  1295.     if (keycmp(p, q) < 0)
  1296.     {
  1297.         r = r->next = p;
  1298.         p = p->next;
  1299.     }
  1300.     else
  1301.     {
  1302.         r = r->next = q;
  1303.         q = q->next;
  1304.     }
  1305.     }
  1306.     r->next = (p ? p : q);
  1307.     return(head.next);
  1308. }
  1309.  
  1310. list *lsort (p)
  1311. /* split list into 2 parts, sort each recursively, merge */
  1312. list *p;
  1313. {
  1314.     list *q, *r;
  1315.  
  1316.     if (p)
  1317.     {
  1318.     q = p;
  1319.     for (r = q->next; r && (r = r->next) != NULL; r = r->next)
  1320.         q = q->next;
  1321.     r = q->next;
  1322.     q->next = NULL;
  1323.     if (r)
  1324.         p = lmerge(lsort(p), lsort(r));
  1325.     }
  1326.     return(p);
  1327. }
  1328.  
  1329. void readfile(inf)
  1330. /* read file into linked list of lines */
  1331. FILE    *inf;
  1332. {
  1333.     int k;
  1334.     list *p;
  1335.  
  1336.     while (fgets(ln, LNSZ, inf))
  1337.     {
  1338.     k = strlen(ln);
  1339.     if (k == 0)
  1340.     {
  1341.         (void) fprintf(stderr, LENGTHZERO);
  1342.         continue;
  1343.     }
  1344.     if (ln[k-1] == '\n')
  1345.     {
  1346.         k--;
  1347. #if CRLFSIZE > 1
  1348.         if (ln[k-1] == '\r')
  1349.         k--;
  1350. #endif /* CRLFSIZE > 1 */
  1351.         ln[k] = '\0';
  1352.     }
  1353.     else
  1354.     {
  1355.         if (k == LNSZ)
  1356.         {
  1357.         (void) fprintf(stderr, TOOLONG);
  1358.         exit(1);
  1359.         }
  1360.         if (ln[0] == 26)
  1361.         {
  1362.         (void) fprintf(stderr, CONTROLZ);
  1363.         continue;
  1364.         }
  1365.         else
  1366.         {
  1367.         (void) fprintf(stderr, NOCRLF);
  1368.         continue;
  1369.         }
  1370.     }
  1371.  
  1372.     p = (list *)mmalloc(k + sizeof(list));
  1373.     p->len = k;
  1374.     memcpy(p->ln, ln, k+1);
  1375.     p->next = listp;
  1376.     listp = p;
  1377.     }
  1378. }
  1379.  
  1380. void writefile (outf)
  1381. /* write file from in-core list */
  1382. FILE    *outf;
  1383. {
  1384.     list *p;
  1385.  
  1386.     for (p = listp; p; p = p->next)
  1387.     {
  1388.     (void) fwrite(p->ln, 1, p->len, outf);
  1389. #if CRLFSIZE > 1
  1390.     (void) fwrite("\r", 1, 1, outf);
  1391. #endif /* CRLFSIZE > 1 */
  1392.     (void) fwrite("\n", 1, 1, outf);
  1393.     }
  1394. }
  1395.  
  1396. #ifdef AMIGA
  1397. void setthebufback(void)
  1398. {
  1399.     static char mybuf[BUFSIZ];
  1400.     setbuf(stdout, mybuf);
  1401. }
  1402. #endif /* AMIGA */
  1403.  
  1404. void mkindex(argc, argv)
  1405. /* make index from given files */
  1406. int argc;
  1407. char    *argv[];
  1408. {
  1409.     char source[PATHLEN], target[PATHLEN];
  1410.     char ln[LNSZ], prevln[LNSZ];
  1411.     FILE *inf, *outf;
  1412.     daddr_t pos;
  1413.     char *infilen, *outfilen, *p, *s;
  1414.     unsigned int buffersize;
  1415.     int i, len, fno;
  1416. #ifdef VHHDR
  1417.     vhhdr hdr;
  1418. #endif /* VHHDR */
  1419.  
  1420.     (void) strcpy(target, argv[0]);
  1421.     (void) strcat(target, IDX);
  1422. #ifndef OSK
  1423.     if ((outf = fopen(target, "wb")) == (FILE *)NULL)
  1424. #else
  1425.     if ((outf = fopen(target, "w")) == (FILE *)NULL)
  1426. #endif
  1427.     {
  1428.     (void) fprintf(stderr, NOOUTPUT, target);
  1429.     exit(1);
  1430.     }
  1431.  
  1432.     setbuf(stdout, (char *)NULL);
  1433. #ifdef AMIGA
  1434.     atexit(setthebufback);
  1435. #endif /* AMIGA */
  1436.  
  1437.     for (fno = 0 ; fno < argc; fno++)
  1438.     {
  1439.     (void) strcpy(source, argv[fno]);
  1440.     (void) strcat(source, TXT);
  1441. #ifndef OSK
  1442.     if ((inf = fopen(source, "rb"))==(FILE *)NULL)
  1443. #else
  1444.     if ((inf = fopen(source, "r"))==(FILE *)NULL)
  1445. #endif
  1446.     {
  1447.         (void) fprintf(stderr, NOINPUT, source);
  1448.         exit(1);
  1449.     }
  1450.  
  1451.     getformat(inf);
  1452.  
  1453.     (void) printf(FORMTYPE, format);
  1454.  
  1455.     *prevln = 0;
  1456.  
  1457.     (void) printf(FILLING);
  1458.  
  1459.     pos = 0L;
  1460.     while (fgets(ln, LNSZ, inf))
  1461.     {
  1462.         if (p = headword(ln))
  1463.         {
  1464.         *p = 0;
  1465.         if (strlcmp(ln, prevln) != 0)
  1466. #ifndef OSK
  1467.             (void) fprintf(outf, "%s%c%ld\r\n",
  1468. #else
  1469.             (void) fprintf(outf, "%s%c%ld\n",
  1470. #endif
  1471.                    ln + HDOFF(ln), SEPARATOR, pos);
  1472.         (void) strcpy(prevln, ln);
  1473.         }
  1474.         pos = ftell(inf);
  1475.     }
  1476.     (void) fclose(inf);
  1477.     }
  1478.  
  1479.     (void) fclose(outf);
  1480.  
  1481.     /* now sort the file */
  1482. #ifndef OSK
  1483.     inf = fopen(target, "rb");
  1484. #else
  1485.     inf = fopen(target, "r");
  1486. #endif
  1487.     if (inf == NULL)
  1488.     {
  1489.     fprintf(stderr, NOINPUT);
  1490.     exit(1);
  1491.     }
  1492.  
  1493.     (void) printf(REREADING);
  1494.  
  1495. #ifndef AOS
  1496.     setvbuf(inf, NULL, _IOFBF, LNSZ + 1);
  1497. #endif /* AOS */
  1498.     readfile(inf);
  1499.     (void) fclose(inf);
  1500.  
  1501.     (void) printf(SORTING);
  1502.     listp = lsort(listp);
  1503.  
  1504.     (void) printf(WRITING);
  1505. #ifndef OSK
  1506.     outf = fopen(target, "wb");
  1507. #else
  1508.     outf = fopen(target, "w");
  1509. #endif
  1510.     if (outf == NULL)
  1511.     {
  1512.     (void) fprintf(stderr, NOOUTPUT);
  1513.     exit(1);
  1514.     }
  1515. #ifndef AOS
  1516.     setvbuf(outf, NULL, _IOFBF, LNSZ + 1);
  1517. #endif /* AOS */
  1518.  
  1519. #ifdef VHHDR
  1520.     hdr.magic = VHMAGIC;
  1521.     hdr.nfiles = argc;
  1522.     (void) fwrite(&hdr, sizeof(vhhdr), 1, outf);
  1523.  
  1524.     for (fno = 0 ; fno < argc; fno++)
  1525.     (void) fwrite(argv[fno], 1, VHPATHSIZ, outf);
  1526. #endif /* VHHDR */
  1527.  
  1528.     writefile(outf);
  1529.     (void) fclose(outf);
  1530.  
  1531.     (void) printf(DONE);
  1532. }
  1533.  
  1534. /* vh.c ends here */
  1535.