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