]> thyrsus.com Git - vh.git/commitdiff
*** empty log message ***
authorEric S. Raymond <esr@thyrsus.com>
Tue, 11 May 1993 21:44:47 +0000 (21:44 +0000)
committerEric S. Raymond <esr@thyrsus.com>
Tue, 11 May 1993 21:44:47 +0000 (21:44 +0000)
COPYING [new file with mode: 0644]
README.AMIGA [new file with mode: 0644]
TurboMake [new file with mode: 0644]
browse.c [new file with mode: 0644]
screen.c [new file with mode: 0644]
screen.h [new file with mode: 0644]
vh.1 [new file with mode: 0644]
vh.c [new file with mode: 0644]
vh.h [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..a43ea21
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+       Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/README.AMIGA b/README.AMIGA
new file mode 100644 (file)
index 0000000..2511bd9
--- /dev/null
@@ -0,0 +1,151 @@
+Some notes on the Amiga port of the jargon browser:
+
+- You need to assign jargon: to the directory where the jargon file lies
+  (so you can refer to the jargon file as jargon:jargon.txt)
+
+- 2.0 users: The jargon program tries to open a custom screen as large as
+  the workbench screen, even if that is very large and autoscrolling. This
+  sometimes fails (or the program won't work correctly). Fix: Open a public
+  screen with the desired size (e.g. with PSX by Steve Tibbett), open a
+  shell on it (e.g. newshell "con:0/0/640/200/Title/SCREEN
+  YourScreenNameHere") and start the jargon program from that shell - it
+  will open a screen as large as the active screen (at least it does for
+  me... :-) )
+
+- 2.0 users again: Due to the Amiga curses port relying on a fixed font
+  height and width of 8 pixels, you cannot use another font than topaz.8. I
+  modified the curses code so that it will explicitely ask for "topaz.8"
+  instead of using the default font - which wouldn't work.
+
+- Users of "PopUpMenu" by Martin Adrian: On the jargon screen, there is
+  a menu bar which shows you the name of the Amiga curses author. Popupmenu
+  seems to get confused as there are no menu items, so it will crash (at
+  least version 4.2 does on my machine). So don't press the right mouse
+  button on the jargon screen when PopUpMenu is enabled (the mouse is not
+  supported by the Amiga port anyway...)
+
+- jargon can be run only from the CLI, when called from workbench it just
+  exits.
+
+This port would not have been possible (at least not this year :-)) without
+Simon John Raybould's (sie@fulcrum.bt.co.uk) Amiga curses (thanks to him).
+I only changed some minor things (added 3 functions for my C compiler (DICE
+2.06.37R, thanks to Matt Dillon), used 2 bitplanes instead of 4 for the
+screen, changed the font to topaz.8 and fixed clearok() so that jargon
+wouldn't clear and redraw the whole screen on every keystroke, diffs are
+included).
+
+Apropos Amiga curses: I used version 1.23 from March 1991, it's
+ftp'able from funic.funet.fi, directory pub/amiga/new (if I remember
+right).
+
+Georg Sassen
+georg@bluemoon.GUN.de
+
+Here are the diffs for Amiga curses:
+
+*** curses-1.23.orig/src/curses.c      Mon Mar 18 21:43:32 1991
+--- curses-1.23/src/curses.c   Sun Dec 22 13:03:21 1991
+*************** static char *rcsid = "$Header: QUANTUM:S
+*** 51,57 ****
+--- 51,61 ----
+  
+  #include "acurses.h"
+  
++ #if 1
++ struct TextAttr foo = {"topaz.font",8,0,0};
++ #endif
+  
++ 
+  /* Main background screen */
+  static struct NewScreen NewScreen = {
+    0,                     /* LeftEdge */
+*************** static struct NewScreen NewScreen = {
+*** 58,68 ****
+--- 62,80 ----
+    0,                     /* TopEdge */
+    0,                     /* Width */
+    0,                     /* Height */
++ #if 0
+    4,                     /* No. Bitplanes */
++ #else
++   2,                     /* No. Bitplanes */
++ #endif
+    0, 1,                  /* DetailPen, BlockPen */
+    HIRES,                 /* ViewModes */
+    CUSTOMSCREEN,          /* Screen type */
++ #if 0
+    (struct TextAttr *)NULL,  /* default font */
++ #else
++   (struct TextAttr *)&foo,  /* topaz 8  */
++ #endif
+    "Curses screen",          /* Screen Title */
+    (struct Gadget *)NULL,    /* Gadget list */
+    (struct BitMap *)NULL     /* custom bitmap */
+*************** static void CleanExit(), CleanUp(), DoEc
+*** 131,137 ****
+  static void ZapCursor(), DrawCursor();
+       
+  /* Define a blank mouse pointer */
+! static USHORT __chip MyMsPtr[] = { 0, 0, 0, 0 };
+       
+  #define NCOLOURS  32
+       
+--- 143,149 ----
+  static void ZapCursor(), DrawCursor();
+  
+  /* Define a blank mouse pointer */
+! static __chip USHORT MyMsPtr[] = { 0, 0, 0, 0 };
+  
+  #define NCOLOURS  32
+  
+*************** initscr()
+*** 228,237 ****
+--- 240,256 ----
+    stdscr = newwin(LINES, COLS, 0, 0);
+    curscr = newwin(LINES, COLS, 0, 0);  /* used for redraws */
+    clearok(curscr, TRUE);  /* Clear curscr on every refresh */
++ #ifndef _DCC
+    if(onbreak(BreakHandler)) {
+      fprintf(stderr, "Failed to set BREAK handler.\n");
+      CleanExit(10);
+    }
++ #else /* onbreak returns old func pointer */
++   if(!onbreak(BreakHandler)) {
++     fprintf(stderr, "Failed to set BREAK handler.\n");
++     CleanExit(10);
++   }
++ #endif
+    return OK;
+  }
+  
+*************** wrefresh(WinPtr)
+*** 961,966 ****
+--- 980,986 ----
+      memcpy(WinStat->LnArry[Line].LRLine, WinStat->LnArry[Line].Line, WinPtr->_maxx+1);
+      memcpy(WinStat->LnArry[Line].LRATTRS, WinStat->LnArry[Line].ATTRS, sizeof(short) * (WinPtr->_maxx+1));
+    }
++   WinPtr->_clear = FALSE; /* clrok() only valid for one wrefresh() call  */
+    DrawCursor();
+    return OK;
+  }
+*************** static int BreakHandler()
+*** 1751,1753 ****
+--- 1771,1787 ----
+    fprintf(stderr, "Amiga Curses aborting after recieving signal\n");
+    return 1;
+  }
++ 
++ #ifdef _DCC
++ int max(int a,b)
++ {
++     return (a>b?a:b);
++ }
++ int min(int a,b)
++ {
++     return (a<b?a:b);
++ }
++ char killchar() {
++     return 0x7f;
++ }
++ #endif
diff --git a/TurboMake b/TurboMake
new file mode 100644 (file)
index 0000000..7d4d02d
--- /dev/null
+++ b/TurboMake
@@ -0,0 +1,51 @@
+#\r
+# Makefile for vh browser -- MSDOS/{Borland|Turbo}C\r
+#\r
+# Set your system type and I/O facilities\r
+# -DUNIX       -- The OS is UNIX\r
+# -DATT                -- The OS is AT&T UNIX (System III, System V)\r
+# -DMSDOS      -- The OS is MSDOS\r
+# -DCURSES     -- curses(3) character-oriented screen handling\r
+# -DATTMOUSE   -- AT&T 6386 /dev/mouse\r
+# -DPOPUP      -- text entry via Mac-like dialogue boxes.\r
+# -DCONST      -- your C has `const'\r
+#\r
+# -DFFGETS      -- faster fgets (Borland/Turbo C only for now)\r
+#\r
+# If you're using Borland Turbo-C under MS-DOS, see TurboMake\r
+#\r
+SYSTYPE=-DMSDOS -DSCROLLBAR -DMOUSE\r
+\r
+!if !$d(MODEL)\r
+MODEL=l\r
+!endif\r
+\r
+!if !$d(C)\r
+# could be bcc or bccx for Borland C; tcc for Turbo C\r
+C=bcc\r
+!endif\r
+\r
+!if $d(ZTC)\r
+CC=ztc -m$(MODEL)i\r
+CFLAGS= -c -g -b -o $(SYSTYPE)\r
+LDFLAGS= -g\r
+!else\r
+CC=$(C) -m$(MODEL) $(X)\r
+CFLAGS= -c -v -Z -O $(SYSTYPE)\r
+LDFLAGS= -v -M\r
+!endif\r
+\r
+.c.obj:\r
+       $(CC) $(CFLAGS) $<\r
+\r
+jargon: vh\r
+       copy vh.exe jargon.exe\r
+\r
+vh: vh.exe\r
+\r
+vh.exe: vh.obj browse.obj screen.obj\r
+       $(CC) $(LDFLAGS) vh.obj browse.obj screen.obj\r
+\r
+vh.obj: vh.c vh.h\r
+browse.obj: browse.c vh.h screen.h\r
+screen.obj: screen.c screen.h\r
diff --git a/browse.c b/browse.c
new file mode 100644 (file)
index 0000000..2385700
--- /dev/null
+++ b/browse.c
@@ -0,0 +1,1452 @@
+/*****************************************************************************
+
+NAME
+   browse.c -- curses(3)-using display front end for vh database browser
+
+SYNOPSIS
+   main(argc, argv)                   --- browser main sequence
+   int argc; char *argv[][];
+
+DESCRIPTION
+   This module defines the main and browse loop for the vh browser;
+all user-interface decisions are made here.  Screen, keyboard and
+mouse handling support may be found in screen.c.
+
+AUTHORS
+   Written by Eric S. Raymond <eric@snark.thyrsus.com> for a UNIX
+port of the 1.1 version of Raymond Gardner's MS-DOS browser, October
+1991.  Please see the source distribution's READ.ME for license
+terms.
+
+*****************************************************************************/
+/*LINTLIBRARY*/
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <ctype.h>
+#include <assert.h>
+#ifdef ATT
+#include <termio.h>
+#endif /* ATT */
+
+#include "screen.h"
+#include "vh.h"
+
+#ifndef CONST
+#define        const
+#endif /* CONST */
+
+#ifdef MSDOS
+/* emulate Borland-style scrollbar */
+#define SBARCH         ACS_CKBOARD     /* scrollbar character */
+#define SBARTOPCH      30              /* scrollbar up arrow */
+#define SBARBOTCH      31              /* scrollbar down arrow */
+#else
+/* approximate X-style scrollbar */
+#define SBARCH         ACS_VLINE       /* scrollbar character */
+#define SBARTOPCH      ACS_TTEE        /* scrollbar up arrow */
+#define SBARBOTCH      ACS_BTEE        /* scrollbar down arrow */
+#endif
+#define SBARTHUMBCH    ACS_BULLET      /* scrollbar thumb character */
+
+#ifdef A_COLOR
+#define NORMPAIR       1
+#define HILITEPAIR     2
+#define SELPAIR                3
+#define PROMPTPAIR     4
+#define FKEYPAIR       5
+#define SBARPAIR       6
+#define SBARENDPAIR    7
+#define SBTHUMBPAIR    8
+#endif /* A_COLOR */
+
+#define        SUPPRESS        0
+#define OPTIONAL       1
+#define FORCED         2
+
+#ifndef UNIX
+#define erasechar()    BS
+#endif /* UNIX */
+
+#define CUTFILE        "%s.cut"
+
+/*
+ * When paging forward or back, it helps the reader to page by a few less
+ * lines than the page depth, so a few of the old ones show to give context.
+ * This controls how many old lines will show.
+ */
+#define OVERLAP        4
+
+/* dialogue/message window parameters */
+#define DTOP   10
+#define DLEFT  10
+#define DHEIGHT        7
+#define DWIDTH 42
+
+/*******************************************************************
+ *
+ * All message texts declared here for internationalization purposes
+ *
+ ******************************************************************/
+
+#ifdef MSDOS
+#define USAGE  "usage: jargon [-cigms] [-b key] [srcname] [indexname]\n\
+       -c --- check textfile/index pair for consistency\n\
+       -i --- enable incremental-lookup and search\n\
+       -g --- generate new index from textfile\n\
+       -m --- force monochrome operation\n\
+       -s --- compensate for snowy EGA monitor\n\
+       -b --- write single entry specified by following key to stdout\n\
+       -r --- display random entry\n"
+#define TBANNER        "TAB-Index  F1-help  F10-exit"
+#define IBANNER        "TAB-Text   F1-help  F10-exit"
+#else
+#define USAGE  "usage: jargon [-cigmr] [-b key] [srcname] [indexname]\n"
+#define TBANNER        "TAB-Index  ^Q-help   ^C-exit"
+#define IBANNER        "TAB-Text   ^Q-help   ^C-exit"
+#endif
+typedef struct
+{
+    int        left;   /* left boundary of pushbutton */
+    int right; /* right boundary of pushbutton */
+    char cmd;  /* command character associated with pushbutton */
+}
+strip;
+static strip panel[] = {{0, 8, TAB}, {11, 17, CTL('Q')}, {21, 27, CTL('C')}};
+#define PANELSTART     (COLS - 1 - strlen((cf==&vhi) ? IBANNER : TBANNER) + 1)
+
+#define NOREFS "No references on this page"
+#define NOSRCH "No previous search"
+#define CUTNOP "Can't open cut file"
+#define CUTNOW "Cutting to jargon.cut..."
+#define CUTDNE "Cutting to jargon.cut...Done"
+
+#define BRSMES "Browsing %s...\n"
+#define IDXMES "Generating index for %s...\n"
+#define CHKMES "Performing consistency check for %s...\n"
+
+/*
+ * These have to be declared static, not macros; we use the address of the
+ * current prompt as a mode indicator.
+ */
+const static char NOMESG[] =   "                   ";
+#ifndef POPUP
+/* we want these fixed-length so the entry line looks good */
+const static char SEARCH[] =   "String search for: ";
+const static char INSRCH[] =   "Search proceeding: ";
+const static char NOTFND[] =   "Search failed:     ";
+const static char LOOKUP[] =   "Looking up entry:  ";
+const static char LOOKING[] =  "Lookup in progress:";
+#else
+/* the popup windows will get sized to these */
+const static char SEARCH[] =   "String search for";
+const static char INSRCH[] =   "Search proceeding";
+const static char NOTFND[] =   "Search failed";
+const static char LOOKUP[] =   "Looking up entry";
+const static char LOOKING[] =  "Lookup in progress";
+const static char BDLOOK[] =   "Lookup failed";
+#endif /* POPUP */
+
+const static char *help_screen[] =
+{
+"                          Jargon File Browser v. 1.5",
+"           Copyright 1991 by Raymond D. Gardner & Eric S. Raymond",
+"             All Rights Reserved; Free Redistribution Encouraged",
+"",
+"      ^L             redraw screen                      (Refresh)",
+"",
+"      space          page forward                       (PgDn)",
+"      ^U             page back                          (PgUp)",
+"      ^N / ^P        scroll forward / back              (Down/Up Arrow)",
+"      ^A / ^O        go to top / end of file            (Home/End)",
+"",
+"      ^J or ^M       chase highlighted reference        (Reference)",
+"      ^F / ^B        next/previous reference            (Right / Left Arrow)",
+"",
+"      ^E             lookup entry by name               (Select)",
+"      ^S             search for string                  (Find)",
+"      ^R             repeat previous search             (Shift-Find)",
+#ifdef UNIX /* keypad() seems to disable recognition of ESC */
+"      ^H             undo last search or lookup         (Undo)",
+#else
+"      ^H or ESC      undo last search or lookup         (Undo)",
+#endif
+"",
+"      ^I             toggle between word list and text  (Tab)",
+"      ^Y             append selected entry to cut file  (Print)",
+"",
+"      Typing an entry name followed by Enter works like an ^E command.",
+"                  Press any key to return to the browser.",
+NULL,
+};
+
+/*******************************************************************
+ *
+ * Shared global data
+ *
+ ******************************************************************/
+
+static char *execname; /* name under which program was invoked */
+static FILEINFO        *cf;    /* pointer to current fileinfo block */
+
+/* highlights */
+static chtype normattr, hiliteattr, selattr, promptattr, fkeyattr, sbarattr;
+static chtype sbarendattr, sbthumbattr;
+
+static char lasthit[LNSZ + 1]; /* last key searched for */
+static char linbuf[LNSZ + 1];  /* line input scratch buffer */
+#ifdef POPUP
+static WINDOW *dwin;           /* dialogue & message window */
+#endif /* POPUP */
+static bool incrsearch;                /* do incremental search? */
+
+/*******************************************************************
+ *
+ * Message and dialogue code
+ *
+ ******************************************************************/
+
+static void waitforit(requeue)
+/* wait for keystroke or mouse click */
+bool   requeue;
+{
+    int        c;
+#ifdef MOUSE
+    int        hsy, hsx;
+
+    /* wait for keypress or button click */
+    while ((c = egetch()) == KEY_MOUSE)
+       if (A_BUTTON_CHANGED & mouse_status(&hsy, &hsx))
+           break;
+#else
+    c = egetch();
+#endif /* MOUSE */
+    if (requeue)
+       ungetch(c);
+}
+
+#if defined(POPUP) && defined(CURSES)
+static void draw_box(win, ty,tx,  by,bx)
+/* draw a box on the screen using best possible forms chars */
+WINDOW *win;
+int ty, tx, by, bx;
+{
+    int        j;
+
+    mvwaddch(win, ty, tx, ACS_ULCORNER);
+    mvwaddch(win, by, bx, ACS_LRCORNER);
+    mvwaddch(win, by, tx, ACS_LLCORNER);
+    mvwaddch(win, ty, bx, ACS_URCORNER);
+    wmove(win, ty, tx+1);
+    for (j = tx + 1; j <= bx - 1; j++)
+       waddch(win, ACS_HLINE);
+    wmove(win, by, tx+1);
+    for (j = tx + 1; j <= bx - 1; j++)
+       waddch(win, ACS_HLINE);
+    for (j = ty + 1; j <= by - 1; j++)
+    { 
+       wmove(win, j, tx);
+       waddch(win, ACS_VLINE);
+    }
+    for (j = ty + 1; j <= by - 1; j++)
+    { 
+       wmove(win, j, bx);
+       waddch(win, ACS_VLINE);
+    }
+    wrefresh(win);
+}
+
+static char *get_dialogue(prompt)
+/* get string from user, with prompts */
+char   *prompt;
+{
+    static char buf[MAXCOLS + 1];
+
+    if (prompt == (char *)NULL)
+    {
+       delwin(dwin);
+       dwin = (WINDOW *)NULL;
+    }
+    else
+    {
+       /* create a dialogue window */
+       if (dwin == (WINDOW *)NULL)
+           dwin = newwin(DHEIGHT, DWIDTH, DTOP, DLEFT);
+
+       wclear(dwin);
+
+       draw_box(dwin, 0, 0, DHEIGHT-1, DWIDTH-1);
+       draw_box(dwin, 2, 1, DHEIGHT-3, DWIDTH-2);
+
+       wattron(dwin, A_BOLD);
+       mvwaddstr(dwin, 1, 10, prompt);
+       wattroff(dwin, A_BOLD);
+
+       echo();
+       mvwgetstr(dwin, 3, 3, buf);
+       noecho();
+
+       return(buf);
+    }
+}
+#endif /* defined(POPUP) && defined(CURSES) */
+
+static void message(s)
+/* write message to prompt line */
+char   *s;
+{
+#ifndef POPUP
+    attrset(promptattr);
+    mvaddstr(LINES-1, 0, s);
+    attrset(normattr);
+    refresh();
+#else
+    WINDOW     *mwin;
+
+    if (dwin)
+    {
+       mvwaddstr(dwin, 5, 10, NOMESG);
+       wattron(dwin, A_BOLD);
+       mvwaddstr(dwin, 5, 10, s);
+       wattroff(dwin, A_BOLD);
+       wrefresh(dwin);
+    }
+    else
+    {
+       mwin = newwin(3, strlen(s) + 2, DTOP, DLEFT);
+       wclear(mwin);
+       draw_box(mwin, 0, 0, 2, strlen(s) + 1);
+
+       wattron(mwin, A_BOLD);
+       mvwaddstr(mwin, 1, 1, s);
+       wattroff(mwin, A_BOLD);
+       wrefresh(mwin);
+
+       waitforit(TRUE);
+
+       overwrite(stdscr, mwin);
+       delwin(mwin);
+       touchline(DTOP, stdscr);
+       wrefresh(stdscr);
+    }
+#endif /* POPUP */
+}
+
+/*******************************************************************
+ *
+ * The browser itself
+ *
+ ******************************************************************/
+
+static void uninitbrowse(doclear)
+/* end the browse, either normally or due to signal */
+bool   doclear;
+{
+    mouse_hide();
+    if (doclear)
+    {
+       clear();
+       refresh();
+    }
+#ifdef CURSES
+    /* weird sex with the tty driver */
+    (void)resetterm();
+    (void)echo();
+#ifndef OLDCURSES
+    (void)flushinp();
+#endif /* OLDCURSES */
+#endif /* CURSES */
+    (void)endwin();
+    exit(0);
+}
+
+static void paintselect(rp, attr)
+/* turn highlight on a selection on or off */
+region *rp;    /* which to highlight */
+chtype attr;   /* attribute to use */
+{
+    int        x, y;
+
+    attrset(attr);
+    if (rp->yl == rp->yr)
+    {
+       move(rp->yl, rp->xl);
+       for (x = rp->xl; x <= rp->xr; x++)
+           addch(inch() & A_CHARTEXT);
+    }
+    else
+    {
+       int c, lastnsp;
+
+       /* don't highlight trailing spaces on line-wrapped references */
+       for (lastnsp = COLS - 1; lastnsp > 0; lastnsp--)
+       {
+           move(rp->yl, lastnsp);
+           if ((inch() & A_CHARTEXT) != ' ')
+               break;
+       }
+
+       /* write trailing-line part of highlight */
+       move(rp->yl, rp->xl);
+       for (x = rp->xl; x <= lastnsp; x++)
+           addch(inch() & A_CHARTEXT);
+
+       /* highlight everything *between* 1st and last lines in region */
+       for (y = rp->yl + 1; y < rp->yr; y++)
+       {
+           move(y, x);
+           for (x = 0; x < COLS - 1; x++)
+               addch(inch() & A_CHARTEXT);
+       }
+
+       /* don't highlight leading spaces on line-wrapped references */
+       for (x = 0; x <= COLS - 1; x++)
+       {
+           move(rp->yr, x);
+           if ((inch() & A_CHARTEXT) != ' ')
+               break;
+       }
+
+       /* write leading-line part of highlight */
+       move(rp->yr, x);
+       for (; x <= rp->xr; x++)
+           addch(inch() & A_CHARTEXT);
+    }
+    attrset(normattr);
+}
+
+static bool chase(x, y, isindex)
+/* try to chase a link at this location */
+int    x, y;
+bool   isindex;
+{
+    long pos;  /* find target */
+    int dummy;
+
+    enqueue(cf);       /* make sure we can un-chase it */
+
+    pos = iflink(x, y, &dummy, &dummy, isindex);
+    if (pos <= 0)
+    {
+       dequeue(cf);
+       return(FALSE);
+    }
+    else
+    {
+       if (isindex)    /* if index, switch to text */
+       {
+           cf = vht;
+           cf->dsptoppos = cf->dspnextpos = NOWHERE;
+       }
+
+       cf->toppos = pos;       /* set for new display */
+       return(TRUE);
+    }
+}
+
+
+static bool search(str, c)
+/* search for given string in file */
+char   *str;
+{
+    int        i;
+
+    message(INSRCH);
+    enqueue(cf);
+    if (c)
+       cf->hitpos = ifind(cf->fp, cf->dspnextpos, c);
+    else
+       cf->hitpos = ffind(cf->fp, cf->dspnextpos, str);
+    if (cf->hitpos == NOWHERE)
+    {
+       message(NOTFND);
+       dequeue(cf);
+       return(FALSE);
+    }
+    else
+    {
+       /* else display hit on fourth line */
+       cf->toppos = cf->hitpos;
+       for (i = 0; i < OVERLAP; i++)
+           cf->toppos = getprevln(cf->fp, cf->toppos, linbuf);
+       if (str != lasthit)
+           (void) strcpy(lasthit, str);
+       return(TRUE);
+    }
+}
+
+static bool lookup(str, c)
+/* search for given entry in file */
+char   *str, c;
+{
+    long pos;                  /* find target */
+
+    enqueue(cf);
+
+    if (c)
+       pos = ilocate(c);
+    else
+    {
+       message(LOOKING);
+       pos = xlocate(str);
+    }
+
+    if (cf == &vhi)            /* if index, switch to text */
+    {
+       cf = vht;
+       cf->dsptoppos = cf->dspnextpos = cf->endpos;
+    }
+
+    if (pos < 0)
+    {
+       cf->toppos = -pos;              /* set for new display */
+       return(FALSE);
+    }
+    else
+    {
+       cf->toppos = pos;               /* set for new display */
+       return(TRUE);
+    }
+}
+
+static daddr_t byname(prompt, str, c)
+/* search or lookup given entry in file, depending on prompt value */
+char   *prompt, *str, c;
+{
+    if (prompt == SEARCH)
+       return(search(str, c));
+    else
+       return(lookup(str, c));
+}
+
+/* entry buffer for lookup and search commands */
+static char    entrybuf[LNSZ + 1];
+static int     entrycnt = 0;
+
+static void enterchar(c, prompt, row, col, maxcol)
+/* accept a character into the entry buffer */
+char   c;
+char   *prompt;
+int    row, col;
+int    maxcol;
+{
+    int        es;
+
+    if (c == '\0')
+    {
+       entrybuf[entrycnt = 0] = '\0';
+       return;
+    }
+
+    es = col + strlen(prompt);
+    attrset(promptattr);
+    if (c == erasechar())
+    {
+       if (entrycnt > 0)
+       {
+           mvaddch(row, es + --entrycnt, ' ');
+           entrybuf[entrycnt] = '\0';
+           move(row, es + entrycnt);
+           ilocate(BS);
+           if (incrsearch)
+               dequeue(cf);
+       }
+    }
+#ifdef CURSES
+    else if (c == killchar())
+    {
+       /* delete everything back to the end of the prompt */
+       while (entrycnt > 0)
+       {
+           mvaddch(row, es + --entrycnt, ' ');
+           entrybuf[entrycnt] = '\0';
+           move(row, es + entrycnt);
+           if (incrsearch)
+               dequeue(cf);
+       }
+       ilocate(DEL);
+    }
+#endif /* CURSES */
+    else if (isprint(c))
+    {
+       mvaddstr(row, col, prompt);
+       if (es + entrycnt < maxcol
+           && (!incrsearch || byname(prompt, (char *)NULL, c) != NOWHERE))
+       {
+           mvaddch(row, es + entrycnt, c);
+           entrybuf[entrycnt++] = c;
+           entrybuf[entrycnt] = '\0';
+       }
+       else if (c == '\0')
+           entrybuf[entrycnt = 0] = '\0';
+       else
+           beep();
+    }
+    attrset(normattr);
+
+    refresh();
+}
+
+static void cutentry(pos, fp)
+/* send the text of the entry starting at pos to the gin file pointer */
+daddr_t pos;
+FILE *fp;
+{
+    (void) fseek(vht->fp, pos, SEEK_SET);
+
+    /* always want first line */
+    (void) fgets(linbuf, LNSZ, vht->fp);
+    (void) fputs(linbuf, fp);
+
+    /* copy succeeding lines till we get to next entry */
+    while (fgets(linbuf, LNSZ, vht->fp) != (char *)NULL)
+       if (linbuf[0] == '=' || headword(linbuf))
+           break;
+       else
+           (void) fputs(linbuf, fp);
+}
+
+static char *nblanks(n)
+/* blank-string return for fast fills; return up to 132 = MAXCOLS */
+int    n;
+{
+    /* MAXCOLS+1 blanks */
+    const static char blanks[] = "                                                                                                                                     ";
+    return(&blanks[sizeof(blanks) - 1 - n]);
+}
+
+static void browse(name, cok)
+/* interactive browser loop */
+char   *name;
+bool   cok;
+{
+    int                i, c;
+    char       **hp;
+    bool       restore_select = FALSE;
+    FILE       *cutfp;
+    int                lightup = FORCED;
+#ifdef MOUSE
+    int                hsy, hsx, mstat;        /* mouse hot spot coordinates */
+    bool       thumbdrag = FALSE;      /* are we in a thumbdrag? */
+#endif /* MOUSE */
+#ifndef POPUP
+    char       *prompt = NOMESG;
+#else
+    char       *cp;
+#endif /* POPUP */
+#ifdef ATT
+    struct termio      tty;
+    int                saveintr;
+#endif /* ATT */
+
+    if (!initbrowse(name))
+       exit(1);
+    cf = vht;
+
+#ifdef UNIX
+    (void) signal(SIGINT,uninitbrowse);
+    (void) signal(SIGQUIT,uninitbrowse);
+    (void) signal(SIGTERM,uninitbrowse);
+    (void) signal(SIGIOT,uninitbrowse);                /* for assert(3) */
+#ifdef ATT
+    (void) ioctl(0, TCGETA, &tty);
+    saveintr = tty.c_cc[VINTR];
+#endif /* ATT */
+#endif /* UNIX */
+
+    (void) initscr();
+    setlastpage();
+
+#ifdef A_COLOR
+    if (cok = (cok && has_colors()))
+    {
+       start_color();
+
+       init_pair(NORMPAIR, COLOR_WHITE, COLOR_BLUE);
+       init_pair(HILITEPAIR, COLOR_RED, COLOR_WHITE);
+       init_pair(SELPAIR, COLOR_YELLOW, COLOR_BLUE);
+       init_pair(PROMPTPAIR, COLOR_BLACK, COLOR_CYAN);
+       init_pair(FKEYPAIR, COLOR_RED, COLOR_CYAN);
+       init_pair(SBARPAIR, COLOR_WHITE, COLOR_BLACK);
+       init_pair(SBARENDPAIR, COLOR_BLUE, COLOR_WHITE);
+       init_pair(SBTHUMBPAIR, COLOR_BLUE, COLOR_BLACK);
+    }
+    mouse_color(cok);
+
+    normattr = cok ? (COLOR_PAIR(NORMPAIR) | A_BOLD) : A_NORMAL;
+    hiliteattr = cok ? COLOR_PAIR(HILITEPAIR) : A_REVERSE;
+    selattr = cok ? (COLOR_PAIR(SELPAIR) | A_BOLD) : A_BOLD;
+    promptattr = cok ? COLOR_PAIR(PROMPTPAIR) : A_REVERSE;
+    fkeyattr = cok ? COLOR_PAIR(FKEYPAIR) : A_BOLD;
+#ifndef MSDOS
+    sbarendattr = sbthumbattr = 
+       sbarattr = cok ? COLOR_PAIR(SBARPAIR) : A_NORMAL;
+#else
+    sbarattr = cok ? COLOR_PAIR(SBARPAIR) : A_REVERSE;
+    sbarendattr = cok ? (COLOR_PAIR(SBARENDPAIR) | A_BOLD) : A_REVERSE;
+    sbthumbattr = cok ? (COLOR_PAIR(SBTHUMBPAIR) | A_BOLD) : A_NORMAL;
+#endif
+#else
+    normattr = A_NORMAL;
+    hiliteattr = A_REVERSE;
+    selattr = A_BOLD;
+    promptattr = A_REVERSE;
+    fkeyattr = A_BOLD;
+    sbarendattr = sbthumbattr = 
+       sbarattr = A_NORMAL;
+#ifdef MSDOS
+    sbarattr = A_REVERSE;
+    sbarendattr = A_REVERSE;
+    sbthumbattr = A_NORMAL;
+#endif /* MSDOS */
+#endif /* A_COLOR */
+
+    mouse_init();
+#ifdef MOUSE
+    mouse_move(LINES - 1, COLS - 1);
+#endif /* MOUSE */
+
+#ifdef CURSES
+    (void)saveterm();
+    (void)nonl();
+    (void)raw();
+    (void)noecho();
+#ifndef OLDCURSES
+    (void)keypad(stdscr, TRUE);
+#endif /* BSD */
+#endif /* CURSES */
+
+#ifdef ATT
+    (void) ioctl(0, TCGETA, &tty);
+    tty.c_cc[VINTR] = saveintr;
+    tty.c_iflag |= BRKINT;
+    tty.c_iflag &=~ IGNBRK;
+    tty.c_lflag |= ISIG;
+    (void) ioctl(0, TCSETA, &tty);
+#endif /* ATT */
+
+    lasthit[0] = '\0';
+
+    for (c = KEY_REFRESH; c != CTL('C') && c != KEY_EXIT; c = egetch())
+    {
+#ifdef DEBUG
+       if (isprint(c))
+           (void) fprintf(stderr, "command: %c\n", c);
+       else if (iscntrl(c))
+           (void) fprintf(stderr, "command: ^%c\n", c + '@');
+       else if (c >= 0x80 & c <= 0x9f)
+           (void) fprintf(stderr, "command: M-^%c\n", (c &~ 0x80) + '@');
+       else if (c >= 0x80)
+           (void) fprintf(stderr, "command: M-%c\n", c &~ 0x80);
+       else
+           (void) fprintf(stderr, "command: 0x%02x\n", c);
+#endif /* DEBUG */
+
+       switch(c)
+       {
+#ifdef MSDOS
+       case KEY_F(10):
+           (void) ungetch(KEY_EXIT);
+           break;
+#endif /* MSDOS */
+
+#ifndef OLDCURSES
+       case KEY_HELP:          /* go to help screen */
+#endif /* OLDCURSES */
+       case CTL('Q'):
+#ifdef MSDOS
+       case KEY_F(1):
+#endif /* MSDOS */
+           restore_select = TRUE;
+           i = 0;
+           for (hp = help_screen; *hp; hp++)
+           {
+               mvaddstr(i++, 0, *hp);
+               addstr(nblanks(COLS - strlen(*hp)));
+           }
+           refresh();
+           cf->dsptoppos = NOWHERE;
+           waitforit(FALSE);
+           /* FALL THROUGH */
+
+#ifndef OLDCURSES
+       case KEY_REFRESH:       /* redraw screen */
+#endif /* OLDCURSES */
+       case CTL('L'):
+           restore_select = TRUE;
+#ifdef CURSES
+           clearok(stdscr, TRUE);
+#endif /* CURSES */
+           break;
+
+       case SP:
+           restore_select = FALSE;
+#ifndef POPUP
+           if (prompt != NOMESG)
+               goto medialspace;
+           /* FALL THROUGH */
+#endif /* POPUP */
+
+#ifndef OLDCURSES
+       case KEY_NPAGE:         /* page forward */
+#endif /* OLDCURSES */
+       pagefwd:
+           enqueue(cf);
+           restore_select = FALSE;
+           for (i = 0; i < LINES - OVERLAP; i++)
+           {
+               if (cf->toppos == cf->lastpagetoppos)
+                   break;
+               cf->toppos = getnextln(cf->fp, cf->toppos, linbuf);
+           }
+           if (cf->sel.xl != NOPLACE)
+           {
+               cf->sel.yl -= i; cf->sel.yr -= i;
+               if (!(restore_select = (cf->sel.yl >= 0)))
+                   cf->sel.xl = NOPLACE;
+           }
+           break;
+
+#ifndef OLDCURSES
+       case KEY_PPAGE: /* page back */
+#endif /* OLDCURSES */
+       case CTL('U'):
+       pagebak:
+           enqueue(cf);
+           restore_select = FALSE;
+           for (i = 0; i < LINES - OVERLAP; i++)
+           {
+               if (cf->toppos == 0)
+                   break;
+               cf->toppos = getprevln(cf->fp, cf->toppos, linbuf);
+           }
+           if (cf->sel.xl != NOPLACE)
+           {
+               cf->sel.yl += i; cf->sel.yr += i;
+               if (!(restore_select = (cf->sel.yr < LINES - 1)))
+                   cf->sel.xl = NOPLACE;
+           }
+           break;
+
+#ifndef OLDCURSES
+       case KEY_UP:
+#endif /* OLDCURSES */
+       case CTL('N'):
+linebak:
+           restore_select = FALSE;
+           if (cf->toppos)
+           {
+               cf->toppos = getprevln(cf->fp, cf->toppos, linbuf);
+               if (cf->sel.xl != NOPLACE)
+               {
+                   cf->sel.yl++; cf->sel.yr++;
+                   if (!(restore_select = (cf->sel.yr < LINES - 1)))
+                       cf->sel.xl = NOPLACE;
+               }
+           }
+           break;
+
+#ifndef OLDCURSES
+       case KEY_DOWN:
+#endif /* OLDCURSES */
+       case CTL('P'):
+linefwd:
+           restore_select = FALSE;
+           if (cf->toppos < cf->lastpagetoppos)
+           {
+               cf->toppos = getnextln(cf->fp, cf->toppos, linbuf);
+               if (cf->sel.xl != NOPLACE)
+               {
+                   cf->sel.yl--; cf->sel.yr--;
+                   if (!(restore_select = (cf->sel.yl >= 0)))
+                       cf->sel.xl = NOPLACE;
+               }
+           }
+           break;
+
+#ifndef OLDCURSES
+       case KEY_ENTER:         /* accept keyboard input */
+#endif /* OLDCURSES */
+       case CTL('J'):
+       case CTL('M'):
+           lightup = FORCED;
+#ifndef POPUP
+           restore_select = FALSE;
+           if (prompt == SEARCH || prompt == LOOKUP)
+           {
+               if (!incrsearch && byname(prompt, entrybuf, '\0') == NOWHERE)
+                   beep();
+               enterchar('\0', LOOKUP, LINES - 1, 0, PANELSTART);
+               prompt = NOMESG;
+               break;
+           }
+           /* nothing in prompt buffer, no search active: FALL THROUGH */
+#endif /* POPUP */
+
+#ifndef OLDCURSES
+       case KEY_REFERENCE:     /* chase reference */
+#endif /* OLDCURSES */
+           restore_select = FALSE;
+           if (cf->sel.xl == NOPLACE)  /* any link? */
+           {
+               message(NOREFS);
+               continue;
+           }
+           else
+               (void) chase(cf->sel.xl, cf->sel.yl, cf == &vhi);
+           break;
+
+#ifndef OLDCURSES
+       case KEY_RIGHT:         /* next reference */
+#endif /* OLDCURSES */
+       case CTL('F'):
+           restore_select = FALSE;
+           if (cf->sel.xl == NOPLACE)
+               message(NOREFS);
+           else
+           {
+               paintselect(&cf->sel, (cf == &vhi) ? normattr : selattr);
+               cf->sel = findnextsel(cf->sel.xl, cf->sel.yl, cf == &vhi);
+               paintselect(&cf->sel, hiliteattr);
+           }
+           refresh();
+           continue;
+
+#ifndef OLDCURSES
+       case KEY_LEFT:          /* previous reference */
+#endif /* OLDCURSES */
+       case CTL('B'):
+           restore_select = FALSE;
+           if (cf->sel.xl == NOPLACE)
+               message(NOREFS);
+           else
+           {
+               paintselect(&cf->sel, (cf == &vhi) ? normattr : selattr);
+               /*
+                * The -1 offset on xsel is important.  It insures that
+                * when findprevsel() starts looking for a previous
+                * tag, it doesn't find the current one again.
+                */
+               cf->sel = findprevsel(cf->sel.xl-1, cf->sel.yl, cf == &vhi);
+               paintselect(&cf->sel, hiliteattr);
+           }
+           refresh();
+           continue;
+
+#ifndef OLDCURSES
+       case KEY_FIND:          /* search for string */
+#endif /* OLDCURSES */
+       case CTL('S'):
+           restore_select = FALSE;
+#ifndef POPUP
+           prompt = SEARCH;
+#else
+           if (cp = get_dialogue(SEARCH))
+               if (search(cp, '\0') != NOWHERE)
+               {
+                   get_dialogue((char *)NULL);
+                   break;
+               }
+               else
+                   continue;
+#endif /* POPUP */
+           break;
+
+#ifndef OLDCURSES
+       case KEY_SFIND:         /* search for last string again */
+#endif /* OLDCURSES */
+        case CTL('R'):
+           restore_select = FALSE;
+           if (lasthit[0] == '\0')
+           {
+               message(NOSRCH);
+               continue;
+           }
+           else
+#ifdef POPUP
+               (void) search(lasthit, '\0');
+#else
+               {
+                   prompt = SEARCH;
+                   (void) strcpy(entrybuf, lasthit);
+                   entrycnt = strlen(lasthit);
+                   (void) ungetch(CTL('J'));
+               }
+#endif /* POPUP */
+           break;
+
+#ifndef OLDCURSES
+       case KEY_SELECT:        /* search for entry */
+#endif /* OLDCURSES */
+       case CTL('E'):
+       lookfor:
+           restore_select = FALSE;
+#ifndef POPUP
+           prompt = LOOKUP;
+#else
+           if (cp = get_dialogue(LOOKUP))
+           {
+               if (lookup(cp, '\0') == NOWHERE)
+                   beep();
+               get_dialogue((char *)NULL);
+               break;
+           }
+#endif /* POPUP */
+           break;
+
+#ifndef OLDCURSES
+       case KEY_HOME:          /* go to beginning of file */
+       case KEY_BEG:
+#endif /* OLDCURSES */
+       case CTL('A'):
+           enqueue(cf);
+           restore_select = FALSE;
+           cf->toppos = 0;
+           break;
+
+#ifndef OLDCURSES
+       case KEY_END:           /* go to end of file */
+#endif /* OLDCURSES */
+       case CTL('O'):
+           enqueue(cf);
+           restore_select = FALSE;
+           cf->toppos = cf->lastpagetoppos;
+           break;
+
+       case BS:                /* this is context-sensitive */
+#ifndef POPUP
+           if (prompt != NOMESG)
+               goto medialspace;
+           /* FALL THROUGH */
+#endif /* POPUP */
+
+#ifndef OLDCURSES
+       case KEY_UNDO:          /* backtrack into the location stack */
+#endif /* OLDCURSES */
+       case ESC:
+           restore_select = TRUE;
+           dequeue(cf);
+           break;
+
+       case TAB:               /* toggle between text and index */
+           restore_select = TRUE;
+           vhi.dsptoppos = vht->dsptoppos = NOWHERE;
+           if (cf == vht)
+               cf = &vhi;
+           else
+               cf = vht;
+           break;
+
+#ifndef OLDCURSES
+       case KEY_PRINT:         /* append selected entry to file */
+#endif /* OLDCURSES */
+       case CTL('Y'):
+           restore_select = FALSE;
+           (void) sprintf(linbuf, CUTFILE, execname);
+           if (cf->sel.xl == NOPLACE)
+           {
+               message(NOREFS);
+               continue;
+           }
+           else if ((cutfp = fopen(linbuf, "a")) == (FILE *)NULL)
+           {
+               message(CUTNOP);
+               continue;
+           }
+           else
+           {
+               long pos;       /* find target */
+               int dummy;
+
+               message(CUTNOW);
+
+               pos = iflink(cf->sel.xl, cf->sel.yl, &dummy,&dummy, cf == &vhi);
+               if (pos >= 0)   /* how can this fail? */
+                   cutentry(pos, cutfp);
+               (void) fclose(cutfp);
+
+               message(CUTDNE);
+               continue;
+           }
+           break;
+
+#ifdef MOUSE
+       /*
+        * Note: we treat a single-button mouse as having the left
+        * button only.
+        *
+        * The mouse_status() function should return the key-status defines
+        * implied below when appropriate.  It should return the zero-origin
+        * mouse hotspot coordinates at character-cell resolution, with
+        * CMDLINE and THUMBCOL as special values designating the command
+        * line and thumb column respectively (on character displays these
+        * would be LINES - 1 and COLS - 1 respectively).
+        */
+       case KEY_MOUSE: /* mouse gesture has occurred */
+           mstat = mouse_status(&hsy, &hsx);
+#ifndef POPUP
+           hsy = (hsy == LINES - 1) ? CMDLINE : hsy;
+#endif /* POPUP */
+           switch(mstat)
+           {
+           case BUTTON1_PRESSED:       /* left button down; chase link, else page */
+               restore_select = FALSE;
+               if (hsy == CMDLINE)             /* clicked on entry line */
+               {
+                   hsx -= PANELSTART;
+                   for (i = 0; i < sizeof(panel) / sizeof(strip); i++)
+                       if (hsx >= panel[i].left && hsx <= panel[i].right)
+                           (void) ungetch(panel[i].cmd);
+                   continue;
+               }
+               else if (hsx == THUMBCOL)       /* click in thumb column */
+               {
+                   if (hsy == 0)               /* clicked top of bar */
+                       goto linebak;
+                   else if (hsy == LASTLINE)   /* clicked bottom of bar */
+                       goto linefwd;
+                   else if (hsy < cf->ythumb)  /* clicked above thumb */
+                       goto pagebak;
+                   else if (hsy > cf->ythumb)  /* clicked below thumb */
+                       goto pagefwd;
+                   else                        /* clicked on the thumb */
+                       thumbdrag = TRUE;
+               }
+               else if (!chase(hsx, hsy, cf == &vhi))
+                   goto pagefwd;       /* click in text somewhere */
+               break;
+
+           case BUTTON1_RELEASED:      /* left button up */
+               restore_select = FALSE;
+               if (thumbdrag && 0 < hsy && hsy < LASTLINE)
+               {
+                   cf->toppos =                /* compute new position */
+                       cf->lastpagetoppos *
+                       (hsy - 1) / (LASTLINE - 2);
+
+                   assert(0 <= cf->toppos &&
+                          cf->toppos <= cf->lastpagetoppos);
+
+                   /* ensure on line boundary */
+                   if (0 < cf->toppos && cf->toppos < cf->lastpagetoppos)
+                       cf->toppos = getnextln(cf->fp, cf->toppos, linbuf);
+               }
+               thumbdrag = FALSE;                  
+               break;
+
+           case BUTTON3_PRESSED:       /* right button down; backtrack */
+               restore_select = TRUE;
+               dequeue(cf);
+               break;
+
+           case BUTTON3_RELEASED:      /* right button up */
+               /* reserved for future expansion */
+               break;
+           }
+           break;
+#endif /* MOUSE */
+
+       default:
+#ifdef POPUP
+           if (isprint(c))
+           {
+               (void) ungetch(c);
+               goto lookfor;
+           }               
+#else
+       medialspace:
+           restore_select = FALSE;
+           if (prompt == NOMESG)
+           {
+               ungetch(c);
+               goto lookfor;
+           }               
+           enterchar(c, prompt, LINES - 1, 0, PANELSTART);
+#endif /* POPUP */
+           lightup = SUPPRESS;
+       }
+
+#ifndef POPUP
+       /* regenerate prompt */
+       attrset(promptattr);
+       move(LINES - 1, 0);
+       addstr(prompt);
+       addstr(entrybuf);
+       addstr(nblanks(PANELSTART - strlen(prompt) - strlen(entrybuf)));
+       mvaddstr(LINES - 1, PANELSTART, (cf == &vhi) ? IBANNER : TBANNER);
+       attrset(normattr);
+#endif /* POPUP */
+
+       if (cf->toppos != cf->dsptoppos)
+       {
+           int i;
+           long newpos = cf->toppos;
+
+#ifdef DEBUG
+           (void) fprintf(stderr,
+                          "screen update triggered, toppos = %ld\n",
+                          cf->toppos);
+#endif /* DEBUG */
+
+           attrset(normattr);
+           for (i = 0; i <= LASTLINE; i++)
+               if ((newpos = getnextln(cf->fp, newpos, linbuf)) != NOWHERE)
+               {
+                   mvaddstr(i, 0, linbuf);
+                   /*
+                    * This is actually faster, and creates less
+                    * flicker, than clearing the screen.
+                    */
+                   addstr(nblanks(COLS - strlen(linbuf)));
+               }
+           cf->dspnextpos = ftell(cf->fp);
+
+#ifdef SCROLLBAR
+           /*
+            * Generate the scroll bar.  Yes, we really do want the denominator
+            * to be lastpagetoppos and not endpos.  This means the thumb only
+            * bottoms out if we're really at the end.
+            */
+           cf->ythumb = 1 + cf->toppos * (LASTLINE - 2) / cf->lastpagetoppos;
+           assert(0 < cf->ythumb && cf->ythumb < LASTLINE);
+           mvaddch(0, COLS, SBARTOPCH | sbarendattr);
+           for (i = 1; i < LASTLINE; i++)       /* set up scrollbar */
+               mvaddch(i, COLS, SBARCH | sbarattr);
+           mvaddch(LASTLINE, COLS, SBARBOTCH | sbarendattr);
+           mvaddch(cf->ythumb, COLS, SBARTHUMBCH | sbthumbattr);
+#endif /* SCROLLBAR */
+       }
+
+       /*
+        * This has to be done whether or not lightup is going to happen.
+        * Otherwise, a following command may enqueue a bogus selection
+        * box as part of state.
+        */
+       cf->sel = findnextsel(0, -1, cf == &vhi);
+
+       if (lightup == SUPPRESS)
+           lightup = OPTIONAL;
+       else 
+       {
+           if (incrsearch)
+               (void) byname(prompt, (char *)NULL, DEL);
+
+           if (lightup == FORCED || cf->toppos != cf->dsptoppos)
+           {
+               lightup = OPTIONAL;
+
+               /* highlight the current select, if any */
+               if (restore_select && cf->sel.xl != NOPLACE)
+                   paintselect(&cf->sel, hiliteattr); /* restore existing select */
+               else
+               {
+                   /* highlight the first selection, if there is one */
+                   if (cf->sel.xl != NOPLACE)
+                       paintselect(&cf->sel, hiliteattr);
+               }
+
+               /* if in text, boldface all potential selects */
+               if (!(cf == &vhi) && cf->sel.xl != NOPLACE)
+               {
+                   region      hibox;
+
+#ifdef DEBUG
+                   (void) fprintf(stderr,
+                                  "current: x = %2d, y = %2d, pos = %ld.\n",
+                                  cf->sel.xl, cf->sel.yl, cf->toppos);
+#endif /* DEBUG */
+                   hibox.xl = cf->sel.xl;
+                   hibox.yl = cf->sel.yl;
+                   for (;;)
+                   {
+                       hibox = findnextsel(hibox.xl, hibox.yl, cf == &vhi);
+#ifdef DEBUG
+                       (void) fprintf(stderr,
+                                      "hibox: x = %2d, y = %2d.\n",
+                                      hibox.xl, hibox.yl);
+#endif /* DEBUG */
+                       if (hibox.xl == cf->sel.xl && hibox.yl == cf->sel.yl)
+                           break;
+                       paintselect(&hibox, selattr);
+                   }
+               }
+           }
+       }
+
+       move(LINES - 1, 0);
+
+       cf->dsptoppos = cf->toppos;
+
+       refresh();
+    }
+
+    uninitbrowse(TRUE);
+}
+
+#ifdef MSDOS
+static char *basename(s)
+/* isolate base name of fully qualified filename (modifies the name passed!) */
+char   *s;
+{
+    char *p;
+    p = strrchr(s, '.');       /* find rightmost dot */
+    if ( strchr(p, '\\') )     /* if a \ comes after it, find string end */
+       p = strchr(p, 0);
+    *p = 0;                    /* terminate at last dot after a \ (if any)*/
+    if ( (p = strrchr(s, '\\')) == NULL ) /* find last \ (if any) */
+       p = s;                  /* take whole string if no \ */
+    else
+       ++p;                    /* else take string after \ */
+    strlwr(p);
+    return p;
+}
+#else
+#define basename(x)    x       /* don't need to strip argv[0] under UNIX */
+#endif /* MSDOS */
+
+/*******************************************************************
+ *
+ * Main sequence
+ *
+ ******************************************************************/
+
+main(argc, argv)
+int    argc;
+char   **argv;
+{
+    char       *dbname, *batchkey = NULL;
+    int                i;
+    bool       doindex = FALSE, docheck = FALSE, dofilter = FALSE;
+    bool       forcemono = FALSE, atrandom = FALSE;
+
+    execname = argv[0];
+    while ((++argv, --argc) && *argv[0] == '-')
+       for (i = 1; argv[0][i]; i++)
+           switch(argv[0][i])
+           {
+           case 'b':
+               batchkey = argv[1];
+               break;
+
+           case 'c':
+               docheck = TRUE;
+               break;
+
+           case 'f':
+               dofilter = TRUE;
+               break;
+
+           case 'g':
+               doindex = TRUE;
+               break;
+
+           case 'i':
+               incrsearch = TRUE;
+               break;
+
+           case 'm':
+               forcemono = TRUE;
+               break;
+
+           case 'r':
+               atrandom = TRUE;
+               break;
+
+#ifdef __TURBOC__
+           case 's':
+               {   /* if CGA that snows: */
+                   extern bool has_snowy_CGA;
+                   has_snowy_CGA = TRUE;
+               }
+               break;
+#endif /* __TURBOC__ */
+
+           default:
+               (void) fprintf(stderr, USAGE);
+               exit(1);
+           }
+
+#ifdef DEBUG
+    (void) freopen("ERRLOG", "w", stderr);
+#endif /* DEBUG */
+
+    /*
+     * User specified a batch-retrieval option.
+     * OK, snarf the keyword from the next argument.
+     */
+    if (batchkey)
+       ++argv, --argc;
+
+    dbname = argc ? argv[0] : basename(execname);
+
+    if (!doindex && !docheck && !atrandom && !dofilter
+       && batchkey == (char *)NULL)
+    {
+       (void) printf(BRSMES, dbname);
+       browse(dbname, !forcemono);
+    }
+
+    if (doindex)
+    {
+       (void) printf(IDXMES, dbname);
+       if (argc)
+           mkindex(argc, argv);
+       else
+           mkindex(1, &dbname);
+    }
+
+    if (docheck)
+    {
+       (void) printf(CHKMES, dbname);
+       chkindex(dbname);
+    }
+
+    if (batchkey)
+    {
+       daddr_t pos;
+
+       if (!initbrowse(dbname))
+           exit(1);
+
+       if ((pos = xlocate(batchkey)) >= 0)
+           cutentry(pos, stdout);
+    }
+
+    if (dofilter)
+    {
+       daddr_t pos;
+       char    batchkey[81];
+
+       if (!initbrowse(dbname))
+           exit(1);
+
+       while (fgets(batchkey, sizeof(batchkey) - 1, stdin) != (char *)NULL)
+       {
+           char        *ep = &batchkey[strlen(batchkey) - 1];
+
+           if (*ep == '\n')
+           {
+               *ep = '\0';
+               if (*--ep == '\r')
+                   *ep = '\0';
+           }
+           if ((pos = xlocate(batchkey)) >= 0)
+               cutentry(pos, stdout);
+       }
+    }
+
+    if (atrandom)
+    {
+       daddr_t pos;
+
+       if (!initbrowse(dbname))
+           exit(1);
+
+       if ((pos = jrandom()) >= 0)
+           cutentry(pos, stdout);
+    }
+
+    exit(0);
+}
+
+/* browse.c ends here */
diff --git a/screen.c b/screen.c
new file mode 100644 (file)
index 0000000..5b90ba1
--- /dev/null
+++ b/screen.c
@@ -0,0 +1,1194 @@
+/*****************************************************************************
+
+NAME
+   screen.c -- augmented pseudo-curses(3) screen support
+
+SYNOPSIS
+   int LINES, COLS;            -- terminal depth & width globals
+
+   int initscr()               -- init screen, set LINES and COLS
+   int endwin()                        -- de-initialize screen code
+   int clear()                 -- clear screen
+   int refresh()               -- refresh real screen from virtual screen
+   int ungetch(c)              -- put c on queue to be grabbed by next getch()
+   int inch()                  -- get char from current update location.
+   int move(y, x)              -- move update cursor to row y colum x
+   int addch(c)                        -- add char c at update location
+   int addstr(s)               -- write string from update location
+   int attrset(a)              -- set attribute (A_NORMAL, A_REVERSE, A_BOLD)
+   bool has_color()            -- do we have color?
+   int napms()                 -- nap for some number of milliseconds
+   void beep();                        -- beep the terminal bell
+
+   int egetch()                        -- get mouse event or character, no echo
+   void readscr(lx,ly,rx,ry, s)        -- get content of screen line
+
+   bool has_mouse()            -- is there a mouse?
+   void mouse_init()           -- initialize mouse
+   int mouse_status(py,px)     -- get mouse location and button state
+   void mouse_show()           -- show mouse cursor
+   void mouse_hide()           -- hide mouse cursor
+
+DESCRIPTION
+   The idea here is to present a system-independent interface that looks like
+a subset of curses(3), with a minimum of hooks for mouse handling and one
+concession to Borland C for optimizations's sake.
+
+   The addch() and addstr() functions move the update cursor to the right after
+writing.  The argument of addch() may have an attribute logical-or'ed in.
+The inch() call returns a char with attribute or'ed in; A_CHARTEXT masks out
+the character only. The following special keycap defines are used:
+
+   KEY_HELP            -- not on PC keyboard
+   KEY_REFRESH         -- not on PC keyboard
+   KEY_NPAGE           -- PgDn
+   KEY_PPAGE           -- PgUp
+   KEY_UP              -- Up Arrow
+   KEY_DOWN            -- Down Arrow
+   KEY_REFERENCE       -- not on PC keyboard
+   KEY_ENTER           -- Enter
+   KEY_RIGHT           -- Right Arrow
+   KEY_LEFT            -- Left Arrow
+   KEY_FIND            -- not on PC keyboard
+   KEY_SFIND           -- not on PC keyboard
+   KEY_SELECT          -- not on PC keyboard
+   KEY_HOME            -- Home
+   KEY_BEG             -- not on PC keyboard
+   KEY_END             -- End
+   KEY_UNDO            -- not on PC keyboard
+   KEY_PRINT           -- not on PC keyboard
+   KEY_MOUSE           -- mouse event happened (not supported by curses(3))
+
+Mouse state defines returned by mouse_status():
+
+   LEFT_DOWN           -- left mouse button down
+   LEFT_UP             -- left mouse button release
+   RIGHT_DOWN          -- right mouse button down
+   RIGHT_UP            -- right mouse button up
+
+Special mouse coordinate values:
+
+   CMDLINE             -- command line of application window
+   THUMBCOL            -- thumb column of application window
+
+BUGS
+   The MS-DOS implementation doesn't do optimization for the 
+scrolling case.
+
+AUTHORS
+   Package interface design and UNIX curses(3) code by Eric S. Raymond.
+   MS-DOS code by Raymond D. Gardner.
+   Please see the READ.ME in this directory for license terms.
+
+******************************************************************************/
+/* LINTLIBRARY */
+#include <assert.h>
+
+#include "screen.h"
+
+#ifdef CURSES
+/*******************************************************************
+ *
+ * Support for curses(3) under UNIX.
+ *
+ ******************************************************************/
+#ifdef MOUSE
+#include <fcntl.h>
+#include <sys/mouse.h>
+
+/*
+ * We want to make the mouse relatively insensitive to small motions.
+ * The code ignores all but 1 in MXSCALE horizontal motions and 1 in MYSCALE
+ * vertical motions.  The factors differ as they do because most fonts have
+ * a roghly 2/1 aspect ratio; thus, to get the same real rate of vertical
+ * motion as horizontal, MYSCALE should be twice as large.
+ */
+#define MXSCALE        2
+#define MYSCALE (MXSCALE * 2)
+
+static int mousefd;    /* mouse file descriptir */
+static int msy, msx;   /* current mouse coordinates */ 
+static int mstat;      /* current mouse status */
+static bool mvis = TRUE;       /* should cursor be visible? */
+static bool mcolor;    /* show mouse cursor in color? */
+#endif /* MOUSE */
+
+void mouse_init()
+/* initialize the mouse */
+{
+#ifdef MOUSE
+    /*
+     * If there's an active mouse, set O_NODELAY on input so we
+     * can alternate polling for keystrokes and mouse events without
+     * blocking.
+     */
+    if ((mousefd = open("/dev/mouse", O_RDONLY)) != ERR)
+       nodelay(stdscr, TRUE);
+#endif /* MOUSE */
+#ifdef SCROLLBAR
+    COLS--;
+#endif /* SCROLLBAR */
+}
+
+void mouse_color(on)
+/* should the mouse cursor be in color? */
+bool   on;
+{
+#ifdef MOUSE
+    if (on && has_colors())
+       init_pair(COLOR_PAIRS - 1, COLOR_BLACK, COLOR_YELLOW);
+    mcolor = on;
+#endif /* MOUSE */
+}
+
+void mouse_move(y, x)
+/* move the mouse hotspot to a given location */
+int    y, x;
+{
+#ifdef MOUSE
+    msy = y;
+    msx = x;
+#endif /* MOUSE */
+}
+
+void mouse_show()
+{
+#ifdef MOUSE
+    mvis = TRUE;
+#endif /* MOUSE */
+}
+
+void mouse_hide()
+{
+#ifdef MOUSE
+    mvis = FALSE;
+#endif /* MOUSE */
+}
+
+int mouse_status(py, px)
+/* get current mouse status, return coordinates */
+int    *py, *px;
+{
+#ifdef MOUSE
+    if (mousefd != ERR)
+    {
+#ifdef SCROLLBAR
+       *px = (msx == COLS) ? THUMBCOL : msx;
+#else
+       *px = msx;
+#endif /* SCROLLBAR */
+       *py = msy;
+       return(mstat);
+    }
+#endif /* MOUSE */
+    return(*px = *py = ERR);
+}
+
+static int cgetch()
+/* get single character or mouse event */
+{
+#ifndef MOUSE
+    return(getch());
+#else
+#ifdef ATTMOUSE
+    struct mouseinfo m;
+#endif /* ATTMOUSE */
+    int mchattr, oldx, oldy, oldmy, oldmx;
+    int        c;
+
+    if (mousefd == ERR)
+       return(getch());
+
+    if (mvis)
+    {
+       getyx(stdscr, oldy, oldx);
+
+       move(msy, msx);
+       mchattr = inch();
+       move(msy, msx);
+       if (mcolor)
+           addch((mchattr & 0xff) | COLOR_PAIR(COLOR_PAIRS - 1));
+       else
+           addch(mchattr ^ A_REVERSE);
+       oldmy = msy; oldmx = msx;
+       refresh();
+    }
+
+    for (;;)
+    {
+       /* first poll for a character */
+       if ((c = getch()) != ERR)
+           break;
+
+       /* then poll for a mouse event */
+#ifdef ATTMOUSE
+       (void) ioctl(mousefd, MOUSEIOCREAD, &m);
+       if ((m.status &~ MOVEMENT) | m.xmotion | m.ymotion)
+       {
+           register i, newx, newy;
+
+           newx = msx;
+           if (m.xmotion < 0)
+               for (i = 0; i < -(m.xmotion / MXSCALE); i++)
+                   if (newx <= 0)
+                       break;
+                   else
+                       newx--;
+           if (m.xmotion > 0)
+               for (i = 0; i < (m.xmotion / MXSCALE); i++)
+#ifdef SCROLLBAR
+                   if (newx >= COLS)
+#else
+                   if (newx >= COLS - 1)
+#endif /* SCROLLBAR */
+                       break;
+                   else
+                       newx++;
+           msx = newx;
+           newy = msy;
+           if (m.ymotion < 0)
+               for (i = 0; i < -(m.ymotion / MYSCALE); i++)
+                   if (newy <= 0)
+                       break;
+                   else
+                       newy--;
+           if (m.ymotion > 0)
+               for (i = 0; i < (m.ymotion / MYSCALE); i++)
+                   if (newy >= LINES - 1)
+                       break;
+                   else
+                       newy++;
+           msy = newy;
+
+           mstat = 0;
+           if (m.status & BUT1CHNG)
+               if (m.status & BUT1STAT)
+                   mstat |= BUTTON1_PRESSED;
+               else
+                   mstat |= BUTTON1_RELEASED;
+           if (m.status & BUT3CHNG)
+               if (m.status & BUT3STAT)
+                   mstat |= BUTTON3_PRESSED;
+               else
+                   mstat |= BUTTON3_RELEASED;
+
+           c = KEY_MOUSE;
+           break;
+       }
+
+       /* keep polling till you get one or the other */
+    }
+#endif /* ATTMOUSE */
+
+    if (mvis)
+    {
+       move(oldmy, oldmx);
+       addch(mchattr);
+       move(oldy, oldx);
+    }
+
+    return(c);
+#endif /* MOUSE */
+}
+
+void readscr(lx, ly, rx, ry, outbuf)
+/* get text from screen */
+int    lx, ly, rx, ry;
+char   *outbuf;
+{
+    int        x, y;
+
+    for (x = lx; x <= rx; x++)
+       for (y = ly; y <= ry; y++)
+       {
+           move(y, x);
+           *outbuf++ = inch() & A_CHARTEXT;
+       }
+    *outbuf = '\0';
+}
+
+#ifndef A_COLOR
+bool has_colors()
+{
+    return(FALSE);
+}
+
+int start_color()
+{
+    /* do nothing */
+}
+int init_pair(pn, fg, bg)
+int pn, fg, bg;
+{
+    /* do nothing */
+}
+
+#endif /* A_COLOR */
+
+#endif /* CURSES */
+
+#ifdef BSD
+/*******************************************************************
+ *
+ * Support for BSD, which is missing some stuff...
+ *
+ ******************************************************************/
+
+int attrset(a)
+/* set write attribute for subsequent operations */
+chtype a;
+{
+    /*
+     * It would be real nice to have more attributes supported in here...
+     */
+    if (a | A_REVERSE)
+       standout();
+    else
+       standend();
+    return(OK);
+}
+
+void beep()
+{
+    (void) putchar('\007');
+}
+
+int start_color()
+{
+    /* do nothing */
+}
+
+/*ARGSUSED0*/
+int init_pair(pn, fg, bg)
+int pn, fg, bg;
+{
+    /* do nothing */
+}
+
+unsigned char acs_map[] =
+{
+   /* ACS_BSSB */      '+',
+   /* ACS_SSBB */      '+',
+   /* ACS_BBSS */      '+',
+   /* ACS_SBBS */      '+',
+   /* ACS_SBSS */      '|',
+   /* ACS_SSSB */      '|',
+   /* ACS_SSBS */      '-',
+   /* ACS_BSSS */      '-',
+   /* ACS_BSBS */      '-',
+   /* ACS_SBSB */      '|',
+   /* ACS_SSSS */      '+',
+   /* ACS_S1 */                '~',    /* scan line 1 */
+   /* ACS_S9 */                '_',    /* scan line 9 */
+   /* ACS_DIAMOND */   '*',    /* diamond */
+   /* ACS_CKBOARD */   '#',    /* checker board */
+   /* ACS_DEGREE */    'o',    /* degree symbol */
+   /* ACS_PLMINUS */   '#',    /* plus/minus */
+   /* ACS_BULLET */    '*',    /* bullet */
+   /* ACS_LARROW */    27,     /* arrow pointing left */
+   /* ACS_RARROW */    '>',    /* arrow pointing right */
+   /* ACS_DARROW */    'V',    /* arrow pointing down */
+   /* ACS_UARROW */    '^',    /* arrow pointing up */
+   /* ACS_BOARD */     '#',    /* board of squares */
+   /* ACS_LANTERN */   '#',    /* lantern symbol */
+   /* ACS_BLOCK */     '#',    /* solid square block */
+};
+#endif /* BSD */
+
+#ifdef MSDOS
+/*******************************************************************
+ *
+ * Support for Borland C under MS-DOS
+ *
+ ******************************************************************/
+#include <dos.h>
+#include <conio.h>
+
+#define GQDEPTH                32      /* depth of character push queue */
+
+int LINES, COLS, COLOR_PAIRS, COLORS = 8;
+
+/* vtype values */
+#define        PGA             (7)
+#define        VGA             (6)
+#define        MCGA            (5)
+#define        EGA             (4)
+#define        CGA             (3)
+#define        HERC            (2)
+#define        MDA             (1)
+#define        NONE            (0)
+#define        VUNKNOWN        (-1)
+
+/* hascolor values: */
+#define        COLOR           (2)
+#define        MONO            (1)
+
+static int hascolor;
+static char screensave[MAXROWS*MAXCOLS*2];     /* save user's screen here */
+
+int bufseg;            /* vid buffer segment addr for emulating gettext/puttext */
+
+#ifdef __TURBOC__
+bool has_snowy_CGA = FALSE;
+#endif /* __TURBOC__ */
+
+int initscr()
+{
+    union REGS r;
+    struct SREGS s;
+    int vtype, vchrome, xmode, *mode = &xmode;
+
+    /* default assumptions */
+    hascolor = 0;
+    LINES = 25;
+    bufseg = 0xb800;
+
+    /* get mode and columns */
+    r.h.ah = 0x0F;
+    int86(0x10, &r, &r);
+    *mode = r.h.al;
+    COLS = r.h.ah;
+
+    r.x.ax = 0x1200;           /* alternate function select */
+    r.x.bx = 0x0010;
+    int86(0x10, &r, &r);
+    if (r.x.bx != 0x0010)      /* must be pga/vga/mcga/ps2/ega */
+    {
+       vtype = EGA;            /* provisional assumption */
+       hascolor = r.h.bh ? MONO : COLOR;
+
+       /* now try for "higher" display than EGA */
+
+       r.x.ax = 0x1A00;        /* get display combination */
+       int86(0x10, &r, &r);
+       if (r.h.al == 0x1A)     /* must be pga/vga/mcga/ps2 */
+       {
+           switch (r.h.bl)
+           {
+           case 0: vtype = NONE;       break;
+           case 1: vtype = MDA;        break;
+           case 2: vtype = CGA;        break;
+           case 4: vtype = EGA;        break;
+           case 5: vtype = EGA;        break;
+           case 6: vtype = PGA;        break;
+           case 7: vtype = VGA;        break;
+           case 8: vtype = VGA;        break;
+           case 0xC:           /* fall through; analog vs. digital display */
+           case 0xA: vtype = MCGA;     break;
+           case 0xB: vtype = MCGA;     break;
+           default:
+               vtype = VUNKNOWN;
+               break;
+           }
+       }
+
+       /* get number of rows on screen */
+       if (vtype == EGA || vtype == MCGA || vtype == VGA)
+       {
+           segread(&s);
+           r.x.ax = 0x1130;    /* get font info */
+           r.h.bh = 0;
+           int86x(0x10, &r, &r, &s);
+           LINES = r.h.dl + 1;
+       } 
+       else
+           LINES = *(int far *)MK_FP(0x40, 0x84);
+    }
+    else
+    {                  /* not pga/vga/mcga/ps2/ega */
+       if (*mode != 7)
+           vtype = CGA;
+       else
+       {
+           vtype = MDA;
+           hascolor = MONO;
+       }
+    }
+
+    bufseg = 0xb800;
+    if (vtype == MDA || hascolor == MONO)
+        bufseg = 0xb000;
+
+    gettext(1, 1, COLS, LINES, screensave);    /* save user's screen */
+#ifdef SCROLLBAR
+    COLS--;
+#endif /* SCROLLBAR */
+
+#ifdef __TURBOC__
+    {
+       extern int _video[12];
+       if (! has_snowy_CGA)
+           _video[5] = 0;
+    }
+#endif
+    return OK;
+}
+
+#ifdef __ZTC__
+/*******************************************************************
+*
+* gettext()/puttext() -- simulate Borland screen i/o routines
+*
+*******************************************************************/
+
+int puttext(int left, int top, int right, int bot, char *buf)
+{
+    int cols, row;
+    char far *vid;
+    char far *vidstart;
+    char *lim;
+    cols = right - left + 1;
+    --left;
+    --top;
+    vidstart = vid = MK_FP(bufseg, top*160+left*2);
+    for ( row = top; row < bot; ++ row )
+    {
+       for ( lim = buf + cols * 2; buf < lim; )
+           *vid++ = *buf++;
+       vid = vidstart += 160;
+    }
+    return 0;
+}
+
+int gettext(int left, int top, int right, int bot, char *buf)
+{
+    int cols, row;
+    char far *vid;
+    char far *vidstart;
+    char *lim;
+
+    cols = right - left + 1;
+    --left;
+    --top;
+    vidstart = vid = MK_FP(bufseg, top*160+left*2);
+    for ( row = top; row < bot; ++ row )
+    {
+       for ( lim = buf + cols * 2; buf < lim; )
+           *buf++ = *vid++;
+       vid = vidstart += 160;
+    }
+    return 0;
+}
+#endif /* __ZTC__ */
+
+
+int endwin()
+/* de-initialize window handling */
+{
+#ifdef SCROLLBAR
+    COLS++;
+#endif /* SCROLLBAR */
+    puttext(1, 1, COLS, LINES, screensave);    /* restore user screen */
+    move(LINES-1,0);
+    return OK;
+}
+
+bool has_colors()
+/* is color available? */
+{
+    return(hascolor == COLOR);
+}
+
+void readscr(int left, int top, int right, int bot, char *buf0)
+/* read the text out of screen region into a buffer */
+{
+    char s[MAXCOLS * 2];
+    register char *buf;
+    register char *p;
+    register int k;
+    int size;
+
+    buf = buf0;
+    k = (bot-top+1)*(right-left+1);
+    assert(k <= MAXCOLS);
+    gettext(left+1, top+1, right+1, bot+1, s);
+    p = s;
+    do
+    {
+       *buf++ = *p;
+       p += 2;
+    } while ( --k );
+    *buf = 0;
+}
+
+int refresh()
+{
+    /* do nothing; this is a write-through implementation */
+    return OK;
+}
+
+/********************** mouse routines ************************/
+
+static bool mse = FALSE;       /* nonzero iff mouse is present */
+
+bool has_mouse()
+/* is there a mouse? */
+{
+    return(mse);
+}
+
+void mouse_color(on)
+/* show mouse cursor in color? */
+bool   on;
+{
+    (void)on;          /* quiet the compiler warning */
+    /* do nothing */
+}
+
+void mouse_init()
+/* initialize the mouse */
+{
+    union REGS r;
+    struct SREGS sregs;
+
+    segread(&sregs);
+    r.x.ax = 0x3533;
+    int86x(0x21, &r, &r, &sregs); /* get mouse driver int addr */
+    if (sregs.es == 0 && r.x.bx == 0) /* if addr is zero, no mouse */
+       return;
+
+    /* if addr points to IRET, no mouse */
+    if (*(unsigned char far *)MK_FP(sregs.es, r.x.bx) == 0xcf)
+       return;
+
+    r.x.ax = 0;
+    int86(0x33, &r, &r);       /* try to init mouse driver */
+    mse = r.x.ax;              /* set mouse status global var */
+       mouse_show();
+}
+
+void mouse_show()
+/* turn on the mouse cursor */
+{
+    union REGS r;
+
+    if (mse)
+    {
+       r.x.ax = 1;
+       int86(0x33, &r, &r);
+    }
+}
+
+void mouse_hide()
+/* turn off mouse cursor */
+{
+    union REGS r;
+
+    if (mse)
+    {
+       r.x.ax = 2;
+       int86(0x33, &r, &r);
+    }
+}
+
+void mouse_move(y, x)
+/* set mouse cursor position (zero-origin coordinates) */
+int    y, x;
+{
+    union REGS r;
+
+    if (mse)
+    {
+       r.x.ax = 4;
+       r.x.cx = x << 3;
+       r.x.dx = y << 3;
+       int86(0x33, &r, &r);
+    }
+}
+
+/******************* screen/cursor routines ****************/
+
+static int cury, curx;         /* virtual update cursor */
+static chtype cattr;           /* current attribute */
+static chtype pairs[64];       /* color pair array */
+
+int move(row, col)
+/* cursor positioning */
+int row, col;
+{
+    union REGS regs;
+
+    mouse_hide();
+    regs.h.ah = 2;
+    regs.h.bh = 0;
+    regs.h.dh = row;
+    regs.h.dl = col;
+
+    int86(0x10, &regs, &regs);
+    mouse_show();
+
+    cury = row;
+    curx = col;
+    return 0;
+}
+
+int getyx(row, col)
+/* get current cursor position */
+int *row;
+int *col;
+{
+    *row = cury;
+    *col = curx;
+    return 0;
+}
+
+static void scroll(forward, nlines, attr, top, bot, left, right)
+/* scroll screen region */
+bool forward;
+int nlines, attr, top, bot, left, right;
+{
+    union REGS regs;
+
+    mouse_hide();
+    regs.h.ah = 6 + !forward;
+    regs.h.al = nlines;
+    regs.h.bh = attr;
+    regs.h.ch = top;
+    regs.h.cl = left;
+    regs.h.dh = bot;
+    regs.h.dl = right;
+    int86(0x10, &regs, &regs);
+    mouse_show();
+}
+
+int clear()
+{
+    scroll(TRUE, 0, pcattr(cattr), 0, LINES - 1, 0, COLS - 1);
+    move(0, 0);
+    return OK;
+}
+
+int pcattr(a)
+/* map A_* and color attributes to the bits in IBM CGA/EGA attribute words */
+chtype a;
+{
+    chtype attr = 0;
+    int p;
+
+#ifdef RDGOPT
+    static chtype prevarg = -1234;     /* very unlikely first value */
+    static int prevattr;
+
+    if ( a == prevarg )                /* if same arg as last one ... */
+       return prevattr;        /* ... return prev value */
+
+    prevarg = a;               /* save for next time */
+#endif /* RDGOPT */
+
+    if ( ! (a & A_COLOR) )
+    {
+       /* monochrome attributes */
+       attr  = 0x0007;                         /* "normal" attr */
+       if (a & A_STANDOUT)             attr  = 0x0070;
+       if (a & A_REVERSE)              attr  = 0x0070;
+       if (a & A_BLINK)                attr |= 0x0080;
+       if (a & A_UNDERLINE)            attr |= 0x0001;
+    }
+    else
+    {
+       /* color setup */
+       if (p = PAIR_NUMBER(a))
+           attr |= pairs[p];
+    }
+    if (a & A_BOLD)            attr |= 0x0008; /* works for color or mono */
+
+#ifdef RDGOPT
+    prevattr = attr;   /* save for next time */
+#endif /* RDGOPT */
+
+    return(attr);
+}
+
+static void swrite(row, col, s0, attr)
+/* write data, starting at given row/col, with given attribute */
+int row, col;
+char *s0;
+int attr;
+{
+    char buf[MAXCOLS * 2];
+    register char *s;
+
+    ++row;
+    ++col;
+    s = s0;
+    if ( *s )
+    {
+       register int *p;
+       register int n;
+       n = strlen(s);
+       p = (int *)buf;
+       memset(buf, attr, n * 2);
+       *(char *)p = *s;
+       --n;
+       if ( n )
+       {
+           do
+           {
+               *(char *)(++p) = *++s;
+           } while ( --n );
+       }
+       ++s;
+    }    
+
+    mouse_hide();
+    if ( s > s0 )
+       puttext(col, row, col + (s - s0) - 1, row, buf);
+    mouse_show();
+}
+
+int attrset(a)
+/* set write attribute for subsequent operations */
+chtype a;
+{
+    cattr = a;
+    return(OK);
+}
+
+int start_color()
+{
+    if (!has_colors())
+       return(ERR);
+
+    COLOR_PAIRS = sizeof(pairs) / sizeof(int);
+
+    return(OK);
+}
+
+int init_pair(n, fg, bg)
+/* initialize a given color pair to given foreground/background combination */
+int    n, fg, bg;
+{
+    /*
+     * By a not-so-amazing coincidence, the numbers of the 8 basic
+     * curses(3) colors are precisely the RGB bit patters needed
+     * to coerce an IBM CGA adapter.
+     */
+    if (n < COLOR_PAIRS)
+    {
+       pairs[n] = (bg << 4) | fg;
+       return(OK);
+    }
+    else
+       return(ERR);
+}
+
+int addstr(s)
+/* write a string to the screen, advancing the update cursor */
+char   *s;
+{
+    swrite(cury, curx, s, pcattr(cattr));
+    curx += strlen(s);
+    return(OK);
+}
+
+int _addch(c)
+/* write a character to the screen, advancing the update cursor */
+chtype c;
+{
+    static char s[2];
+    chtype attr;
+
+    if ( (attr = c & A_ATTRIBUTES) == 0 )
+       attr = cattr;
+    s[0] = c;
+    swrite(cury, curx, s, pcattr(attr));
+    curx++;
+    return(OK);
+}
+
+/*********** keyboard & mouse handling *****************/
+
+/* status variables for last mouse event */
+static int lastx, lasty;       /* last mouse position */
+static int lastevent;  /* last button event */
+
+/* We use MSDOS function 6 (direct console i/o) to avoid problems
+** with control-c/control-break checking
+*/
+static int dos06(int ch) /* ch is -1 for input; anything else for output */
+{
+#define ZFLAG 0x40
+    union REGS r;
+
+    r.h.ah = 6;
+    r.h.dl = ch;
+    intdos(&r, &r);
+    if ( ch == -1 )
+       return r.x.flags & ZFLAG ? -1 : r.h.al;
+    else
+       return 0;
+}
+
+static int cinp(int nowait)
+{
+    int r;
+
+    do
+    {
+       r = dos06(-1);
+    } while ( ! nowait && r == -1 );
+    if ( r == 0 )
+    {
+       r = 256 + dos06(-1);
+       assert(r > 256);
+    }
+    else if ( r == -1 )
+    {
+       r = 0;
+    }
+    return r;
+}
+
+static int getkey()
+{
+    return cinp(FALSE);
+}
+
+static int getkeynowait()
+{
+    return cinp(TRUE);
+}
+
+int mse_status_raw(py, px)
+int *px, *py;
+{
+    union REGS r;
+
+    r.x.ax = 3;
+    int86(0x33, &r, &r);
+    *px = r.x.cx >> 3; /* translate coords to col/row */
+    *py = r.x.dx >> 3;
+    return r.x.bx;
+}
+
+static int cgetch()
+/* return next keypress or mouse click */
+{
+    static int left_was_down = FALSE;
+    static int right_was_down = FALSE;
+    int code;
+
+    /* no mouse? then just get a keystroke */
+    if (!mse)
+       code = getkey();
+    else               /* there's a mouse; wait on event */
+       for (;;)
+       {
+           int x, y, mse_stat;
+
+           if (code = getkeynowait())
+               break;
+#if 00
+           mse_stat = mouse_status(&y, &x);
+#endif
+           /* poll the mouse */
+           mse_stat = mse_status_raw(&lasty, &lastx);
+           
+           if ( mse_stat & 1 )
+           {
+               if ( ! left_was_down )
+               {
+                   left_was_down = TRUE;
+                   lastevent = (lastevent & ~ BUTTON1_RELEASED) | BUTTON1_PRESSED;
+                   return KEY_MOUSE;
+               }
+               else
+               {
+#ifdef SCROLLBAR
+                   napms(150);
+                   if ( mse_status_raw(&lasty, &lastx) & 1 && lastx == COLS )
+                   {
+                       lastevent =
+                               (lastevent & ~ BUTTON1_RELEASED) | BUTTON1_PRESSED;
+                       return KEY_MOUSE;
+                   }
+#endif /* SCROLLBAR */
+               }
+           }
+           else
+           {
+               if ( left_was_down )
+               {
+                   left_was_down = FALSE;
+                   lastevent = (lastevent & ~ BUTTON1_PRESSED) | BUTTON1_RELEASED;
+                   return KEY_MOUSE;
+               }
+               else
+                   lastevent = 0;
+           }
+
+           if ( mse_stat & 2 )
+           {
+               if ( ! right_was_down )
+               {
+                   right_was_down = TRUE;
+                   lastevent = (lastevent & ~ BUTTON3_RELEASED) | BUTTON3_PRESSED;
+                   return KEY_MOUSE;
+               }
+           }
+           else
+           {
+               if ( right_was_down )
+               {
+                   right_was_down = FALSE;
+                   lastevent = (lastevent & ~ BUTTON3_PRESSED) | BUTTON3_RELEASED;
+                   return KEY_MOUSE;
+               }
+               else
+                   lastevent = 0;
+           }
+
+#if 00
+           lastevent = 0;
+           if ((mse_stat & 0x01) != left_was_down)
+           {
+               lastevent |= (left_was_down ? BUTTON1_RELEASED : BUTTON1_PRESSED);
+               left_was_down = !left_was_down;
+               return(KEY_MOUSE);
+           }
+
+           if ((mse_stat & 0x02) != right_was_down)
+           {
+               lastevent |= (right_was_down ? BUTTON3_RELEASED : BUTTON3_PRESSED);
+               right_was_down = !right_was_down;
+               return(KEY_MOUSE);
+           }
+#endif
+       }
+
+    /* special-key code translation */
+    switch (code)
+    {
+    case 315:  return(KEY_F(1));       /* break; */
+    case 316:  return(KEY_F(2));       /* break; */
+    case 317:  return(KEY_F(3));       /* break; */
+    case 318:  return(KEY_F(4));       /* break; */
+    case 319:  return(KEY_F(5));       /* break; */
+    case 320:  return(KEY_F(6));       /* break; */
+    case 321:  return(KEY_F(7));       /* break; */
+    case 322:  return(KEY_F(8));       /* break; */
+    case 323:  return(KEY_F(9));       /* break; */
+    case 324:  return(KEY_F(10));      /* break; */
+
+    case 328:  return(KEY_UP);         /* break; */
+    case 336:  return(KEY_DOWN);       /* break; */
+    case 331:  return(KEY_LEFT);       /* break; */
+    case 333:  return(KEY_RIGHT);      /* break; */
+
+    case 335:  return(KEY_END);        /* break; */
+    case 327:  return(KEY_HOME);       /* break; */
+    case 337:  return(KEY_NPAGE);      /* break; */
+    case 329:  return(KEY_PPAGE);      /* break; */
+
+    default:           break;
+    }
+    return(code);
+}
+
+int mouse_status(py, px)
+/* return status caught by last mouse button event */
+int    *py, *px;
+{
+    *py = lasty;
+#ifdef SCROLLBAR
+    *px = (lastx == COLS) ? THUMBCOL : lastx;
+#else
+    *px = lastx;
+#endif /* SCROLLBAR */
+    return(lastevent);
+}
+
+int inch()
+/* get character and attribute at current update position */
+{
+    char       buf[4];
+
+    gettext(curx + 1, cury + 1, curx + 1, cury + 1, buf);
+    return(buf[0]);
+}
+
+#include <time.h>
+#define        millisecs()     ((clock() * 10000)/(int)(double)(0.5+CLK_TCK*10))
+
+int napms(n)
+/* sleep for n milliseconds */
+int    n;
+{
+    long       t0 = millisecs() + n;
+
+    while (millisecs() < t0)
+       continue;
+    return(millisecs());
+}
+
+void beep()
+{
+    (void) putchar('\007');
+}
+
+unsigned char acs_map[] =
+{
+   /* ACS_BSSB */      218,
+   /* ACS_SSBB */      192,
+   /* ACS_BBSS */      191,
+   /* ACS_SBBS */      217,
+   /* ACS_SBSS */      180,
+   /* ACS_SSSB */      195,
+   /* ACS_SSBS */      193,
+   /* ACS_BSSS */      194,
+   /* ACS_BSBS */      196,
+   /* ACS_SBSB */      179,
+   /* ACS_SSSS */      197,
+   /* ACS_S1 */                223,    /* scan line 1 (actually top-half on) */
+   /* ACS_S9 */                22,     /* scan line 9 */
+   /* ACS_DIAMOND */   254,    /* diamond (actually bullet) */
+   /* ACS_CKBOARD */   178,    /* checker board (stipple) */
+   /* ACS_DEGREE */    248,    /* degree symbol */
+   /* ACS_PLMINUS */   241,    /* plus/minus */
+   /* ACS_BULLET */    254,    /* bullet */
+   /* ACS_LARROW */    27,     /* arrow pointing left */
+   /* ACS_RARROW */    26,     /* arrow pointing right */
+   /* ACS_DARROW */    25,     /* arrow pointing down */
+   /* ACS_UARROW */    24,     /* arrow pointing up */
+   /* ACS_BOARD */     220,    /* board of squares */
+   /* ACS_LANTERN */   15,     /* lantern symbol */
+   /* ACS_BLOCK */     219,    /* solid square block */
+};
+#endif /* MSDOS */
+
+#if !defined(CURSES) || defined(BSD) || defined(AMIGA)
+/* character push queue */
+static chtype getchq[GQDEPTH];
+static int gqc = 0;
+
+int egetch()
+/* get single character or mouse event */
+{
+    /* queued characters waiting? then go */
+    if (gqc > 0)
+       return(getchq[--gqc]);
+    else
+       return(cgetch());
+}
+
+int ungetch(c)
+/* try to queue a character for input */
+int    c;
+{
+    if (gqc >= GQDEPTH)
+       return(ERR);
+    else
+    {
+       getchq[gqc++] = c;
+       return(OK);
+    }
+}
+#else
+int egetch()
+/* get single character or mouse event */
+{
+    return(cgetch());
+}
+#endif /* !defined(CURSES) || defined(BSD) */
+
+/* screen.c ends here */
diff --git a/screen.h b/screen.h
new file mode 100644 (file)
index 0000000..3377e5f
--- /dev/null
+++ b/screen.h
@@ -0,0 +1,223 @@
+/* screen.h -- interface to screen and keyboard handling */
+
+#define MAXROWS        66      /* most lines per screen we'll try to deal with */
+#define MAXCOLS        132     /* most characters per line we'll essay */
+
+#ifdef ATTMOUSE
+#define MOUSE          /* generic mouse handing */
+#endif /* ATTMOUSE */
+
+#ifdef linux
+#undef BSD
+#endif /* linux */
+
+#ifdef BSD
+#define OLDCURSES
+#endif /* BSD */
+
+#ifdef CURSES
+#ifdef linux
+#include <ncurses.h>
+#else
+#include <curses.h>
+#endif /* linux */
+#else
+typedef        char bool;
+#define FALSE  0
+#define TRUE   1
+#define OK     0
+#define ERR    -1
+
+/*
+ * This holds a character and attributes.  Below, we have to do
+ * some grotty things with macros and casts in case sizeof(long)
+ * != sizeof(int).
+ */
+typedef unsigned long chtype;  /* the idea is to force 32 bits */
+
+extern int LINES, COLS;                /* terminal depth & width globals */
+extern int COLORS, COLOR_PAIRS;        /* max color range globals */
+
+extern int initscr();  /* init screen, set LINES and COLS */
+extern int endwin();   /* de-initialize screen code */
+extern int init_pair();        /* initialize color pair */
+extern int clear();    /* clear screen */
+extern int refresh();  /* refresh real screen from virtual screen */
+extern int ungetch();  /* put char on queue to be grabbed by next getch() */
+extern int inch();     /* get char from screen's current update location */
+extern int getyx();    /* get current update cursor position */
+extern int move();     /* move update cursor to row y colum x */
+extern int _addch();   /* add char/attribute at update location */
+#define addch(c)       _addch((chtype)(c))
+extern int addstr();   /* write string from update location */
+extern int attrset();  /* set attribute (A_NORMAL, A_REVERSE, A_BOLD) */
+extern bool has_colors();      /* do we have color? */
+extern int start_color();      /* enable color support */
+extern int color_pair();       /* set up a color pair */       
+extern int napms();    /* nap for some number of milliseconds */
+extern void beep();    /* ring the terminal bell */
+
+/* the (void)s are to suppress "Code has no effect" warnings */
+#define        mvaddch(y,x,ch)         (void)(move(y,x) == ERR ? ERR : addch(ch))
+#define        mvaddstr(y,x,str)       (void)(move(y,x) == ERR ? ERR : addstr(str))
+#endif /* CURSES */
+
+#ifndef A_COLOR
+/* color defines */
+#define A_COLOR                0xff0000
+#define COLOR_PAIR(n)  ((chtype)(n) << 16)
+#define PAIR_NUMBER(n) (((chtype)(n) & A_COLOR) >> 16)
+#define COLOR_BLACK    0
+#define COLOR_BLUE     1
+#define COLOR_GREEN    2
+#define COLOR_CYAN     3
+#define COLOR_RED      4
+#define COLOR_MAGENTA  5
+#define COLOR_YELLOW   6
+#define COLOR_WHITE    7
+#endif /* A_COLOR */
+
+#ifndef KEY_EXIT
+/* special keystroke defines --- picked to coincide with curses(3) values  */
+#define KEY_EXIT       0551    /* not on PC keyboard */
+#define KEY_HELP       0553    /* not on PC keyboard */
+#define KEY_REFRESH    0565    /* not on PC keyboard */
+#define KEY_NPAGE      0522    /* PgDn */
+#define KEY_PPAGE      0523    /* PgUp */
+#define KEY_UP         0403    /* Up Arrow */
+#define KEY_DOWN       0402    /* Down Arrow */
+#define KEY_REFERENCE  0564    /* not on PC keyboard */
+#define KEY_ENTER      0527    /* Enter */
+#define KEY_RIGHT      0405    /* Right Arrow */
+#define KEY_LEFT       0404    /* Left Arrow */
+#define KEY_FIND       0552    /* not on PC keyboard */
+#define KEY_SFIND      0605    /* not on PC keyboard */
+#define KEY_SELECT     0601    /* not on PC keyboard */
+#define KEY_HOME       0406    /* Home */
+#define KEY_BEG                0542    /* not on PC keyboard */
+#define KEY_END                0550    /* End */
+#define KEY_UNDO       0630    /* not on PC keyboard */
+#define KEY_PRINT      0532    /* not on PC keyboard */
+#define KEY_FBASE      0410    /* function key base */
+#define KEY_F(n)       (KEY_FBASE + (n))
+#endif /* KEY_EXIT */
+
+#ifndef ACS_HLINE
+/* alternate char set --- indices picked to coincide with curses(3) values */
+       /* VT100 symbols */
+extern unsigned char acs_map[96];
+#define ACS_BSSB       (acs_map[0])
+#define ACS_SSBB       (acs_map[1])
+#define ACS_BBSS       (acs_map[2])
+#define ACS_SBBS       (acs_map[3])
+#define ACS_SBSS       (acs_map[4])
+#define ACS_SSSB       (acs_map[5])
+#define ACS_SSBS       (acs_map[6])
+#define ACS_BSSS       (acs_map[7])
+#define ACS_BSBS       (acs_map[8])
+#define ACS_SBSB       (acs_map[9])
+#define ACS_SSSS       (acs_map[10])
+#define ACS_ULCORNER   ACS_BSSB
+#define ACS_LLCORNER   ACS_SSBB
+#define ACS_URCORNER   ACS_BBSS
+#define ACS_LRCORNER   ACS_SBBS
+#define ACS_RTEE       ACS_SBSS
+#define ACS_LTEE       ACS_SSSB
+#define ACS_BTEE       ACS_SSBS
+#define ACS_TTEE       ACS_BSSS
+#define ACS_HLINE      ACS_BSBS
+#define ACS_VLINE      ACS_SBSB
+#define ACS_PLUS       ACS_SSSS
+#define ACS_S1         (acs_map[11])   /* scan line 1 */
+#define ACS_S9         (acs_map[12])   /* scan line 9 */
+#define ACS_DIAMOND    (acs_map[13])   /* diamond */
+#define ACS_CKBOARD    (acs_map[14])   /* checker board (stipple) */
+#define ACS_DEGREE     (acs_map[15])   /* degree symbol */
+#define ACS_PLMINUS    (acs_map[16])   /* plus/minus */
+#define ACS_BULLET     (acs_map[17])   /* bullet */
+       /* Teletype 5410v1 symbols */
+#define ACS_LARROW     (acs_map[18])   /* arrow pointing left */
+#define ACS_RARROW     (acs_map[19])   /* arrow pointing right */
+#define ACS_DARROW     (acs_map[20])   /* arrow pointing down */
+#define ACS_UARROW     (acs_map[21])   /* arrow pointing up */
+#define ACS_BOARD      (acs_map[22])   /* board of squares */
+#define ACS_LANTERN    (acs_map[23])   /* lantern symbol */
+#define ACS_BLOCK      (acs_map[24])   /* solid square block */
+#endif
+
+#ifndef A_CHARTEXT
+/* highlight defines */
+#define A_CHARTEXT     0x00007f
+#define A_ATTRIBUTES   0xffff00
+#define A_NORMAL       0x000000
+#define A_STANDOUT     0x000100
+#define A_UNDERLINE    0x000200
+#define A_REVERSE      0x000400
+#define A_BLINK                0x000800
+#define A_BOLD         0x001000
+#define A_ALTCHARSET   0x002000
+#endif /* A_CHARTEXT */
+
+#ifdef OLDCURSES
+typedef int chtype;
+#define resetterm()    resetty()
+#define saveterm()     savetty()
+#define has_colors()   FALSE
+#define GQDEPTH        10
+extern int egetch(), ungetch(), attrset();
+#endif /* OLDCURSES */
+
+#ifdef linux
+#define resetterm()    resetty()
+#define saveterm()     savetty()
+#define has_colors()   FALSE
+#endif
+
+/*
+ * Mouse event handing.  The KEY_MOUSE define has to be distinct from
+ * any keystroke we might want to process.
+ *
+ * SVr4 defines KEY_MOUSE; SVr3 does not.  If we don't get it from the
+ * curses.h file, define it here.
+ */
+#ifndef KEY_MOUSE
+#define KEY_MOUSE      0631
+
+#define A_BUTTON_CHANGED       0x0f    /* any change in button status? */
+#define BUTTON1_PRESSED                0x01    /* left mouse button down */
+#define BUTTON1_RELEASED       0x02    /* left mouse button release */
+#define BUTTON3_PRESSED                0x04    /* right mouse button down */
+#define BUTTON3_RELEASED       0x08    /* right mouse button up */
+#endif /* KEY_MOUSE */
+
+/* we'll define these */
+extern int egetch();           /* get mouse event or character, no echo */
+extern void readscr();         /* get content of given region */
+extern void mouse_init();      /* initialize mouse */
+extern void mouse_move();      /* move mouse hot spot */
+extern int mouse_status();     /* get mouse location and button state */
+extern void mouse_show();      /* show mouse cursor */
+extern void mouse_hide();      /* hide mouse cursor */
+extern void mouse_color();     /* show mouse cursor in color? */
+
+/* special coordinate values returned by mouse_status() */
+#define CMDLINE                -1      /* command line of application window */
+#define THUMBCOL       -1      /* thumb column of application window */
+
+#ifdef AMIGA
+#define     F_OK    0
+typedef short chtype;
+#define GQDEPTH        10
+#endif /* AMIGA */
+  
+/* handy names for special characters */
+#define CTL(c) ((c) & 0x1f)
+#define TAB    0x09    /* tab */
+#define BS     0x08    /* backspace */
+#define CR     0x0d    /* carriage return */
+#define LF     0x0a    /* line feed */
+#define ESC    0x1b    /* escape */
+#define SP     0x20    /* space */
+#define DEL    0x7f    /* delete */
+
+/* screen.h ends here */
diff --git a/vh.1 b/vh.1
new file mode 100644 (file)
index 0000000..d2eb278
--- /dev/null
+++ b/vh.1
@@ -0,0 +1,176 @@
+.TH VH 1 "10 May 1993" "UNIX"
+.IX "vh" "" "vh \(em -- volks-hypertext browser for the Jargon File"
+.ds ud /usr/lib/vh
+.ds fv "2.9.6
+.SH NAME
+vh \- volks-hypertext browser for the Jargon File
+.SH SYNOPSIS
+\fBvh\fR [-cfgimrs] [-b key] [document]
+.SH DESCRIPTION
+This program is a handy hypertext browser, originally written for the
+Jargon File but convenient for use with text files marked up in the
+simple format dcumented below.  It allows you to page forward and back
+through a document, and supports reference chasing so that you can
+easily follow chains of "See" and "See also" pointers through the
+text.
+
+Normal usage is simply to type the name of your document to the shell.
+Thus, `jargon' browses the Jargon File.  Interactive help explaining
+the keyboard commands is available from the screen.  If the program
+sees a mouse, a fairly complete `point-and-shoot' interface is also
+available.  Color will be used if it is available, but may be
+suppressed with the -m option.  If you are using this program under
+DOS and your CGA develops snow when the video RAM is written directly,
+use -s.
+
+It is useful to know that there are two modes; one browses the text
+file, the other the entry index (if you chase a selection in the entry
+index you are popped to text mode at the corresponding entry).
+.SH MOUSE USAGE
+The following section applies only to systems with attached and active
+mice.  If you are using a Mac or some other system with a deficient
+mouse, the single mouse button is treated as a `left' button.
+
+You will notice a scrollbar on the right edge of the screen.  One of
+the characters of the scrollbar is different from the rest.  This is
+called the `thumb' or `scroll box'.  As you move through the file, this
+gives you an approximate indication of where you are in the file.  If
+the thumb is one-third of the way down the screen, you are about
+one-third of the way through the file.  It works in both the text and
+index files.
+
+If you have a mouse, you can click the left button directly on a
+reference on the screen and jump to it.  You can also scroll or
+page forward or backward by clicking on the scrollbar.  There are
+up and down arrows above and below the scroll bar for scrolling
+line by line.  Clicking on the scrollbar below the thumb will
+page forward, and clicking above it will page back.  You can also
+"drag" the thumb by clicking on it and holding, then moving up or
+down before releasing.  This allows you to jump quickly through
+the file.  Holding the mouse down on the arrows or above or below
+the thumb will cause the scrolling or paging action to repeat
+until you release the button.  This is one of only two places where
+holding the button has any special effect (the other is described below).
+
+Clicking the right button will cause the program to backtrack the
+same way as the Esc or Backspace keys.
+
+You may also click the left button on the function key prompts on
+the bottom line to get the same effect as pressing the function
+keys themselves.  The ^Q (Help) entry is special; if you click it
+with the mouse, it will come up, but disappear when you let go of the
+mouse button.  This is a feature, not a bug; if you want help to hang
+out till you tell it to go away, type control-Q.
+
+In the text file only, if you click on the main screen (i.e. not
+on the scrollbar or the prompt line) outside of a reference in
+curly braces, the program will page forward one screen.
+
+The -i option enables an incremental-lookup feature (but note that
+this feature may not be available in all ports).  In this mode, lookup
+and string search both behave like an Emacs incremental search.  That
+is, after you type each letter of a key, your viewpoint moves forward
+to the next key (if any) containing the substring you've already typed.  If
+there is no such key, the browser beeps and drops out of
+incremental-lookup mode.  String search works similarly, behaving
+almost exactly like an Emacs ^S.  As a reminder that you're in these
+incremental modes, highlights do not light up on the screens you pass
+through; when you want to leave the mode, press enter and the current
+page's highlights will light up.
+
+The -b option runs vh in batch mode to retrieve the entry
+corresponding to a particular given key.  The entry, if any, is
+written to standard output.
+
+The -f (filter) option accepts a list of entry headwords on stdin and
+sends those entries to stdout.
+
+Finally, the -r option picks an entry at random and sends it to
+standard output.  This may be useful for `fortune cookie' applications.
+.SH INDEX GENERATION
+Two data files, the default text and index for the Jargon File, should live
+in \*(ud.  To generate an index from a new version, cd to this library
+directory.  Copy or move the textfile to \*(ud/jargon.txt and run the
+command
+.B jargon -g.
+
+For experimental purposes, it is possible to override the default
+document name with a command-line argument.  Thus, assuming a file
+.B example.tex
+is in proper format, you may generate an index for
+.I example.txt
+with
+.B vh -g example
+and browse it with
+.B vh example
+.P
+When you have the `example' database ready for public use, simply link
+or copy vh to the name `example'.  If no arguments are given, this
+program tries to browse a database with the name it was invoked under.
+
+The -c mode does some format and consistency checks on a text/index
+pair.  Use this if it looks as though your lookups are landing in the
+wrong places; it will let you know if your index is out of date.
+.SH TEXT FILE FORMAT
+`Proper format' means:
+
+1. References are surrounded by {} or {{}}.
+
+If the first character of the text file is not a `=', the code also assumes:
+
+2. Reference targets begin with a `:' at the left margin and are bounded
+on the right by another `:' (an optional second `:' may follow that).
+
+This second rule is useful for filtering out false hits from the index.
+.SH FILES
+A copy of vh called  `foo' would require the following:
+.TP 3.5i
+.I \*(ud/foo.txt
+Text of your document.
+.TP 3.5i
+.I \*(ud/foo.idx
+Index of your document.
+.TP 3.5i
+.I ./foo.cut
+Where entries selected by the ^Y (Print) command are appended to.
+.P
+The Jargon File can be obtained by FTP from various archive sites.
+A copy lives more or less permanently as
+.I pub/jargon298.ascii
+at
+.I prep.ai.mit.edu
+(18.71.0.38).
+Future updates will be made available there.
+.SH ENVIRONMENT VARIABLES
+.TP 3.5i
+.I VHPATH
+Search path for vh documents. Defaults to ".:/usr/lib/vh"
+.SH BUGS
+If a selection wraps around the right margin, only a mouse click on
+the part before the line wrap will chase it; clicks on the portion
+trailing into the next line simply cause a page forward.  This glitch
+was judged more trouble to fix than it's worth.
+
+Use of the ESC key inhibits refresh under some curses(3) versions;
+thus, you might have to do a ^L to force screen update afterwards.
+Crocking around this in the obvious way would introduce screen flicker for
+everybody.  Use BS.
+
+Because UNIX's pseudorandom-number generator isn't very good, the
+fortune-cookie mode under UNIX isn't either.
+
+Output from a -f list may include alphabetical section headers which
+are not, strictly speaking, part of the desired text.
+.SH AUTHORS
+Concept, DOS support, and database code from the MS-DOS `jargon'
+program by Raymond D. Gardner <FidoNet 1:104/89.2>; new features,
+docs, and curses(3)-using UNIX code by Eric S. Raymond
+<eric@snark.thyrsus.com>.
+.SH SEE ALSO
+.I The New Hacker's Dictionary,
+by Eric S. Raymond (ed.), MIT Press 1991, ISBN 0-262-68069-6.  The
+Jargon File (version 2.9.6), nicely typeset and bound with additional
+front matter.
+
+Please email your Jargon File entries and corrections to
+jargon@snark.thyrsus.com.
diff --git a/vh.c b/vh.c
new file mode 100644 (file)
index 0000000..8daea7a
--- /dev/null
+++ b/vh.c
@@ -0,0 +1,1498 @@
+/******************************************************************************
+
+NAME
+   vh.c --- retrieval primitives for vh-format text
+
+SYNOPSIS
+   vh [-icm] [textfile] [indexfile]
+
+DESCRIPTION
+   Contains display-independent primitives for implementing a
+simple hypertext browser.
+
+AUTHORS
+   Adapted by Eric S. Raymond <eric@snark.thyrsus.com> from the 1.1 version
+of Raymond Gardner's MS-DOS browser, October 1991.
+   Please see the READ.ME in this directory for license terms.
+
+PORTING NOTES
+   Some effort has been made to make this code independent of whether the
+underlying OS uses \n or \r\n as a line terminator.  If CRLFSIZE is defined
+to 1, \n only is assumed; if it is 2, \r\n is assumed.
+
+BUGS
+   Multi-file database support is only half implemented.
+
+**************************************************************************/
+/*LINTLIBRARY*/
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <time.h>
+
+typedef int bool;
+#define        FALSE           0
+#define        TRUE            1
+
+#include "vh.h"
+
+#ifndef F_OK
+#define F_OK 0
+#endif /* F_OK */
+
+#define VHPATHSIZ      128
+
+#define TAB    0x09    /* tab */
+#define BS     0x08    /* backspace */
+#define CR     0x0D    /* carriage return */
+#define LF     0x0A    /* line feed */
+#define SP     0x20    /* space */
+#define DEL    0x7f    /* delete */
+
+#define max(a,b)       ((a) > (b) ? (a) : (b))
+
+#ifdef ATT
+#define srand(n)       srand48(n)
+#define rand()         lrand48()
+extern long lrand48();
+extern void srand48();
+/*
+ * Try this if ungetch() fails to resolve.
+ *
+ * #define ungetch ungetc
+ */
+#endif /* isxdigit */
+
+extern char    *malloc();
+extern long    atol();
+
+extern int LINES, COLS;                /* initialize screen size into these */
+
+#ifdef UNIX
+#define CRLFSIZE       1       /* \n only */
+#endif /* UNIX */
+
+#ifndef CRLFSIZE
+#define CRLFSIZE       2       /* \r\n as in MS-DOS, etc. */
+#endif /* CRLFSIZE */
+
+#define WARNSIZE       75              /* warn of lines longer than this */
+#define ENTRYMARK      ':'
+#define        SEPARATOR       (' ' - 2)
+#define        STRSZ           80              /* for various strings */
+
+#ifndef VHPATH
+#ifdef MSDOS
+#define VHPATH ".;/usr/lib/vh"
+#else
+#ifndef AMIGA
+#define VHPATH ".:/usr/lib/vh"
+#else
+#define VHPATH "s:;jargon:"
+#endif
+#endif /* MSDOS */
+#endif /* VHPATH */
+#define TXT            ".txt"
+#define IDX            ".idx"
+#define TXTLEN         4
+
+/*
+ * This was carefully tuned using prof(1) under UNIX SVr3.  Unless you have
+ * profiling tools at least as good, best not mess with it!
+ */
+#define DBBUF          4096            /* optimal buffer size */
+
+#ifdef MSDOS
+#ifdef DBBUF
+#undef DBBUF
+#endif
+#define DBBUF          512             /* better size for MSDOS/Borland C -- rdg */
+#endif
+
+#define IS_TAG(s, x)   (s[x] == LTAG || (x == 0 && s[0] == ENTRYMARK))
+
+/*******************************************************************
+ *
+ * All message texts declared here for internationalization purposes
+ *
+ ******************************************************************/
+
+#define FORMTYPE       "vh: format type is %d, "
+#define OUTASYNC       "vh: index is out of sync with text.\n"
+#define BADMAGIC       "vh: index file %s doesn't look like an index\n"
+#define CANTFIND       "vh: can't find database files for %s\n"
+#define CANTOPEN       "vh: can't open %s\n"
+#define NOMEMORY       "vh: out of memory.\n"
+#define TOOMANY                "vh: too many index items.\n"
+#define DANGLING       "\"%s%s\", line %ld: {%s} dangling reference\n"
+#define OUTERROR       "vh: error writing output -- disk full?\n"
+#define LENGTHZERO     "vh: line length 0 -- ignored.\n"
+#define TOOLONG                "vh: line too long\n"
+#define CONTROLZ       "vh: last line is Control-Z(s) -- ignored\n"
+#define NOCRLF         "vh: last line not terminated -- CR/LF added\n"
+#define NOINPUT                "vh: can't open input file %s\n"
+#define NOOUTPUT       "vh: can't open output file %s\n"
+#define FILLING                "filling index\n"
+#define LONGLINE       "\"%s%s\", line %ld: line too long (%d)\n"
+#define REREADING      "vh: rereading, "
+#define SORTING                "sorting, "
+#define WRITING                "writing, "
+#define BADREFS                "checking for bad references...\n"
+#define DONE           "done.\n"
+
+/*******************************************************************
+ *
+ * Global data
+ *
+ ******************************************************************/
+
+static FILEINFO        textblk;
+
+FILEINFO       vhi, *vht = &textblk;
+
+static char    *xp[ITEMSMAX];  /* index pointer table */
+static int     nitems;         /* xp item count */
+
+#if defined(__TURBOC__) && defined(FFGETS)
+/*******************************************************************
+ *
+ * ffgets() -- faster replacement for fgets() (very Borland-specific!)
+ *             depends on Borland's FILE structure & usage
+ *             similar method may be useful on other systems
+ *   
+ *   Note that implementing this doesn't require having any library
+ *   source.  All you need to know is in the getc() macro in stdio.h.
+ *
+ ******************************************************************/
+char *ffgets(s, n, f)
+char *s;
+int n;
+FILE *f;
+{
+    char *s0;
+    int c;
+
+    s0 = s;
+    --n;
+    while ( n )
+    {
+       register int k;
+       register char *p;
+
+       k = f->level;
+       if ( k > n )
+           k = n;
+       if ( k == 0 )
+       {
+           c = fgetc(f);
+           if ( c == EOF )
+           {
+               *s = '\0';
+               return NULL;
+           }
+           *s++ = c;
+           --n;
+           if ( c == '\n' )
+               break;
+       }
+       else
+       {
+           p = f->curp;
+           while ( *p++ != '\n' && --k )
+               ;
+           k = p - f->curp;
+           memmove(s, f->curp, k);
+           f->level -= k;
+           f->curp += k;
+           n -= k;
+           s += k;
+           if ( s[-1] == '\n' )
+               break;
+       }
+    }
+    *s = '\0';
+    return s0;
+}
+#if 00
+/* another approach ... */
+char *ffgets(s, n, f)
+char *s;
+int n;
+FILE *f;
+{
+    char *s0, *p;
+    int k, c;
+
+    s0 = s;
+    n--;
+    while ( n )
+    {
+       k = f->level;
+       if ( k > n )
+           k = n;
+       if ( k < 1 )
+       {
+           c = fgetc(f);
+           if ( c == EOF )
+           {
+               *s = '\0';
+               return NULL;
+           }
+           *s++ = c;
+           --n;
+           if ( c == '\n' )
+               break;
+       }
+       else
+       {
+           memmove(s, f->curp, k);
+           s[k] = '\0';
+           p = strchr(s, '\n');
+           if ( p )
+               n = k = p - s + 1;
+           f->level -= k;
+           f->curp += k;
+           s += k;
+           n -= k;
+       }
+    }
+    *s = '\0';
+    return s0;
+}
+#endif
+#endif /* defined(__TURBOC__) && defined(FFGETS) */
+
+#ifndef MSDOS
+/*******************************************************************
+ *
+ * Emulations of Borland C library functions
+ *
+ ******************************************************************/
+
+static bool strnicmp(s1, s2, n)
+/* case-insensitive length-limited string compare */
+register char *s1, *s2;
+register n;
+{
+#ifdef BSD
+    char       ls1;
+    char       ls2;
+
+    while (--n >= 0) {
+       ls1 = *s1;
+       ls2 = *s2++;
+       if (isupper(ls1))
+               ls1 = tolower(*s1);
+       if (isupper(ls2))
+               ls2 = tolower(ls2);
+       if (ls1 != ls2)
+               break;
+       if (*s1++ == '\0')
+               return(0);
+    }
+    return((n < 0) ? 0 : (ls1-ls2));
+#else
+    while (--n >= 0 && tolower(*s1) == tolower(*s2++))
+       if (*s1++ == '\0')
+           return(0);
+    return((n < 0) ? 0 : (tolower(*s1) - tolower(*--s2)));
+#endif
+}
+
+static void strlwr(s)
+/* force string to lower case */
+char   *s;
+{
+    register char *cp;
+
+    for (cp = s; *cp; cp++)
+#ifdef BSD
+       if (isupper(*cp))
+#endif
+               *cp = tolower(*cp);
+}
+
+#ifndef linux
+static char *strstr(t, s)
+/* find s in t */
+char   *s, *t;
+{
+    char       *cp;
+
+    for (cp = t; *cp; cp++)
+       if (strncmp(cp, s, strlen(s)) == 0)
+           return(cp);
+    return((char *)NULL);
+}
+#endif /* !linux */
+#endif /* MSDOS */
+
+/*******************************************************************
+ *
+ * All file-type dependent stuff
+ *
+ ******************************************************************/
+
+static int format;     /* markup type, defaults to ORIGINAL296 */
+#define ORIGINAL296    0       /* Jargon File 2.9.6 version */
+#define COLON297       1       /* Jargon File 2.9.7+ with colons */
+
+#define HDOFF(s)       (s[0] == ENTRYMARK)
+
+static void getformat(fp)
+/* figure out what format we're looking at */
+FILE   *fp;
+{
+    (void) fseek(fp, 0L, SEEK_SET);
+    if (fgetc(fp) != '=')
+       format = COLON297;
+}
+
+char *headword(ln)
+/* is this line an entry? */
+char   *ln;
+{
+    char       *p;
+
+    if (ln[0] == ' ' || (format > ORIGINAL296 && (ln[0] != ENTRYMARK)))
+       return((char *)NULL);
+
+    for (p = ln + HDOFF(ln); p = strchr(p, ENTRYMARK); ++p)
+       if (isspace(p[1]) || (p[1] == ENTRYMARK && isspace(p[2])))
+           break;
+
+    return(p);
+}
+
+/*******************************************************************
+ *
+ * Sequential entry access
+ *
+ * This supports the following entry points:
+ *     getnextln() --- get line beginning at given position
+ *     getprevln() --- get *previous* line from given position
+ *
+ ******************************************************************/
+
+static void detab(s)
+/* expand tabs in s, in place; assumes tab stops every 8 chars */
+char *s;
+{
+    char *p;
+    int k, n, i;
+
+    while ((p = strchr(s, TAB)) != NULL)
+    {
+       /* while any tabs */
+       k = p - s;              /* offset to tab */
+       n = (k + 8) / 8 * 8;    /* next tab stop */
+       for (i = strlen(s + k + 1); i >= 0; i--)
+           s[n + i] = s[k + 1 + i];
+       memset(&s[k], ' ', n - k); /* blank fill */
+    }
+}
+
+/* daddr_t getnextln(FILE *fp, daddr_t pos, char *ln) -- get next line of file
+**  takes position where a line starts, gets line into buffer ln
+**  removes trailing CR/LF and expands tabs
+**  returns position of next byte after line (i.e. start of next line)
+**  returns NOWHERE at EOF
+*/
+daddr_t getnextln(fp, pos, ln)
+FILE *fp;
+daddr_t pos;
+char *ln;
+{
+    int k;
+    char *p;
+    if (ftell(fp) != pos)      /* slight optimization for Borland & Zortech*/
+       (void) fseek(fp, pos, SEEK_SET);
+    if (fgets(ln, LNSZ, fp))
+    {
+       k = strlen(ln);
+       ln[k - 1] = '\0';
+#if CRLFSIZE > 1
+       if (ln[k - 2] == CR)
+           ln[k - 2] = '\0';   /* fixed bug here 11/28/91 rdg */
+#endif /* CRLFSIZE > 1 */
+       pos += k;
+    }
+    else
+       pos = NOWHERE;
+    detab(ln);
+    if (p = strchr(ln, SEPARATOR))
+       *p = '\0';
+    return(pos);
+}
+
+/* daddr_t getprevln(FILE *fp, daddr_t pos, char *ln) -- get prev line in file
+**  takes position where a line starts, gets _previous_ line into buffer ln
+**  removes trailing CR/LF and expands tabs
+**  returns position of line returned
+**  returns NOWHERE at BOF
+*/
+daddr_t getprevln(fp, pos, ln)
+FILE *fp;
+daddr_t pos;
+char *ln;
+{
+    int n;
+    char *p;
+
+    assert(pos > 0);
+    n = LNSZ;
+    if (pos < LNSZ)
+       n = pos;
+    (void) fseek(fp, pos - n, SEEK_SET);
+    (void) fread(ln, 1, n, fp);
+    p = &ln[n-1];
+    while (p != ln)
+    {
+       int     i;
+
+       --p;
+       if (*p == LF)
+       {
+           ++p;
+           n = &ln[n] - p;
+           for (i = 0; i < n; i++)
+               ln[i] = p[i];
+/*         memmove(ln, p, n);  */
+           break;
+       }
+    }
+    assert(p > ln || (p == ln && pos == n));
+    ln[n - 1] = '\0';
+#if CRLFSIZE > 1
+    if (ln[n - 2] == CR)
+       ln[n - 2] = '\0';
+#endif /* CRLFSIZE > 1 */
+    pos -= n;
+    detab(ln);
+    if (p = strchr(ln, SEPARATOR))
+       *p = '\0';
+    return(pos);
+}
+
+/*******************************************************************
+ *
+ * Fortune-cookie mode
+ *
+ * This supports the following entry points:
+ *     jrandom()       -- return the offset of a random entry
+ *
+ ******************************************************************/
+
+daddr_t jrandom()
+/* grab a fortune cookie */
+{
+    int        r = rand() % nitems;
+
+    return(atol(xp[r] + strlen(xp[r]) + 1));
+}
+
+/*******************************************************************
+ *
+ * Entry-access by name
+ *
+ * This supports the following entry points:
+ *      xlocate() --- go to entry by name
+ *     ilocate() --- go to entry by name (incremental)
+ *     ffind() --- find string in file
+ *     ifind() --- find string in file (incremental)
+ *
+ ******************************************************************/
+
+static daddr_t ixlocate(s, isincrsearch)
+/* binary-search index for x; return offset if found, else -offset of next */
+char *s;
+bool isincrsearch;
+{
+    int lo, mid, hi, k, hit;
+    daddr_t pos;
+
+    strlwr(s);
+#define NOTFOUND (-1)
+    lo = 0;
+    hi = nitems - 1;
+    hit = NOTFOUND;            /* your basic binary search */
+    while (hit == NOTFOUND && lo <= hi)
+    {
+       mid = (lo + hi) / 2;
+       if ((k = strcmp(s, xp[mid])) < 0)
+           hi = mid - 1;
+       else if (k > 0)
+           lo = mid + 1;
+       else
+           hit = mid;
+    }
+
+    /*
+     * if not found, and item wanted exceeds item found,
+     * and there's room to move up, go to next higher item
+     */
+    if (hit==NOTFOUND && strcmp(s, xp[mid]) > 0 && mid < nitems-1)
+    {
+       ++mid;
+       assert(strcmp(s, xp[mid]) < 0);
+    }
+    /* the file position is stored right after the index string */
+    pos = atol(xp[mid] + strlen(xp[mid]) + 1);
+
+    if (isincrsearch)
+    {
+       if (strncmp(s, xp[mid], strlen(s)))
+           pos = -pos;
+    }
+    else
+    {
+       /* if no hit, return negative pos */
+       if (hit == NOTFOUND)
+           pos = -pos;
+    }
+    return(pos);
+}
+
+daddr_t xlocate(s)
+char *s;
+{
+    return(ixlocate(s, FALSE));
+}
+
+daddr_t ilocate(c)
+/* incremental-lookup through entry key list */
+char c;
+{
+    static char key[LNSZ], *ep = key;
+    static int oldhit;
+
+    if (c == BS)
+    {
+       if (ep > key)
+           *--ep = '\0';
+    }
+    else if (isprint(c))       /* incremental-search for given character */
+    {
+       *ep++ = tolower(c);
+       *ep = '\0';
+       return(ixlocate(key, TRUE));
+    }
+    else if (c == DEL) /* reset from previous incremental lookup */
+    {
+       key[0] = '\0';
+       ep = key;
+       oldhit = 0;
+    }
+    return(NOWHERE);
+}
+
+daddr_t ffind(fp, pos, ss)
+/* case-blind search forward for ss; return offset if found, else NOWHERE */
+FILE *fp;
+daddr_t pos;
+char *ss;
+{
+    char s[LNSZ], ln[LNSZ];
+
+    (void) strcpy(s, ss);
+    strlwr(s);
+    (void) fseek(fp, pos, SEEK_SET);
+    while (fgets(ln, LNSZ, fp))
+    {
+       strlwr(ln);
+       if (strstr(ln, s))
+           return(pos);
+       pos = ftell(fp);        /* save pos before reading next line */
+    }
+    return(NOWHERE);
+}
+
+daddr_t ifind(fp, pos, c)
+/* incremental-search forward */
+FILE *fp;
+daddr_t pos;
+char c;
+{
+    static char key[LNSZ], *ep = key;
+    static int oldhit;
+
+    if (c == BS)
+    {
+       if (ep > key)
+           *--ep = '\0';
+       return(pos);
+    }
+    else if (isprint(c))       /* incremental-search for given character */
+    {
+       *ep++ = tolower(c);
+       *ep = '\0';
+       return(ffind(fp, pos, key));
+    }
+    else if (c == DEL) /* reset from previous incremental search */
+    {
+       key[0] = '\0';
+       ep = key;
+       oldhit = 0;
+       return(pos);
+    }
+    return(NOWHERE);   /* should never reach here */
+}
+
+/*******************************************************************
+ *
+ * Screen-fetch and line-retrieval code
+ *
+ * Entry points:
+ *     iflink() --- test for presence of a reference
+ *     findnextsel() --- find next link on current screen
+ *     findprevsel() --- find previous link on current screen
+ *
+ * These functions expect to be able to call:
+ *     readscr() --- get text from screen 
+ *
+ ******************************************************************/
+
+/* left tag and right tag of textual references */
+#define                LTAG            '{'
+#define                RTAG            '}'
+
+/* daddr_t iflink() -- test for link at screen position, find right tag pos
+*/
+daddr_t iflink(x, y, xrtag, yrtag, isindex)
+int x,y;
+int *xrtag, *yrtag;
+bool isindex;
+{
+    char *cp, s[MAXWIDTH];
+    char term[STRSZ];
+    int i;
+    daddr_t pos;
+
+    readscr(0, y, COLS-1, y, s); /* get line on screen */
+       
+    if (isindex)               /* if index, x is col 0 */
+    {
+       (void) strcpy(term, s);
+       for (cp = term + strlen(term) - 1; *cp == ' '; cp--)
+           *cp = '\0';
+       x = strlen(term) - 1;
+    }
+    else if ((cp = headword(s)) && x < cp - s) /* a headword? */
+    {
+       i = *cp;
+       *cp = '\0';
+       (void) strcpy(term, s + HDOFF(s));
+       *cp = i;
+       x = (cp - s) - 1;
+    }
+    else                       /* not index or headword, find left tag */
+    {
+       int x0 = x;
+
+       while (x >= 0 && s[x] != LTAG)
+           --x;
+       if (x < 0)              /* if no left tag, then no link */
+           return(0L);
+       assert(IS_TAG(s, x));
+
+       /* find next non-LTAG char */
+       while (x < COLS && s[x] == LTAG)
+           ++x;
+       if (x >= COLS)          /* if none, no tag */
+           return(0L);
+
+       /* scan the link text up to RTAG, put into term[] */
+       i = 0;
+       while (x < COLS-1 && s[x] != RTAG)
+           term[i++] = s[x++];
+       --x;
+       if (x < x0)             /* if we are left of start point, return 0 */
+           return(0L);         /* in case user clicks to right of link */
+       else if (x >= COLS-2)   /* in case link wraps across line */
+       {
+           ++y;
+           if (y > LASTLINE)
+               return(0L);
+           readscr(0, y, COLS-2, y, s);
+           while (i > 0 && term[i - 1] == ' ')
+               --i;
+           term[i++] = ' ';
+           x = 0;
+           while (x < COLS-1 && s[x] == ' ')
+               ++x;
+           while (x < COLS-1 && s[x] != RTAG)
+               term[i++] = s[x++];
+           --x;
+           if (x >= COLS)
+               return(0L);
+       }
+
+       term[i] = '\0'; /* terminate the term */
+    }
+
+    /* look up in index table */
+    pos = xlocate(term);
+    if (pos > 0)               /* if found, set the right tag x/y coords */
+    {
+       *yrtag = y;
+       *xrtag = x;
+    }
+    return(pos);       /* return position of link target, or 0L if none */
+}
+
+/* region findnextsel() -- find next selection (i.e. link reference)
+**     given x/y coords of a link selection on the screen, find the next
+**  one onscreen searching left to right, then down, wrapping last line
+**  to top line, until back to original position if only one link onscreen;
+**  coordinates are placed in the result.
+*/
+region findnextsel(x, y, isindex)
+int x, y;
+bool isindex;
+{
+    char s[MAXWIDTH];
+    int x0, y0, j;
+    daddr_t pos;
+    region res;
+
+    if (isindex)               /* if index, just go to next row */
+    {
+       ++y;
+       if (y > LASTLINE)       /* wrap line to top if at end */
+           y = 0;
+       res.yl = res.yr = y;
+       res.xl = 0;
+       res.xr = COLS - 2;
+       return(res);
+    }
+
+    if (y < 0)         /* if no selection currently, look for one */
+       y = 0;
+
+    x0 = x;
+    y0 = y;
+    readscr(0, y, COLS-1, y, s);       /* get screen line */
+    while (x < COLS && IS_TAG(s, x))
+       ++x;
+    for (;;)
+    {
+       for (; x < COLS; ++x)
+       {
+           if (IS_TAG(s, x))
+           {
+               while (x < COLS && s[x] == LTAG || (x == 0 && s[0] == ENTRYMARK))
+                   ++x;
+               /* look up link */
+               if (iflink(x, y, &res.xr, &res.yr, isindex) > 0)
+               {
+                   res.xl = x; /* if found, set x/y and return */
+                   res.yl = y;
+                   return(res);
+               }
+           }
+       }
+       x = 0;                  /* past end of row; reset x pos */
+       ++y;                    /* step to next line, wrap around if at end */
+       if (y > LASTLINE)
+           y = 0;
+       if (y == y0 && x == x0) /* return if back at start point */
+       {
+           res.xl = res.yl = NOPLACE;
+           return(res);
+       }
+       readscr(0, y, COLS-1, y, s);    /* get screen line */
+    }
+}
+
+/* void findprevsel() -- find prev selection (i.e. link reference)
+**     given x/y coords of a link selection on the screen, find the previous
+**  one onscreen searching right to left, then up, wrapping top line
+**  to last line, until back to original position if only one link onscreen;
+**  coordinates are placed in the result.
+*/
+region findprevsel(x, y, isindex)
+int x, y;
+bool isindex;
+{
+    char s[MAXWIDTH];
+    int x0, y0, j;
+    daddr_t pos;
+    region res;
+
+    if (isindex)               /* if index, get prev row */
+    {
+       --y;
+       if (y < 0)              /* wrap line to last if at top */
+           y = LASTLINE;
+       res.yl = res.yr = y;
+       res.xl = 0;
+       res.xr = COLS - 2;
+       return(res);
+    }
+
+    x0 = x;
+    y0 = y;
+    readscr(0, y, COLS-1, y, s); /* get screen line */
+    while (x >= 0 && IS_TAG(s, x))
+       --x;
+    for (;;)
+    {
+       for (; x >= 0; --x)
+       {
+           if (IS_TAG(s, x))
+           {
+               while (x < COLS && IS_TAG(s, x))
+                   ++x;
+               --x;            /* back up to LTAG; look up link */
+               if (iflink(x, y, &res.xr, &res.yr, isindex) > 0)
+               {
+                   res.xl = ++x;       /* if found, set x/y and return */
+                   res.yl = y;
+                   return(res);
+               }
+           }
+       }
+       x = COLS - 1;   /* past beginning of row; reset x pos */
+       --y;                    /* step to prev line, wrap around if at top */
+       if (y < 0)
+           y = LASTLINE;
+       if (y == y0 && x == x0) /* return if back at start point */
+       {
+           res.xl = res.yl = NOPLACE;
+           return(res);
+       }
+       readscr(0, y, COLS-1, y, s); /* get prev line, continue */
+    }
+}
+
+/*******************************************************************
+ *
+ * Position stack management
+ *
+ * This supports the following entry points:
+ *     enqueue() --- push a placemark
+ *     dequeue() --- pop a placemark
+ *
+ ******************************************************************/
+
+void enqueue(f)
+/* add file position & link info to backtrack stack */
+FILEINFO       *f;
+{
+    int k;
+
+    k = f->btscnt;
+
+    /* if full, move it up to make room */
+    if (k == BTSMAX)
+    {
+       int n;
+       for ( n = (BTSMAX-1) * sizeof(f->bts[0]); n--; )
+       {
+           memcpy(&f->bts[0], &f->bts[1], sizeof(f->bts[0]));
+           memcpy(&f->selbts[0], &f->selbts[1], sizeof(f->selbts[0]));
+       }
+       --k;
+       f->btscnt = k;          /* 9/27/91 rdg  fixed bts overflow bug */
+    }
+    f->bts[k] = f->toppos;
+    f->selbts[k].xl = f->sel.xl;
+    f->selbts[k].yl = f->sel.yl;
+    f->selbts[k].xr = f->sel.xr;
+    f->selbts[k].yr = f->sel.yr;
+#ifdef DEBUG
+    (void) fprintf(stderr, "enqueue[%d]: x = %d, y = %d, pos = %ld.\n",
+                  k, f->sel.xl, f->sel.yl, f->toppos);
+#endif /* DEBUG */
+
+    if (k == 0 || f->bts[k] != f->bts[k-1] || /* only enqueue if changed */
+           memcmp(&f->selbts[k], &f->selbts[k-1], sizeof(f->selbts[k])) != 0)
+       ++f->btscnt;
+#ifdef DEBUG
+    else
+       (void) fprintf(stderr, "enqueue[%d]: is duplicate, popped.\n", k);
+#endif /* DEBUG */
+}
+
+void dequeue(f)
+/* pull file position & link info from stack */
+FILEINFO       *f;
+{
+    if (f->btscnt)     /* don't attempt pop if empty */
+    {
+       --f->btscnt;
+       f->toppos = f->bts[f->btscnt];
+       f->sel.xl = f->selbts[f->btscnt].xl;
+       f->sel.yl = f->selbts[f->btscnt].yl;
+       f->sel.xr = f->selbts[f->btscnt].xr;
+       f->sel.yr = f->selbts[f->btscnt].yr;
+
+#ifdef DEBUG
+       (void) fprintf(stderr, "dequeue[%d]: x = %d, y = %d, pos = %ld.\n",
+                      f->btscnt, f->sel.xl, f->sel.yl, f->toppos);
+#endif /* DEBUG */
+    }
+#ifdef DEBUG
+    else
+       (void) fprintf(stderr, "dequeue: failed, no stack space.\n");
+#endif /* DEBUG */
+}
+
+/*******************************************************************
+ *
+ * Browse support
+ *
+ * Entry points:
+ *     initbrowse() --- set up in-core structures for given file pair
+ *     setlastpage() --- set lastpagetoppos members
+ *
+ ******************************************************************/
+
+/* validate_synchronization(FILE *fp, char *ln)
+** check file against index table, to try to be sure they match up
+** will sample 10 index entries, evenly spaced, including first and last
+** for each entry, looks at text file to see if text matches index
+*/
+static void validate_synchronization(fp, ln)
+FILE *fp;
+char *ln;
+{
+#define                nsamples                10
+    int i, k;
+    daddr_t pos;
+       
+    for (i = 0; i < nsamples; ++i)
+    {
+       k = (i * (nitems - 1)) / (nsamples - 1); /* sample index */
+       pos = xlocate(xp[k]); /* look up pos */
+       assert(pos > 0);
+       if (fseek(fp, pos, SEEK_SET) < 0) /* seek in file */
+           break;
+       getnextln(fp, pos, ln); /* get text line */
+       if (strnicmp(ln + HDOFF(ln), xp[k], strlen(xp[k])) != 0) /* match it */
+           break;
+    }
+    /* exit if any mismatch */
+    if (i < nsamples)
+    {
+       (void) fprintf(stderr, OUTASYNC);
+       exit(1);
+    }
+}
+
+static bool idxsearch(name, path)
+/* look for database along defined search path */
+char   *name, *path;
+{
+    extern char        *getenv();
+    char       *srch, *cp;
+
+    if ((srch = getenv("VHPATH")) == (char *)NULL)
+       srch = VHPATH;
+
+#ifdef MSDOS
+#define LISTSEP        ";"
+#define PATHSEP        "\\"
+#else
+#ifndef AMIGA
+#define LISTSEP        ":"
+#else
+#define LISTSEP        ";"
+#endif /* AMIGA */
+#define PATHSEP        "/"
+#endif /* MSDOS */
+
+    cp = strtok(srch, LISTSEP);
+    do {
+       (void) strcpy(path, cp);
+#ifdef AMIGA
+if (path[strlen(path) - 1] != ':') { /* e.g. "DH0:" */
+#endif
+       if (path[strlen(path) - 1] != PATHSEP[0])
+           (void) strcat(path, PATHSEP);
+#ifdef AMIGA
+}
+#endif
+       (void) strcat(path, name);
+       (void) strcat(path, IDX);
+       if (access(path, F_OK) == 0)
+           return(TRUE);
+    } while
+       (cp = strtok((char *)NULL, LISTSEP));
+    return(FALSE);
+}
+
+bool initbrowse(name)
+char   *name;
+{
+    char ln[LNSZ + 1], path[PATHLEN], *cp;
+#ifdef VHHDR
+    vhhdr hdr;
+#endif /* VHHDR */
+    int k, fno;
+
+    /* look for the database files */
+    if (!idxsearch(name, path))
+    {
+       (void) fprintf(stderr, CANTFIND, name);
+       return(FALSE);
+    }
+
+    if ((vhi.fp = fopen(path, "rb"))==(FILE*)NULL)     /* open index file */
+    {
+       (void) fprintf(stderr, CANTOPEN, path);
+       return(FALSE);
+    }
+#ifndef BSD
+    (void) setvbuf(vhi.fp, (char *)NULL, _IOFBF, DBBUF);
+#endif /* BSD */
+
+    vhi.btscnt = 0;
+    vhi.sel.xl = NOPLACE;
+
+#ifdef VHHDR
+    (void) fread(&hdr, sizeof(vhhdr), 1, vhi.fp);
+    if (hdr.magic != VHMAGIC)
+    {
+       (void) fprintf(stderr, BADMAGIC, path);
+       return(FALSE);
+    }
+
+    /* read in text file names */
+    for (fno = 0 ; fno < hdr.nfiles; fno++)
+       (void) fread(ln, 1, VHPATHSIZ, vhi.fp);
+#endif /* VHHDR */
+
+    /* fill index table */
+    for (nitems = 0; nitems < ITEMSMAX; ++nitems)
+    {
+       if (fgets(ln, LNSZ, vhi.fp) == NULL)
+           break;
+       k = strlen(ln) - 1;
+       ln[k] = '\0';
+#if CRLFSIZE > 1
+       if (ln[k-1] == '\r')
+           ln[--k] = '\0';
+#endif /* CRLFSIZE > 1 */
+       if ((xp[nitems] = malloc(k + 1)) == NULL)
+       {
+           (void) fprintf(stderr, NOMEMORY);
+           return(TRUE);
+       }
+       strcpy(xp[nitems], ln);
+       assert(strchr(ln, SEPARATOR)); /* better have separator */
+       *strchr(xp[nitems], SEPARATOR) = '\0'; /* replace it w/ null byte */
+       strlwr(xp[nitems]);     /* force items in table to lowercase */
+    }
+
+    /* did we overflow? */
+    if (nitems == ITEMSMAX)
+    {
+       (void) fprintf(stderr, TOOMANY);
+       return(FALSE);
+    }
+
+    vhi.toppos = 0;
+
+    /* this will go inside a loop */
+
+    vht->btscnt = 0;
+    vht->sel.xl = NOPLACE;
+
+    (void) strcpy(ln, path);
+    (void) strcpy(ln + strlen(ln) - TXTLEN, TXT);
+    if ((vht->fp = fopen(ln, "rb"))==(FILE*)NULL)      /* open text file */
+    {
+       (void) fprintf(stderr, CANTOPEN, path);
+       return(FALSE);
+    }
+#ifndef BSD
+    (void) setvbuf(vht->fp, (char *)NULL, _IOFBF, DBBUF);
+#endif /* BSD */
+
+    getformat(vht->fp);        /* what file format are we looking at? */
+    vht->toppos = 0;
+
+    /* set random-number seed, in case we're after a fortune cookie */
+    srand(time((time_t *)0));
+
+    return(TRUE);
+}
+
+void setlastpage()
+/* this needs to be called *after* initscr() */
+{      
+    char ln[LNSZ + 1];
+    int        i;
+
+    /* set up last page top pos for index */
+    (void) fseek(vhi.fp, 0L, SEEK_END);        /* seek end of index file */
+    vhi.dsptoppos = vhi.lastpagetoppos = vhi.dspnextpos = vhi.endpos =ftell(vhi.fp);
+    for (i = 0; i <= LASTLINE; ++i)
+       if (vhi.lastpagetoppos)
+           vhi.lastpagetoppos = getprevln(vhi.fp, vhi.lastpagetoppos, ln);
+    if (vhi.lastpagetoppos == 0L)      /* avoid divide-by-zero error */
+       vhi.lastpagetoppos = 1L;
+    (void) fseek(vhi.fp, 0L, SEEK_SET);        /* seek start of index file */
+
+    (void) fseek(vht->fp, 0L, SEEK_END); /* seek eof */
+    vht->dsptoppos = vht->lastpagetoppos = vht->dspnextpos = vht->endpos =ftell(vht->fp);
+    for (i = 0; i <= LASTLINE; ++i)
+       if (vht->lastpagetoppos)
+           vht->lastpagetoppos = getprevln(vht->fp, vht->lastpagetoppos, ln);
+    if (vht->lastpagetoppos == 0L)     /* avoid divide-by-zero error */
+       vht->lastpagetoppos = 1L;
+    (void) fseek(vht->fp, 0L, SEEK_SET);       /* back to top of file */
+}
+
+/*******************************************************************
+ *
+ * Consistency checking
+ *
+ ******************************************************************/
+
+void chkindex(name)
+/* look for over-long lines, dangling references, self-references */
+char   *name;
+{
+    long lnum = 1;
+    char refbuf[LNSZ + 1], *refpt = refbuf;
+    int c, len = 0, depth = 0;
+
+    /* time for consistency check */
+    initbrowse(name);
+
+    (void) fseek(vht->fp, 0L, SEEK_SET);
+    while ((c = fgetc(vht->fp)) != EOF)
+       if (c == '\n')
+       {
+           if (len > WARNSIZE + CRLFSIZE)
+               (void) printf(LONGLINE, name, TXT, lnum, len);
+           len = 0;
+
+           ++lnum;
+           if (depth != 0 && refpt[-1] != ' ')
+               *refpt++ = ' ';
+       }
+       else if (c == LTAG)
+       {
+           ++len;
+           ungetc(c = fgetc(vht->fp), vht->fp);
+           if (isprint(c))
+               ++depth;
+       }
+       else if (depth > 0 && c == RTAG)
+       {
+           ++len;
+           --depth;
+           if (depth == 0 && refpt != refbuf)
+           {
+               *refpt = '\0';
+               if (xlocate(refbuf) < 0)
+                   (void) printf(DANGLING, name, TXT, lnum, refbuf);
+               refpt = refbuf;
+           }
+       }
+       else if (depth != 0)
+       {
+           ++len;
+           if (!isspace(c) || refpt[-1] != ' ')
+               *refpt++ = c;
+       }
+
+    validate_synchronization(vht->fp, refbuf); /* validate */
+}
+
+/*******************************************************************
+ *
+ * File indexing code
+ *
+ * Entry points:
+ *     mkindex(name) -- generate index file from text
+ *
+ ******************************************************************/
+
+static char ln[LNSZ + 1];
+
+typedef struct list_struct
+{
+    struct list_struct *next;
+    unsigned int       len;
+    char               ln[1];
+}
+list;
+
+static list *listp = NULL;
+
+static char *mmalloc(n)
+/* malloc(3) with error message and abort */
+unsigned       n;
+{
+    char *p;
+       
+    if ((p = malloc(n)) == NULL)
+    {
+       (void) fprintf(stderr, NOMEMORY);
+       exit(1);
+    }
+    return(p);
+}
+
+#define UNSCH(cp)              ((int)(*((unsigned char *)(cp))))
+
+static int strlcmp(a, b)
+/* compare, smashing case */
+char *a, *b;
+{
+#ifdef BSD
+    return(strnicmp(a,b,max(strlen(a),strlen(b))));
+#else
+    char *lim;
+    int v;
+
+    for (; *a && *b; a++, b++)
+    {
+       v = tolower(UNSCH(a)) - tolower(UNSCH(b));
+       if (v)
+           return v;
+    }
+    return(tolower(UNSCH(a)) - tolower(UNSCH(b)));
+#endif
+}
+
+static int keycmp(p, q)
+/* compare keys for equality */
+list *p, *q;
+{
+    return(strlcmp(p->ln, q->ln));
+}
+
+static list *lmerge (p, q)
+/* merge 2 lists under dummy head item */
+list *p, *q;
+{
+    list *r, head;
+
+    for (r = &head; p && q;)
+    {
+       if (keycmp(p, q) < 0)
+       {
+           r = r->next = p;
+           p = p->next;
+       }
+       else
+       {
+           r = r->next = q;
+           q = q->next;
+       }
+    }
+    r->next = (p ? p : q);
+    return(head.next);
+}
+
+static list *lsort (p)
+/* split list into 2 parts, sort each recursively, merge */
+list *p;
+{
+    list *q, *r;
+
+    if (p)
+    {
+       q = p;
+       for (r = q->next; r && (r = r->next) != NULL; r = r->next)
+           q = q->next;
+       r = q->next;
+       q->next = NULL;
+       if (r)
+           p = lmerge(lsort(p), lsort(r));
+    }
+    return(p);
+}
+
+static void readfile(inf)
+/* read file into linked list of lines */
+FILE   *inf;
+{
+    int k;
+    list *p;
+
+    while (fgets(ln, LNSZ, inf))
+    {
+       k = strlen(ln);
+       if (k == 0)
+       {
+           (void) fprintf(stderr, LENGTHZERO);
+           continue;
+       }
+       if (ln[k-1] == '\n')
+       {
+           k--;
+#if CRLFSIZE > 1
+           if (ln[k-1] == '\r')
+               k--;
+#endif /* CRLFSIZE > 1 */
+           ln[k] = '\0';
+       }
+       else
+       {
+           if (k == LNSZ)
+           {
+               (void) fprintf(stderr, TOOLONG);
+               exit(1);
+           }
+           if (ln[0] == 26)
+           {
+               (void) fprintf(stderr, CONTROLZ);
+               continue;
+           }
+           else
+           {
+               (void) fprintf(stderr, NOCRLF);
+               continue;
+           }
+       }
+                       
+       p = (list *)mmalloc(k + sizeof(list));
+       p->len = k;
+       memcpy(p->ln, ln, k+1);
+       p->next = listp;
+       listp = p;
+    }
+}
+
+static void writefile (outf)
+/* write file from in-core list */
+FILE   *outf;
+{
+    list *p;
+
+    for (p = listp; p; p = p->next)
+    {
+       (void) fwrite(p->ln, 1, p->len, outf);
+#if CRLFSIZE > 1
+       (void) fwrite("\r", 1, 1, outf);
+#endif /* CRLFSIZE > 1 */
+       (void) fwrite("\n", 1, 1, outf);
+    }
+}
+
+#ifdef AMIGA
+void setthebufback(void)
+{
+    static char mybuf[BUFSIZ];
+    setbuf(stdout, mybuf);
+}
+#endif /* AMIGA */
+
+void mkindex(argc, argv)
+/* make index from given files */
+int    argc;
+char   *argv[];
+{
+    char source[PATHLEN], target[PATHLEN];
+    char ln[LNSZ], prevln[LNSZ];
+    FILE *inf, *outf;
+    daddr_t pos;
+    char *infilen, *outfilen, *p, *s;
+    unsigned int buffersize;
+    int i, len, fno;
+#ifdef VHHDR
+    vhhdr hdr;
+#endif /* VHHDR */
+
+    (void) strcpy(target, argv[0]);
+    (void) strcat(target, IDX);
+    if ((outf = fopen(target, "wb")) == (FILE *)NULL)
+    {
+       (void) fprintf(stderr, NOOUTPUT, target);
+       exit(1);
+    }
+
+    setbuf(stdout, (char *)NULL);
+#ifdef AMIGA
+    atexit(setthebufback);
+#endif /* AMIGA */
+
+    for (fno = 0 ; fno < argc; fno++)
+    {
+       (void) strcpy(source, argv[fno]);
+       (void) strcat(source, TXT);
+       if ((inf = fopen(source, "rb"))==(FILE *)NULL)
+       {
+           (void) fprintf(stderr, NOINPUT, source);
+           exit(1);
+       }
+
+       getformat(inf);
+
+       (void) printf(FORMTYPE, format);
+
+       *prevln = 0;
+
+       (void) printf(FILLING);
+
+       pos = 0L;
+       while (fgets(ln, LNSZ, inf))
+       {
+           if (p = headword(ln))
+           {
+               *p = 0;
+               if (strlcmp(ln, prevln) != 0)
+                   (void) fprintf(outf, "%s%c%ld\r\n",
+                                  ln + HDOFF(ln), SEPARATOR, pos);
+               (void) strcpy(prevln, ln);
+           }
+           pos = ftell(inf);
+       }
+       (void) fclose(inf);
+    }
+
+    (void) fclose(outf);
+
+    /* now sort the file */
+    inf = fopen(target, "rb");
+    if (inf == NULL)
+    {
+       fprintf(stderr, NOINPUT);
+       exit(1);
+    }
+
+    (void) printf(REREADING);
+
+#ifndef AOS
+    setvbuf(inf, NULL, _IOFBF, LNSZ + 1);
+#endif /* AOS */
+    readfile(inf);
+    (void) fclose(inf);
+
+    (void) printf(SORTING);
+    listp = lsort(listp);
+
+    (void) printf(WRITING);
+    outf = fopen(target, "wb");
+    if (outf == NULL)
+    {
+       (void) fprintf(stderr, NOOUTPUT);
+       exit(1);
+    }
+#ifndef AOS
+    setvbuf(outf, NULL, _IOFBF, LNSZ + 1);
+#endif /* AOS */
+
+#ifdef VHHDR
+    hdr.magic = VHMAGIC;
+    hdr.nfiles = argc;
+    (void) fwrite(&hdr, sizeof(vhhdr), 1, outf);
+
+    for (fno = 0 ; fno < argc; fno++)
+       (void) fwrite(argv[fno], 1, VHPATHSIZ, outf);
+#endif /* VHHDR */
+
+    writefile(outf);
+    (void) fclose(outf);
+
+    (void) printf(DONE);
+}
+
+/* vh.c ends here */
diff --git a/vh.h b/vh.h
new file mode 100644 (file)
index 0000000..23dee1c
--- /dev/null
+++ b/vh.h
@@ -0,0 +1,97 @@
+/* vh.h --- interface to display-independent retrieval code */
+
+#define PATHLEN                256     /* maximum filename size */
+#define        BTSMAX          100     /* backtrack stack size */
+#define        NOPLACE         (-1)    /* out-of-bounds for screen coordinate */
+#define NOWHERE                (-1L)   /* invalid file position */
+#define        LNSZ            256     /* maximum text line width */
+#define MAXWIDTH       256     /* maximum screen width */
+#define        ITEMSMAX        2400    /* max index items per document */
+
+#ifndef UNIX
+typedef long daddr_t;
+#else
+#if !defined(AOS)
+#include <unistd.h>
+#endif /* !defined(AOS) */
+#include <sys/types.h>
+#endif /*  */
+
+#ifndef SEEK_SET       /* AOS is bogus */
+#define SEEK_SET       0
+#define SEEK_END       2
+#endif
+
+/* index of last line on screen available for text display */
+#ifdef POPUP
+#define LASTLINE       (LINES - 1)
+#else
+#define LASTLINE       (LINES - 2)
+#endif /* POPUP */
+
+/* this structure appears at the beginning of each index */
+typedef struct
+{
+    long       magic;          /* magic number, distinguishes file type */
+    int                nfiles;         /* count of text files in the database */
+                               /* multi-files isn't implemented yet */
+}
+vhhdr;
+
+/* this structure represents a select region */
+typedef struct
+{
+    int        xl, yl, xr, yr;
+}
+region;
+
+/*
+ * FILEINFO -- everything we need to keep context of text and index
+ */
+typedef struct fileinfo
+{
+    FILE *fp;                  /* file pointer */
+    daddr_t endpos;            /* end file pos (== file length) */
+    daddr_t toppos;            /* pos of new top line on screen */
+    daddr_t dsptoppos;         /* pos of current top line on screen */
+    daddr_t dspnextpos;                /* pos of line after last line on screen */
+    daddr_t lastpagetoppos;    /* pos of top line of last page */
+    daddr_t hitpos;            /* pos where string search hit */
+    region sel;                        /* selected region on screen (if any) */
+    int ythumb;                        /* current thumb row */
+    int btscnt;                        /* backtrack stack current count */
+    daddr_t bts[BTSMAX];       /* backtrack stack of file positions */
+    region selbts[BTSMAX];     /* backtrack stack of link selections */
+}
+FILEINFO;
+
+/* this function must be defined for vh.c to compile */
+extern void gettext();         /* fetch text from terminal screen */
+
+/* entry points of vh.c */
+extern void chkindex();                /* check for pathologies in file */
+extern void mkindex();         /* generate index for file */
+extern char *headword();       /* is given line a headword? */
+extern bool initbrowse();      /* initialize browse */
+extern void setlastpage();     /* set lastpagetoppos members */
+extern daddr_t iflink();       /* test for presence of link */
+extern daddr_t getnextln();    /* get next line */
+extern daddr_t getprevln();    /* get previous line */
+extern daddr_t xlocate();      /* go to term by name */
+extern daddr_t ilocate();      /* incremental-locate term */
+extern daddr_t ffind();                /* find string in file */
+extern daddr_t ifind();                /* incremental-find string in file */
+extern daddr_t jrandom();      /* return offset of random entry */
+extern region findnextsel();   /* find next possible selection */
+extern region findprevsel();   /* find previous possible selection */
+extern void enqueue();         /* stack a location */
+extern void dequeue();         /* unstack a location */
+extern FILEINFO vhi;           /* file info for index */
+extern FILEINFO *vht;          /* file info for text */
+
+#if defined(__TURBOC__) && defined(FFGETS)
+extern char *ffgets();         /* faster fgets */
+#define fgets(s,n,f) ffgets((s), (n), (f))
+#endif /* defined(__TURBOC__) && defined(FFGETS) */
+
+/* vh.h ends here */