--- /dev/null
+ 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.
--- /dev/null
+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
--- /dev/null
+#\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
--- /dev/null
+/*****************************************************************************
+
+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 */
--- /dev/null
+/*****************************************************************************
+
+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, ®s, ®s);
+ 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, ®s, ®s);
+ 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 */
--- /dev/null
+/* 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 */
--- /dev/null
+.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.
--- /dev/null
+/******************************************************************************
+
+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 */
--- /dev/null
+/* 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 */