lq-text Full Text Retrieval Database Part 07/13
Liam R. E. Quin
lee at sq.sq.com
Mon Mar 4 12:06:13 AEST 1991
: cut here --- cut here --
: To unbundle, sh this file
#! /bin/sh
: part 07
echo x - lq-text/src/lqtext/lqkwik.c 1>&2
sed 's/^X//' >lq-text/src/lqtext/lqkwik.c <<'@@@End of lq-text/src/lqtext/lqkwik.c'
X/* lqkwik.c -- Copyright 1991 Liam R. Quin. All Rights Reserved.
X * This code is NOT in the public domain.
X * See the file COPYRIGHT for full details.
X */
X
X/* lqkwik -- produce a keyword-in-context list of matches...
X * Liam R. Quin, February 1991 and later...
X *
X * $Id: lqkwik.c,v 1.1 91/03/02 20:37:47 lee Rel1-10 $
X */
X
X#define COLS 65 /* the width of kwik word index */
X#define WORDCOL 25 /* where to put the word in the index */
X#define GAPWIDTH 2 /* space before the word itself */
X#define SCREENWIDTH 79
X
Xint Cols = COLS;
Xint WordCol = WORDCOL;
Xint GapWidth = GAPWIDTH;
Xint ScreenWidth = SCREENWIDTH;
X
Xstatic int LinesAbove = 5;
Xstatic int linesBelow = 5;
X
X#include "globals.h" /* defines and declarations for database filenames */
X
X#include <malloc.h>
X#include <fcntl.h>
X#include <ctype.h>
X#include <sys/types.h> /* for fileinfo.h */
X#include <sys/stat.h>
X
X#include <stdio.h>
X
X#include "fileinfo.h"
X#include "wordinfo.h"
X#include "wordrules.h"
X#include "pblock.h"
X#include "emalloc.h"
X
X/** Unix system calls that need declaring: **/
Xextern long lseek();
Xextern int open(), close();
Xextern int read();
Xextern void exit();
Xextern int stat();
X
X/** Unix/C Library Functions that need declaring: **/
X#ifndef tolower
X extern int tolower();
X#endif
Xextern int strlen();
Xextern int strcmp();
Xextern unsigned sleep();
Xextern int atoi();
Xextern long atol();
Xextern void perror();
X
X/** lqtext library functions that need declaring: **/
Xextern int MySystem();
Xextern int TooCommon();
Xextern void SetDefault();
Xextern void DefaultUsage();
X
X/** Functions within this file that are used before being defined: **/
Xint ReadMatchFile();
Xint ShowFile();
Xvoid Output();
X
X/** **/
X
X/** some useful macros: **/
X#define max(choir,boy) (((choir)>(boy))?(choir):(boy))
X#define min(choir,boy) (((choir)<(boy))?(choir):(boy))
X
X/** **/
X
Xint AsciiTrace = 0;
X
Xextern int errno;
X
Xchar *progname = "lqkwik"; /* set from argv[] in main() */
X
Xint SelectedNames = -1;
XFILE *InfoStream = 0;
X
Xstatic char *Revision = "@(#) showfile.c 2.2";
X
Xint
Xmain(argc, argv)
X int argc;
X char *argv[];
X{
X extern int optind, getopt();
X extern char *optarg; /* for getopt */
X int ch; /* for getopt */
X int ErrFlag = 0; /* see how getopt makes programs cleaner? */
X int NumberOfFiles;
X char *FileWithMatches = (char *) 0;
X char **MatchList;
X int MatchCount = 0;
X int Origc;
X int Right = Cols - (WordCol + GapWidth);
X int Left = WordCol - GapWidth;
X
X progname = argv[0];
X
X SetDefaults(argc, argv);
X
X /* All lq-text programs must call SetDefaults() before getopt, and
X * must then be prepared to ignore options z with arg and Z without.
X */
X while ((ch = getopt(argc, argv, "a:b:f:l:r:g:w:o:z:ZVvx")) != EOF) {
X switch (ch) {
X case 'z':
X break; /* done by SetDefaults(); */
X case 'V':
X fprintf(stderr, "%s version %s\n", progname, Revision);
X break;
X case 'v':
X AsciiTrace = 1;
X break;
X case 'f':
X FileWithMatches = optarg;
X break;
X case 'g':
X GapWidth = atoi(optarg);
X break;
X case 'l':
X Left = atoi(optarg);
X break;
X case 'r':
X Right = atoi(optarg);
X break;
X case 'w':
X ScreenWidth = atoi(optarg);
X break;
X case 'x':
X ErrFlag = (-1);
X break;
X case '?':
X default:
X ErrFlag = 1;
X }
X }
X
X if (ErrFlag < 0) { /* -x or -xv was used */
X fprintf(stderr, "usage: %s [-xv] [options] [matches...]\n", progname);
X fprintf(stderr,
X "use %s -x, -xv or -xvv for more detailed explanations.\n", progname);
X
X if (AsciiTrace) {
X DefaultUsage();
X fprintf(stderr, "\n\
X -f file -- \"file\" contains a list of matches, one per line\n");
X fprintf(stderr, "\
X -g n -- set gap between text and matched phrase to n [%d]\n\
X -l n -- display n characters to the left of each phrase [%d]\n\
X -r n -- display r chars to the right of each phrase's start [%d]\n\
X -w n -- truncate the line after n characters [default: %d]\n",
X GapWidth,
X Left,
X Right,
X ScreenWidth
X );
X }
X if (AsciiTrace > 1) {
X fputs("\
X Matches should be in the form of\n\
X BlockNumber WordInBlock FileName\n\
X where BlockBumber and WordInBlock are positive numbers.\n\
X (This is the format produced by the lqword -l command.)\n\
X", stderr);
X }
X exit(0);
X } else if (ErrFlag > 0) {
X fprintf(stderr, "use %s -x for an explanation.\n", progname);
X exit(1);
X }
X
X Cols = Left + Right + GapWidth;
X WordCol = Left + GapWidth;
X
X if (AsciiTrace) {
X fprintf(stderr,"Left:%d Right:%d Cols:%d Gap:%d WC:%d SW:%d\n",
X Left, Right, Cols, GapWidth, WordCol,ScreenWidth);
X }
X
X if (ScreenWidth <= Cols + 2) {
X fprintf(stderr,
X "%s: ScreenWidth %d, %d text cols -- no room for file names!\n",
X progname, ScreenWidth, Cols);
X exit(1);
X }
X
X /* open the file for the selected output */
X if (SelectedNames > 0) {
X if ((InfoStream = fdopen(SelectedNames, "w")) == (FILE *) 0) {
X int e = errno;
X
X fprintf(stderr, "%s: -o %d: can't open stream ",
X progname, SelectedNames);
X errno = e;
X perror("for writing");
X exit(1);
X }
X }
X
X /* check that we can get at the file containing the matches, if one
X * was supplied.
X */
X if (FileWithMatches) {
X struct stat StatBuf;
X char *msg = 0;
X
X if (stat(FileWithMatches, &StatBuf) < 0) {
X int e = errno; /* on many systems, fprintf() changes errno! */
X fprintf(stderr, "%s: can't open match-list file ", FileWithMatches);
X errno = e;
X perror(progname);
X exit(1);
X } else if (AsciiTrace) {
X switch (StatBuf.st_mode & S_IFMT) {
X case S_IFDIR:
X fprintf(stderr,
X "%s: ca't read matches from \"%s\" -- it's a directory!\n",
X progname, FileWithMatches);
X exit(1);
X case S_IFREG:
X break;
X#ifdef S_IFIFO
X case S_IFIFO:
X msg = "named pipe or fifo";
X /* fall through */
X#endif
X case S_IFCHR:
X if (!msg) msg = "raw special device";
X /* fall through */
X case S_IFBLK:
X if (!msg) msg = "block special device";
X /* fall through */
X#ifdef S_IFNAM
X case S_IFNAM:
X if (!msg) msg = "named special file"; /* wot dat? */
X /* fall through */
X#endif
X default:
X if (!msg) msg = "special file";
X
X fprintf(stderr,
X "%s: warning: file \"%s\" containing matches is a %s\n",
X progname, FileWithMatches, msg);
X
X /* but continue anyway... */
X
X }
X }
X /* Now read the file, and make an array of matches... */
X if (ReadMatchFile(FileWithMatches, StatBuf.st_size, &MatchCount, &MatchList) < 0) {
X fprintf(stderr, "%s: couldn't read matches from \"%s\"\n",
X progname, FileWithMatches);
X exit(1);
X }
X }
X
X argv += optind;
X argc -= optind;
X
X if (MatchCount) {
X argc = MatchCount;
X argv = MatchList;
X }
X
X if (argc < 3) {
X fprintf(stderr,
X "%s: matches must have at least 3 parts; use -xv for an explanation\n",
X progname);
X exit(1);
X } else if (argc % 3) {
X /* Note: I could detect lqword output here (i.e., without -l) */
X fprintf(stderr, "%s: can't understand match format;\n", progname);
X fprintf(stderr, "%s: use -xv for more explanation.\n", progname);
X exit(1);
X }
X
X Origc = argc;
X
X NumberOfFiles = argc / 3;
X
X while (argc > 0) {
X int Where;
X
X if (ShowFile(argv[2], atol(*argv), (unsigned) atoi(argv[1])) < 0) {
X int i;
X
X /* This avoids repeated messages about the same file */
X for (i = argc - 3; i > 0; i -= 3) {
X if (STREQ(argv[2], argv[2 + 3])) {
X argv += 3;
X } else {
X break;
X }
X }
X argc = i + 3; /* so we can subtract 3 ... */
X
X }
X argv += 3;
X argc -= 3;
X }
X
X return 0;
X}
X
Xint
XReadMatchFile(FileWithMatches, FileSize, MatchCount, MatchList)
X char *FileWithMatches;
X off_t FileSize;
X int *MatchCount;
X char ** *MatchList;
X{
X extern char *strtok();
X
X int fd;
X char **Result;
X char *StorageArea;
X char *p;
X unsigned int n_matches;
X int BytesRead;
X int i;
X char *NextStr;
X
X if (!FileWithMatches || !*FileWithMatches) {
X fprintf(stderr, "%s: match-list file (from -f) has empty name!\n",
X progname);
X exit(1);
X }
X
X if ((fd = open(FileWithMatches, O_RDONLY)) == 0) {
X int e = errno;
X fprintf(stderr, "%s: can't open match-list file ", progname);
X errno = e;
X perror(FileWithMatches);
X exit(1);
X }
X
X /* We know the number of bytes, and each space or newline will get
X * turned into a null, so here goes...
X * The +1 below is to ensure that there is space for a \0, even if a
X * pesky user didn't put a \n at * the end of the file...
X * Sometimes I hate emacs...
X */
X if ((StorageArea = malloc((unsigned) FileSize + 1)) == (char *) 0) {
X fprintf(stderr, "%s: not enough memory to read match-list \"%s\"\n",
X progname, FileWithMatches);
X exit(1);
X }
X
X /* now read the list... */
X if ((BytesRead = read(fd, StorageArea, FileSize)) != FileSize) {
X if (BytesRead < 0) {
X int e = errno;
X
X fprintf(stderr, "%s: couldn't read %u bytes from ",
X progname, FileSize);
X errno = e;
X perror(FileWithMatches);
X exit(1);
X } else {
X int e = errno;
X
X fprintf(stderr, "%s: ", progname);
X if (BytesRead > 4) { /* minimum plausible for 3 items */
X fprintf(stderr, "warning: ");
X }
X fprintf(stderr, "only read %u bytes, not %u, from file ",
X progname, BytesRead, FileSize);
X errno = e;
X if (errno) perror(FileWithMatches);
X else fprintf(stderr, "\"%s\"\n", FileWithMatches);
X
X if (BytesRead <= 4) exit(1);
X
X StorageArea = realloc(StorageArea, (unsigned) (BytesRead + 1));
X if (StorageArea == (char *) 0) { /* unlikely, it got smaller */
X fprintf(stderr, "%s: can't realloc for \"%s\"\n",
X progname, FileWithMatches);
X exit(1);
X }
X FileSize = BytesRead;
X }
X }
X
X /* null-terminate it */
X StorageArea[FileSize] = '\0';
X
X /* got the data, now make an array... first, count the matches */
X for (n_matches = 1, p = StorageArea; p - StorageArea < FileSize; p++) {
X if isspace(*p) ++n_matches;
X }
X
X /* If there *was* trailing new-line, we overestimated by one.
X * This doesn't matter. If memory is that tight, initscr() will fail.
X * In any case, allow extra space for a trailing null entry.
X */
X ++n_matches;
X if (n_matches < 3) n_matches = 3;
X
X Result = (char **) malloc((unsigned) n_matches * sizeof(char *));
X if (Result == (char **) 0) {
X fprintf(stderr, "%s: out of memory reading match file \"%s\"\n",
X progname, FileWithMatches);
X exit(1);
X }
X
X /* Now step through the Storage Area filling in the pointers to the args */
X ;
X
X NextStr = (char *) 0;
X i = -1;
X for (p = StorageArea; p - StorageArea <= BytesRead; p++) {
X if (!NextStr) NextStr = p;
X if (isspace(*p) || p - StorageArea == BytesRead) {
X if (p - StorageArea != BytesRead) *p = '\0';
X while (isspace(*p)) { /* eat multiple blanks */
X p++;
X }
X if (++i >= n_matches) {
X n_matches += 20;
X if ((Result = (char **)
X realloc((char *) Result, n_matches * sizeof(char *))) ==
X (char **) 0) {
X fprintf(stderr,
X "%s: out of memory [%u] in match-file \"%s\"\n",
X progname, n_matches * sizeof(char *), FileWithMatches);
X /* TODO -- return with fewer matches -- NOTDONE */
X exit(1);
X }
X }
X *p = '\0'; /* OK at the very end cos of the extra byte! */
X Result[i] = NextStr;
X NextStr = (char *) 0;
X }
X }
X
X if (i + 2 < n_matches) {
X Result = (char **) realloc((char *)Result,
X (unsigned) (i+2) * sizeof(char **));
X if (Result == (char **) 0) {
X fprintf(stderr, "%s: no memory for match-list from \"%s\"\n",
X progname, FileWithMatches);
X exit(1);
X }
X }
X
X if (close(fd) < 0) {
X fprintf(stderr, "%s: warning: obscure problem closing %d (\"%s\")\n",
X progname, fd, FileWithMatches);
X sleep(5);
X }
X
X (*MatchList) = Result;
X return (*MatchCount = i);
X}
X
Xint
XShowFile(FileName, BlockInFile, WordInBlock)
X char *FileName;
X unsigned long BlockInFile;
X unsigned int WordInBlock;
X{
X static char *Buffer = 0;
X int fd;
X static unsigned int BufLen;
X int AmountRead;
X register char *p;
X register char *q;
X int InTargetWord = 0;
X char *StartOfMyWord;
X int ThisWord = 0;
X char *Start;
X char *ThisLine = emalloc(Cols + 1); /* +1 for trailing \0 */
X char *FirstBit = emalloc(WordCol - GapWidth + 1); /* +1 for trailing \0 */
X char *LastBit = emalloc(Cols - WordCol + 1); /* +1 for trailing \0 */
X char *FirstStart;
X
X if (Buffer == (char *) 0) {
X BufLen = Cols * 10;
X if (BufLen < FileBlockSize * 3) BufLen = FileBlockSize * 3;
X Buffer = emalloc(BufLen);
X }
X
X errno = 0;
X
X if ((fd = open(FileName, O_RDONLY, 0)) < 0) {
X int e = errno;
X char *doc;
X
X if ((doc = FindFile(FileName)) == (char *) 0) {
X fprintf(stderr, "%s: %s: ", progname, FileName);
X errno = e;
X perror(FileName);
X efree(ThisLine); efree(FirstBit); efree(LastBit);
X return -1;
X } else if ((fd = open(doc, O_RDONLY, 0)) < 0) {
X fprintf(stderr, "%s: %s: ", progname, FileName);
X errno = e;
X perror(doc);
X efree(ThisLine); efree(FirstBit); efree(LastBit);
X return -1;
X }
X FileName = doc;
X }
X
X errno = 0;
X if (lseek(fd, BlockInFile? (long) ((BlockInFile - 1) * FileBlockSize) : 0L,
X 0) < 0) {
X int e = errno;
X fprintf(stderr, "%s: %s: ", progname, FileName);
X errno = e;
X perror("lseek");
X efree(ThisLine); efree(FirstBit); efree(LastBit);
X return;
X }
X
X errno = 0;
X if ((AmountRead = read(fd, Buffer, BufLen)) < MinWordLength) {
X int e = errno;
X fprintf(stderr, "%s: %s: ", progname, FileName);
X errno = e;
X perror("read");
X efree(ThisLine); efree(FirstBit); efree(LastBit);
X return -1;
X }
X
X
X /** Find the required word */
X if (BlockInFile) {
X /* start 1 char before the end of the previous block */
X StartOfMyWord = &Buffer[FileBlockSize - 1];
X /* perhaps the last word of the previous block spans the block
X * boundary?
X */
X while (WithinWord(*StartOfMyWord)) StartOfMyWord++;
X if (StartOfMyWord < &Buffer[FileBlockSize]) {
X StartOfMyWord = &Buffer[FileBlockSize];
X }
X } else {
X StartOfMyWord = Buffer;
X }
X
X (void) close(fd);
X
X for (ThisWord = 0; ThisWord <= WordInBlock + 1; ThisWord++) {
Xbored:
X /* skip to the start of a word */
X while (!StartsWord(*StartOfMyWord)) {
X ++StartOfMyWord;
X }
X
X Start = StartOfMyWord;
X
X /* find the end of the word */
X while (WithinWord(*StartOfMyWord)) {
X if (*StartOfMyWord == '\'' && !EndsWord(StartOfMyWord[1])) break;
X StartOfMyWord++;
X }
X
X /* Assert: StartOfMyWord points 1 character beyond the end of the
X * word pointed to by Start
X */
X /* see if it's long enough */
X if (StartOfMyWord - Start < MinWordLength) {
X goto bored;
X }
X
X /** See if it's the right one */
X if (ThisWord == WordInBlock) {
X StartOfMyWord = Start;
X break;
X }
X }
X
X
X /* Find context before the keyword */
X
X q = &FirstBit[WordCol - GapWidth];
X *q-- = '\0';
X
X for (p = StartOfMyWord - 1; p >= Buffer; --p, --q) {
X *q = (isspace(*p)) ? ' ' : *p;
X if (q == FirstBit) break;
X }
X
X FirstStart = q;
X
X /* now build up the rest of the buffer */
X
X q = LastBit;
X *q = '\0';
X
X InTargetWord = 0;
X
X for (p = StartOfMyWord; p - Buffer < AmountRead; p++) {
X if (q >= &LastBit[Cols - WordCol]) break;
X
X switch (InTargetWord) {
X case 0:
X if (StartsWord(*p)) {
X InTargetWord = 1;
X }
X break;
X case 1:
X if (!WithinWord(*p)) {
X InTargetWord = 2;
X }
X }
X if (isspace(*p)) {
X *q = ' ';
X } else {
X *q = *p;
X }
X *++q = '\0';
X if (q >= &LastBit[Cols - WordCol]) break;
X }
X
X printf("%*.*s", WordCol - GapWidth, WordCol - GapWidth, FirstStart);
X
X /* do the gap */
X {
X
X register int i;
X
X for (i = GapWidth; i > 0; i--) {
X putchar(' ');
X }
X }
X
X printf("%-*.*s", Cols - WordCol, Cols - WordCol, LastBit);
X
X printf(":");
X {
X int OverShoot = Cols + 2 + strlen(FileName) - ScreenWidth; /* +2 is ": " */
X
X if (OverShoot > 0) {
X FileName += OverShoot + 2;
X printf("...");
X } else {
X putchar(' ');
X }
X
X }
X printf("%s\n", FileName);
X
X efree(ThisLine); efree(FirstBit); efree(LastBit);
X return 0;
X}
X
X
X/*
X * $Log: lqkwik.c,v $
X * Revision 1.1 91/03/02 20:37:47 lee
X * Initial revision
X *
X *
X */
@@@End of lq-text/src/lqtext/lqkwik.c
echo x - lq-text/src/lqtext/lqphrase.c 1>&2
sed 's/^X//' >lq-text/src/lqtext/lqphrase.c <<'@@@End of lq-text/src/lqtext/lqphrase.c'
X/* lqphrase.c -- Copyright 1989, 1990 Liam R. Quin. All Rights Reserved.
X * This code is NOT in the public domain.
X * See the file COPYRIGHT for full details.
X *
X * $Id: lqphrase.c,v 1.4 90/10/06 00:50:56 lee Rel1-10 $
X */
X
X/* lqphrase, part of Liam Quin's text retrieval package...
X *
X * lqphrase is intended to be an example of one way to use the programming
X * interface to lq-text.
X *
X * The idea is quite simple:
X * Simply take a phrase p, held in a string (char *p), and call
X * t_Phrase *Phrase = String2Phrase(p);
X * The result, if not null, contains only one interesting thing at this
X * point:
X * Phrase->ModifiedString
X * is the canonical version of p -- with common and short words removed.
X * for example,
X * p = "The boy sat down in His Boat and playd with his toes.";
X * might result in Phrase->ModifiedString containing
X * "[*the] boy sat down [in] [*his] boat [*and] [?playd] with [*his] toe"
X * Common words are marked with a *, and unknown words with ?.
X * An attempt may have been made to reduce plurals.
X * Since this phrase contains a word not in the database (playd), it will
X * never match anything. As a result, it is a good idea to print this string
X * (possibly massaging it first) so users can see what is going on. If you
X * have it, the curses-based "lqtext" does this.
X *
X * If we change "playd" to "played", the above string is equivalent to
X * "[*the] boy sat down [xx] [*the] boat [*the] played with [*the] toe"
X * In other words, all common words are equivalent. The package remembers
X * that one or more common words were skipped, and also that one or more
X * lumps of letters too small to make up a word were skipped.
X * The following are equivalent:
X * L.R.E. Quin L. Quin L.R.Quin X.X.Quin
X * in a QUIN a QuIn
X * and the following are not the same as those:
X * Quin (no preceding garbage)
X * L.R.E. quin (first letter of `Quin' is not upper case (the rest is ignored)
X * [*the] Quin (common words are not the same as skipped letters)
X * L. Quin's (the presence of the posessive ('s) is significant)
X * L. Quins (plural (two Quins) not the same as singular)
X * L. Quinn (spelt incorrectly!)
X *
X * Now, having sorted that out, we have our canonical string (and lots of
X * other things) in Phrase, so we can now call
X * MakeMatches(Phrase);
X * This will return the number of matches (*NOT* the number of files) for
X * the given ModifiedPhrase in the database.
X * This can take several seconds, so again, it can be worth printing out
X * the modified string as soon as it is available, so the user is looking at
X * that whilst MakeMatches is working! I have experimented with faster
X * versions of MakeMatches involving binary search, but the extra complexity
X * slowed things down on smaller databases. I don't have enough disk space
X * here to make a large enough database to do real timings, sorry.
X *
X * Now we have done MakeMatches, we can marck along the linked list of
X * pointers to linked lists of arrays of matches. Clear? No? Well,
X * that's why there's en axample. See Match() below.
X *
X * Now, each match currently gives us
X * t_FID FID; Files are numbered from 1 in the database
X * unsigned long BlockInFile; -- the block in the file
X * unsigned char WordInBlock; -- the word in the block
X * unsigned char StuffBefore; -- the amount of leading garbage
X * unsigned char Flags, including (see wordrules.h):
X *
X * WPF_WASPLURAL The word... ended in s
X * WPF_UPPERCASE ...Started with a capital letter
X * WPF_POSSESSIVE ...ended in 's
X * WPF_ENDEDINING ...ended in ing
X * WPF_LASTWASCOMMON the previous word was common
X * WPF_LASTHADLETTERS we skipped some letters to get here
X * WPF_LASTINBLOCK I'm the last word in this block
X *
X */
X
X#include "globals.h" /* defines and declarations for database filenames */
X
X#include <stdio.h> /* stderr, also for fileinfo.h */
X#include <fcntl.h>
X#include <sys/types.h>
X#include <malloc.h>
X#include "emalloc.h"
X#include "fileinfo.h" /* for wordinfo.h */
X#include "wordinfo.h"
X#include "pblock.h"
X#include "phrase.h"
X
X#ifndef STREQ
X# define STREQ(boy,girl) ((*(boy) == *(girl)) && (!strcmp((boy),(girl))))
X#endif
X
Xextern int AsciiTrace;
Xextern t_PhraseCaseMatch PhraseMatchLevel;
X
X/** System calls and functions... **/
X/** Unix system calls used in this file: **/
Xextern void exit();
X
X/** Unix Library Functions used: **/
X/** lqtext library functions: **/
Xextern void SetDefaults();
Xextern void DefaultUsage();
X
X/** functions used before they're defined within this file: **/
Xvoid Match();
X/** **/
X
Xstatic char *Revision = "@(#) $Id: lqphrase.c,v 1.4 90/10/06 00:50:56 lee Rel1-10 $";
X
Xchar *progname = "tryphrase";
X
Xint SilentMode = 0; /* don't print matches if set to one */
X
Xint
Xmain(argc, argv)
X int argc;
X char *argv[];
X{
X extern int optind, getopt();
X /** extern char *optarg; (unused at present) **/
X int ch;
X int ErrorFlag = 0;
X
X progname = argv[0];
X
X SetDefaults(argc, argv);
X
X while ((ch = getopt(argc, argv, "Zz:ahpslxVv")) != EOF) {
X switch (ch) {
X case 'z':
X case 'Z':
X break; /* done by SetDefaults(); */
X case 'V':
X fprintf(stderr, "%s version %s\n", progname, Revision);
X break;
X case 'v': /* same as -t 1 */
X AsciiTrace = 1;
X break;
X case 'l':
X break; /* list mode is the default */
X case 's':
X SilentMode = 1;
X break;
X case 'x':
X ErrorFlag = (-1);
X break;
X case '?':
X ErrorFlag = 1;
X }
X }
X
X /* Normally put call to lrqError here to give a helpful message,
X * but not yet ready to ship the error handling package, sorry
X */
X if (ErrorFlag) {
X fprintf(stderr, "Usage: %s [options] \"phrase\" [...]\n", progname);
X fprintf(stderr, "%s: options are:\n", progname);
X fputs("\
X -l -- list mode, suitable for lqshow (the default)\n\
X -s -- silent mode; exit status indicates success of matching\n\
X\n", stderr);
X
X DefaultUsage();
X exit( ErrorFlag > 0 ? 1 : 0); /* 0 means -x was used */
X }
X
X if (AsciiTrace > 1) {
X switch (PhraseMatchLevel) {
X case PCM_HalfCase:
X fprintf(stderr, "%s: Matching phrases heuristically.\n", progname);
X break;
X case PCM_SameCase:
X fprintf(stderr, "%s: Matching phrases precisely.\n", progname);
X break;
X case PCM_AnyCase:
X fprintf(stderr, "%s: Matching phrases approximately.\n", progname);
X break;
X default:
X fprintf(stderr, "%s: internall error, case matching is %d\n",
X progname, PhraseMatchLevel);
X exit(2);
X }
X }
X
X while (optind < argc) {
X Match(argv[optind++]);
X }
X
X if (SilentMode) {
X /* if we got to here we didn't find anything */
X exit(1);
X }
X return 0;
X}
X
Xvoid
XMatch(Phrase)
X char *Phrase;
X{
X extern t_Phrase *String2Phrase();
X extern t_FileInfo *GetFileInfo();
X extern long MakeMatches();
X
X t_Phrase *P;
X t_MatchList *Matches;
X t_FID LastFID = (t_FID) 0;
X t_FileInfo *FileInfo = 0;
X
X if (!Phrase || !*Phrase) return;
X if ((P = String2Phrase(Phrase)) == (t_Phrase *) 0) return;
X
X if (MakeMatches(P) <= 0L) return;
X
X if (P) {
X for (Matches = P->Matches; Matches != (t_MatchList *) 0;
X Matches = Matches->Next) {
X if (Matches->Match != (t_Match *) 0) {
X if (Matches->Match->Where->FID != LastFID) {
X t_FID FID = Matches->Match->Where->FID;
X /*TODO: use DestroyFileInfo instead of efree:... */
X if (FileInfo) efree((char *) FileInfo);
X if ((FileInfo = GetFileInfo(FID)) == (t_FileInfo *) 0) {
X continue;
X }
X LastFID = FID;
X }
X
X /* Now that we know that we have something to print... */
X if (SilentMode) {
X exit(0); /* OK, found something */
X }
X if (AsciiTrace) {
X printf("%-7lu %-7u %-3d %-3d %s\n",
X Matches->Match->Where->BlockInFile,
X (unsigned) Matches->Match->Where->WordInBlock,
X (unsigned) Matches->Match->Where->StuffBefore,
X (unsigned) Matches->Match->Where->Flags,
X FileInfo->Name);
X } else {
X printf("%-7.7lu %-7.7u %s\n",
X Matches->Match->Where->BlockInFile,
X Matches->Match->Where->WordInBlock,
X FileInfo->Name);
X }
X }
X }
X }
X}
X
X/*
X * $Log: lqphrase.c,v $
X * Revision 1.4 90/10/06 00:50:56 lee
X * Prepared for first beta release.
X *
X * Revision 1.3 90/08/29 21:45:29 lee
X * Alpha release
X *
X * Revision 1.2 90/08/09 19:17:16 lee
X * *** empty log message ***
X *
X * Revision 1.1 90/03/24 20:22:49 lee
X * Initial revision
X *
X */
X
@@@End of lq-text/src/lqtext/lqphrase.c
echo x - lq-text/src/lqtext/lqshow.c 1>&2
sed 's/^X//' >lq-text/src/lqtext/lqshow.c <<'@@@End of lq-text/src/lqtext/lqshow.c'
X/* lqshow.c -- Copyright 1989, 1990 Liam R. Quin. All Rights Reserved.
X * This code is NOT in the public domain.
X * See the file COPYRIGHT for full details.
X */
X
X/* lqshow -- show a file according to keywords, highlighting matches
X * Liam R. Quin, September 1989 and later...
X *
X * $Id: lqshow.c,v 1.9 91/03/03 00:18:26 lee Rel1-10 $
X */
X
X#include "globals.h" /* defines and declarations for database filenames */
X
X#ifdef SYSV
X /* for lint: */
X extern int w32addch();
X#endif
X#ifdef ultrix
X# include <cursesX.h>
X#else
X# include <curses.h>
X#endif
X#include <malloc.h>
X#include <fcntl.h>
X#include <ctype.h>
X#include <sys/types.h> /* for fileinfo.h */
X#include <sys/stat.h>
X
X/* Check for old (or BSD) curses: */
X#ifndef A_STANDOUT
X# define A_STANDOUT 10193 /* random */
X# define A_UNDERLINE 312
X# define attrset(a) ((a == 0) ? standend() : standout())
Xtypedef char chtype; /* long on sysV */
Xbeep() {
X fprintf(stderr, "\007");
X fflush(stderr);
X}
X#endif
X
X#include "fileinfo.h"
X#include "wordinfo.h"
X#include "wordrules.h"
X#include "pblock.h"
X#include "emalloc.h"
X
X/** Unix system calls that need declaring: **/
Xextern long lseek();
Xextern int open(), close();
Xextern int read();
Xextern void exit();
Xextern int stat();
X
X/** Unix/C Library Functions that need declaring: **/
X#ifndef tolower
X extern int tolower();
X#endif
Xextern int strlen();
Xextern int strcmp();
Xextern unsigned sleep();
Xextern int atoi();
Xextern long atol();
Xextern void perror();
X
X/** Curses library functions: **/
X#ifdef SYSV
Xextern int box32();
X#endif
X#ifndef nonl
X extern int nonl();
X#endif /**nonl*/
X#ifndef noecho
Xextern int noecho();
X#endif
X#ifndef wmove
X extern int wmove();
X#endif
X#ifndef waddstr
X extern int waddstr();
X#endif
X#ifndef wrefresh
X extern int wrefresh();
X#endif
X#ifndef beep
X extern int beep();
X#endif
X#ifndef wprintw
X extern int printw();
X#endif
Xextern int mvwprintw(), delwin(), wclear(), wclrtoeol(), endwin();
X
X/** lqtext library functions that need declaring: **/
Xextern int MySystem();
Xextern int TooCommon();
Xextern void SetDefault();
Xextern void DefaultUsage();
X
X/** Functions within this file that are used before being defined: **/
Xint ReadMatchFile();
Xvoid ShowFile();
Xvoid Output();
X
X/** **/
X
X/** some useful macros: **/
X#define max(choir,boy) (((choir)>(boy))?(choir):(boy))
X#define min(choir,boy) (((choir)<(boy))?(choir):(boy))
X
X/** **/
X
Xint AsciiTrace = 0;
X
X/* number of lines above and below each match to show by default. */
X#define DFLTABOVE 6
X#define DFLTBELOW 9
X
Xint LinesBelow = DFLTBELOW;
Xint LinesAbove = DFLTABOVE;
X
X#define DISPLAY_TOP 3
X
Xextern int errno;
X
Xchar *progname;
Xint ThisRow, ThisCol;
Xint SelectedNames = -1;
XFILE *InfoStream = 0;
X
Xstatic char *Revision = "@(#) showfile.c 2.2";
X
Xint
Xmain(argc, argv)
X int argc;
X char *argv[];
X{
X extern int optind, getopt();
X extern char *optarg; /* for getopt */
X int ch; /* for getopt */
X int ErrFlag = 0; /* see how getopt makes programs cleaner? */
X int NumberOfFiles;
X char **Origv;
X int Origc;
X char *FileWithMatches = (char *) 0;
X char **MatchList;
X int MatchCount = 0;
X
X progname = argv[0];
X
X SetDefaults(argc, argv);
X
X /* All lq-text programs must call SetDefaults() before getopt, and
X * must then be prepared to ignore options z with arg and Z without.
X */
X while ((ch = getopt(argc, argv, "a:b:f:o:z:ZVvx")) != EOF) {
X switch (ch) {
X case 'z':
X break; /* done by SetDefaults(); */
X case 'V':
X fprintf(stderr, "%s version %s\n", progname, Revision);
X break;
X case 'v':
X AsciiTrace = 1;
X break;
X case 'a': /* lines above */
X LinesAbove = atoi(optarg); /* need cknum() */
X break;
X case 'b':
X LinesBelow = atoi(optarg);
X break;
X case 'f':
X FileWithMatches = optarg;
X break;
X case 'o': /* -o fd --- write the selected files to fp */
X if (SelectedNames >= 0) {
X fprintf(stderr,
X "%s: -o %d -o %s: you must not give more than one -o option.\n",
X progname, SelectedNames, optarg);
X ErrFlag = (-1);
X } else {
X if (!isdigit(*optarg)) {
X fprintf(stderr, "%s: -o must be followed by a number\n",
X progname);
X exit(1);
X }
X SelectedNames = atoi(optarg);
X break;
X }
X break;
X case 'x':
X ErrFlag = (-1);
X break;
X case '?':
X default:
X ErrFlag = 1;
X }
X }
X
X if (ErrFlag < 0) { /* -x or -xv was used */
X fprintf(stderr, "usage: %s [-xv] [options] [matches...]\n", progname);
X fprintf(stderr,
X "use %s -x, -xv or -xvv for more detailed explanations.\n", progname);
X
X if (AsciiTrace) {
X DefaultUsage();
X fprintf(stderr, "\n\
X -a above - set the number of lines shown above each matching\n\
X match to \"above\" [default is %d]\n", DFLTABOVE);
X fprintf(stderr, "\
X -b below - set the number of lines shown below each match\n\
X match to \"above\" [default is %d]\n", DFLTBELOW);
X fprintf(stderr, "\
X -f file -- \"file\" contains a list of matches, one per line\n");
X }
X if (AsciiTrace > 1) {
X fputs("\
X Matches should be in the form of\n\
X BlockNumber WordInBlock FileName\n\
X where BlockBumber and WordInBlock are positive numbers.\n\
X (This is the format produced by the lqword -l command.)\n\
X", stderr);
X }
X exit(0);
X } else if (ErrFlag > 0) {
X fprintf(stderr, "use %s -x for an explanation.\n", progname);
X exit(1);
X }
X
X /* open the file for the selected output */
X if (SelectedNames > 0) {
X if ((InfoStream = fdopen(SelectedNames, "w")) == (FILE *) 0) {
X int e = errno;
X
X fprintf(stderr, "%s: -o %d: can't open stream ",
X progname, SelectedNames);
X errno = e;
X perror("for writing");
X exit(1);
X }
X }
X
X /* check that we can get at the file containing the matches, if one
X * was supplied.
X */
X if (FileWithMatches) {
X struct stat StatBuf;
X char *msg = 0;
X
X if (stat(FileWithMatches, &StatBuf) < 0) {
X int e = errno; /* on many systems, fprintf() changes errno! */
X fprintf(stderr, "%s: can't open match-list file ", FileWithMatches);
X errno = e;
X perror(progname);
X exit(1);
X } else if (AsciiTrace) {
X switch (StatBuf.st_mode & S_IFMT) {
X case S_IFDIR:
X fprintf(stderr,
X "%s: ca't read matches from \"%s\" -- it's a directory!\n",
X progname, FileWithMatches);
X exit(1);
X case S_IFREG:
X break;
X#ifdef S_IFIFO
X case S_IFIFO:
X msg = "named pipe or fifo";
X /* fall through */
X#endif
X case S_IFCHR:
X if (!msg) msg = "raw special device";
X /* fall through */
X case S_IFBLK:
X if (!msg) msg = "block special device";
X /* fall through */
X#ifdef S_IFNAM
X case S_IFNAM:
X if (!msg) msg = "named special file"; /* wot dat? */
X /* fall through */
X#endif
X default:
X if (!msg) msg = "special file";
X
X fprintf(stderr,
X "%s: warning: file \"%s\" containing matches is a %s\n",
X progname, FileWithMatches, msg);
X
X /* but continue anyway... */
X
X }
X }
X /* Now read the file, and make an array of matches... */
X if (ReadMatchFile(FileWithMatches, StatBuf.st_size, &MatchCount, &MatchList) < 0) {
X fprintf(stderr, "%s: couldn't read matches from \"%s\"\n",
X progname, FileWithMatches);
X exit(1);
X }
X }
X
X argv += optind;
X argc -= optind;
X
X if (MatchCount) {
X argc = MatchCount;
X argv = MatchList;
X }
X
X if (argc < 3) {
X fprintf(stderr,
X "%s: matches must have at least 3 parts; use -xv for an explanation\n",
X progname);
X exit(1);
X } else if (argc % 3) {
X /* Note: I could detect lqword output here (i.e., without -l) */
X fprintf(stderr, "%s: can't understand match format;\n", progname);
X fprintf(stderr, "%s: use -xv for more explanation.\n", progname);
X exit(1);
X }
X
X Origv = argv;
X Origc = argc;
X
X ThisRow = DISPLAY_TOP - 1;
X NumberOfFiles = argc / 3;
X
X initscr();
X nonl();
X raw();
X noecho();
X
X while (argc > 0) {
X char Buffer[120];
X int Where;
X
X ThisRow = DISPLAY_TOP;
X ThisCol = (-1);
X ShowFile(argv[2], atol(*argv), (unsigned) atoi(argv[1]), argc - Origc);
X (void) sprintf(Buffer, "Match %d of %d",
X NumberOfFiles - (argc / 3) + 1, NumberOfFiles);
X Where = COLS - (strlen(Buffer) + 10);
X mvwaddstr(stdscr, LINES - 1, Where, Buffer);
X refresh(); /* Where (above) is in case mvwaddstr is a macro */
X
X if (argc > 0) {
X attrset(A_STANDOUT);
X mvwaddstr(stdscr, LINES - 1, 0, "[Press SPACE to continue]");
X attrset(0);
X wmove(stdscr, 0, 0);
X (void) refresh();
X switch (getch()) {
X case '?':
X case 'x':
X case 'h':
X case 'i':
X#ifdef KEY_HELP
X case KEY_HELP:
X#endif
X {
X WINDOW *HelpWin = newwin(12, 40, 5, (COLS - 40) / 2);
X
X if (HelpWin == (WINDOW *) 0) {
X beep();
X } else {
X#ifndef ACS_HLINE
X box(HelpWin, '#', '#');
X#else
X box(HelpWin, 0, 0);
X /* Versions of curses with ASC_HLINE take 0 to
X * mean that line-drawing should be done
X * "properly".
X */
X#endif
X wmove(HelpWin, 1, 2);
X mvwprintw(HelpWin, 1,2, "x, ? -- print this explanation");
X mvwprintw(HelpWin, 2,2, "space -- go to next match");
X mvwprintw(HelpWin, 3,2, "return -- go to next match");
X mvwprintw(HelpWin, 4,2, "0, ^, F -- go to First match");
X mvwprintw(HelpWin, 5,2, "$, L -- go to the Last match");
X mvwprintw(HelpWin, 6,2, "n, + -- go to the next file");
X mvwprintw(HelpWin, 7,2, "p, - -- go to previous file");
X mvwprintw(HelpWin, 8,2, "s, g -- save this filename");
X mvwprintw(HelpWin, 9,2, "u, d -- drop this filename");
X mvwprintw(HelpWin, 10,2, "q, Q -- quit browsing");
X wrefresh(HelpWin);
X (void) getch();
X delwin(HelpWin);
X#ifndef CURSESX /* This is because 4.2 BSD a brain-dead curses... */
X clearok(stdscr, TRUE);
X wrefresh(stdscr);
X#endif
X }
X }
X break;
X case 'q':
X case 'Q':
X goto AllDone;
X /* the goto is to surmount an AT&T compiler bug */
X case '0': /* reset to beginning */
X case '1':
X case 'f': case 'F':
X case '^': case '6': /* (6 is often unshifted ^) */
X argc = Origc;
X argv = Origv;
X break;
X case '$': /* to the end */
X case 'l': case 'L': /* Last match */
X argv += (argc - 3);
X argc = 3;
X break;
X case 'v': /* view the file -- use PAGER */
X {
X char Buffer[4096];
X char *doc;
X int e = errno;
X
X if ((doc = FindFile(argv[2])) == (char *) 0) {
X errno = e;
X perror(argv[2]);
X sleep(2);
X goto AllDone;
X }
X
X (void) sprintf(Buffer, "%s \"%s\"", PAGER, doc);
X (void) MySystem(Buffer);
X clearok(stdscr, TRUE);
X wrefresh(stdscr);
X }
X break;
X case 's': /* keep this filename for later use */
X case 'k': case 'g': /* keep, get */
X fprintf(InfoStream, "%c %s\n", 's', argv[2]);
X break; /*NOTDONE*/
X case 'd': /* delete this file from the list */
X fprintf(InfoStream, "%c %s\n", 'd', argv[2]);
X break; /* NOTDONE */
X case 'R': /* revert to initial state */
X break; /* NOTDONE*/
X case '-':
X case 'p':
X {
X char *p = argv[2];
X char **Argv = argv;
X int Argc = argc;
X
X while (Argc + 3 <= Origc && STREQ(Argv[2], p)) {
X Argv -= 3;
X Argc += 3;
X }
X
X if (Argc == argc) {
X beep();
X } else {
X argv = Argv;
X argc = Argc;
X }
X }
X break;
X case '+':
X case 'n':
X {
X char *p = argv[2];
X char **Argv = argv;
X int Argc = argc;
X
X while (Argc > 3 && STREQ(Argv[2], p)) {
X Argv += 3;
X Argc -= 3;
X }
X
X if (Argc == argc) {
X beep();
X } else {
X argv = Argv;
X argc = Argc;
X }
X }
X break;
X case 'R' ^ 64: /* control-R */
X case 'L' ^ 64: /* control-L */
X clearok(stdscr, TRUE);
X wrefresh(stdscr);
X break;
X case '=':
X clearok(stdscr, TRUE);
X wrefresh(stdscr);
X {
X FILE *Pager = popen(PAGER, "w");
X char **p;
X int i;
X
X if (!p) {
X beep();
X break;
X }
X for (p = Origv, i = Origc; i > 0; i -= 3, p += 3) {
X (void) fprintf(Pager, "%s\n", *p);
X }
X (void) pclose(Pager);
X }
X clearok(stdscr, TRUE);
X wrefresh(stdscr);
X break;
X case ' ':
X case '\r':
X case '\n':
X argv += 3;
X argc -= 3;
X break;
X default:
X beep();
X break;
X }
X }
X }
X
XAllDone:
X wmove(stdscr, LINES - 1, 0);
X clrtoeol();
X /* Try to revent the screen from scrolling when we exit */
X wmove(stdscr, LINES - 2, 0);
X refresh();
X endwin();
X return 0;
X}
X
Xint
XReadMatchFile(FileWithMatches, FileSize, MatchCount, MatchList)
X char *FileWithMatches;
X off_t FileSize;
X int *MatchCount;
X char ** *MatchList;
X{
X extern char *strtok();
X
X int fd;
X char **Result;
X char *StorageArea;
X char *p;
X unsigned int n_matches;
X int BytesRead;
X int i;
X char *NextStr;
X
X if (!FileWithMatches || !*FileWithMatches) {
X fprintf(stderr, "%s: match-list file (from -f) has empty name!\n",
X progname);
X exit(1);
X }
X
X if ((fd = open(FileWithMatches, O_RDONLY)) == 0) {
X int e = errno;
X fprintf(stderr, "%s: can't open match-list file ", progname);
X errno = e;
X perror(FileWithMatches);
X exit(1);
X }
X
X /* We know the number of bytes, and each space or newline will get
X * turned into a null, so here goes...
X * The +1 below is to ensure that there is space for a \0, even if a
X * pesky user didn't put a \n at * the end of the file...
X * Sometimes I hate emacs...
X */
X if ((StorageArea = malloc((unsigned) FileSize + 1)) == (char *) 0) {
X fprintf(stderr, "%s: not enough memory to read match-list \"%s\"\n",
X progname, FileWithMatches);
X exit(1);
X }
X
X /* now read the list... */
X if ((BytesRead = read(fd, StorageArea, FileSize)) != FileSize) {
X if (BytesRead < 0) {
X int e = errno;
X
X fprintf(stderr, "%s: couldn't read %u bytes from ",
X progname, FileSize);
X errno = e;
X perror(FileWithMatches);
X exit(1);
X } else {
X int e = errno;
X
X fprintf(stderr, "%s: ", progname);
X if (BytesRead > 4) { /* minimum plausible for 3 items */
X fprintf(stderr, "warning: ");
X }
X fprintf(stderr, "only read %u bytes, not %u, from file ",
X progname, BytesRead, FileSize);
X errno = e;
X if (errno) perror(FileWithMatches);
X else fprintf(stderr, "\"%s\"\n", FileWithMatches);
X
X if (BytesRead <= 4) exit(1);
X
X StorageArea = realloc(StorageArea, (unsigned) (BytesRead + 1));
X if (StorageArea == (char *) 0) { /* unlikely, it got smaller */
X fprintf(stderr, "%s: can't realloc for \"%s\"\n",
X progname, FileWithMatches);
X exit(1);
X }
X FileSize = BytesRead;
X }
X }
X
X /* null-terminate it */
X StorageArea[FileSize] = '\0';
X
X /* got the data, now make an array... first, count the matches */
X for (n_matches = 1, p = StorageArea; p - StorageArea < FileSize; p++) {
X if isspace(*p) ++n_matches;
X }
X
X /* If there *was* trailing new-line, we overestimated by one.
X * This doesn't matter. If memory is that tight, initscr() will fail.
X * In any case, allow extra space for a trailing null entry.
X */
X ++n_matches;
X if (n_matches < 3) n_matches = 3;
X
X Result = (char **) malloc((unsigned) n_matches * sizeof(char *));
X if (Result == (char **) 0) {
X fprintf(stderr, "%s: out of memory reading match file \"%s\"\n",
X progname, FileWithMatches);
X exit(1);
X }
X
X /* Now step through the Storage Area filling in the pointers to the args */
X ;
X
X NextStr = (char *) 0;
X i = -1;
X for (p = StorageArea; p - StorageArea <= BytesRead; p++) {
X if (!NextStr) NextStr = p;
X if (isspace(*p) || p - StorageArea == BytesRead) {
X if (p - StorageArea != BytesRead) *p = '\0';
X while (isspace(*p)) { /* eat multiple blanks */
X p++;
X }
X if (++i >= n_matches) {
X n_matches += 20;
X if ((Result = (char **)
X realloc((char *) Result, n_matches * sizeof(char *))) ==
X (char **) 0) {
X fprintf(stderr,
X "%s: out of memory [%u] in match-file \"%s\"\n",
X progname, n_matches * sizeof(char *), FileWithMatches);
X /* TODO -- return with fewer matches -- NOTDONE */
X exit(1);
X }
X }
X *p = '\0'; /* OK at the very end cos of the extra byte! */
X Result[i] = NextStr;
X NextStr = (char *) 0;
X }
X }
X
X if (i + 2 < n_matches) {
X Result = (char **) realloc((char *)Result,
X (unsigned) (i+2) * sizeof(char **));
X if (Result == (char **) 0) {
X fprintf(stderr, "%s: no memory for match-list from \"%s\"\n",
X progname, FileWithMatches);
X exit(1);
X }
X }
X
X if (close(fd) < 0) {
X fprintf(stderr, "%s: warning: obscure problem closing %d (\"%s\")\n",
X progname, fd, FileWithMatches);
X sleep(5);
X }
X
X (*MatchList) = Result;
X return (*MatchCount = i);
X}
X
Xvoid
XShowFile(FileName, BlockInFile, WordInBlock, UniqueID)
X char *FileName;
X unsigned long BlockInFile;
X unsigned int WordInBlock;
X int UniqueID;
X{
X static char *Buffer = 0;
X int fd;
X static unsigned int BufLen;
X int AmountRead;
X register char *p;
X int LinesFound;
X int InTargetWord = 0;
X char *StartOfMyWord;
X int ThisWord = 0;
X unsigned long FirstLumpSize;
X char *Start;
X static int LastID = (-1);
X
X if (UniqueID == LastID) {
X return;
X } else {
X LastID = UniqueID;
X }
X wclear(stdscr);
X ThisRow = DISPLAY_TOP;
X
X if (Buffer == (char *) 0) {
X BufLen = COLS * (LinesAbove + LinesBelow + 1) + 1;
X if (BufLen < FileBlockSize * 3) BufLen = FileBlockSize * 3;
X Buffer = emalloc(BufLen);
X }
X
X errno = 0;
X
X if ((fd = open(FileName, O_RDONLY, 0)) < 0) {
X int e = errno;
X char *doc;
X
X if ((doc = FindFile(FileName)) == (char *) 0) {
X errno = e;
X perror(FileName);
X sleep(2);
X return;
X } else if ((fd = open(doc, O_RDONLY, 0)) < 0) {
X perror(doc);
X sleep(2);
X return;
X }
X FileName = doc;
X }
X
X /* display a helpful message: */
X move(DISPLAY_TOP, 0);
X clrtoeol();
X move(DISPLAY_TOP - 1, 0);
X clrtoeol();
X mvwprintw(stdscr, DISPLAY_TOP - 1, 0,
X "Block %lu/Word %u in document: ", BlockInFile, WordInBlock);
X attrset(A_UNDERLINE);
X wprintw(stdscr, "%s", FileName);
X attrset(0);
X
X errno = 0;
X if (lseek(fd, BlockInFile? (long) ((BlockInFile - 1) * FileBlockSize) : 0L,
X 0) < 0) {
X perror("lseek");
X sleep(2);
X clearok(stdscr, TRUE);
X close(fd);
X return;
X }
X
X errno = 0;
X if ((AmountRead = read(fd, Buffer, BufLen)) < MinWordLength) {
X perror("read");
X sleep(5);
X close(fd);
X clearok(stdscr, TRUE);
X return;
X }
X
X /* clear the bottom bit of screen */
X {
X register int i;
X
X for (i = ThisRow; i < LINES; i++) {
X move(i, 0);
X wclrtoeol(stdscr);
X }
X }
X
X /** Find the required word */
X if (BlockInFile) {
X /* start 1 char before the end of the previous block */
X StartOfMyWord = &Buffer[FileBlockSize - 1];
X /* perhaps the last word of the previous block spans the block
X * boundary?
X */
X while (WithinWord(*StartOfMyWord)) StartOfMyWord++;
X if (StartOfMyWord < &Buffer[FileBlockSize]) {
X StartOfMyWord = &Buffer[FileBlockSize];
X }
X } else {
X StartOfMyWord = Buffer;
X }
X
X for (ThisWord = 0; ThisWord <= WordInBlock + 1; ThisWord++) {
Xbored:
X /* skip to the start of a word */
X while (!StartsWord(*StartOfMyWord)) {
X ++StartOfMyWord;
X }
X
X Start = StartOfMyWord;
X
X /* find the end of the word */
X while (WithinWord(*StartOfMyWord)) {
X if (*StartOfMyWord == '\'' && !EndsWord(StartOfMyWord[1])) break;
X StartOfMyWord++;
X }
X
X /* Assert: StartOfMyWord points 1 character beyond the end of the
X * word pointed to by Start
X */
X /* see if it's long enough */
X if (StartOfMyWord - Start < MinWordLength) {
X goto bored;
X }
X
X#if 0
X /* see if it is too common */
X {
X extern char *WordRoot();
X
X t_WordInfo W;
X register char *p, *q;
X char RootBuf[MaxWordLength + 1];
X
X for (p = RootBuf, q = Start; *q; p++, q++) {
X if (q == StartOfMyWord) break;
X *p = isupper(*q) ? tolower(*q) : *q;
X }
X *p = '\0';
X
X W.Word = RootBuf;
X W.Length = strlen(W.Word);
X W.WordPlace.Flags = 0;
X
X (void) WordRoot(&W);
X
X if (TooCommon(&W)) goto bored;
X
X }
X#endif
X
X /** See if it's the right one */
X if (ThisWord == WordInBlock) {
X StartOfMyWord = Start;
X break;
X }
X }
X
X FirstLumpSize = StartOfMyWord - Buffer;
X
X /* Find N lines before it */
X LinesFound = 0;
X for (p = StartOfMyWord; p > Buffer; --p) {
X if (*p == '\n') {
X if (++LinesFound > LinesAbove) break;
X }
X }
X
X /* display them */
X while (p < StartOfMyWord) {
X Output(*p); /* Output might be a macro later */
X p++;
X }
X
X /* find N lines after it */
X
X LinesFound = 0;
X while (p - Buffer < AmountRead) {
X switch (InTargetWord) {
X case 0:
X if (StartsWord(*p)) {
X attrset(A_STANDOUT);
X InTargetWord = 1;
X }
X break;
X case 1:
X if (!WithinWord(*p)) {
X InTargetWord = 2;
X attrset(0);
X }
X }
X Output(*p);
X
X if (*p == '\n') {
X if (++LinesFound > LinesBelow) break;
X }
X p++;
X }
X
X (void) refresh();
X
X (void) close(fd);
X return;
X}
X
Xvoid
XOutput(ch)
X int ch;
X{
X switch(ch) {
X default:
X if (++ThisCol > COLS) {
X if (++ThisRow >= LINES - 1) {
X ThisRow = DISPLAY_TOP;
X }
X ThisCol = 0;
X }
X if (ThisCol <= 0) {
X ThisCol = 0;
X move(ThisRow, ThisCol);
X clrtoeol();
X }
X mvwaddch(stdscr, ThisRow, ThisCol, (chtype) ch);
X break;
X case '\n':
X if (++ThisRow >= LINES - 1) {
X ThisRow = DISPLAY_TOP;
X }
X ThisCol = (-1);
X move(ThisRow, 0);
X clrtoeol();
X break;
X case '\t':
X ThisCol |= 7;
X break;
X case ' ':
X ThisCol++;
X }
X}
X
X/*
X * $Log: lqshow.c,v $
X * Revision 1.9 91/03/03 00:18:26 lee
X * No longer needs to check common words.
X *
X * Revision 1.8 90/10/06 00:50:58 lee
X * Prepared for first beta release.
X *
X * Revision 1.7 90/10/05 23:49:25 lee
X * Moved the Match %d of %d message left somewhat.
X *
X * Revision 1.6 90/10/03 21:26:47 lee
X * Removed BSD/SYSV diffs and used CURSESX instead.
X *
X * Revision 1.5 90/08/29 21:45:34 lee
X * Alpha release
X *
X * Revision 1.4 90/08/09 19:17:17 lee
X * BSD lint and Saber
X *
X * Revision 1.3 90/07/11 10:57:46 lee
X * Added limited ultrix support... also some small optimisations and
X * changes to the help screen.
X *
X * Revision 1.2 90/04/21 16:06:24 lee
X * Cleaned up the gode for gcc -W
X *
X * Revision 1.1 90/02/14 18:32:39 lee
X * Initial revision
X *
X * Revision 2.1 89/10/02 01:15:51 lee
X * New index format, with Block/WordInBlock/Flags/BytesSkipped info.
X *
X * Revision 1.3 89/09/17 23:04:04 lee
X * Various fixes; NumberInBlock now a short...
X *
X * Revision 1.2 89/09/16 21:18:35 lee
X * First demonstratable version.
X *
X * Revision 1.1 89/09/16 20:02:58 lee
X * Initial revision
X *
X */
@@@End of lq-text/src/lqtext/lqshow.c
echo end of part 07
--
Liam R. E. Quin, lee at sq.com, SoftQuad Inc., Toronto, +1 (416) 963-8337
More information about the Alt.sources
mailing list