From b477d335ca988223e42103f1d32f69ac93ca5747 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Tue, 11 May 1993 21:44:47 +0000 Subject: [PATCH] *** empty log message *** --- COPYING | 339 ++++++++++++ README.AMIGA | 151 +++++ TurboMake | 51 ++ browse.c | 1452 ++++++++++++++++++++++++++++++++++++++++++++++++ screen.c | 1194 ++++++++++++++++++++++++++++++++++++++++ screen.h | 223 ++++++++ vh.1 | 176 ++++++ vh.c | 1498 ++++++++++++++++++++++++++++++++++++++++++++++++++ vh.h | 97 ++++ 9 files changed, 5181 insertions(+) create mode 100644 COPYING create mode 100644 README.AMIGA create mode 100644 TurboMake create mode 100644 browse.c create mode 100644 screen.c create mode 100644 screen.h create mode 100644 vh.1 create mode 100644 vh.c create mode 100644 vh.h 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 */ -- 2.43.0