/* Use menu interface as a simple front end to a prototype * text retrieval program... * * Liam R. Quin, September 11th 1989, November 1993 * This file is not in the public domain; see the file COPYRIGHT * for more informatiom. * * $Id: text.c,v 1.16 1996/06/13 19:40:13 lee Exp lee $ * */ #ifdef ultrix # include #else # include #endif #ifndef A_STANDOUT # include "oldcurses.h" #endif #include #include "globals.h" #include "menu.h" #include "error.h" #ifdef HAVE_STRING_H # include #else # include #endif #include #ifdef HAVE_FCNTL_H # ifdef HAVE_SYSV_FCNTL_H # include # endif # include /* for O_RDONLY etc */ #endif #ifdef HAVE_STDLIB_H # include #else # include #endif #ifdef HAVE_UNISTD_H # include #endif #include "fileinfo.h" #include "wordinfo.h" #include "wordrules.h" #include "pblock.h" #include "phrase.h" #include "emalloc.h" #include "liblqtext.h" #ifndef KEY_LEFT # define KEY_LEFT '\b' #endif int MenuUsed = 0; /* (sigh) */ /* The above is needed because of a bug in "m". */ /* Some handy macros */ #ifndef new # ifdef __STDC__ # define new(type) (type *) emalloc("text.x" #type, sizeof(type)) # else # define new(type) (type *) emalloc("text.x:type", sizeof(type)) # endif #endif #ifndef STREQ /* STREQ is an optimisation due to Henry Spencer, utzoo!henry */ # define STREQ(henry,utzoo) ((*(henry)==(*(utzoo)))&&!strcmp((henry),(utzoo))) #endif char *cmdname = (char *) 0; char *progname = "[menu]"; /** Unix system calls that need to be declared **/ /** Unix/C library functions that need to be declared **/ /** Curses functions that need to be declared: **/ #ifdef ANCIENT_WAY_OF_DOING_THINGS #ifndef beep extern int beep(); #endif #ifndef endwin extern int endwin(); #endif #ifdef A_STANDOUT # ifndef isendwin extern int isendwin(); /* only needed on V.3.2 I think */ # endif #endif #ifndef keypad extern int keypad(); #endif #ifndef mvwaddstr extern int mvwaddstr(); #endif #ifndef mvwprintw extern int mvwprintw(); #endif #ifndef waddstr extern int waddstr(); #endif #ifndef wclear wclear(); #endif #ifndef wclrtoeol extern int wclrtoeol(); #endif #ifndef wmove extern int wmove(); #endif #ifndef wrefresh extern int wrefresh(); #endif #ifndef noecho extern int noecho(); #endif #ifndef nonl extern int nonl(); #endif #ifdef CURSESX extern int w32addch(); #endif #endif /** lqtext functions that need to be declared: **/ /** libmenu functions that need to be declared **/ /** Functions within this file that must be declared: **/ void Usage( #ifdef HAVE_PROTO int exitstatus, t_lqdbOptions *Options #endif ); t_MenuBar *SetUpMenus(); void DisplayPhrases(); static void SetMessage( #ifdef HAVE_PROTO char *s #endif ); static void ClearMessage(); static void ShowInstructions(); static void ClearInstructions(); static void StartCurses(); static void EndCurses(); /** **/ static int PhraseCount = 0; static t_LQTEXT_Database *db; int main(argc, argv) int argc; char *argv[]; { /* (unused) extern char *optarg; */ extern int optind; /** end getopts stuff **/ t_MenuBar *MenuBar; t_lqdbOptions *Options; void SetUpScreen(); int optchar; /** start getopts stuff **/ int errflag = 0; /* Make progname point to the last component of argv[0] */ progname = strrchr(argv[0], '/'); if (progname) { if (*++progname == '\0') { /* the filename ends in a slash! */ --progname; /* (no Unix filename can do end in '/'!!!) */ } } else { progname = argv[0]; } /* cmdname lets you say CMDNAME=`basename $0` in a shell script * for better error reporting */ cmdname = getenv("CMDNAME"); Options = LQT_InitFromArgv(argc, argv); while ((optchar = getopt(argc, argv, "xz:vZvV")) != EOF) { switch (optchar) { case 'v': case 'z': case 'Z': /* already dealt with by Defaults */ break; case 'x': Usage(0, Options); /*NOTREACHED*/ break; case 'V': error(ERR_FATAL, "Version: $Id: text.c,v 1.16 1996/06/13 19:40:13 lee Exp lee $"); /*NOTREACHED*/ exit(1); case '?': errflag++; } } if (errflag) { Usage(1, Options); } db = LQT_OpenDatabase(Options, O_RDONLY, 0); if (!db || LQT_ObtainReadOnlyAccess(db) < 0) { error(ERR_FATAL, "couldn't open lq-text database"); } StartCurses(); MenuBar = SetUpMenus(); SetUpScreen(); PhraseCount = 0; while (optind < argc) { PhraseCount += AddPhrase(argv[optind++]); } if (PhraseCount) { SetMessage("Select MATCH ALL from the BROWSE menu"); DisplayPhrases(); } while (UseMenuBar(MenuBar) != -2) { if (PhraseCount == 0) { ShowInstructions(); } } EndCurses(); exit(0); /*NOTREACHED*/ return 1; /* this is for lint and gcc -Wall... */ } void Usage(exitstatus, Options) int exitstatus; t_lqdbOptions *Options; { if (cmdname) fprintf(stderr, "%s: ", cmdname); fprintf(stderr, "%s: usage: %s [-Vxvcd...] [phrase ...]\n", progname, progname); LQT_PrintDefaultUsage(Options); fprintf(stderr, "\t-V\t-- print %s version info\n", progname); exit(exitstatus); } static int ThisCol = 0; static int ThisRow = 5; /* A list of phrases: */ static t_Phrase *PhraseList = (t_Phrase *) 0; int GetWords() { int i; char Buffer[1024]; char *q; t_Phrase *PP; ThisRow = 5; if (PhraseCount && PhraseList) { t_Phrase *p; PP = PhraseList; while (PP) { p = PP->Next; (void) efree((char *) PP); /* BUG: does not free everything: NOTDONE */ /* (I need a destructor function here!!!!!) */ PP = p; } PhraseList = (t_Phrase *) 0; } PhraseCount = 0; for (i = 2; i < LINES; i++) { move(i ,0); clrtoeol(); } SetMessage( "Type words or phrases to look for, one per line, then press F1 or ^D"); move(ThisRow, ThisCol); refresh(); q = (char *) 0; PhraseCount = 0; while ((i = getch()) != KEY_F(1)) { decode: if (i == KEY_F(1) || (ThisCol <= 1 && i == '\004')) { break; } else if (isprint(i)) { mvwaddch(stdscr, ThisRow, ThisCol, i); if (q == (char *) 0) q = Buffer; if (q - Buffer >= sizeof(Buffer)) { beep(); } else { *q++ = i; *q = '\0'; } ++ThisCol; } else switch (i) { case 'X' ^ 64: /* contol X */ case 'U' ^ 64: /* ^U */ /* delete to start of line */ ThisCol = 0; move(ThisRow, ThisCol); clrtoeol(); if (q != (char *) 0) q = Buffer; break; case 'W' ^ 64: /* control-W */ if (ThisCol && q && q > Buffer) { *q = ' '; while (ThisCol > 0 && q > Buffer && isspace(*q)) { q--; --ThisCol; } while (ThisCol > 0 && q > Buffer && !isspace(*q)) { q--; --ThisCol; } *q = '\0'; move(ThisRow, ThisCol); clrtoeol(); } else if (!ThisCol) { beep(); } break; case '\010': /* control-h */ case '\177': /* DEL */ case 255: /* DEL */ mvwaddch(stdscr, ThisRow, ThisCol, ' '); if (ThisCol > 0) { --ThisCol; if (q != (char *) 0 && q > Buffer) --q; } break; case ' ': case '\t': ++ThisCol; if (q != (char *) 0) { if (q - Buffer >= sizeof(Buffer)) { beep(); } else { *q++ = ' '; *q = '\0'; } } break; case '\n': case '\r': /* Add word to list */ if (q) { int ok; *q = '\0'; if (*Buffer) { ok = AddPhrase(Buffer); PhraseCount += ok; if (ok) ++ThisRow; } } ThisCol = 0; q = (char *) 0; break; case '\033': /* ESC */ switch (i = getch()) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': i = KEY_F(i - '0'); goto decode; default: beep(); } default: beep(); } if (ThisCol >= COLS - 1) { ThisCol = 0; ThisRow++; } if (ThisRow >= LINES) break; mvwaddch(stdscr, ThisRow, ThisCol, ' '); move(ThisRow, ThisCol); refresh(); } if (q) { *q = '\0'; if (*Buffer) PhraseCount += AddPhrase(Buffer); } SetMessage("Select MATCH ALL from the ALL WORDS menu to find files..."); refresh(); return 0; } void DisplayPhrases() { t_Phrase **PP; int i = 0; if (PhraseCount <= 0) return; ThisRow = 5; for (PP = &PhraseList; *PP; PP = &(*PP)->Next) { (void) move(ThisRow, 0); clrtoeol(); ++i; mvwprintw(stdscr, ThisRow, 0, "%-2d. %-7.7d %s", i, /* No matches yet */ 0, ((*PP)->ModifiedString ? (*PP)->ModifiedString : (*PP)->OriginalString ) ); ++ThisRow; } } int AddPhrase(String) char *String; { t_Phrase **PP; if (!String || !*String) return 0; /* Add it to the list */ for (PP = &PhraseList; *PP; PP = &(*PP)->Next) { /* void */ ; } (void) move(ThisRow, 0); clrtoeol(); if ((*PP = LQT_StringToPhrase(db, String)) == (t_Phrase *) 0) { int spacecount = 0; char *p; for (p = String; *p; p++) { if (*p == ' ') spacecount++; } switch (spacecount) { case 0: p = "This word does not"; break; case 1: p = "These words don't"; break; case 2: p = "These words do not"; break; default: p = "None of these words"; break; } error(0, "\ %s:\n\ %s appear\n\ in the database dictionary", String, p); return 0; } /* The user sees the phrases numbered from 1, not from zero */ mvwprintw(stdscr, ThisRow, 0, "%-2d. %-7.7d %s", PhraseCount + 1, /* No matches yet */ 0, (*PP)->ModifiedString); return 1; } int MF_MatchAll() { t_Phrase *PP; int Row = 5; /* as per ThisRow */ long Total = 0; if (PhraseList == (t_Phrase *) 0) { SetMessage( "Select NEW WORDS from the FILE menu and type words to match"); return KEY_LEFT; } mvwprintw(stdscr, LINES - 1, 0, "Searching..."); refresh(); for (PP = PhraseList; PP != (t_Phrase *) 0; PP = PP->Next) { move(Row, 4); wclrtoeol(stdscr); PP->NumberOfMatches = LQT_MakeMatches(db, PP); Total += PP->NumberOfMatches; mvwprintw(stdscr, Row, 4, "%-7.7ld %s", PP->NumberOfMatches, PP->ModifiedString); ++Row; } mvwprintw(stdscr, LINES - 1, 0, "Finished... "); if (Total) { SetMessage("Select BROWSE ALL from the ALL WORDS menu to see the files"); } else { char *m; switch (db->PhraseMatchLevel) { case PCM_HalfCase: /* this is the normal case */ default: m = "(select ROUGH from the MATCHING menu, or choose new words)"; break; case PCM_SameCase: m = "(select USER MATCHING from the MATCHING menu, or choose new words)"; break; case PCM_AnyCase: m = "(not found --- choose new words or phrases)"; break; } SetMessage(m); } refresh(); return 0; } int MF_SetAnyMatch() { db->PhraseMatchLevel = PCM_AnyCase; return 0; } int MF_SetRoughMatch() { db->PhraseMatchLevel = PCM_AnyCase; return 0; } int MF_SetUserMatch() { db->PhraseMatchLevel = PCM_HalfCase; return 0; } int MF_SetExactMatch() { db->PhraseMatchLevel = PCM_SameCase; return 0; } long WriteFileList(Name, fp) char *Name; FILE *fp; { t_Phrase *PP; t_MatchList *Matches = (t_MatchList *) 0; t_FID FID = (t_FID) 0; t_FileInfo *FileInfo = (t_FileInfo *) 0; long Total = 0L; /* For each phrase... */ for (PP = PhraseList; PP != (t_Phrase *) 0; PP = PP->Next) { int WordsInPhrase; if (!PP || !PP->Matches) continue; WordsInPhrase = LQT_NumberOfWordsInPhrase(db, PP); /* For each match */ for (Matches = PP->Matches; Matches != (t_MatchList *) 0; Matches = Matches->Next) { if (Matches->Match != (t_Match *) 0) { if (Matches->Match->Where->FID != FID || !FileInfo) { if (FileInfo) (void) efree((char *) FileInfo); FID = Matches->Match->Where->FID; FileInfo = LQT_FIDToFileInfo( db, FID ); } fprintf(fp, "%s%d %lu %lu %lu %s", FileInfo ? "" : "# ", WordsInPhrase, Matches->Match->Where->BlockInFile, Matches->Match->Where->WordInBlock, FID, FileInfo ? FileInfo->Name : " unknown_" ); if (FileInfo) { fputc('\n', fp); ++Total; } else { fprintf(fp, "%ld\n", (long) FID); } } } } return Total; } /* TODO * -- use signal handling to ensure that the tmp file gets removed * even if there is an interrupt.... * NOTDONE FIXME */ int MF_BrowseAll() { extern char *mktemp(); char *t; FILE *fp = (FILE *) 0; char TmpBuf[30]; /* enough for /tmp/text=a124356\0 */ #ifndef LQSHOW # define LQSHOW "lqshow" #endif char SystemBuffer[sizeof(LQSHOW) + sizeof(" -f ") + sizeof TmpBuf + 3]; /* the +3 above is for spaces and a trailing \0 */ long Total; if (PhraseCount == 0) { SetMessage("select NEW WORDS from the FILE menu first."); return 0; } ClearMessage(); (void) sprintf(TmpBuf, "/tmp/text=XXXXXX"); t = mktemp(TmpBuf); if (t == (char *) 0 || (fp = fopen(t, "w")) == (FILE *) 0) { error(0, "\ Can't create tempory file\n\ (\"%s\")\n\ to hold the list of matches,\n\ sorry.\n\ ", t ? t : "[internal_error]"); return -1; } Total = WriteFileList(t, fp); (void) fclose(fp); if (Total) { #ifdef CURSESX /* lqshow works in raw mode, so no need to fiddle tty settings */ #else /* We need to set the tty modes for lqshow: */ (void) nocbreak(); (void) echo(); (void) nl(); (void) keypad(stdscr, FALSE); #endif clearok(stdscr, TRUE); (void) sprintf(SystemBuffer, "%s -f %s", LQSHOW, t); (void) system(SystemBuffer); #ifndef CURSESX (void) cbreak(); (void) noecho(); (void) nonl(); (void) keypad(stdscr, TRUE); #endif SetMessage("(save the matches using SAVE LIST from the FILE menu)"); } else { SetMessage("(no matches; select MATCH ALL before BROWSE ALL)"); } if (t) (void) unlink(t); return 0; } int MF_EndProg() { endwin(); exit(0); /*NOTREACHED*/ return (-1); } int MF_OpenList() { beep(); /* NOTDONE */ SetMessage("Function OPEN LIST unimplemented -- sorry"); return 0; } int MF_SaveList() { extern char *AskForString(); char *FileName; FILE *fp; t_Phrase *PP; if (PhraseCount == 0) { SetMessage("No matches... nothing to save"); return 0; } FileName = AskForString( "Enter the name of a file in which to save the list:", 2048, (WINDOW *) 0, 7, 5); if (FileName == (char *) 0) { beep(); return 0; } if ((fp = fopen(FileName, "r")) != (FILE *) 0) { char *yorn; char *Result = (char *) 0; (void) fclose(fp); yorn = "\ The file already exists. Type replace to replace it, \n\ or type keep to keep it as it is, and then press Enter: "; while (!Result || !STREQ(Result, "replace")) { /* the 7 is max(strlen("replace"), strlen("keep")), the * longest allowed input string */ Result = AskForString(yorn, 7, (WINDOW *) 0, 7, 5); if (STREQ(Result, "keep")) { SetMessage("(the list of matches was not saved)"); (void) efree((char *) Result); (void) efree(FileName); return 0; } } if (Result) (void) efree(Result); } if ((fp = fopen(FileName, "w")) == (FILE *) 0) { SetMessage("Couldn't creat the file to save the list of matches"); beep(); refresh(); (void) efree(FileName); return 0; } /* get rid of any lingering dialogue boxes... */ SetMessage("Saving list..."); (void) refresh(); fprintf(fp, "## file created automatically by lqtext -- do not edit!\n"); /* Write out the list of phrases */ for (PP = PhraseList; PP != (t_Phrase *) 0; PP = PP->Next) { (void) fprintf(fp, "## %s\n", PP->OriginalString); } (void) WriteFileList(FileName, fp); (void) fclose(fp); SetMessage("List saved. Type q if you want to quit now."); return 0; } int MF_ViewFile() { extern char *AskForString(); char *p; int Retval = 0; p = AskForString( "Enter a filename: ", 2048, (WINDOW *) 0, 5, 5); if (p && *p) { char *sysbuf = emalloc("text:ViewFile", strlen(p) + sizeof(DEFAULT_PAGER) + 3); (void) sprintf(sysbuf, "%s %s", DEFAULT_PAGER, p); LQU_CursesSafeystem(sysbuf, &Retval); (void) efree(sysbuf); } else { beep(); } return Retval; } int MF_ListFiles() { #ifndef LQFILE # define LQFILE "lqfile" #endif char *buf = emalloc("text:ListFile", sizeof(LQFILE) + sizeof(DEFAULT_PAGER) + 6); int Retval = 0; /* +4 is for "-a | " and \0 */ if (!buf) { beep(); return -1; } (void) sprintf(buf, "%s -a | %s", LQFILE, DEFAULT_PAGER); LQU_CursesSafeystem(buf, &Retval); (void) efree(buf); return Retval; } int MF_AddNewFile() { extern char *AskForString(); char *p; p = AskForString( "Enter a file to add: ", 2048, (WINDOW *) 0, 5, 5); if (p && *p) { char sysbuf[2048 + 50]; (void) sprintf(sysbuf, "lqaddfile -v \"%s\"", p); LQU_CursesSafeystem(sysbuf, (int *) 0); } else { beep(); } return 0; } int MF_NotDone() { mvwaddstr(stdscr, LINES - 1, 1, "NOTDONE"); return 0; } static t_MenuBar *MainMenuBar; t_MenuBar * SetUpMenus() { extern t_Menu *NewMenu(); t_Menu *Menu; MainMenuBar = new(t_MenuBar); if (MainMenuBar == (t_MenuBar *) 0) { error(ERR_FATAL|ERR_MEMORY, "Not enough memory for main menu bar"); } MainMenuBar->MenuBarId = 0; /* Private (oh for c++) */ MainMenuBar->HowManyMenus = 0; MainMenuBar->SelectedMenu = 0; MainMenuBar->ScrollOffset = 0; /* Now some menus: */ Menu = NewMenu("File"); Menu->Description = "\ File Operations -- \n\ New Words -- selecting files based on keywords; \n\ Save/Open List -- remembering a list of files for later use \n\ Finish -- leave the program (you can also type \"q\") \n\ \n\ You probably want to use \"New Words\" to start with, \n\ and then use the \"All Words\" menu to see the result."; Menu->Items = (t_MenuItem *) emalloc( "text::SetUpMenus.MenuItem", sizeof(t_MenuItem) * 4 ); Menu->Items[0].Name = "New Words"; Menu->Items[0].NameLength = 0; Menu->Items[0].Function = GetWords; Menu->Items[0].Description = "Type in a new list of words\n\ for which to search"; Menu->Items[1].Name = "Save List"; Menu->Items[1].NameLength = 0; Menu->Items[1].Function = MF_SaveList; Menu->Items[1].Description = "Save List -- Save the list of files that\n\ you have found in a file"; Menu->Items[2].Name = "Open List"; Menu->Items[2].NameLength = 0; Menu->Items[2].Function = MF_OpenList; Menu->Items[2].Description = "Open List -- Open a list of filenames\n\ that you previously saved"; Menu->Items[3].Name = "Finish"; Menu->Items[3].NameLength = 0; Menu->Items[3].Function = MF_EndProg; Menu->Items[3].Description = "Finish -- leave the program"; Menu->HowManyItems = 4; MainMenuBar->Menus[MainMenuBar->HowManyMenus++] = Menu; /* Now choosing words */ Menu = NewMenu("All Words"); Menu->Description = "\ Main Word Menu\n\ Once you have typed one or more phrases using \n\ \"New Words\" from the \"File\" menu, you can \n\ use this menu to find the list of files which \n\ contain the phrases, and then you can look at \n\ the files with \"Browse All\".\ "; Menu->Items = (t_MenuItem *) emalloc("Word Menu",sizeof(t_MenuItem) * 3); Menu->Items[0].Name = "Match All"; Menu->Items[0].NameLength = 0; Menu->Items[0].Function = MF_MatchAll; Menu->Items[0].Description = "Look for files containing\n\ the phrases you have entered."; Menu->Items[1].Name = "Browse All"; Menu->Items[1].NameLength = 0; Menu->Items[1].Function = MF_BrowseAll; Menu->Items[1].Description = "Browse through the matches (if any)\n"; Menu->HowManyItems = 2; MainMenuBar->Menus[MainMenuBar->HowManyMenus++] = Menu; /* Setting match level */ Menu = NewMenu("Matching"); Menu->Description = "\ Matching Menu\n\ Use this menu if you want finer control over \n\ the way phrases are matched.\n\ \n\ The \"Rough Matching\" option will generally \n\ produce the most matches.\n\ After you have changed the match level, \n\ you have to use \"Match All\" from the \n\ \"All Words\" menu.\ "; Menu->Items = (t_MenuItem *) emalloc("MatchingMenu",sizeof(t_MenuItem) * 3); Menu->Items[0].Name = "Exact Matching"; Menu->Items[0].NameLength = 0; Menu->Items[0].Function = MF_SetExactMatch; Menu->Items[0].Description = "Match phrases as exactly as possible,\n\ including Case, pluralS and Possessive's"; Menu->SelectedLine = 1; /* the default */ Menu->Items[1].Name = "User Matching"; Menu->Items[1].NameLength = 0; Menu->Items[1].Function = MF_SetUserMatch; Menu->Items[1].Description = "Match phrases roughly, but match Case,\n\ pluralS and so on exactly when given"; Menu->Items[2].Name = "Rough Matching"; Menu->Items[2].NameLength = 0; Menu->Items[2].Function = MF_SetRoughMatch; Menu->Items[2].Description = "Ensure that words in phrases are in\n\ the same order as you type, but don't\n\ bother about cAsE, plurals, etc."; Menu->HowManyItems = 3; MainMenuBar->Menus[MainMenuBar->HowManyMenus++] = Menu; /* Indexing files... */ Menu = NewMenu("Indexing"); Menu->Description = "\ Indexing Menu\n\ Use this menu to add new files to the index, \n\ to examine the list of indexed files, or\n\ to examine the files themselves.\ "; Menu->Items = (t_MenuItem *) emalloc("IndexingMenu",sizeof(t_MenuItem) * 3); Menu->Items[0].Name = "Add New file"; Menu->Items[0].NameLength = 0; Menu->Items[0].Function = MF_AddNewFile; Menu->Items[0].Description = "\ Add a new file to the index, \n\ so that it can be found later. "; Menu->Items[1].Name = "List Index"; Menu->Items[1].NameLength = 0; Menu->Items[1].Function = MF_ListFiles; Menu->Items[1].Description = "\ See the list of files that are \n\ already in the index."; Menu->Items[2].Name = "View File"; Menu->Items[2].NameLength = 0; Menu->Items[2].Function = MF_ViewFile; Menu->Items[2].Description = "Look at a file (using pg) "; Menu->HowManyItems = 3; MainMenuBar->Menus[MainMenuBar->HowManyMenus++] = Menu; /* Patterns... */ Menu = NewMenu("Patterns"); Menu->Description = "\ Patterns Menu\n\ This menu lets you specify the order in which files \n\ are presented, and also how inexact words --- like \n\ \"someth*\" or \"Is[ia][ia]*h\" --- are matched. \n\ See the explanations of the individual menu items \n\ for more details of what they do.\ "; Menu->Items = (t_MenuItem *) emalloc("PatternsMenu",sizeof(t_MenuItem) * 9); Menu->Items[0].Name = "Prefer All"; Menu->Items[0].NameLength = 0; Menu->Items[0].Function = MF_NotDone; Menu->Items[0].Description = "\ Put files containing all of the phrases \n\ first in the list when browsing. "; Menu->Items[1].Name = "Prefer Most"; Menu->Items[1].NameLength = 0; Menu->Items[1].Function = MF_NotDone; Menu->Items[1].Description = "\ Put the files containing the most matches\n\ first in the list when browsing."; Menu->Items[2].Name = "Prefer Oldest"; Menu->Items[2].NameLength = 0; Menu->Items[2].Function = MF_NotDone; Menu->Items[2].Description = "\ Put the oldest files entered into the database \n\ first in the list when browsing. "; Menu->Items[3].Name = "Unsorted"; Menu->Items[3].NameLength = 0; Menu->Items[3].Function = MF_NotDone; Menu->Items[3].Description = "\ Don't bother sorting the list of files before browsing; \n\ this is usually the fastest option."; Menu->Items[4].Name = "Reverse"; Menu->Items[4].NameLength = 0; Menu->Items[4].Function = MF_NotDone; Menu->Items[4].Description = "\ Reverse the order in which files are presented \n\ (so, for example, if they are to be sorted on \n\ the number of matches, you will get the files \n\ with only one match before those containing several \n\ matches, and so on) "; Menu->Items[5].Name = "No Patterns"; Menu->Items[5].NameLength = 0; Menu->Items[5].Function = MF_NotDone; Menu->Items[5].Description = "Don't allow patterns in words or phrases. "; Menu->Items[6].Name = "Simple Patterns"; Menu->Items[6].NameLength = 0; Menu->Items[6].Function = MF_NotDone; Menu->Items[6].Description = "\ When you type words to be matched, a * will \n\ mean any sequence of characters, and a ? a single character, \n\ so that *day would find all occurrences of Monday, Tuesday, \n\ Holiday, and so on, whilst bo? would look for all \n\ three-letter words beginning in \"bo\", such as box and boy. "; Menu->Items[7].Name = "Complex patterns"; Menu->Items[7].NameLength = 0; Menu->Items[7].Function = MF_NotDone; Menu->Items[7].Description = "\ Allow full (\"egrep\"-style) regular expressions when looking for \n\ words. This means that when you are entering NEW WORDS to search \n\ for, certain characters have special meaning: \n\ x* matches any number of \"x\", where \"x\" can be anything;\n\ . matches a single character (letter or digit);\n\ a|b means either \"a\" or \"b\" (within a single word); \n\ [xyz] matches a single character that's an x, y or z; you\n\ can use a - to represent a range, as in [a-zA-Z0-9],\n\ and of course [aeiou]* would mean 0 or more vowels.\n\ See the User Reference Book for more detail on this.\n\ "; Menu->HowManyItems = 8; MainMenuBar->Menus[MainMenuBar->HowManyMenus++] = Menu; return MainMenuBar; } WINDOW *WordInfoBox = 0; void SetUpScreen() { /* Put some important info on the screen itself... * One could use menus to change the screen display, perhaps. * A more general program would use a screen form to do this. */ /* First, version information */ mvwprintw(stdscr, 10, 0, "%s revision %s", progname, "$Revision: 1.16 $"); mvwaddstr(stdscr, 11, 0, "[press return to continue]"); refresh(); (void) getch(); clear(); refresh(); /* Now, we want to tell the user what to do... */ ShowInstructions(); } static void SetMessage(s) char *s; { move(3, 1); clrtoeol(); mvwaddstr(stdscr, 3, 1, s); } static void ClearMessage() { move(3, 1); clrtoeol(); } static void ShowInstructions() { char *s; s = "\ Use the arrow keys to choose a menu at the top of the screen, \n\ and then use the space bar to select it. \n\ Use the up and down arrow keys to choose a command, \n\ and then press space. \n\ You can press x for an eXplanation of any menu or menu item. \n\ \n\ You probably want to start by selecting the FILE menu,\n\ and choosing NEW WORDS from it. \n\ "; ClearInstructions(); mvwaddstr(stdscr, LINES - 12, 3, s); refresh(); } static void ClearInstructions() { int i; /* get rid of "Type the list... and press F1" */ /* ClearMessage(); */ /* get rid of the instructions */ for (i = LINES - 12; i < LINES; i++) { if (PhraseCount - 5 < i) { /* don't overwrite phrases */ move(i, 0); clrtoeol(); } } } static void StartCurses() { if (InCurses) { error(ERR_FATAL|ERR_INTERNAL, "StartCurses() called in curses mode"); } (void) initscr(); InCurses = 1; (void) cbreak(); (void) noecho(); (void) nonl(); (void) keypad(stdscr, TRUE); } static void EndCurses() { (void) attrset(0L); clear(); (void) refresh(); InCurses = 0; (void) endwin(); }