From: Eric S. Raymond Date: Tue, 11 May 1993 21:44:47 +0000 (+0000) Subject: *** empty log message *** X-Git-Tag: 1.6~10 X-Git-Url: http://thyrsus.com/gitweb/?a=commitdiff_plain;h=b477d335ca988223e42103f1d32f69ac93ca5747;p=vh.git *** empty log message *** --- b477d335ca988223e42103f1d32f69ac93ca5747 diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + 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.) + +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. + + 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. + + 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 + + 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. + + + Copyright (C) 19yy + + 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. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/README.AMIGA b/README.AMIGA new file mode 100644 index 0000000..2511bd9 --- /dev/null +++ b/README.AMIGA @@ -0,0 +1,151 @@ +Some notes on the Amiga port of the jargon browser: + +- You need to assign jargon: to the directory where the jargon file lies + (so you can refer to the jargon file as jargon:jargon.txt) + +- 2.0 users: The jargon program tries to open a custom screen as large as + the workbench screen, even if that is very large and autoscrolling. This + sometimes fails (or the program won't work correctly). Fix: Open a public + screen with the desired size (e.g. with PSX by Steve Tibbett), open a + shell on it (e.g. newshell "con:0/0/640/200/Title/SCREEN + YourScreenNameHere") and start the jargon program from that shell - it + will open a screen as large as the active screen (at least it does for + me... :-) ) + +- 2.0 users again: Due to the Amiga curses port relying on a fixed font + height and width of 8 pixels, you cannot use another font than topaz.8. I + modified the curses code so that it will explicitely ask for "topaz.8" + instead of using the default font - which wouldn't work. + +- Users of "PopUpMenu" by Martin Adrian: On the jargon screen, there is + a menu bar which shows you the name of the Amiga curses author. Popupmenu + seems to get confused as there are no menu items, so it will crash (at + least version 4.2 does on my machine). So don't press the right mouse + button on the jargon screen when PopUpMenu is enabled (the mouse is not + supported by the Amiga port anyway...) + +- jargon can be run only from the CLI, when called from workbench it just + exits. + +This port would not have been possible (at least not this year :-)) without +Simon John Raybould's (sie@fulcrum.bt.co.uk) Amiga curses (thanks to him). +I only changed some minor things (added 3 functions for my C compiler (DICE +2.06.37R, thanks to Matt Dillon), used 2 bitplanes instead of 4 for the +screen, changed the font to topaz.8 and fixed clearok() so that jargon +wouldn't clear and redraw the whole screen on every keystroke, diffs are +included). + +Apropos Amiga curses: I used version 1.23 from March 1991, it's +ftp'able from funic.funet.fi, directory pub/amiga/new (if I remember +right). + +Georg Sassen +georg@bluemoon.GUN.de + +Here are the diffs for Amiga curses: + +*** curses-1.23.orig/src/curses.c Mon Mar 18 21:43:32 1991 +--- curses-1.23/src/curses.c Sun Dec 22 13:03:21 1991 +*************** static char *rcsid = "$Header: QUANTUM:S +*** 51,57 **** +--- 51,61 ---- + + #include "acurses.h" + ++ #if 1 ++ struct TextAttr foo = {"topaz.font",8,0,0}; ++ #endif + ++ + /* Main background screen */ + static struct NewScreen NewScreen = { + 0, /* LeftEdge */ +*************** static struct NewScreen NewScreen = { +*** 58,68 **** +--- 62,80 ---- + 0, /* TopEdge */ + 0, /* Width */ + 0, /* Height */ ++ #if 0 + 4, /* No. Bitplanes */ ++ #else ++ 2, /* No. Bitplanes */ ++ #endif + 0, 1, /* DetailPen, BlockPen */ + HIRES, /* ViewModes */ + CUSTOMSCREEN, /* Screen type */ ++ #if 0 + (struct TextAttr *)NULL, /* default font */ ++ #else ++ (struct TextAttr *)&foo, /* topaz 8 */ ++ #endif + "Curses screen", /* Screen Title */ + (struct Gadget *)NULL, /* Gadget list */ + (struct BitMap *)NULL /* custom bitmap */ +*************** static void CleanExit(), CleanUp(), DoEc +*** 131,137 **** + static void ZapCursor(), DrawCursor(); + + /* Define a blank mouse pointer */ +! static USHORT __chip MyMsPtr[] = { 0, 0, 0, 0 }; + + #define NCOLOURS 32 + +--- 143,149 ---- + static void ZapCursor(), DrawCursor(); + + /* Define a blank mouse pointer */ +! static __chip USHORT MyMsPtr[] = { 0, 0, 0, 0 }; + + #define NCOLOURS 32 + +*************** initscr() +*** 228,237 **** +--- 240,256 ---- + stdscr = newwin(LINES, COLS, 0, 0); + curscr = newwin(LINES, COLS, 0, 0); /* used for redraws */ + clearok(curscr, TRUE); /* Clear curscr on every refresh */ ++ #ifndef _DCC + if(onbreak(BreakHandler)) { + fprintf(stderr, "Failed to set BREAK handler.\n"); + CleanExit(10); + } ++ #else /* onbreak returns old func pointer */ ++ if(!onbreak(BreakHandler)) { ++ fprintf(stderr, "Failed to set BREAK handler.\n"); ++ CleanExit(10); ++ } ++ #endif + return OK; + } + +*************** wrefresh(WinPtr) +*** 961,966 **** +--- 980,986 ---- + memcpy(WinStat->LnArry[Line].LRLine, WinStat->LnArry[Line].Line, WinPtr->_maxx+1); + memcpy(WinStat->LnArry[Line].LRATTRS, WinStat->LnArry[Line].ATTRS, sizeof(short) * (WinPtr->_maxx+1)); + } ++ WinPtr->_clear = FALSE; /* clrok() only valid for one wrefresh() call */ + DrawCursor(); + return OK; + } +*************** static int BreakHandler() +*** 1751,1753 **** +--- 1771,1787 ---- + fprintf(stderr, "Amiga Curses aborting after recieving signal\n"); + return 1; + } ++ ++ #ifdef _DCC ++ int max(int a,b) ++ { ++ return (a>b?a:b); ++ } ++ int min(int a,b) ++ { ++ return (a 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 +#include +#include +#include +#include +#ifdef ATT +#include +#endif /* ATT */ + +#include "screen.h" +#include "vh.h" + +#ifndef CONST +#define const +#endif /* CONST */ + +#ifdef MSDOS +/* emulate Borland-style scrollbar */ +#define SBARCH ACS_CKBOARD /* scrollbar character */ +#define SBARTOPCH 30 /* scrollbar up arrow */ +#define SBARBOTCH 31 /* scrollbar down arrow */ +#else +/* approximate X-style scrollbar */ +#define SBARCH ACS_VLINE /* scrollbar character */ +#define SBARTOPCH ACS_TTEE /* scrollbar up arrow */ +#define SBARBOTCH ACS_BTEE /* scrollbar down arrow */ +#endif +#define SBARTHUMBCH ACS_BULLET /* scrollbar thumb character */ + +#ifdef A_COLOR +#define NORMPAIR 1 +#define HILITEPAIR 2 +#define SELPAIR 3 +#define PROMPTPAIR 4 +#define FKEYPAIR 5 +#define SBARPAIR 6 +#define SBARENDPAIR 7 +#define SBTHUMBPAIR 8 +#endif /* A_COLOR */ + +#define SUPPRESS 0 +#define OPTIONAL 1 +#define FORCED 2 + +#ifndef UNIX +#define erasechar() BS +#endif /* UNIX */ + +#define CUTFILE "%s.cut" + +/* + * When paging forward or back, it helps the reader to page by a few less + * lines than the page depth, so a few of the old ones show to give context. + * This controls how many old lines will show. + */ +#define OVERLAP 4 + +/* dialogue/message window parameters */ +#define DTOP 10 +#define DLEFT 10 +#define DHEIGHT 7 +#define DWIDTH 42 + +/******************************************************************* + * + * All message texts declared here for internationalization purposes + * + ******************************************************************/ + +#ifdef MSDOS +#define USAGE "usage: jargon [-cigms] [-b key] [srcname] [indexname]\n\ + -c --- check textfile/index pair for consistency\n\ + -i --- enable incremental-lookup and search\n\ + -g --- generate new index from textfile\n\ + -m --- force monochrome operation\n\ + -s --- compensate for snowy EGA monitor\n\ + -b --- write single entry specified by following key to stdout\n\ + -r --- display random entry\n" +#define TBANNER "TAB-Index F1-help F10-exit" +#define IBANNER "TAB-Text F1-help F10-exit" +#else +#define USAGE "usage: jargon [-cigmr] [-b key] [srcname] [indexname]\n" +#define TBANNER "TAB-Index ^Q-help ^C-exit" +#define IBANNER "TAB-Text ^Q-help ^C-exit" +#endif +typedef struct +{ + int left; /* left boundary of pushbutton */ + int right; /* right boundary of pushbutton */ + char cmd; /* command character associated with pushbutton */ +} +strip; +static strip panel[] = {{0, 8, TAB}, {11, 17, CTL('Q')}, {21, 27, CTL('C')}}; +#define PANELSTART (COLS - 1 - strlen((cf==&vhi) ? IBANNER : TBANNER) + 1) + +#define NOREFS "No references on this page" +#define NOSRCH "No previous search" +#define CUTNOP "Can't open cut file" +#define CUTNOW "Cutting to jargon.cut..." +#define CUTDNE "Cutting to jargon.cut...Done" + +#define BRSMES "Browsing %s...\n" +#define IDXMES "Generating index for %s...\n" +#define CHKMES "Performing consistency check for %s...\n" + +/* + * These have to be declared static, not macros; we use the address of the + * current prompt as a mode indicator. + */ +const static char NOMESG[] = " "; +#ifndef POPUP +/* we want these fixed-length so the entry line looks good */ +const static char SEARCH[] = "String search for: "; +const static char INSRCH[] = "Search proceeding: "; +const static char NOTFND[] = "Search failed: "; +const static char LOOKUP[] = "Looking up entry: "; +const static char LOOKING[] = "Lookup in progress:"; +#else +/* the popup windows will get sized to these */ +const static char SEARCH[] = "String search for"; +const static char INSRCH[] = "Search proceeding"; +const static char NOTFND[] = "Search failed"; +const static char LOOKUP[] = "Looking up entry"; +const static char LOOKING[] = "Lookup in progress"; +const static char BDLOOK[] = "Lookup failed"; +#endif /* POPUP */ + +const static char *help_screen[] = +{ +" Jargon File Browser v. 1.5", +" Copyright 1991 by Raymond D. Gardner & Eric S. Raymond", +" All Rights Reserved; Free Redistribution Encouraged", +"", +" ^L redraw screen (Refresh)", +"", +" space page forward (PgDn)", +" ^U page back (PgUp)", +" ^N / ^P scroll forward / back (Down/Up Arrow)", +" ^A / ^O go to top / end of file (Home/End)", +"", +" ^J or ^M chase highlighted reference (Reference)", +" ^F / ^B next/previous reference (Right / Left Arrow)", +"", +" ^E lookup entry by name (Select)", +" ^S search for string (Find)", +" ^R repeat previous search (Shift-Find)", +#ifdef UNIX /* keypad() seems to disable recognition of ESC */ +" ^H undo last search or lookup (Undo)", +#else +" ^H or ESC undo last search or lookup (Undo)", +#endif +"", +" ^I toggle between word list and text (Tab)", +" ^Y append selected entry to cut file (Print)", +"", +" Typing an entry name followed by Enter works like an ^E command.", +" Press any key to return to the browser.", +NULL, +}; + +/******************************************************************* + * + * Shared global data + * + ******************************************************************/ + +static char *execname; /* name under which program was invoked */ +static FILEINFO *cf; /* pointer to current fileinfo block */ + +/* highlights */ +static chtype normattr, hiliteattr, selattr, promptattr, fkeyattr, sbarattr; +static chtype sbarendattr, sbthumbattr; + +static char lasthit[LNSZ + 1]; /* last key searched for */ +static char linbuf[LNSZ + 1]; /* line input scratch buffer */ +#ifdef POPUP +static WINDOW *dwin; /* dialogue & message window */ +#endif /* POPUP */ +static bool incrsearch; /* do incremental search? */ + +/******************************************************************* + * + * Message and dialogue code + * + ******************************************************************/ + +static void waitforit(requeue) +/* wait for keystroke or mouse click */ +bool requeue; +{ + int c; +#ifdef MOUSE + int hsy, hsx; + + /* wait for keypress or button click */ + while ((c = egetch()) == KEY_MOUSE) + if (A_BUTTON_CHANGED & mouse_status(&hsy, &hsx)) + break; +#else + c = egetch(); +#endif /* MOUSE */ + if (requeue) + ungetch(c); +} + +#if defined(POPUP) && defined(CURSES) +static void draw_box(win, ty,tx, by,bx) +/* draw a box on the screen using best possible forms chars */ +WINDOW *win; +int ty, tx, by, bx; +{ + int j; + + mvwaddch(win, ty, tx, ACS_ULCORNER); + mvwaddch(win, by, bx, ACS_LRCORNER); + mvwaddch(win, by, tx, ACS_LLCORNER); + mvwaddch(win, ty, bx, ACS_URCORNER); + wmove(win, ty, tx+1); + for (j = tx + 1; j <= bx - 1; j++) + waddch(win, ACS_HLINE); + wmove(win, by, tx+1); + for (j = tx + 1; j <= bx - 1; j++) + waddch(win, ACS_HLINE); + for (j = ty + 1; j <= by - 1; j++) + { + wmove(win, j, tx); + waddch(win, ACS_VLINE); + } + for (j = ty + 1; j <= by - 1; j++) + { + wmove(win, j, bx); + waddch(win, ACS_VLINE); + } + wrefresh(win); +} + +static char *get_dialogue(prompt) +/* get string from user, with prompts */ +char *prompt; +{ + static char buf[MAXCOLS + 1]; + + if (prompt == (char *)NULL) + { + delwin(dwin); + dwin = (WINDOW *)NULL; + } + else + { + /* create a dialogue window */ + if (dwin == (WINDOW *)NULL) + dwin = newwin(DHEIGHT, DWIDTH, DTOP, DLEFT); + + wclear(dwin); + + draw_box(dwin, 0, 0, DHEIGHT-1, DWIDTH-1); + draw_box(dwin, 2, 1, DHEIGHT-3, DWIDTH-2); + + wattron(dwin, A_BOLD); + mvwaddstr(dwin, 1, 10, prompt); + wattroff(dwin, A_BOLD); + + echo(); + mvwgetstr(dwin, 3, 3, buf); + noecho(); + + return(buf); + } +} +#endif /* defined(POPUP) && defined(CURSES) */ + +static void message(s) +/* write message to prompt line */ +char *s; +{ +#ifndef POPUP + attrset(promptattr); + mvaddstr(LINES-1, 0, s); + attrset(normattr); + refresh(); +#else + WINDOW *mwin; + + if (dwin) + { + mvwaddstr(dwin, 5, 10, NOMESG); + wattron(dwin, A_BOLD); + mvwaddstr(dwin, 5, 10, s); + wattroff(dwin, A_BOLD); + wrefresh(dwin); + } + else + { + mwin = newwin(3, strlen(s) + 2, DTOP, DLEFT); + wclear(mwin); + draw_box(mwin, 0, 0, 2, strlen(s) + 1); + + wattron(mwin, A_BOLD); + mvwaddstr(mwin, 1, 1, s); + wattroff(mwin, A_BOLD); + wrefresh(mwin); + + waitforit(TRUE); + + overwrite(stdscr, mwin); + delwin(mwin); + touchline(DTOP, stdscr); + wrefresh(stdscr); + } +#endif /* POPUP */ +} + +/******************************************************************* + * + * The browser itself + * + ******************************************************************/ + +static void uninitbrowse(doclear) +/* end the browse, either normally or due to signal */ +bool doclear; +{ + mouse_hide(); + if (doclear) + { + clear(); + refresh(); + } +#ifdef CURSES + /* weird sex with the tty driver */ + (void)resetterm(); + (void)echo(); +#ifndef OLDCURSES + (void)flushinp(); +#endif /* OLDCURSES */ +#endif /* CURSES */ + (void)endwin(); + exit(0); +} + +static void paintselect(rp, attr) +/* turn highlight on a selection on or off */ +region *rp; /* which to highlight */ +chtype attr; /* attribute to use */ +{ + int x, y; + + attrset(attr); + if (rp->yl == rp->yr) + { + move(rp->yl, rp->xl); + for (x = rp->xl; x <= rp->xr; x++) + addch(inch() & A_CHARTEXT); + } + else + { + int c, lastnsp; + + /* don't highlight trailing spaces on line-wrapped references */ + for (lastnsp = COLS - 1; lastnsp > 0; lastnsp--) + { + move(rp->yl, lastnsp); + if ((inch() & A_CHARTEXT) != ' ') + break; + } + + /* write trailing-line part of highlight */ + move(rp->yl, rp->xl); + for (x = rp->xl; x <= lastnsp; x++) + addch(inch() & A_CHARTEXT); + + /* highlight everything *between* 1st and last lines in region */ + for (y = rp->yl + 1; y < rp->yr; y++) + { + move(y, x); + for (x = 0; x < COLS - 1; x++) + addch(inch() & A_CHARTEXT); + } + + /* don't highlight leading spaces on line-wrapped references */ + for (x = 0; x <= COLS - 1; x++) + { + move(rp->yr, x); + if ((inch() & A_CHARTEXT) != ' ') + break; + } + + /* write leading-line part of highlight */ + move(rp->yr, x); + for (; x <= rp->xr; x++) + addch(inch() & A_CHARTEXT); + } + attrset(normattr); +} + +static bool chase(x, y, isindex) +/* try to chase a link at this location */ +int x, y; +bool isindex; +{ + long pos; /* find target */ + int dummy; + + enqueue(cf); /* make sure we can un-chase it */ + + pos = iflink(x, y, &dummy, &dummy, isindex); + if (pos <= 0) + { + dequeue(cf); + return(FALSE); + } + else + { + if (isindex) /* if index, switch to text */ + { + cf = vht; + cf->dsptoppos = cf->dspnextpos = NOWHERE; + } + + cf->toppos = pos; /* set for new display */ + return(TRUE); + } +} + + +static bool search(str, c) +/* search for given string in file */ +char *str; +{ + int i; + + message(INSRCH); + enqueue(cf); + if (c) + cf->hitpos = ifind(cf->fp, cf->dspnextpos, c); + else + cf->hitpos = ffind(cf->fp, cf->dspnextpos, str); + if (cf->hitpos == NOWHERE) + { + message(NOTFND); + dequeue(cf); + return(FALSE); + } + else + { + /* else display hit on fourth line */ + cf->toppos = cf->hitpos; + for (i = 0; i < OVERLAP; i++) + cf->toppos = getprevln(cf->fp, cf->toppos, linbuf); + if (str != lasthit) + (void) strcpy(lasthit, str); + return(TRUE); + } +} + +static bool lookup(str, c) +/* search for given entry in file */ +char *str, c; +{ + long pos; /* find target */ + + enqueue(cf); + + if (c) + pos = ilocate(c); + else + { + message(LOOKING); + pos = xlocate(str); + } + + if (cf == &vhi) /* if index, switch to text */ + { + cf = vht; + cf->dsptoppos = cf->dspnextpos = cf->endpos; + } + + if (pos < 0) + { + cf->toppos = -pos; /* set for new display */ + return(FALSE); + } + else + { + cf->toppos = pos; /* set for new display */ + return(TRUE); + } +} + +static daddr_t byname(prompt, str, c) +/* search or lookup given entry in file, depending on prompt value */ +char *prompt, *str, c; +{ + if (prompt == SEARCH) + return(search(str, c)); + else + return(lookup(str, c)); +} + +/* entry buffer for lookup and search commands */ +static char entrybuf[LNSZ + 1]; +static int entrycnt = 0; + +static void enterchar(c, prompt, row, col, maxcol) +/* accept a character into the entry buffer */ +char c; +char *prompt; +int row, col; +int maxcol; +{ + int es; + + if (c == '\0') + { + entrybuf[entrycnt = 0] = '\0'; + return; + } + + es = col + strlen(prompt); + attrset(promptattr); + if (c == erasechar()) + { + if (entrycnt > 0) + { + mvaddch(row, es + --entrycnt, ' '); + entrybuf[entrycnt] = '\0'; + move(row, es + entrycnt); + ilocate(BS); + if (incrsearch) + dequeue(cf); + } + } +#ifdef CURSES + else if (c == killchar()) + { + /* delete everything back to the end of the prompt */ + while (entrycnt > 0) + { + mvaddch(row, es + --entrycnt, ' '); + entrybuf[entrycnt] = '\0'; + move(row, es + entrycnt); + if (incrsearch) + dequeue(cf); + } + ilocate(DEL); + } +#endif /* CURSES */ + else if (isprint(c)) + { + mvaddstr(row, col, prompt); + if (es + entrycnt < maxcol + && (!incrsearch || byname(prompt, (char *)NULL, c) != NOWHERE)) + { + mvaddch(row, es + entrycnt, c); + entrybuf[entrycnt++] = c; + entrybuf[entrycnt] = '\0'; + } + else if (c == '\0') + entrybuf[entrycnt = 0] = '\0'; + else + beep(); + } + attrset(normattr); + + refresh(); +} + +static void cutentry(pos, fp) +/* send the text of the entry starting at pos to the gin file pointer */ +daddr_t pos; +FILE *fp; +{ + (void) fseek(vht->fp, pos, SEEK_SET); + + /* always want first line */ + (void) fgets(linbuf, LNSZ, vht->fp); + (void) fputs(linbuf, fp); + + /* copy succeeding lines till we get to next entry */ + while (fgets(linbuf, LNSZ, vht->fp) != (char *)NULL) + if (linbuf[0] == '=' || headword(linbuf)) + break; + else + (void) fputs(linbuf, fp); +} + +static char *nblanks(n) +/* blank-string return for fast fills; return up to 132 = MAXCOLS */ +int n; +{ + /* MAXCOLS+1 blanks */ + const static char blanks[] = " "; + return(&blanks[sizeof(blanks) - 1 - n]); +} + +static void browse(name, cok) +/* interactive browser loop */ +char *name; +bool cok; +{ + int i, c; + char **hp; + bool restore_select = FALSE; + FILE *cutfp; + int lightup = FORCED; +#ifdef MOUSE + int hsy, hsx, mstat; /* mouse hot spot coordinates */ + bool thumbdrag = FALSE; /* are we in a thumbdrag? */ +#endif /* MOUSE */ +#ifndef POPUP + char *prompt = NOMESG; +#else + char *cp; +#endif /* POPUP */ +#ifdef ATT + struct termio tty; + int saveintr; +#endif /* ATT */ + + if (!initbrowse(name)) + exit(1); + cf = vht; + +#ifdef UNIX + (void) signal(SIGINT,uninitbrowse); + (void) signal(SIGQUIT,uninitbrowse); + (void) signal(SIGTERM,uninitbrowse); + (void) signal(SIGIOT,uninitbrowse); /* for assert(3) */ +#ifdef ATT + (void) ioctl(0, TCGETA, &tty); + saveintr = tty.c_cc[VINTR]; +#endif /* ATT */ +#endif /* UNIX */ + + (void) initscr(); + setlastpage(); + +#ifdef A_COLOR + if (cok = (cok && has_colors())) + { + start_color(); + + init_pair(NORMPAIR, COLOR_WHITE, COLOR_BLUE); + init_pair(HILITEPAIR, COLOR_RED, COLOR_WHITE); + init_pair(SELPAIR, COLOR_YELLOW, COLOR_BLUE); + init_pair(PROMPTPAIR, COLOR_BLACK, COLOR_CYAN); + init_pair(FKEYPAIR, COLOR_RED, COLOR_CYAN); + init_pair(SBARPAIR, COLOR_WHITE, COLOR_BLACK); + init_pair(SBARENDPAIR, COLOR_BLUE, COLOR_WHITE); + init_pair(SBTHUMBPAIR, COLOR_BLUE, COLOR_BLACK); + } + mouse_color(cok); + + normattr = cok ? (COLOR_PAIR(NORMPAIR) | A_BOLD) : A_NORMAL; + hiliteattr = cok ? COLOR_PAIR(HILITEPAIR) : A_REVERSE; + selattr = cok ? (COLOR_PAIR(SELPAIR) | A_BOLD) : A_BOLD; + promptattr = cok ? COLOR_PAIR(PROMPTPAIR) : A_REVERSE; + fkeyattr = cok ? COLOR_PAIR(FKEYPAIR) : A_BOLD; +#ifndef MSDOS + sbarendattr = sbthumbattr = + sbarattr = cok ? COLOR_PAIR(SBARPAIR) : A_NORMAL; +#else + sbarattr = cok ? COLOR_PAIR(SBARPAIR) : A_REVERSE; + sbarendattr = cok ? (COLOR_PAIR(SBARENDPAIR) | A_BOLD) : A_REVERSE; + sbthumbattr = cok ? (COLOR_PAIR(SBTHUMBPAIR) | A_BOLD) : A_NORMAL; +#endif +#else + normattr = A_NORMAL; + hiliteattr = A_REVERSE; + selattr = A_BOLD; + promptattr = A_REVERSE; + fkeyattr = A_BOLD; + sbarendattr = sbthumbattr = + sbarattr = A_NORMAL; +#ifdef MSDOS + sbarattr = A_REVERSE; + sbarendattr = A_REVERSE; + sbthumbattr = A_NORMAL; +#endif /* MSDOS */ +#endif /* A_COLOR */ + + mouse_init(); +#ifdef MOUSE + mouse_move(LINES - 1, COLS - 1); +#endif /* MOUSE */ + +#ifdef CURSES + (void)saveterm(); + (void)nonl(); + (void)raw(); + (void)noecho(); +#ifndef OLDCURSES + (void)keypad(stdscr, TRUE); +#endif /* BSD */ +#endif /* CURSES */ + +#ifdef ATT + (void) ioctl(0, TCGETA, &tty); + tty.c_cc[VINTR] = saveintr; + tty.c_iflag |= BRKINT; + tty.c_iflag &=~ IGNBRK; + tty.c_lflag |= ISIG; + (void) ioctl(0, TCSETA, &tty); +#endif /* ATT */ + + lasthit[0] = '\0'; + + for (c = KEY_REFRESH; c != CTL('C') && c != KEY_EXIT; c = egetch()) + { +#ifdef DEBUG + if (isprint(c)) + (void) fprintf(stderr, "command: %c\n", c); + else if (iscntrl(c)) + (void) fprintf(stderr, "command: ^%c\n", c + '@'); + else if (c >= 0x80 & c <= 0x9f) + (void) fprintf(stderr, "command: M-^%c\n", (c &~ 0x80) + '@'); + else if (c >= 0x80) + (void) fprintf(stderr, "command: M-%c\n", c &~ 0x80); + else + (void) fprintf(stderr, "command: 0x%02x\n", c); +#endif /* DEBUG */ + + switch(c) + { +#ifdef MSDOS + case KEY_F(10): + (void) ungetch(KEY_EXIT); + break; +#endif /* MSDOS */ + +#ifndef OLDCURSES + case KEY_HELP: /* go to help screen */ +#endif /* OLDCURSES */ + case CTL('Q'): +#ifdef MSDOS + case KEY_F(1): +#endif /* MSDOS */ + restore_select = TRUE; + i = 0; + for (hp = help_screen; *hp; hp++) + { + mvaddstr(i++, 0, *hp); + addstr(nblanks(COLS - strlen(*hp))); + } + refresh(); + cf->dsptoppos = NOWHERE; + waitforit(FALSE); + /* FALL THROUGH */ + +#ifndef OLDCURSES + case KEY_REFRESH: /* redraw screen */ +#endif /* OLDCURSES */ + case CTL('L'): + restore_select = TRUE; +#ifdef CURSES + clearok(stdscr, TRUE); +#endif /* CURSES */ + break; + + case SP: + restore_select = FALSE; +#ifndef POPUP + if (prompt != NOMESG) + goto medialspace; + /* FALL THROUGH */ +#endif /* POPUP */ + +#ifndef OLDCURSES + case KEY_NPAGE: /* page forward */ +#endif /* OLDCURSES */ + pagefwd: + enqueue(cf); + restore_select = FALSE; + for (i = 0; i < LINES - OVERLAP; i++) + { + if (cf->toppos == cf->lastpagetoppos) + break; + cf->toppos = getnextln(cf->fp, cf->toppos, linbuf); + } + if (cf->sel.xl != NOPLACE) + { + cf->sel.yl -= i; cf->sel.yr -= i; + if (!(restore_select = (cf->sel.yl >= 0))) + cf->sel.xl = NOPLACE; + } + break; + +#ifndef OLDCURSES + case KEY_PPAGE: /* page back */ +#endif /* OLDCURSES */ + case CTL('U'): + pagebak: + enqueue(cf); + restore_select = FALSE; + for (i = 0; i < LINES - OVERLAP; i++) + { + if (cf->toppos == 0) + break; + cf->toppos = getprevln(cf->fp, cf->toppos, linbuf); + } + if (cf->sel.xl != NOPLACE) + { + cf->sel.yl += i; cf->sel.yr += i; + if (!(restore_select = (cf->sel.yr < LINES - 1))) + cf->sel.xl = NOPLACE; + } + break; + +#ifndef OLDCURSES + case KEY_UP: +#endif /* OLDCURSES */ + case CTL('N'): +linebak: + restore_select = FALSE; + if (cf->toppos) + { + cf->toppos = getprevln(cf->fp, cf->toppos, linbuf); + if (cf->sel.xl != NOPLACE) + { + cf->sel.yl++; cf->sel.yr++; + if (!(restore_select = (cf->sel.yr < LINES - 1))) + cf->sel.xl = NOPLACE; + } + } + break; + +#ifndef OLDCURSES + case KEY_DOWN: +#endif /* OLDCURSES */ + case CTL('P'): +linefwd: + restore_select = FALSE; + if (cf->toppos < cf->lastpagetoppos) + { + cf->toppos = getnextln(cf->fp, cf->toppos, linbuf); + if (cf->sel.xl != NOPLACE) + { + cf->sel.yl--; cf->sel.yr--; + if (!(restore_select = (cf->sel.yl >= 0))) + cf->sel.xl = NOPLACE; + } + } + break; + +#ifndef OLDCURSES + case KEY_ENTER: /* accept keyboard input */ +#endif /* OLDCURSES */ + case CTL('J'): + case CTL('M'): + lightup = FORCED; +#ifndef POPUP + restore_select = FALSE; + if (prompt == SEARCH || prompt == LOOKUP) + { + if (!incrsearch && byname(prompt, entrybuf, '\0') == NOWHERE) + beep(); + enterchar('\0', LOOKUP, LINES - 1, 0, PANELSTART); + prompt = NOMESG; + break; + } + /* nothing in prompt buffer, no search active: FALL THROUGH */ +#endif /* POPUP */ + +#ifndef OLDCURSES + case KEY_REFERENCE: /* chase reference */ +#endif /* OLDCURSES */ + restore_select = FALSE; + if (cf->sel.xl == NOPLACE) /* any link? */ + { + message(NOREFS); + continue; + } + else + (void) chase(cf->sel.xl, cf->sel.yl, cf == &vhi); + break; + +#ifndef OLDCURSES + case KEY_RIGHT: /* next reference */ +#endif /* OLDCURSES */ + case CTL('F'): + restore_select = FALSE; + if (cf->sel.xl == NOPLACE) + message(NOREFS); + else + { + paintselect(&cf->sel, (cf == &vhi) ? normattr : selattr); + cf->sel = findnextsel(cf->sel.xl, cf->sel.yl, cf == &vhi); + paintselect(&cf->sel, hiliteattr); + } + refresh(); + continue; + +#ifndef OLDCURSES + case KEY_LEFT: /* previous reference */ +#endif /* OLDCURSES */ + case CTL('B'): + restore_select = FALSE; + if (cf->sel.xl == NOPLACE) + message(NOREFS); + else + { + paintselect(&cf->sel, (cf == &vhi) ? normattr : selattr); + /* + * The -1 offset on xsel is important. It insures that + * when findprevsel() starts looking for a previous + * tag, it doesn't find the current one again. + */ + cf->sel = findprevsel(cf->sel.xl-1, cf->sel.yl, cf == &vhi); + paintselect(&cf->sel, hiliteattr); + } + refresh(); + continue; + +#ifndef OLDCURSES + case KEY_FIND: /* search for string */ +#endif /* OLDCURSES */ + case CTL('S'): + restore_select = FALSE; +#ifndef POPUP + prompt = SEARCH; +#else + if (cp = get_dialogue(SEARCH)) + if (search(cp, '\0') != NOWHERE) + { + get_dialogue((char *)NULL); + break; + } + else + continue; +#endif /* POPUP */ + break; + +#ifndef OLDCURSES + case KEY_SFIND: /* search for last string again */ +#endif /* OLDCURSES */ + case CTL('R'): + restore_select = FALSE; + if (lasthit[0] == '\0') + { + message(NOSRCH); + continue; + } + else +#ifdef POPUP + (void) search(lasthit, '\0'); +#else + { + prompt = SEARCH; + (void) strcpy(entrybuf, lasthit); + entrycnt = strlen(lasthit); + (void) ungetch(CTL('J')); + } +#endif /* POPUP */ + break; + +#ifndef OLDCURSES + case KEY_SELECT: /* search for entry */ +#endif /* OLDCURSES */ + case CTL('E'): + lookfor: + restore_select = FALSE; +#ifndef POPUP + prompt = LOOKUP; +#else + if (cp = get_dialogue(LOOKUP)) + { + if (lookup(cp, '\0') == NOWHERE) + beep(); + get_dialogue((char *)NULL); + break; + } +#endif /* POPUP */ + break; + +#ifndef OLDCURSES + case KEY_HOME: /* go to beginning of file */ + case KEY_BEG: +#endif /* OLDCURSES */ + case CTL('A'): + enqueue(cf); + restore_select = FALSE; + cf->toppos = 0; + break; + +#ifndef OLDCURSES + case KEY_END: /* go to end of file */ +#endif /* OLDCURSES */ + case CTL('O'): + enqueue(cf); + restore_select = FALSE; + cf->toppos = cf->lastpagetoppos; + break; + + case BS: /* this is context-sensitive */ +#ifndef POPUP + if (prompt != NOMESG) + goto medialspace; + /* FALL THROUGH */ +#endif /* POPUP */ + +#ifndef OLDCURSES + case KEY_UNDO: /* backtrack into the location stack */ +#endif /* OLDCURSES */ + case ESC: + restore_select = TRUE; + dequeue(cf); + break; + + case TAB: /* toggle between text and index */ + restore_select = TRUE; + vhi.dsptoppos = vht->dsptoppos = NOWHERE; + if (cf == vht) + cf = &vhi; + else + cf = vht; + break; + +#ifndef OLDCURSES + case KEY_PRINT: /* append selected entry to file */ +#endif /* OLDCURSES */ + case CTL('Y'): + restore_select = FALSE; + (void) sprintf(linbuf, CUTFILE, execname); + if (cf->sel.xl == NOPLACE) + { + message(NOREFS); + continue; + } + else if ((cutfp = fopen(linbuf, "a")) == (FILE *)NULL) + { + message(CUTNOP); + continue; + } + else + { + long pos; /* find target */ + int dummy; + + message(CUTNOW); + + pos = iflink(cf->sel.xl, cf->sel.yl, &dummy,&dummy, cf == &vhi); + if (pos >= 0) /* how can this fail? */ + cutentry(pos, cutfp); + (void) fclose(cutfp); + + message(CUTDNE); + continue; + } + break; + +#ifdef MOUSE + /* + * Note: we treat a single-button mouse as having the left + * button only. + * + * The mouse_status() function should return the key-status defines + * implied below when appropriate. It should return the zero-origin + * mouse hotspot coordinates at character-cell resolution, with + * CMDLINE and THUMBCOL as special values designating the command + * line and thumb column respectively (on character displays these + * would be LINES - 1 and COLS - 1 respectively). + */ + case KEY_MOUSE: /* mouse gesture has occurred */ + mstat = mouse_status(&hsy, &hsx); +#ifndef POPUP + hsy = (hsy == LINES - 1) ? CMDLINE : hsy; +#endif /* POPUP */ + switch(mstat) + { + case BUTTON1_PRESSED: /* left button down; chase link, else page */ + restore_select = FALSE; + if (hsy == CMDLINE) /* clicked on entry line */ + { + hsx -= PANELSTART; + for (i = 0; i < sizeof(panel) / sizeof(strip); i++) + if (hsx >= panel[i].left && hsx <= panel[i].right) + (void) ungetch(panel[i].cmd); + continue; + } + else if (hsx == THUMBCOL) /* click in thumb column */ + { + if (hsy == 0) /* clicked top of bar */ + goto linebak; + else if (hsy == LASTLINE) /* clicked bottom of bar */ + goto linefwd; + else if (hsy < cf->ythumb) /* clicked above thumb */ + goto pagebak; + else if (hsy > cf->ythumb) /* clicked below thumb */ + goto pagefwd; + else /* clicked on the thumb */ + thumbdrag = TRUE; + } + else if (!chase(hsx, hsy, cf == &vhi)) + goto pagefwd; /* click in text somewhere */ + break; + + case BUTTON1_RELEASED: /* left button up */ + restore_select = FALSE; + if (thumbdrag && 0 < hsy && hsy < LASTLINE) + { + cf->toppos = /* compute new position */ + cf->lastpagetoppos * + (hsy - 1) / (LASTLINE - 2); + + assert(0 <= cf->toppos && + cf->toppos <= cf->lastpagetoppos); + + /* ensure on line boundary */ + if (0 < cf->toppos && cf->toppos < cf->lastpagetoppos) + cf->toppos = getnextln(cf->fp, cf->toppos, linbuf); + } + thumbdrag = FALSE; + break; + + case BUTTON3_PRESSED: /* right button down; backtrack */ + restore_select = TRUE; + dequeue(cf); + break; + + case BUTTON3_RELEASED: /* right button up */ + /* reserved for future expansion */ + break; + } + break; +#endif /* MOUSE */ + + default: +#ifdef POPUP + if (isprint(c)) + { + (void) ungetch(c); + goto lookfor; + } +#else + medialspace: + restore_select = FALSE; + if (prompt == NOMESG) + { + ungetch(c); + goto lookfor; + } + enterchar(c, prompt, LINES - 1, 0, PANELSTART); +#endif /* POPUP */ + lightup = SUPPRESS; + } + +#ifndef POPUP + /* regenerate prompt */ + attrset(promptattr); + move(LINES - 1, 0); + addstr(prompt); + addstr(entrybuf); + addstr(nblanks(PANELSTART - strlen(prompt) - strlen(entrybuf))); + mvaddstr(LINES - 1, PANELSTART, (cf == &vhi) ? IBANNER : TBANNER); + attrset(normattr); +#endif /* POPUP */ + + if (cf->toppos != cf->dsptoppos) + { + int i; + long newpos = cf->toppos; + +#ifdef DEBUG + (void) fprintf(stderr, + "screen update triggered, toppos = %ld\n", + cf->toppos); +#endif /* DEBUG */ + + attrset(normattr); + for (i = 0; i <= LASTLINE; i++) + if ((newpos = getnextln(cf->fp, newpos, linbuf)) != NOWHERE) + { + mvaddstr(i, 0, linbuf); + /* + * This is actually faster, and creates less + * flicker, than clearing the screen. + */ + addstr(nblanks(COLS - strlen(linbuf))); + } + cf->dspnextpos = ftell(cf->fp); + +#ifdef SCROLLBAR + /* + * Generate the scroll bar. Yes, we really do want the denominator + * to be lastpagetoppos and not endpos. This means the thumb only + * bottoms out if we're really at the end. + */ + cf->ythumb = 1 + cf->toppos * (LASTLINE - 2) / cf->lastpagetoppos; + assert(0 < cf->ythumb && cf->ythumb < LASTLINE); + mvaddch(0, COLS, SBARTOPCH | sbarendattr); + for (i = 1; i < LASTLINE; i++) /* set up scrollbar */ + mvaddch(i, COLS, SBARCH | sbarattr); + mvaddch(LASTLINE, COLS, SBARBOTCH | sbarendattr); + mvaddch(cf->ythumb, COLS, SBARTHUMBCH | sbthumbattr); +#endif /* SCROLLBAR */ + } + + /* + * This has to be done whether or not lightup is going to happen. + * Otherwise, a following command may enqueue a bogus selection + * box as part of state. + */ + cf->sel = findnextsel(0, -1, cf == &vhi); + + if (lightup == SUPPRESS) + lightup = OPTIONAL; + else + { + if (incrsearch) + (void) byname(prompt, (char *)NULL, DEL); + + if (lightup == FORCED || cf->toppos != cf->dsptoppos) + { + lightup = OPTIONAL; + + /* highlight the current select, if any */ + if (restore_select && cf->sel.xl != NOPLACE) + paintselect(&cf->sel, hiliteattr); /* restore existing select */ + else + { + /* highlight the first selection, if there is one */ + if (cf->sel.xl != NOPLACE) + paintselect(&cf->sel, hiliteattr); + } + + /* if in text, boldface all potential selects */ + if (!(cf == &vhi) && cf->sel.xl != NOPLACE) + { + region hibox; + +#ifdef DEBUG + (void) fprintf(stderr, + "current: x = %2d, y = %2d, pos = %ld.\n", + cf->sel.xl, cf->sel.yl, cf->toppos); +#endif /* DEBUG */ + hibox.xl = cf->sel.xl; + hibox.yl = cf->sel.yl; + for (;;) + { + hibox = findnextsel(hibox.xl, hibox.yl, cf == &vhi); +#ifdef DEBUG + (void) fprintf(stderr, + "hibox: x = %2d, y = %2d.\n", + hibox.xl, hibox.yl); +#endif /* DEBUG */ + if (hibox.xl == cf->sel.xl && hibox.yl == cf->sel.yl) + break; + paintselect(&hibox, selattr); + } + } + } + } + + move(LINES - 1, 0); + + cf->dsptoppos = cf->toppos; + + refresh(); + } + + uninitbrowse(TRUE); +} + +#ifdef MSDOS +static char *basename(s) +/* isolate base name of fully qualified filename (modifies the name passed!) */ +char *s; +{ + char *p; + p = strrchr(s, '.'); /* find rightmost dot */ + if ( strchr(p, '\\') ) /* if a \ comes after it, find string end */ + p = strchr(p, 0); + *p = 0; /* terminate at last dot after a \ (if any)*/ + if ( (p = strrchr(s, '\\')) == NULL ) /* find last \ (if any) */ + p = s; /* take whole string if no \ */ + else + ++p; /* else take string after \ */ + strlwr(p); + return p; +} +#else +#define basename(x) x /* don't need to strip argv[0] under UNIX */ +#endif /* MSDOS */ + +/******************************************************************* + * + * Main sequence + * + ******************************************************************/ + +main(argc, argv) +int argc; +char **argv; +{ + char *dbname, *batchkey = NULL; + int i; + bool doindex = FALSE, docheck = FALSE, dofilter = FALSE; + bool forcemono = FALSE, atrandom = FALSE; + + execname = argv[0]; + while ((++argv, --argc) && *argv[0] == '-') + for (i = 1; argv[0][i]; i++) + switch(argv[0][i]) + { + case 'b': + batchkey = argv[1]; + break; + + case 'c': + docheck = TRUE; + break; + + case 'f': + dofilter = TRUE; + break; + + case 'g': + doindex = TRUE; + break; + + case 'i': + incrsearch = TRUE; + break; + + case 'm': + forcemono = TRUE; + break; + + case 'r': + atrandom = TRUE; + break; + +#ifdef __TURBOC__ + case 's': + { /* if CGA that snows: */ + extern bool has_snowy_CGA; + has_snowy_CGA = TRUE; + } + break; +#endif /* __TURBOC__ */ + + default: + (void) fprintf(stderr, USAGE); + exit(1); + } + +#ifdef DEBUG + (void) freopen("ERRLOG", "w", stderr); +#endif /* DEBUG */ + + /* + * User specified a batch-retrieval option. + * OK, snarf the keyword from the next argument. + */ + if (batchkey) + ++argv, --argc; + + dbname = argc ? argv[0] : basename(execname); + + if (!doindex && !docheck && !atrandom && !dofilter + && batchkey == (char *)NULL) + { + (void) printf(BRSMES, dbname); + browse(dbname, !forcemono); + } + + if (doindex) + { + (void) printf(IDXMES, dbname); + if (argc) + mkindex(argc, argv); + else + mkindex(1, &dbname); + } + + if (docheck) + { + (void) printf(CHKMES, dbname); + chkindex(dbname); + } + + if (batchkey) + { + daddr_t pos; + + if (!initbrowse(dbname)) + exit(1); + + if ((pos = xlocate(batchkey)) >= 0) + cutentry(pos, stdout); + } + + if (dofilter) + { + daddr_t pos; + char batchkey[81]; + + if (!initbrowse(dbname)) + exit(1); + + while (fgets(batchkey, sizeof(batchkey) - 1, stdin) != (char *)NULL) + { + char *ep = &batchkey[strlen(batchkey) - 1]; + + if (*ep == '\n') + { + *ep = '\0'; + if (*--ep == '\r') + *ep = '\0'; + } + if ((pos = xlocate(batchkey)) >= 0) + cutentry(pos, stdout); + } + } + + if (atrandom) + { + daddr_t pos; + + if (!initbrowse(dbname)) + exit(1); + + if ((pos = jrandom()) >= 0) + cutentry(pos, stdout); + } + + exit(0); +} + +/* browse.c ends here */ diff --git a/screen.c b/screen.c new file mode 100644 index 0000000..5b90ba1 --- /dev/null +++ b/screen.c @@ -0,0 +1,1194 @@ +/***************************************************************************** + +NAME + screen.c -- augmented pseudo-curses(3) screen support + +SYNOPSIS + int LINES, COLS; -- terminal depth & width globals + + int initscr() -- init screen, set LINES and COLS + int endwin() -- de-initialize screen code + int clear() -- clear screen + int refresh() -- refresh real screen from virtual screen + int ungetch(c) -- put c on queue to be grabbed by next getch() + int inch() -- get char from current update location. + int move(y, x) -- move update cursor to row y colum x + int addch(c) -- add char c at update location + int addstr(s) -- write string from update location + int attrset(a) -- set attribute (A_NORMAL, A_REVERSE, A_BOLD) + bool has_color() -- do we have color? + int napms() -- nap for some number of milliseconds + void beep(); -- beep the terminal bell + + int egetch() -- get mouse event or character, no echo + void readscr(lx,ly,rx,ry, s) -- get content of screen line + + bool has_mouse() -- is there a mouse? + void mouse_init() -- initialize mouse + int mouse_status(py,px) -- get mouse location and button state + void mouse_show() -- show mouse cursor + void mouse_hide() -- hide mouse cursor + +DESCRIPTION + The idea here is to present a system-independent interface that looks like +a subset of curses(3), with a minimum of hooks for mouse handling and one +concession to Borland C for optimizations's sake. + + The addch() and addstr() functions move the update cursor to the right after +writing. The argument of addch() may have an attribute logical-or'ed in. +The inch() call returns a char with attribute or'ed in; A_CHARTEXT masks out +the character only. The following special keycap defines are used: + + KEY_HELP -- not on PC keyboard + KEY_REFRESH -- not on PC keyboard + KEY_NPAGE -- PgDn + KEY_PPAGE -- PgUp + KEY_UP -- Up Arrow + KEY_DOWN -- Down Arrow + KEY_REFERENCE -- not on PC keyboard + KEY_ENTER -- Enter + KEY_RIGHT -- Right Arrow + KEY_LEFT -- Left Arrow + KEY_FIND -- not on PC keyboard + KEY_SFIND -- not on PC keyboard + KEY_SELECT -- not on PC keyboard + KEY_HOME -- Home + KEY_BEG -- not on PC keyboard + KEY_END -- End + KEY_UNDO -- not on PC keyboard + KEY_PRINT -- not on PC keyboard + KEY_MOUSE -- mouse event happened (not supported by curses(3)) + +Mouse state defines returned by mouse_status(): + + LEFT_DOWN -- left mouse button down + LEFT_UP -- left mouse button release + RIGHT_DOWN -- right mouse button down + RIGHT_UP -- right mouse button up + +Special mouse coordinate values: + + CMDLINE -- command line of application window + THUMBCOL -- thumb column of application window + +BUGS + The MS-DOS implementation doesn't do optimization for the +scrolling case. + +AUTHORS + Package interface design and UNIX curses(3) code by Eric S. Raymond. + MS-DOS code by Raymond D. Gardner. + Please see the READ.ME in this directory for license terms. + +******************************************************************************/ +/* LINTLIBRARY */ +#include + +#include "screen.h" + +#ifdef CURSES +/******************************************************************* + * + * Support for curses(3) under UNIX. + * + ******************************************************************/ +#ifdef MOUSE +#include +#include + +/* + * 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 +#include + +#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 +#define millisecs() ((clock() * 10000)/(int)(double)(0.5+CLK_TCK*10)) + +int napms(n) +/* sleep for n milliseconds */ +int n; +{ + long t0 = millisecs() + n; + + while (millisecs() < t0) + continue; + return(millisecs()); +} + +void beep() +{ + (void) putchar('\007'); +} + +unsigned char acs_map[] = +{ + /* ACS_BSSB */ 218, + /* ACS_SSBB */ 192, + /* ACS_BBSS */ 191, + /* ACS_SBBS */ 217, + /* ACS_SBSS */ 180, + /* ACS_SSSB */ 195, + /* ACS_SSBS */ 193, + /* ACS_BSSS */ 194, + /* ACS_BSBS */ 196, + /* ACS_SBSB */ 179, + /* ACS_SSSS */ 197, + /* ACS_S1 */ 223, /* scan line 1 (actually top-half on) */ + /* ACS_S9 */ 22, /* scan line 9 */ + /* ACS_DIAMOND */ 254, /* diamond (actually bullet) */ + /* ACS_CKBOARD */ 178, /* checker board (stipple) */ + /* ACS_DEGREE */ 248, /* degree symbol */ + /* ACS_PLMINUS */ 241, /* plus/minus */ + /* ACS_BULLET */ 254, /* bullet */ + /* ACS_LARROW */ 27, /* arrow pointing left */ + /* ACS_RARROW */ 26, /* arrow pointing right */ + /* ACS_DARROW */ 25, /* arrow pointing down */ + /* ACS_UARROW */ 24, /* arrow pointing up */ + /* ACS_BOARD */ 220, /* board of squares */ + /* ACS_LANTERN */ 15, /* lantern symbol */ + /* ACS_BLOCK */ 219, /* solid square block */ +}; +#endif /* MSDOS */ + +#if !defined(CURSES) || defined(BSD) || defined(AMIGA) +/* character push queue */ +static chtype getchq[GQDEPTH]; +static int gqc = 0; + +int egetch() +/* get single character or mouse event */ +{ + /* queued characters waiting? then go */ + if (gqc > 0) + return(getchq[--gqc]); + else + return(cgetch()); +} + +int ungetch(c) +/* try to queue a character for input */ +int c; +{ + if (gqc >= GQDEPTH) + return(ERR); + else + { + getchq[gqc++] = c; + return(OK); + } +} +#else +int egetch() +/* get single character or mouse event */ +{ + return(cgetch()); +} +#endif /* !defined(CURSES) || defined(BSD) */ + +/* screen.c ends here */ diff --git a/screen.h b/screen.h new file mode 100644 index 0000000..3377e5f --- /dev/null +++ b/screen.h @@ -0,0 +1,223 @@ +/* screen.h -- interface to screen and keyboard handling */ + +#define MAXROWS 66 /* most lines per screen we'll try to deal with */ +#define MAXCOLS 132 /* most characters per line we'll essay */ + +#ifdef ATTMOUSE +#define MOUSE /* generic mouse handing */ +#endif /* ATTMOUSE */ + +#ifdef linux +#undef BSD +#endif /* linux */ + +#ifdef BSD +#define OLDCURSES +#endif /* BSD */ + +#ifdef CURSES +#ifdef linux +#include +#else +#include +#endif /* linux */ +#else +typedef char bool; +#define FALSE 0 +#define TRUE 1 +#define OK 0 +#define ERR -1 + +/* + * This holds a character and attributes. Below, we have to do + * some grotty things with macros and casts in case sizeof(long) + * != sizeof(int). + */ +typedef unsigned long chtype; /* the idea is to force 32 bits */ + +extern int LINES, COLS; /* terminal depth & width globals */ +extern int COLORS, COLOR_PAIRS; /* max color range globals */ + +extern int initscr(); /* init screen, set LINES and COLS */ +extern int endwin(); /* de-initialize screen code */ +extern int init_pair(); /* initialize color pair */ +extern int clear(); /* clear screen */ +extern int refresh(); /* refresh real screen from virtual screen */ +extern int ungetch(); /* put char on queue to be grabbed by next getch() */ +extern int inch(); /* get char from screen's current update location */ +extern int getyx(); /* get current update cursor position */ +extern int move(); /* move update cursor to row y colum x */ +extern int _addch(); /* add char/attribute at update location */ +#define addch(c) _addch((chtype)(c)) +extern int addstr(); /* write string from update location */ +extern int attrset(); /* set attribute (A_NORMAL, A_REVERSE, A_BOLD) */ +extern bool has_colors(); /* do we have color? */ +extern int start_color(); /* enable color support */ +extern int color_pair(); /* set up a color pair */ +extern int napms(); /* nap for some number of milliseconds */ +extern void beep(); /* ring the terminal bell */ + +/* the (void)s are to suppress "Code has no effect" warnings */ +#define mvaddch(y,x,ch) (void)(move(y,x) == ERR ? ERR : addch(ch)) +#define mvaddstr(y,x,str) (void)(move(y,x) == ERR ? ERR : addstr(str)) +#endif /* CURSES */ + +#ifndef A_COLOR +/* color defines */ +#define A_COLOR 0xff0000 +#define COLOR_PAIR(n) ((chtype)(n) << 16) +#define PAIR_NUMBER(n) (((chtype)(n) & A_COLOR) >> 16) +#define COLOR_BLACK 0 +#define COLOR_BLUE 1 +#define COLOR_GREEN 2 +#define COLOR_CYAN 3 +#define COLOR_RED 4 +#define COLOR_MAGENTA 5 +#define COLOR_YELLOW 6 +#define COLOR_WHITE 7 +#endif /* A_COLOR */ + +#ifndef KEY_EXIT +/* special keystroke defines --- picked to coincide with curses(3) values */ +#define KEY_EXIT 0551 /* not on PC keyboard */ +#define KEY_HELP 0553 /* not on PC keyboard */ +#define KEY_REFRESH 0565 /* not on PC keyboard */ +#define KEY_NPAGE 0522 /* PgDn */ +#define KEY_PPAGE 0523 /* PgUp */ +#define KEY_UP 0403 /* Up Arrow */ +#define KEY_DOWN 0402 /* Down Arrow */ +#define KEY_REFERENCE 0564 /* not on PC keyboard */ +#define KEY_ENTER 0527 /* Enter */ +#define KEY_RIGHT 0405 /* Right Arrow */ +#define KEY_LEFT 0404 /* Left Arrow */ +#define KEY_FIND 0552 /* not on PC keyboard */ +#define KEY_SFIND 0605 /* not on PC keyboard */ +#define KEY_SELECT 0601 /* not on PC keyboard */ +#define KEY_HOME 0406 /* Home */ +#define KEY_BEG 0542 /* not on PC keyboard */ +#define KEY_END 0550 /* End */ +#define KEY_UNDO 0630 /* not on PC keyboard */ +#define KEY_PRINT 0532 /* not on PC keyboard */ +#define KEY_FBASE 0410 /* function key base */ +#define KEY_F(n) (KEY_FBASE + (n)) +#endif /* KEY_EXIT */ + +#ifndef ACS_HLINE +/* alternate char set --- indices picked to coincide with curses(3) values */ + /* VT100 symbols */ +extern unsigned char acs_map[96]; +#define ACS_BSSB (acs_map[0]) +#define ACS_SSBB (acs_map[1]) +#define ACS_BBSS (acs_map[2]) +#define ACS_SBBS (acs_map[3]) +#define ACS_SBSS (acs_map[4]) +#define ACS_SSSB (acs_map[5]) +#define ACS_SSBS (acs_map[6]) +#define ACS_BSSS (acs_map[7]) +#define ACS_BSBS (acs_map[8]) +#define ACS_SBSB (acs_map[9]) +#define ACS_SSSS (acs_map[10]) +#define ACS_ULCORNER ACS_BSSB +#define ACS_LLCORNER ACS_SSBB +#define ACS_URCORNER ACS_BBSS +#define ACS_LRCORNER ACS_SBBS +#define ACS_RTEE ACS_SBSS +#define ACS_LTEE ACS_SSSB +#define ACS_BTEE ACS_SSBS +#define ACS_TTEE ACS_BSSS +#define ACS_HLINE ACS_BSBS +#define ACS_VLINE ACS_SBSB +#define ACS_PLUS ACS_SSSS +#define ACS_S1 (acs_map[11]) /* scan line 1 */ +#define ACS_S9 (acs_map[12]) /* scan line 9 */ +#define ACS_DIAMOND (acs_map[13]) /* diamond */ +#define ACS_CKBOARD (acs_map[14]) /* checker board (stipple) */ +#define ACS_DEGREE (acs_map[15]) /* degree symbol */ +#define ACS_PLMINUS (acs_map[16]) /* plus/minus */ +#define ACS_BULLET (acs_map[17]) /* bullet */ + /* Teletype 5410v1 symbols */ +#define ACS_LARROW (acs_map[18]) /* arrow pointing left */ +#define ACS_RARROW (acs_map[19]) /* arrow pointing right */ +#define ACS_DARROW (acs_map[20]) /* arrow pointing down */ +#define ACS_UARROW (acs_map[21]) /* arrow pointing up */ +#define ACS_BOARD (acs_map[22]) /* board of squares */ +#define ACS_LANTERN (acs_map[23]) /* lantern symbol */ +#define ACS_BLOCK (acs_map[24]) /* solid square block */ +#endif + +#ifndef A_CHARTEXT +/* highlight defines */ +#define A_CHARTEXT 0x00007f +#define A_ATTRIBUTES 0xffff00 +#define A_NORMAL 0x000000 +#define A_STANDOUT 0x000100 +#define A_UNDERLINE 0x000200 +#define A_REVERSE 0x000400 +#define A_BLINK 0x000800 +#define A_BOLD 0x001000 +#define A_ALTCHARSET 0x002000 +#endif /* A_CHARTEXT */ + +#ifdef OLDCURSES +typedef int chtype; +#define resetterm() resetty() +#define saveterm() savetty() +#define has_colors() FALSE +#define GQDEPTH 10 +extern int egetch(), ungetch(), attrset(); +#endif /* OLDCURSES */ + +#ifdef linux +#define resetterm() resetty() +#define saveterm() savetty() +#define has_colors() FALSE +#endif + +/* + * Mouse event handing. The KEY_MOUSE define has to be distinct from + * any keystroke we might want to process. + * + * SVr4 defines KEY_MOUSE; SVr3 does not. If we don't get it from the + * curses.h file, define it here. + */ +#ifndef KEY_MOUSE +#define KEY_MOUSE 0631 + +#define A_BUTTON_CHANGED 0x0f /* any change in button status? */ +#define BUTTON1_PRESSED 0x01 /* left mouse button down */ +#define BUTTON1_RELEASED 0x02 /* left mouse button release */ +#define BUTTON3_PRESSED 0x04 /* right mouse button down */ +#define BUTTON3_RELEASED 0x08 /* right mouse button up */ +#endif /* KEY_MOUSE */ + +/* we'll define these */ +extern int egetch(); /* get mouse event or character, no echo */ +extern void readscr(); /* get content of given region */ +extern void mouse_init(); /* initialize mouse */ +extern void mouse_move(); /* move mouse hot spot */ +extern int mouse_status(); /* get mouse location and button state */ +extern void mouse_show(); /* show mouse cursor */ +extern void mouse_hide(); /* hide mouse cursor */ +extern void mouse_color(); /* show mouse cursor in color? */ + +/* special coordinate values returned by mouse_status() */ +#define CMDLINE -1 /* command line of application window */ +#define THUMBCOL -1 /* thumb column of application window */ + +#ifdef AMIGA +#define F_OK 0 +typedef short chtype; +#define GQDEPTH 10 +#endif /* AMIGA */ + +/* handy names for special characters */ +#define CTL(c) ((c) & 0x1f) +#define TAB 0x09 /* tab */ +#define BS 0x08 /* backspace */ +#define CR 0x0d /* carriage return */ +#define LF 0x0a /* line feed */ +#define ESC 0x1b /* escape */ +#define SP 0x20 /* space */ +#define DEL 0x7f /* delete */ + +/* screen.h ends here */ diff --git a/vh.1 b/vh.1 new file mode 100644 index 0000000..d2eb278 --- /dev/null +++ b/vh.1 @@ -0,0 +1,176 @@ +.TH VH 1 "10 May 1993" "UNIX" +.IX "vh" "" "vh \(em -- volks-hypertext browser for the Jargon File" +.ds ud /usr/lib/vh +.ds fv "2.9.6 +.SH NAME +vh \- volks-hypertext browser for the Jargon File +.SH SYNOPSIS +\fBvh\fR [-cfgimrs] [-b key] [document] +.SH DESCRIPTION +This program is a handy hypertext browser, originally written for the +Jargon File but convenient for use with text files marked up in the +simple format dcumented below. It allows you to page forward and back +through a document, and supports reference chasing so that you can +easily follow chains of "See" and "See also" pointers through the +text. + +Normal usage is simply to type the name of your document to the shell. +Thus, `jargon' browses the Jargon File. Interactive help explaining +the keyboard commands is available from the screen. If the program +sees a mouse, a fairly complete `point-and-shoot' interface is also +available. Color will be used if it is available, but may be +suppressed with the -m option. If you are using this program under +DOS and your CGA develops snow when the video RAM is written directly, +use -s. + +It is useful to know that there are two modes; one browses the text +file, the other the entry index (if you chase a selection in the entry +index you are popped to text mode at the corresponding entry). +.SH MOUSE USAGE +The following section applies only to systems with attached and active +mice. If you are using a Mac or some other system with a deficient +mouse, the single mouse button is treated as a `left' button. + +You will notice a scrollbar on the right edge of the screen. One of +the characters of the scrollbar is different from the rest. This is +called the `thumb' or `scroll box'. As you move through the file, this +gives you an approximate indication of where you are in the file. If +the thumb is one-third of the way down the screen, you are about +one-third of the way through the file. It works in both the text and +index files. + +If you have a mouse, you can click the left button directly on a +reference on the screen and jump to it. You can also scroll or +page forward or backward by clicking on the scrollbar. There are +up and down arrows above and below the scroll bar for scrolling +line by line. Clicking on the scrollbar below the thumb will +page forward, and clicking above it will page back. You can also +"drag" the thumb by clicking on it and holding, then moving up or +down before releasing. This allows you to jump quickly through +the file. Holding the mouse down on the arrows or above or below +the thumb will cause the scrolling or paging action to repeat +until you release the button. This is one of only two places where +holding the button has any special effect (the other is described below). + +Clicking the right button will cause the program to backtrack the +same way as the Esc or Backspace keys. + +You may also click the left button on the function key prompts on +the bottom line to get the same effect as pressing the function +keys themselves. The ^Q (Help) entry is special; if you click it +with the mouse, it will come up, but disappear when you let go of the +mouse button. This is a feature, not a bug; if you want help to hang +out till you tell it to go away, type control-Q. + +In the text file only, if you click on the main screen (i.e. not +on the scrollbar or the prompt line) outside of a reference in +curly braces, the program will page forward one screen. + +The -i option enables an incremental-lookup feature (but note that +this feature may not be available in all ports). In this mode, lookup +and string search both behave like an Emacs incremental search. That +is, after you type each letter of a key, your viewpoint moves forward +to the next key (if any) containing the substring you've already typed. If +there is no such key, the browser beeps and drops out of +incremental-lookup mode. String search works similarly, behaving +almost exactly like an Emacs ^S. As a reminder that you're in these +incremental modes, highlights do not light up on the screens you pass +through; when you want to leave the mode, press enter and the current +page's highlights will light up. + +The -b option runs vh in batch mode to retrieve the entry +corresponding to a particular given key. The entry, if any, is +written to standard output. + +The -f (filter) option accepts a list of entry headwords on stdin and +sends those entries to stdout. + +Finally, the -r option picks an entry at random and sends it to +standard output. This may be useful for `fortune cookie' applications. +.SH INDEX GENERATION +Two data files, the default text and index for the Jargon File, should live +in \*(ud. To generate an index from a new version, cd to this library +directory. Copy or move the textfile to \*(ud/jargon.txt and run the +command +.B jargon -g. + +For experimental purposes, it is possible to override the default +document name with a command-line argument. Thus, assuming a file +.B example.tex +is in proper format, you may generate an index for +.I example.txt +with +.B vh -g example +and browse it with +.B vh example +.P +When you have the `example' database ready for public use, simply link +or copy vh to the name `example'. If no arguments are given, this +program tries to browse a database with the name it was invoked under. + +The -c mode does some format and consistency checks on a text/index +pair. Use this if it looks as though your lookups are landing in the +wrong places; it will let you know if your index is out of date. +.SH TEXT FILE FORMAT +`Proper format' means: + +1. References are surrounded by {} or {{}}. + +If the first character of the text file is not a `=', the code also assumes: + +2. Reference targets begin with a `:' at the left margin and are bounded +on the right by another `:' (an optional second `:' may follow that). + +This second rule is useful for filtering out false hits from the index. +.SH FILES +A copy of vh called `foo' would require the following: +.TP 3.5i +.I \*(ud/foo.txt +Text of your document. +.TP 3.5i +.I \*(ud/foo.idx +Index of your document. +.TP 3.5i +.I ./foo.cut +Where entries selected by the ^Y (Print) command are appended to. +.P +The Jargon File can be obtained by FTP from various archive sites. +A copy lives more or less permanently as +.I pub/jargon298.ascii +at +.I prep.ai.mit.edu +(18.71.0.38). +Future updates will be made available there. +.SH ENVIRONMENT VARIABLES +.TP 3.5i +.I VHPATH +Search path for vh documents. Defaults to ".:/usr/lib/vh" +.SH BUGS +If a selection wraps around the right margin, only a mouse click on +the part before the line wrap will chase it; clicks on the portion +trailing into the next line simply cause a page forward. This glitch +was judged more trouble to fix than it's worth. + +Use of the ESC key inhibits refresh under some curses(3) versions; +thus, you might have to do a ^L to force screen update afterwards. +Crocking around this in the obvious way would introduce screen flicker for +everybody. Use BS. + +Because UNIX's pseudorandom-number generator isn't very good, the +fortune-cookie mode under UNIX isn't either. + +Output from a -f list may include alphabetical section headers which +are not, strictly speaking, part of the desired text. +.SH AUTHORS +Concept, DOS support, and database code from the MS-DOS `jargon' +program by Raymond D. Gardner ; new features, +docs, and curses(3)-using UNIX code by Eric S. Raymond +. +.SH SEE ALSO +.I The New Hacker's Dictionary, +by Eric S. Raymond (ed.), MIT Press 1991, ISBN 0-262-68069-6. The +Jargon File (version 2.9.6), nicely typeset and bound with additional +front matter. + +Please email your Jargon File entries and corrections to +jargon@snark.thyrsus.com. diff --git a/vh.c b/vh.c new file mode 100644 index 0000000..8daea7a --- /dev/null +++ b/vh.c @@ -0,0 +1,1498 @@ +/****************************************************************************** + +NAME + vh.c --- retrieval primitives for vh-format text + +SYNOPSIS + vh [-icm] [textfile] [indexfile] + +DESCRIPTION + Contains display-independent primitives for implementing a +simple hypertext browser. + +AUTHORS + Adapted by Eric S. Raymond 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 +#include +#include +#include +#include + +typedef int bool; +#define FALSE 0 +#define TRUE 1 + +#include "vh.h" + +#ifndef F_OK +#define F_OK 0 +#endif /* F_OK */ + +#define VHPATHSIZ 128 + +#define TAB 0x09 /* tab */ +#define BS 0x08 /* backspace */ +#define CR 0x0D /* carriage return */ +#define LF 0x0A /* line feed */ +#define SP 0x20 /* space */ +#define DEL 0x7f /* delete */ + +#define max(a,b) ((a) > (b) ? (a) : (b)) + +#ifdef ATT +#define srand(n) srand48(n) +#define rand() lrand48() +extern long lrand48(); +extern void srand48(); +/* + * Try this if ungetch() fails to resolve. + * + * #define ungetch ungetc + */ +#endif /* isxdigit */ + +extern char *malloc(); +extern long atol(); + +extern int LINES, COLS; /* initialize screen size into these */ + +#ifdef UNIX +#define CRLFSIZE 1 /* \n only */ +#endif /* UNIX */ + +#ifndef CRLFSIZE +#define CRLFSIZE 2 /* \r\n as in MS-DOS, etc. */ +#endif /* CRLFSIZE */ + +#define WARNSIZE 75 /* warn of lines longer than this */ +#define ENTRYMARK ':' +#define SEPARATOR (' ' - 2) +#define STRSZ 80 /* for various strings */ + +#ifndef VHPATH +#ifdef MSDOS +#define VHPATH ".;/usr/lib/vh" +#else +#ifndef AMIGA +#define VHPATH ".:/usr/lib/vh" +#else +#define VHPATH "s:;jargon:" +#endif +#endif /* MSDOS */ +#endif /* VHPATH */ +#define TXT ".txt" +#define IDX ".idx" +#define TXTLEN 4 + +/* + * This was carefully tuned using prof(1) under UNIX SVr3. Unless you have + * profiling tools at least as good, best not mess with it! + */ +#define DBBUF 4096 /* optimal buffer size */ + +#ifdef MSDOS +#ifdef DBBUF +#undef DBBUF +#endif +#define DBBUF 512 /* better size for MSDOS/Borland C -- rdg */ +#endif + +#define IS_TAG(s, x) (s[x] == LTAG || (x == 0 && s[0] == ENTRYMARK)) + +/******************************************************************* + * + * All message texts declared here for internationalization purposes + * + ******************************************************************/ + +#define FORMTYPE "vh: format type is %d, " +#define OUTASYNC "vh: index is out of sync with text.\n" +#define BADMAGIC "vh: index file %s doesn't look like an index\n" +#define CANTFIND "vh: can't find database files for %s\n" +#define CANTOPEN "vh: can't open %s\n" +#define NOMEMORY "vh: out of memory.\n" +#define TOOMANY "vh: too many index items.\n" +#define DANGLING "\"%s%s\", line %ld: {%s} dangling reference\n" +#define OUTERROR "vh: error writing output -- disk full?\n" +#define LENGTHZERO "vh: line length 0 -- ignored.\n" +#define TOOLONG "vh: line too long\n" +#define CONTROLZ "vh: last line is Control-Z(s) -- ignored\n" +#define NOCRLF "vh: last line not terminated -- CR/LF added\n" +#define NOINPUT "vh: can't open input file %s\n" +#define NOOUTPUT "vh: can't open output file %s\n" +#define FILLING "filling index\n" +#define LONGLINE "\"%s%s\", line %ld: line too long (%d)\n" +#define REREADING "vh: rereading, " +#define SORTING "sorting, " +#define WRITING "writing, " +#define BADREFS "checking for bad references...\n" +#define DONE "done.\n" + +/******************************************************************* + * + * Global data + * + ******************************************************************/ + +static FILEINFO textblk; + +FILEINFO vhi, *vht = &textblk; + +static char *xp[ITEMSMAX]; /* index pointer table */ +static int nitems; /* xp item count */ + +#if defined(__TURBOC__) && defined(FFGETS) +/******************************************************************* + * + * ffgets() -- faster replacement for fgets() (very Borland-specific!) + * depends on Borland's FILE structure & usage + * similar method may be useful on other systems + * + * Note that implementing this doesn't require having any library + * source. All you need to know is in the getc() macro in stdio.h. + * + ******************************************************************/ +char *ffgets(s, n, f) +char *s; +int n; +FILE *f; +{ + char *s0; + int c; + + s0 = s; + --n; + while ( n ) + { + register int k; + register char *p; + + k = f->level; + if ( k > n ) + k = n; + if ( k == 0 ) + { + c = fgetc(f); + if ( c == EOF ) + { + *s = '\0'; + return NULL; + } + *s++ = c; + --n; + if ( c == '\n' ) + break; + } + else + { + p = f->curp; + while ( *p++ != '\n' && --k ) + ; + k = p - f->curp; + memmove(s, f->curp, k); + f->level -= k; + f->curp += k; + n -= k; + s += k; + if ( s[-1] == '\n' ) + break; + } + } + *s = '\0'; + return s0; +} +#if 00 +/* another approach ... */ +char *ffgets(s, n, f) +char *s; +int n; +FILE *f; +{ + char *s0, *p; + int k, c; + + s0 = s; + n--; + while ( n ) + { + k = f->level; + if ( k > n ) + k = n; + if ( k < 1 ) + { + c = fgetc(f); + if ( c == EOF ) + { + *s = '\0'; + return NULL; + } + *s++ = c; + --n; + if ( c == '\n' ) + break; + } + else + { + memmove(s, f->curp, k); + s[k] = '\0'; + p = strchr(s, '\n'); + if ( p ) + n = k = p - s + 1; + f->level -= k; + f->curp += k; + s += k; + n -= k; + } + } + *s = '\0'; + return s0; +} +#endif +#endif /* defined(__TURBOC__) && defined(FFGETS) */ + +#ifndef MSDOS +/******************************************************************* + * + * Emulations of Borland C library functions + * + ******************************************************************/ + +static bool strnicmp(s1, s2, n) +/* case-insensitive length-limited string compare */ +register char *s1, *s2; +register n; +{ +#ifdef BSD + char ls1; + char ls2; + + while (--n >= 0) { + ls1 = *s1; + ls2 = *s2++; + if (isupper(ls1)) + ls1 = tolower(*s1); + if (isupper(ls2)) + ls2 = tolower(ls2); + if (ls1 != ls2) + break; + if (*s1++ == '\0') + return(0); + } + return((n < 0) ? 0 : (ls1-ls2)); +#else + while (--n >= 0 && tolower(*s1) == tolower(*s2++)) + if (*s1++ == '\0') + return(0); + return((n < 0) ? 0 : (tolower(*s1) - tolower(*--s2))); +#endif +} + +static void strlwr(s) +/* force string to lower case */ +char *s; +{ + register char *cp; + + for (cp = s; *cp; cp++) +#ifdef BSD + if (isupper(*cp)) +#endif + *cp = tolower(*cp); +} + +#ifndef linux +static char *strstr(t, s) +/* find s in t */ +char *s, *t; +{ + char *cp; + + for (cp = t; *cp; cp++) + if (strncmp(cp, s, strlen(s)) == 0) + return(cp); + return((char *)NULL); +} +#endif /* !linux */ +#endif /* MSDOS */ + +/******************************************************************* + * + * All file-type dependent stuff + * + ******************************************************************/ + +static int format; /* markup type, defaults to ORIGINAL296 */ +#define ORIGINAL296 0 /* Jargon File 2.9.6 version */ +#define COLON297 1 /* Jargon File 2.9.7+ with colons */ + +#define HDOFF(s) (s[0] == ENTRYMARK) + +static void getformat(fp) +/* figure out what format we're looking at */ +FILE *fp; +{ + (void) fseek(fp, 0L, SEEK_SET); + if (fgetc(fp) != '=') + format = COLON297; +} + +char *headword(ln) +/* is this line an entry? */ +char *ln; +{ + char *p; + + if (ln[0] == ' ' || (format > ORIGINAL296 && (ln[0] != ENTRYMARK))) + return((char *)NULL); + + for (p = ln + HDOFF(ln); p = strchr(p, ENTRYMARK); ++p) + if (isspace(p[1]) || (p[1] == ENTRYMARK && isspace(p[2]))) + break; + + return(p); +} + +/******************************************************************* + * + * Sequential entry access + * + * This supports the following entry points: + * getnextln() --- get line beginning at given position + * getprevln() --- get *previous* line from given position + * + ******************************************************************/ + +static void detab(s) +/* expand tabs in s, in place; assumes tab stops every 8 chars */ +char *s; +{ + char *p; + int k, n, i; + + while ((p = strchr(s, TAB)) != NULL) + { + /* while any tabs */ + k = p - s; /* offset to tab */ + n = (k + 8) / 8 * 8; /* next tab stop */ + for (i = strlen(s + k + 1); i >= 0; i--) + s[n + i] = s[k + 1 + i]; + memset(&s[k], ' ', n - k); /* blank fill */ + } +} + +/* daddr_t getnextln(FILE *fp, daddr_t pos, char *ln) -- get next line of file +** takes position where a line starts, gets line into buffer ln +** removes trailing CR/LF and expands tabs +** returns position of next byte after line (i.e. start of next line) +** returns NOWHERE at EOF +*/ +daddr_t getnextln(fp, pos, ln) +FILE *fp; +daddr_t pos; +char *ln; +{ + int k; + char *p; + if (ftell(fp) != pos) /* slight optimization for Borland & Zortech*/ + (void) fseek(fp, pos, SEEK_SET); + if (fgets(ln, LNSZ, fp)) + { + k = strlen(ln); + ln[k - 1] = '\0'; +#if CRLFSIZE > 1 + if (ln[k - 2] == CR) + ln[k - 2] = '\0'; /* fixed bug here 11/28/91 rdg */ +#endif /* CRLFSIZE > 1 */ + pos += k; + } + else + pos = NOWHERE; + detab(ln); + if (p = strchr(ln, SEPARATOR)) + *p = '\0'; + return(pos); +} + +/* daddr_t getprevln(FILE *fp, daddr_t pos, char *ln) -- get prev line in file +** takes position where a line starts, gets _previous_ line into buffer ln +** removes trailing CR/LF and expands tabs +** returns position of line returned +** returns NOWHERE at BOF +*/ +daddr_t getprevln(fp, pos, ln) +FILE *fp; +daddr_t pos; +char *ln; +{ + int n; + char *p; + + assert(pos > 0); + n = LNSZ; + if (pos < LNSZ) + n = pos; + (void) fseek(fp, pos - n, SEEK_SET); + (void) fread(ln, 1, n, fp); + p = &ln[n-1]; + while (p != ln) + { + int i; + + --p; + if (*p == LF) + { + ++p; + n = &ln[n] - p; + for (i = 0; i < n; i++) + ln[i] = p[i]; +/* memmove(ln, p, n); */ + break; + } + } + assert(p > ln || (p == ln && pos == n)); + ln[n - 1] = '\0'; +#if CRLFSIZE > 1 + if (ln[n - 2] == CR) + ln[n - 2] = '\0'; +#endif /* CRLFSIZE > 1 */ + pos -= n; + detab(ln); + if (p = strchr(ln, SEPARATOR)) + *p = '\0'; + return(pos); +} + +/******************************************************************* + * + * Fortune-cookie mode + * + * This supports the following entry points: + * jrandom() -- return the offset of a random entry + * + ******************************************************************/ + +daddr_t jrandom() +/* grab a fortune cookie */ +{ + int r = rand() % nitems; + + return(atol(xp[r] + strlen(xp[r]) + 1)); +} + +/******************************************************************* + * + * Entry-access by name + * + * This supports the following entry points: + * xlocate() --- go to entry by name + * ilocate() --- go to entry by name (incremental) + * ffind() --- find string in file + * ifind() --- find string in file (incremental) + * + ******************************************************************/ + +static daddr_t ixlocate(s, isincrsearch) +/* binary-search index for x; return offset if found, else -offset of next */ +char *s; +bool isincrsearch; +{ + int lo, mid, hi, k, hit; + daddr_t pos; + + strlwr(s); +#define NOTFOUND (-1) + lo = 0; + hi = nitems - 1; + hit = NOTFOUND; /* your basic binary search */ + while (hit == NOTFOUND && lo <= hi) + { + mid = (lo + hi) / 2; + if ((k = strcmp(s, xp[mid])) < 0) + hi = mid - 1; + else if (k > 0) + lo = mid + 1; + else + hit = mid; + } + + /* + * if not found, and item wanted exceeds item found, + * and there's room to move up, go to next higher item + */ + if (hit==NOTFOUND && strcmp(s, xp[mid]) > 0 && mid < nitems-1) + { + ++mid; + assert(strcmp(s, xp[mid]) < 0); + } + /* the file position is stored right after the index string */ + pos = atol(xp[mid] + strlen(xp[mid]) + 1); + + if (isincrsearch) + { + if (strncmp(s, xp[mid], strlen(s))) + pos = -pos; + } + else + { + /* if no hit, return negative pos */ + if (hit == NOTFOUND) + pos = -pos; + } + return(pos); +} + +daddr_t xlocate(s) +char *s; +{ + return(ixlocate(s, FALSE)); +} + +daddr_t ilocate(c) +/* incremental-lookup through entry key list */ +char c; +{ + static char key[LNSZ], *ep = key; + static int oldhit; + + if (c == BS) + { + if (ep > key) + *--ep = '\0'; + } + else if (isprint(c)) /* incremental-search for given character */ + { + *ep++ = tolower(c); + *ep = '\0'; + return(ixlocate(key, TRUE)); + } + else if (c == DEL) /* reset from previous incremental lookup */ + { + key[0] = '\0'; + ep = key; + oldhit = 0; + } + return(NOWHERE); +} + +daddr_t ffind(fp, pos, ss) +/* case-blind search forward for ss; return offset if found, else NOWHERE */ +FILE *fp; +daddr_t pos; +char *ss; +{ + char s[LNSZ], ln[LNSZ]; + + (void) strcpy(s, ss); + strlwr(s); + (void) fseek(fp, pos, SEEK_SET); + while (fgets(ln, LNSZ, fp)) + { + strlwr(ln); + if (strstr(ln, s)) + return(pos); + pos = ftell(fp); /* save pos before reading next line */ + } + return(NOWHERE); +} + +daddr_t ifind(fp, pos, c) +/* incremental-search forward */ +FILE *fp; +daddr_t pos; +char c; +{ + static char key[LNSZ], *ep = key; + static int oldhit; + + if (c == BS) + { + if (ep > key) + *--ep = '\0'; + return(pos); + } + else if (isprint(c)) /* incremental-search for given character */ + { + *ep++ = tolower(c); + *ep = '\0'; + return(ffind(fp, pos, key)); + } + else if (c == DEL) /* reset from previous incremental search */ + { + key[0] = '\0'; + ep = key; + oldhit = 0; + return(pos); + } + return(NOWHERE); /* should never reach here */ +} + +/******************************************************************* + * + * Screen-fetch and line-retrieval code + * + * Entry points: + * iflink() --- test for presence of a reference + * findnextsel() --- find next link on current screen + * findprevsel() --- find previous link on current screen + * + * These functions expect to be able to call: + * readscr() --- get text from screen + * + ******************************************************************/ + +/* left tag and right tag of textual references */ +#define LTAG '{' +#define RTAG '}' + +/* daddr_t iflink() -- test for link at screen position, find right tag pos +*/ +daddr_t iflink(x, y, xrtag, yrtag, isindex) +int x,y; +int *xrtag, *yrtag; +bool isindex; +{ + char *cp, s[MAXWIDTH]; + char term[STRSZ]; + int i; + daddr_t pos; + + readscr(0, y, COLS-1, y, s); /* get line on screen */ + + if (isindex) /* if index, x is col 0 */ + { + (void) strcpy(term, s); + for (cp = term + strlen(term) - 1; *cp == ' '; cp--) + *cp = '\0'; + x = strlen(term) - 1; + } + else if ((cp = headword(s)) && x < cp - s) /* a headword? */ + { + i = *cp; + *cp = '\0'; + (void) strcpy(term, s + HDOFF(s)); + *cp = i; + x = (cp - s) - 1; + } + else /* not index or headword, find left tag */ + { + int x0 = x; + + while (x >= 0 && s[x] != LTAG) + --x; + if (x < 0) /* if no left tag, then no link */ + return(0L); + assert(IS_TAG(s, x)); + + /* find next non-LTAG char */ + while (x < COLS && s[x] == LTAG) + ++x; + if (x >= COLS) /* if none, no tag */ + return(0L); + + /* scan the link text up to RTAG, put into term[] */ + i = 0; + while (x < COLS-1 && s[x] != RTAG) + term[i++] = s[x++]; + --x; + if (x < x0) /* if we are left of start point, return 0 */ + return(0L); /* in case user clicks to right of link */ + else if (x >= COLS-2) /* in case link wraps across line */ + { + ++y; + if (y > LASTLINE) + return(0L); + readscr(0, y, COLS-2, y, s); + while (i > 0 && term[i - 1] == ' ') + --i; + term[i++] = ' '; + x = 0; + while (x < COLS-1 && s[x] == ' ') + ++x; + while (x < COLS-1 && s[x] != RTAG) + term[i++] = s[x++]; + --x; + if (x >= COLS) + return(0L); + } + + term[i] = '\0'; /* terminate the term */ + } + + /* look up in index table */ + pos = xlocate(term); + if (pos > 0) /* if found, set the right tag x/y coords */ + { + *yrtag = y; + *xrtag = x; + } + return(pos); /* return position of link target, or 0L if none */ +} + +/* region findnextsel() -- find next selection (i.e. link reference) +** given x/y coords of a link selection on the screen, find the next +** one onscreen searching left to right, then down, wrapping last line +** to top line, until back to original position if only one link onscreen; +** coordinates are placed in the result. +*/ +region findnextsel(x, y, isindex) +int x, y; +bool isindex; +{ + char s[MAXWIDTH]; + int x0, y0, j; + daddr_t pos; + region res; + + if (isindex) /* if index, just go to next row */ + { + ++y; + if (y > LASTLINE) /* wrap line to top if at end */ + y = 0; + res.yl = res.yr = y; + res.xl = 0; + res.xr = COLS - 2; + return(res); + } + + if (y < 0) /* if no selection currently, look for one */ + y = 0; + + x0 = x; + y0 = y; + readscr(0, y, COLS-1, y, s); /* get screen line */ + while (x < COLS && IS_TAG(s, x)) + ++x; + for (;;) + { + for (; x < COLS; ++x) + { + if (IS_TAG(s, x)) + { + while (x < COLS && s[x] == LTAG || (x == 0 && s[0] == ENTRYMARK)) + ++x; + /* look up link */ + if (iflink(x, y, &res.xr, &res.yr, isindex) > 0) + { + res.xl = x; /* if found, set x/y and return */ + res.yl = y; + return(res); + } + } + } + x = 0; /* past end of row; reset x pos */ + ++y; /* step to next line, wrap around if at end */ + if (y > LASTLINE) + y = 0; + if (y == y0 && x == x0) /* return if back at start point */ + { + res.xl = res.yl = NOPLACE; + return(res); + } + readscr(0, y, COLS-1, y, s); /* get screen line */ + } +} + +/* void findprevsel() -- find prev selection (i.e. link reference) +** given x/y coords of a link selection on the screen, find the previous +** one onscreen searching right to left, then up, wrapping top line +** to last line, until back to original position if only one link onscreen; +** coordinates are placed in the result. +*/ +region findprevsel(x, y, isindex) +int x, y; +bool isindex; +{ + char s[MAXWIDTH]; + int x0, y0, j; + daddr_t pos; + region res; + + if (isindex) /* if index, get prev row */ + { + --y; + if (y < 0) /* wrap line to last if at top */ + y = LASTLINE; + res.yl = res.yr = y; + res.xl = 0; + res.xr = COLS - 2; + return(res); + } + + x0 = x; + y0 = y; + readscr(0, y, COLS-1, y, s); /* get screen line */ + while (x >= 0 && IS_TAG(s, x)) + --x; + for (;;) + { + for (; x >= 0; --x) + { + if (IS_TAG(s, x)) + { + while (x < COLS && IS_TAG(s, x)) + ++x; + --x; /* back up to LTAG; look up link */ + if (iflink(x, y, &res.xr, &res.yr, isindex) > 0) + { + res.xl = ++x; /* if found, set x/y and return */ + res.yl = y; + return(res); + } + } + } + x = COLS - 1; /* past beginning of row; reset x pos */ + --y; /* step to prev line, wrap around if at top */ + if (y < 0) + y = LASTLINE; + if (y == y0 && x == x0) /* return if back at start point */ + { + res.xl = res.yl = NOPLACE; + return(res); + } + readscr(0, y, COLS-1, y, s); /* get prev line, continue */ + } +} + +/******************************************************************* + * + * Position stack management + * + * This supports the following entry points: + * enqueue() --- push a placemark + * dequeue() --- pop a placemark + * + ******************************************************************/ + +void enqueue(f) +/* add file position & link info to backtrack stack */ +FILEINFO *f; +{ + int k; + + k = f->btscnt; + + /* if full, move it up to make room */ + if (k == BTSMAX) + { + int n; + for ( n = (BTSMAX-1) * sizeof(f->bts[0]); n--; ) + { + memcpy(&f->bts[0], &f->bts[1], sizeof(f->bts[0])); + memcpy(&f->selbts[0], &f->selbts[1], sizeof(f->selbts[0])); + } + --k; + f->btscnt = k; /* 9/27/91 rdg fixed bts overflow bug */ + } + f->bts[k] = f->toppos; + f->selbts[k].xl = f->sel.xl; + f->selbts[k].yl = f->sel.yl; + f->selbts[k].xr = f->sel.xr; + f->selbts[k].yr = f->sel.yr; +#ifdef DEBUG + (void) fprintf(stderr, "enqueue[%d]: x = %d, y = %d, pos = %ld.\n", + k, f->sel.xl, f->sel.yl, f->toppos); +#endif /* DEBUG */ + + if (k == 0 || f->bts[k] != f->bts[k-1] || /* only enqueue if changed */ + memcmp(&f->selbts[k], &f->selbts[k-1], sizeof(f->selbts[k])) != 0) + ++f->btscnt; +#ifdef DEBUG + else + (void) fprintf(stderr, "enqueue[%d]: is duplicate, popped.\n", k); +#endif /* DEBUG */ +} + +void dequeue(f) +/* pull file position & link info from stack */ +FILEINFO *f; +{ + if (f->btscnt) /* don't attempt pop if empty */ + { + --f->btscnt; + f->toppos = f->bts[f->btscnt]; + f->sel.xl = f->selbts[f->btscnt].xl; + f->sel.yl = f->selbts[f->btscnt].yl; + f->sel.xr = f->selbts[f->btscnt].xr; + f->sel.yr = f->selbts[f->btscnt].yr; + +#ifdef DEBUG + (void) fprintf(stderr, "dequeue[%d]: x = %d, y = %d, pos = %ld.\n", + f->btscnt, f->sel.xl, f->sel.yl, f->toppos); +#endif /* DEBUG */ + } +#ifdef DEBUG + else + (void) fprintf(stderr, "dequeue: failed, no stack space.\n"); +#endif /* DEBUG */ +} + +/******************************************************************* + * + * Browse support + * + * Entry points: + * initbrowse() --- set up in-core structures for given file pair + * setlastpage() --- set lastpagetoppos members + * + ******************************************************************/ + +/* validate_synchronization(FILE *fp, char *ln) +** check file against index table, to try to be sure they match up +** will sample 10 index entries, evenly spaced, including first and last +** for each entry, looks at text file to see if text matches index +*/ +static void validate_synchronization(fp, ln) +FILE *fp; +char *ln; +{ +#define nsamples 10 + int i, k; + daddr_t pos; + + for (i = 0; i < nsamples; ++i) + { + k = (i * (nitems - 1)) / (nsamples - 1); /* sample index */ + pos = xlocate(xp[k]); /* look up pos */ + assert(pos > 0); + if (fseek(fp, pos, SEEK_SET) < 0) /* seek in file */ + break; + getnextln(fp, pos, ln); /* get text line */ + if (strnicmp(ln + HDOFF(ln), xp[k], strlen(xp[k])) != 0) /* match it */ + break; + } + /* exit if any mismatch */ + if (i < nsamples) + { + (void) fprintf(stderr, OUTASYNC); + exit(1); + } +} + +static bool idxsearch(name, path) +/* look for database along defined search path */ +char *name, *path; +{ + extern char *getenv(); + char *srch, *cp; + + if ((srch = getenv("VHPATH")) == (char *)NULL) + srch = VHPATH; + +#ifdef MSDOS +#define LISTSEP ";" +#define PATHSEP "\\" +#else +#ifndef AMIGA +#define LISTSEP ":" +#else +#define LISTSEP ";" +#endif /* AMIGA */ +#define PATHSEP "/" +#endif /* MSDOS */ + + cp = strtok(srch, LISTSEP); + do { + (void) strcpy(path, cp); +#ifdef AMIGA +if (path[strlen(path) - 1] != ':') { /* e.g. "DH0:" */ +#endif + if (path[strlen(path) - 1] != PATHSEP[0]) + (void) strcat(path, PATHSEP); +#ifdef AMIGA +} +#endif + (void) strcat(path, name); + (void) strcat(path, IDX); + if (access(path, F_OK) == 0) + return(TRUE); + } while + (cp = strtok((char *)NULL, LISTSEP)); + return(FALSE); +} + +bool initbrowse(name) +char *name; +{ + char ln[LNSZ + 1], path[PATHLEN], *cp; +#ifdef VHHDR + vhhdr hdr; +#endif /* VHHDR */ + int k, fno; + + /* look for the database files */ + if (!idxsearch(name, path)) + { + (void) fprintf(stderr, CANTFIND, name); + return(FALSE); + } + + if ((vhi.fp = fopen(path, "rb"))==(FILE*)NULL) /* open index file */ + { + (void) fprintf(stderr, CANTOPEN, path); + return(FALSE); + } +#ifndef BSD + (void) setvbuf(vhi.fp, (char *)NULL, _IOFBF, DBBUF); +#endif /* BSD */ + + vhi.btscnt = 0; + vhi.sel.xl = NOPLACE; + +#ifdef VHHDR + (void) fread(&hdr, sizeof(vhhdr), 1, vhi.fp); + if (hdr.magic != VHMAGIC) + { + (void) fprintf(stderr, BADMAGIC, path); + return(FALSE); + } + + /* read in text file names */ + for (fno = 0 ; fno < hdr.nfiles; fno++) + (void) fread(ln, 1, VHPATHSIZ, vhi.fp); +#endif /* VHHDR */ + + /* fill index table */ + for (nitems = 0; nitems < ITEMSMAX; ++nitems) + { + if (fgets(ln, LNSZ, vhi.fp) == NULL) + break; + k = strlen(ln) - 1; + ln[k] = '\0'; +#if CRLFSIZE > 1 + if (ln[k-1] == '\r') + ln[--k] = '\0'; +#endif /* CRLFSIZE > 1 */ + if ((xp[nitems] = malloc(k + 1)) == NULL) + { + (void) fprintf(stderr, NOMEMORY); + return(TRUE); + } + strcpy(xp[nitems], ln); + assert(strchr(ln, SEPARATOR)); /* better have separator */ + *strchr(xp[nitems], SEPARATOR) = '\0'; /* replace it w/ null byte */ + strlwr(xp[nitems]); /* force items in table to lowercase */ + } + + /* did we overflow? */ + if (nitems == ITEMSMAX) + { + (void) fprintf(stderr, TOOMANY); + return(FALSE); + } + + vhi.toppos = 0; + + /* this will go inside a loop */ + + vht->btscnt = 0; + vht->sel.xl = NOPLACE; + + (void) strcpy(ln, path); + (void) strcpy(ln + strlen(ln) - TXTLEN, TXT); + if ((vht->fp = fopen(ln, "rb"))==(FILE*)NULL) /* open text file */ + { + (void) fprintf(stderr, CANTOPEN, path); + return(FALSE); + } +#ifndef BSD + (void) setvbuf(vht->fp, (char *)NULL, _IOFBF, DBBUF); +#endif /* BSD */ + + getformat(vht->fp); /* what file format are we looking at? */ + vht->toppos = 0; + + /* set random-number seed, in case we're after a fortune cookie */ + srand(time((time_t *)0)); + + return(TRUE); +} + +void setlastpage() +/* this needs to be called *after* initscr() */ +{ + char ln[LNSZ + 1]; + int i; + + /* set up last page top pos for index */ + (void) fseek(vhi.fp, 0L, SEEK_END); /* seek end of index file */ + vhi.dsptoppos = vhi.lastpagetoppos = vhi.dspnextpos = vhi.endpos =ftell(vhi.fp); + for (i = 0; i <= LASTLINE; ++i) + if (vhi.lastpagetoppos) + vhi.lastpagetoppos = getprevln(vhi.fp, vhi.lastpagetoppos, ln); + if (vhi.lastpagetoppos == 0L) /* avoid divide-by-zero error */ + vhi.lastpagetoppos = 1L; + (void) fseek(vhi.fp, 0L, SEEK_SET); /* seek start of index file */ + + (void) fseek(vht->fp, 0L, SEEK_END); /* seek eof */ + vht->dsptoppos = vht->lastpagetoppos = vht->dspnextpos = vht->endpos =ftell(vht->fp); + for (i = 0; i <= LASTLINE; ++i) + if (vht->lastpagetoppos) + vht->lastpagetoppos = getprevln(vht->fp, vht->lastpagetoppos, ln); + if (vht->lastpagetoppos == 0L) /* avoid divide-by-zero error */ + vht->lastpagetoppos = 1L; + (void) fseek(vht->fp, 0L, SEEK_SET); /* back to top of file */ +} + +/******************************************************************* + * + * Consistency checking + * + ******************************************************************/ + +void chkindex(name) +/* look for over-long lines, dangling references, self-references */ +char *name; +{ + long lnum = 1; + char refbuf[LNSZ + 1], *refpt = refbuf; + int c, len = 0, depth = 0; + + /* time for consistency check */ + initbrowse(name); + + (void) fseek(vht->fp, 0L, SEEK_SET); + while ((c = fgetc(vht->fp)) != EOF) + if (c == '\n') + { + if (len > WARNSIZE + CRLFSIZE) + (void) printf(LONGLINE, name, TXT, lnum, len); + len = 0; + + ++lnum; + if (depth != 0 && refpt[-1] != ' ') + *refpt++ = ' '; + } + else if (c == LTAG) + { + ++len; + ungetc(c = fgetc(vht->fp), vht->fp); + if (isprint(c)) + ++depth; + } + else if (depth > 0 && c == RTAG) + { + ++len; + --depth; + if (depth == 0 && refpt != refbuf) + { + *refpt = '\0'; + if (xlocate(refbuf) < 0) + (void) printf(DANGLING, name, TXT, lnum, refbuf); + refpt = refbuf; + } + } + else if (depth != 0) + { + ++len; + if (!isspace(c) || refpt[-1] != ' ') + *refpt++ = c; + } + + validate_synchronization(vht->fp, refbuf); /* validate */ +} + +/******************************************************************* + * + * File indexing code + * + * Entry points: + * mkindex(name) -- generate index file from text + * + ******************************************************************/ + +static char ln[LNSZ + 1]; + +typedef struct list_struct +{ + struct list_struct *next; + unsigned int len; + char ln[1]; +} +list; + +static list *listp = NULL; + +static char *mmalloc(n) +/* malloc(3) with error message and abort */ +unsigned n; +{ + char *p; + + if ((p = malloc(n)) == NULL) + { + (void) fprintf(stderr, NOMEMORY); + exit(1); + } + return(p); +} + +#define UNSCH(cp) ((int)(*((unsigned char *)(cp)))) + +static int strlcmp(a, b) +/* compare, smashing case */ +char *a, *b; +{ +#ifdef BSD + return(strnicmp(a,b,max(strlen(a),strlen(b)))); +#else + char *lim; + int v; + + for (; *a && *b; a++, b++) + { + v = tolower(UNSCH(a)) - tolower(UNSCH(b)); + if (v) + return v; + } + return(tolower(UNSCH(a)) - tolower(UNSCH(b))); +#endif +} + +static int keycmp(p, q) +/* compare keys for equality */ +list *p, *q; +{ + return(strlcmp(p->ln, q->ln)); +} + +static list *lmerge (p, q) +/* merge 2 lists under dummy head item */ +list *p, *q; +{ + list *r, head; + + for (r = &head; p && q;) + { + if (keycmp(p, q) < 0) + { + r = r->next = p; + p = p->next; + } + else + { + r = r->next = q; + q = q->next; + } + } + r->next = (p ? p : q); + return(head.next); +} + +static list *lsort (p) +/* split list into 2 parts, sort each recursively, merge */ +list *p; +{ + list *q, *r; + + if (p) + { + q = p; + for (r = q->next; r && (r = r->next) != NULL; r = r->next) + q = q->next; + r = q->next; + q->next = NULL; + if (r) + p = lmerge(lsort(p), lsort(r)); + } + return(p); +} + +static void readfile(inf) +/* read file into linked list of lines */ +FILE *inf; +{ + int k; + list *p; + + while (fgets(ln, LNSZ, inf)) + { + k = strlen(ln); + if (k == 0) + { + (void) fprintf(stderr, LENGTHZERO); + continue; + } + if (ln[k-1] == '\n') + { + k--; +#if CRLFSIZE > 1 + if (ln[k-1] == '\r') + k--; +#endif /* CRLFSIZE > 1 */ + ln[k] = '\0'; + } + else + { + if (k == LNSZ) + { + (void) fprintf(stderr, TOOLONG); + exit(1); + } + if (ln[0] == 26) + { + (void) fprintf(stderr, CONTROLZ); + continue; + } + else + { + (void) fprintf(stderr, NOCRLF); + continue; + } + } + + p = (list *)mmalloc(k + sizeof(list)); + p->len = k; + memcpy(p->ln, ln, k+1); + p->next = listp; + listp = p; + } +} + +static void writefile (outf) +/* write file from in-core list */ +FILE *outf; +{ + list *p; + + for (p = listp; p; p = p->next) + { + (void) fwrite(p->ln, 1, p->len, outf); +#if CRLFSIZE > 1 + (void) fwrite("\r", 1, 1, outf); +#endif /* CRLFSIZE > 1 */ + (void) fwrite("\n", 1, 1, outf); + } +} + +#ifdef AMIGA +void setthebufback(void) +{ + static char mybuf[BUFSIZ]; + setbuf(stdout, mybuf); +} +#endif /* AMIGA */ + +void mkindex(argc, argv) +/* make index from given files */ +int argc; +char *argv[]; +{ + char source[PATHLEN], target[PATHLEN]; + char ln[LNSZ], prevln[LNSZ]; + FILE *inf, *outf; + daddr_t pos; + char *infilen, *outfilen, *p, *s; + unsigned int buffersize; + int i, len, fno; +#ifdef VHHDR + vhhdr hdr; +#endif /* VHHDR */ + + (void) strcpy(target, argv[0]); + (void) strcat(target, IDX); + if ((outf = fopen(target, "wb")) == (FILE *)NULL) + { + (void) fprintf(stderr, NOOUTPUT, target); + exit(1); + } + + setbuf(stdout, (char *)NULL); +#ifdef AMIGA + atexit(setthebufback); +#endif /* AMIGA */ + + for (fno = 0 ; fno < argc; fno++) + { + (void) strcpy(source, argv[fno]); + (void) strcat(source, TXT); + if ((inf = fopen(source, "rb"))==(FILE *)NULL) + { + (void) fprintf(stderr, NOINPUT, source); + exit(1); + } + + getformat(inf); + + (void) printf(FORMTYPE, format); + + *prevln = 0; + + (void) printf(FILLING); + + pos = 0L; + while (fgets(ln, LNSZ, inf)) + { + if (p = headword(ln)) + { + *p = 0; + if (strlcmp(ln, prevln) != 0) + (void) fprintf(outf, "%s%c%ld\r\n", + ln + HDOFF(ln), SEPARATOR, pos); + (void) strcpy(prevln, ln); + } + pos = ftell(inf); + } + (void) fclose(inf); + } + + (void) fclose(outf); + + /* now sort the file */ + inf = fopen(target, "rb"); + if (inf == NULL) + { + fprintf(stderr, NOINPUT); + exit(1); + } + + (void) printf(REREADING); + +#ifndef AOS + setvbuf(inf, NULL, _IOFBF, LNSZ + 1); +#endif /* AOS */ + readfile(inf); + (void) fclose(inf); + + (void) printf(SORTING); + listp = lsort(listp); + + (void) printf(WRITING); + outf = fopen(target, "wb"); + if (outf == NULL) + { + (void) fprintf(stderr, NOOUTPUT); + exit(1); + } +#ifndef AOS + setvbuf(outf, NULL, _IOFBF, LNSZ + 1); +#endif /* AOS */ + +#ifdef VHHDR + hdr.magic = VHMAGIC; + hdr.nfiles = argc; + (void) fwrite(&hdr, sizeof(vhhdr), 1, outf); + + for (fno = 0 ; fno < argc; fno++) + (void) fwrite(argv[fno], 1, VHPATHSIZ, outf); +#endif /* VHHDR */ + + writefile(outf); + (void) fclose(outf); + + (void) printf(DONE); +} + +/* vh.c ends here */ diff --git a/vh.h b/vh.h new file mode 100644 index 0000000..23dee1c --- /dev/null +++ b/vh.h @@ -0,0 +1,97 @@ +/* vh.h --- interface to display-independent retrieval code */ + +#define PATHLEN 256 /* maximum filename size */ +#define BTSMAX 100 /* backtrack stack size */ +#define NOPLACE (-1) /* out-of-bounds for screen coordinate */ +#define NOWHERE (-1L) /* invalid file position */ +#define LNSZ 256 /* maximum text line width */ +#define MAXWIDTH 256 /* maximum screen width */ +#define ITEMSMAX 2400 /* max index items per document */ + +#ifndef UNIX +typedef long daddr_t; +#else +#if !defined(AOS) +#include +#endif /* !defined(AOS) */ +#include +#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 */