/* * Concordance Programming Language Spell Check Program * * Copyright (c) 1990, 1991 Dataflight Software. * All Rights Reserved. * 3200 Airport Avenue, Suite 19 * Santa Monica, CA 90230 * * ALL RIGHTS RESERVED. * * Unauthorized distribution, adaptation or use may be * subject to civil and criminal penalties. * */ /**************************************************************** * Name: spell * * Synopsis: Spell check program. Scans documents and asks * * user to correct misspelled words. * * Note: This program creates a file to store skipped * * words. The file name is created by combining * * the data base path/name with the file extension * * ".SK" This will cause the file to be created * * in the same directory as the data base. To the * * casual user it will actually look like a file * * that was created by Concordance. However, we * * use a two letter file extension to designate it * * as a user created file. We leave all three * * letter data base file extensions to Dataflight * * Software. This avoids future conflicts. * ****************************************************************/ /* The program can be run from Concordance, or called with the ** the run() function by passing Spell() the data base handle. */ main() { int error, line, i; /* { error recovery loop. It catches any runtime bugs. */ if (ver() < 6.60) { cls(); puts(0,0,"Version 6.60 required for spell checking."); getkey(); return; } while(1) { eval("Spell(0)",error,line); while(keypress()) getkey(); /* Exit the program if no error was encountered, or ** if the exit() function was used. exit() produces ** an error code 124, "Program stopped." */ if (error == 124) exit(); if (error == 0) break; /* Notify the user of error recovery, close all files, rerun program. */ if (messageBox("Recover from error "+str(error)+" on line "+str(line)+"?", "Spell Check", MB_YESNO) == IDYES) return; for(i = 0; i <= 16; i = i + 1) { closedb(i); btclose(i); close(i); } } } /*********************************************************************/ char SPELLCHECK = 1, DOCUMENTS = 2, PRINT = 3; Spell(int db) { int CurrentMenu; int i; char string[100]; initialize(); if (db.documents < 0) { if (getfile("Database","*.DCB",string) == CR) db = op}b(string); } /* Set the global default values used in this file. */ if (db.documents < 0) return(messageBox("Please open a data base first.", "Spell Checker", MB_OK)); else { /* query(db,ActiveQuery = db.query); Current active/selected query. */ FirstDocument = docno(db); /* First document to spell check. */ LastDocument = count(db); /* Last document to spell check. */ for(i = 1; i <= db.fields; i = i + 1) FieldOrder[i] = db.order[i]; } Status(db,TRUE); CurrentMenu = SPELLCHECK; while(CurrentMenu > 0) { /* Display one of this module's menus. */ switch(CurrentMenu) { case SPELLCHECK: i = SpellMenu(db); break; case DOCUMENTS: i = DocumentsMenu(db); break; case PRINT: i = Print(db); break; default: i = 0; CurrentMenu = SPELLCHECK; break; } /* See which menu should be displayed next. */ cursoroff(); switch(i) { case LEFT: if (CurrentMenu > SPELLCHECK) CurrentMenu = CurrentMenu - 1; else CurrentMenu = PRINT; break; case RIGHT: if (CurrentMenu < PRINT) CurrentMenu = CurrentMenu + 1; else CurrentMenu = SPELLCHECK; break; case 0: case ESC: if (CurrentMenu == SPELLCHECK) CurrentMenu = -1; else CurrentMenu = SPELLCHECK; break; } } SaveConfig(); } /* Spell() */ /*********************************************************************/ text MMenu[6]; SpellMenu(int db) { int finished, key, next, i; char string[80]; text screen; /* Initialize global variables and assign menu options. ** A data base isn't opened explicitly here, it is assumed ** that a data base may already be open, with handle = 0. ** All functions check for an open data base before ** processing with an "if (db.documents < 0) return;" */ /* MMenu[00] = "Spell Checker"; ** MMenu[01] = "Fields..."; ** MMenu[02] = "Edit Skip List"; ** MMenu[03] = "Clear Skip List"; ** MMenu[04] = "Go"; */ /* Display menu and act on selection. */ next = 1; screen = save(0,10,8,28); while(finished == FALSE) switch(next = menu(0, 10, 8, 28, MMenu, next,"FECGX")) { /* Undocumented feature: Display available memory */ case 'X': string = trim(str(memavl()/1024,9,0,','))+"K"; puts(0,80-len(string),string,MenuColor_); break; /* case 1: Change the query. */ /* if (db.documents >= 0) { */ /* restore(0,10,screen); */ /* query(db,ActiveQuery = GetNInfo("Query:",ActiveQuery,db.query)); */ /* FirstDocument = docno(db); First document to spell check. */ /* LastDocument = count(db); Last document to spell check. */ /* Status(db,TRUE); */ /* screen = save(0,10,8,28); */ /* } */ /* break; */ case 1: /* Mark fields to spell check. */ string = "markfields(db,FieldOrder,'Mark fields to spell check',0,'E')"; eval('run("mark",string)',i); if (i) if (i == 138) messageBox("Can't find the MARK program to run", "Spell Checker", MB_OK); else messageBox("Error "+str(i)+" while running MARK.CPL", "Spell Checker", MB_OK); for(i = 1; i <= db.fields; i = i + 1) db.order[i] = FieldOrder[i]; Status(db,TRUE); break; case 2: /* Edit the skip list. */ if (db.documents >= 0) EditList(db.database+SkipExtension); break; case 3: /* Clear the skip list. */ if (db.documents >= 0) { if (exist(db.database+SkipExtension)) erase(db.database+SkipExtension); else messageBox("The list has already been cleared.", "Spell Checker", MB_OK); } break; case 4: /* { spell checking. */ if (db.documents < 0) Message("Please open a database first."); else SpellCheck(db); cls(); Status(db,TRUE); break; case 0: case LEFT: case RIGHT: case ESC: /* Return to Concordance */ finished = TRUE; break; default: next = 1; break; } restore(0,10,screen); return(next); } /* Spell() */ /****************************************************************/ text DMenu[3]; DocumentsMenu(int db) { int finished, key, next, i; char string[80]; text screen; /* DMenu[00] = "Document Range"; ** DMenu[01] = "First..."; ** DMenu[02] = "Last..."; */ /* Display menu and act on selection. */ screen = save(0,27,5,46); next = 1; while(finished == FALSE) switch(next = menu(0, 27, 5, 46, DMenu, next,"FL")) { case 1: restore(0,27,screen); if (db.documents > 0) FirstDocument = max(1,GetNInfo("First Document:",FirstDocument,count(db))); break; case 2: restore(0,27,screen); if (db.documents >= 0) LastDocument = GetNInfo("Last Document:",LastDocument,count(db)); break; case 0: case LEFT: case RIGHT: case ESC: /* Return to Concordance */ finished = TRUE; break; default: next = 1; break; } restore(0,27,screen); return(next); } /* DocumentsMenu() */ /**************************************************************** * Name: EditList * * Synopsis: Edits btree file allowing deletion and adds. * ****************************************************************/ EditList(text FileName) { char key[125]; int data, handle, i, finished; text options[5], screen, EditScreen; /* Assign menu options to the menu's array. */ options[1] = "Add to Dictionary"; options[2] = "Delete Entry"; options[3] = "Quit"; finished = 0; /* Open the dictionary file and cycle until the user hits ESC key. */ if ((handle = btopen(FileName)) == EOF) handle = btcreate(FileName,0); if (handle == EOF) Message("Can't open or create the file: "+FileName); else { EditScreen = save(6,20,22,61); puts(22,22,"[CtrlEnter] New Words"); while(finished == 0) switch(btmenu(handle, 6, 20, 22, 61, "Skip Words List", key, data)) { case 0: finished = 1; case 1: case 2: if (key == "") finished = 1; else { /* Use the selected word as the menu's title. */ /* Ask user what they want to do: Add or Delete. */ options[0] = key = lower(key); screen = save(11,48,17,70); switch(menu(11, 48, 17, 70, options, 1,"ADQ")) { case 'A': case 1: btinsert(handle,key,0); case 'D': case 2: btdelete(handle,key); case 'Q': case 3: finished = 1; } restore(11,48,screen); } break; case -1: Message("btmenu() processing error."); finished = 1; break; } cursor(0,0); btclose(handle); restore(6, 20, EditScreen); } return(0); } /* EditList() */ /**************************************************************** * Name: Status * * Synopsis: Displays data base and program status. * ****************************************************************/ Status(int db, initScreen) { cursoroff(); puts(0,0,pad(FileName(db.database),'L',80),MenuColor_); puts(1,0,rep('-',80),MenuColor_); scroll(2,0,MaxRow_,79,0,0); if (db.documents >= 0) { puts(0,8,"|",MenuColor_); puts(1,8,"-",MenuColor_); } if (initScreen) SpellStatus(db); } /* Status() */ /**************************************************************** * Name: SpellStatus * * Synopsis: Displays data base and program status. * ****************************************************************/ SpellStatus(int db) { char string[125]; int i; puts(0,12,MMenu[0],MenuColor_); puts(0,29,DMenu[0],MenuColor_); puts(0,55,PMenu[0],MenuColor_); string = "markfields(db,FieldOrder,'Mark fields to spell check',5,0)"; eval('run("mark",string)',i); if (db.documents >= 0) { /* query(db,ActiveQuery,string); */ string = pad(str(docno(db),4,0,'Z')+" "+string,'L',40)+str(count(db),9,0,',')+" Documents"; string = string + str(hits(db),9,0,',')+" Occurrences"; puts(4,0,string); } } /* SpellStatus() */ /**************************************************************** * Name: SpellCheck * * Synopsis: Spell checks database. * ****************************************************************/ SpellCheck(int db) { int i, key, document; int skip, dict; text line; char string[80]; /* Return right away if the data base is not open. */ /* The data base must be opened before entering this routine. */ cls(); Status(db,FALSE); /* Open or create a btree to hold the words the user */ /* wants to skip. This can be used as an audit trail */ /* later by printing all skipped words. */ if ((skip = btopen(db.database+SkipExtension)) == EOF) skip = btcreate(db.database+SkipExtension,0); /* Prompt the user for the main 27,000 word dictionary. */ /* Return if it can't be found, otherwise every word */ /* would likely be considered a misspelling. */ if ((dict = btopen("WORDS.LST")) == EOF) { if ((dict = btopen("..\WORDS.LST")) == EOF) { if (getfile("Main Dictionary","*.LST",string) == CR) dict = btopen(string); } } if (dict == EOF) { btclose(skip); Message("Error an encountered while opening main dictionary: Words.lst"); return; } /* This is the main processing loop. It starts with the CURRENT document. */ /* Processing continues until there are no more documents to process. The */ /* loop may be processing the results of a search, or the entire DB, it */ /* does not know which, nor does it care. */ /* The loop displays the document number, then cycles through the fields. */ /* It is set to spell check only Paragraph fields. It could be set to run */ /* against fixed length text fields by changing the "db.type" test. */ document = FirstDocument; for(i = goto(db,document);(i > 0) and (docno(db) <= LastDocument); i = next(db,document)) { line = "Document "+trim(str(document,12,0,','))+" of "+trim(str(LastDocument,12,0,',')); puts(0,80 - len(line),line,QueryHighlight_,QueryHighlightBackground_); for(i = 1; (i <= db.fields) and (key <> ESC); i = i + 1) if ((db.type[i] == 'P') and FieldOrder[i]) key = CheckField(db,i,skip,dict); while(keypress()) key = getkey(); if (key == ESC) { btclose(skip); btclose(dict); return; } } btclose(skip); btclose(dict); cls(); } /* SpellCheck() */ /**************************************************************** * Name: CheckField * * Synopsis: Spell checks the selected field. * ****************************************************************/ CheckField(int db, field, skip, dict) { int i, l, data, key[1], j; char string[80]; /* Display some status information and start checking the field. */ cursoroff(); puts(0,10,db.name[1]+": "+pad(ToString(db,1),'L',20),MenuColor_); box(13,0,MaxRow_ - 1,79,'3D'); puts(13,1,db.name[field]); puts(2,0,"Scanning..."); /* This loop cycles through the length of the selected field. */ /* It is important to get the length of the field at the */ /* bottom of the loop before testing the loop-control again. */ /* The user may have edited the field and changed the field's */ /* length in the Correction() function. */ /* The function stores the number of characters scanned in */ /* the "i" variable and displays the count to keep the user */ /* aware of scanning progress. It uses the wordlen() function */ /* to locate a word and its length. Posessive forms of a word */ /* are stripped of their "'s" suffixes. The LookUp() function */ /* returns a zero value if the word was not located in the */ /* main dictionary or in the skip-words dictionary. Zero */ /* return values go to the user for possible correction. The */ /* Correction() function returns the length of the word, */ /* which may have been changed. Processing continues by */ /* adding the length of the word to the current scan position */ /* stored in "i" and continuing from there. Processing stops */ /* when the field is fully scanned. The user can manually */ /* cancel the scan by pressing the ESC key at any time. */ i = 1; j = len(db->field); wrap(db->field,77); while((i < j) and (key[0] <> ESC)) { puts(2,12,str(i,6,0,',')); while((i < j) and ((l = wordlen(db,addr(db->field,i))) == 0)) i = i + 1; if (i < len(db->field)) { memcpy(string,addr(db->field,i),l); string[l] = 0; /* if (j = match(string = substr(db->field,i,l),"'s",1)) string = substr(string,1,j - 1); */ if (LookUp(string, dict, skip) == FALSE) { if ((l = Correction(db, field, i, l, dict, skip, string, key)) == 0) { while(wordlen(db,addr(db->field,i)) and (i > 1)) i = i - 1; } } } i = i + l; j = len(db->field); while(keypress()) key[0] = getkey(); } wrap(db->field, 65000); return(key[0]); } /* CheckField() */ /**************************************************************** * Name: Correction * * Synopsis: Misspelling was found, correct it. * * Return: Length of the word. * ****************************************************************/ Correction(int db, field, offset, length, dict, skip; char string[]; int key[]) { int LineOffset, LineLength; int finished, i, row, data, error; text option[7], screen, SScreen; char word[80]; cursor(0,0); option[1] = "Ignore this word"; option[2] = "Skip all occurrences"; option[3] = "Correct word"; option[4] = "Edit document"; option[5] = "Look-up word"; option[6] = "Global editing"; LineOffset = findline(db->field, offset, LineLength); if (i = findpline(db->field, offset, LineLength)) row = 15; else { row = 14; i = 1; } show(db->field,14,1,MaxRow_ - 2,78,i); puts(row,(offset - LineOffset)+1,string,QueryHighlight_,QueryHighlightBackground_); option[0] = string; screen = save(3,0,12,30); while(keypress()) getkey(); finished = FALSE; while(finished == FALSE) switch(menu(3, 0, 12, 30, option, 1,"ISCELG")) { /* Ignore this word. */ case 1: finished = TRUE; /* Skip all occurrences. */ case 2: btinsert(skip,lower(string),0); finished = TRUE; /* Correct the word. */ /* Just show it and let the user correct it. */ case 3: key[0] = 0; /* XXXXXXXXXXXXXXXXXXXXXXXXXX */ while((key[0] <> CR) and (key[0] <> ESC)) key[0] = getline(4,35,40,string); if (key[0] == CR) { eval("db->field = substr(db->field,1,offset - 1)+string+addr(db->field,offset+length)",length); if (length) Message("Insufficient memory to make correction."); /* Set length of word to zero so that the */ /* correction is spell checked again. */ length = 0; show(db->field,14,1,MaxRow_ - 2,78,i); finished = TRUE; } scroll( 4,35, 4, 79, 0, 1); key[0] = 0; /* Edit the record in the window. */ case 4: key[0] = 0; length = 0; puts(13,20," [Esc] Abandon [F10] Save ", TextColor_); /* Save the field before editing. */ eval("SScreen = db->field",error); if (error) Message("Warning: Insufficient memory to abandon any edits."); while((key[0] <> ESC) and (key[0] <> F10) and (key[0] <> CTRLENTER)) key[0] = edit(db->field,15,1,MaxRow_ - 2,78,0,i,offset); if ((key[0] == ESC) and (error == 0)) db->field = SScreen; SScreen = ""; key[0] = 0; finished = TRUE; /* Status(db,FALSE); */ puts(MaxRow_,0,rep(' ',80)); break; /* Display the main dictionary with sounds-alike words. */ case 5: word = string; SScreen = save(3,40,12,70); key[0] = btmenu(dict, 3, 40, 12, 70, "Look up", word, data); restore(3,40,SScreen); SScreen = ""; /* Did the user enter a correction? Replace word if so. */ if ((key[0] == 1) or (key[0] == 2)) { db->field = substr(db->field,1,offset - 1)+ word+ addr(db->field,offset+length); show(db->field,14,1,MaxRow_ - 2,78,i); finished = TRUE; /* Set length of word to zero so that the */ /* correction is spell checked again. */ length = 0; } key[0] = 0; case 6: /* Global Editing option. Save and restore the current query and document number. */ i = docno(db); search(db,string); global(db); query(db,-1); goto(db,i); case 0: /* User hit ESC key, let's finish up and return. */ finished = TRUE; length = len(db->field); key[0] = ESC; break; } restore(3,0,screen); cursoroff(); return(length); } /* Correction() */ /**************************************************************** * Name: ToString * * Synopsis: Converts any field to a short string. * ****************************************************************/ ToString(int db, field) { text line; switch(db.type[field]) { case 'T': line = db->field; case 'D': line = dtoc(db->field); case 'N': line = str(db->field); case 'P': /* Note that Carriage Returns and Line Feeds are not */ /* converted to spaces in the returned line. */ line = substr(db->field,1,25); } return(line); } /* ToString() */ /**************************************************************** * Name: LookUp * * Synopsis: Find word in the skip or standard dictionaries. * * Return: A nonzero value if word is located. * * Note: The "word" parameter must NOT be declared as a * * char array. LookUp() modifies the variable, * * declaring it as a char array would also modify * * the value in the function that called LookUp(). * ****************************************************************/ LookUp(text word; int dict, skip) { int data; /* Ignore all numbers. */ if (isdigit(addr(word,1))) return(TRUE); /* Look for the word in the English dictionary. */ if (btfind(dict,word,data) == 0) return(TRUE); /* Look for the word in the English dictionary in all lower case. */ word = lower(word); if (btfind(dict,word,data) == 0) return(TRUE); /* All words in the skip dictionary are stored in lower case. This */ /* could be changed so that they are stored EXACTLY as they occur */ /* in the data base since capitalization may be important, but */ /* would introduce a problem in ignoring capitalized first words. */ if (btfind(skip,word,data) == 0) return(TRUE); /* Didn't find the word, it's a misspelling. */ return(FALSE); } /* LookUp() */ /**************************************************************** * Name: Print * * Synopsis: Display print options menu. * ****************************************************************/ text PMenu[09]; Print(int db) { text screen, temp; int finished, next, key, row, col; char string[MAXSETUP+1]; /* Assign options to the Print Menu. */ /* PMenu[00] = "Print"; ** PMenu[01] = "Print"; ** PMenu[02] = "Lines per Page"; ** PMenu[03] = "Width of Page"; ** PMenu[04] = "Margin"; ** PMenu[05] = "Underline Setup String"; ** PMenu[06] = "Normal Setup String"; ** PMenu[07] = "Initialization String"; ** PMenu[08] = "Default Printers"; */ /* Screen coordinates where user input is received. */ row = 15; col = 57; screen = save(0,45,14,74); while(finished == FALSE) { switch(next = menu(0, 45, 11, 70, PMenu, next,"PLWMUNID")) { case 1: restore(0,45,screen); PrintPac(db,FirstDocument,LastDocument); Status(db,TRUE); break; case 2: restore(0,45,screen); LinesPerPage = GetNInfo("Lines per Page:",LinesPerPage,250); break; case 3: restore(0,45,screen); Width = GetNInfo("Page Width:",Width,250); break; case 4: restore(0,45,screen); LeftMargin = GetNInfo("Left Margin:",LeftMargin,250); break; case 5: restore(0,45,screen); string = BoldOn; GetInfo("Underline:",string); BoldOn = string; break; case 6: restore(0,45,screen); string = BoldOff; GetInfo("Normal:",string); BoldOff = string; break; case 7: restore(0,45,screen); string = PrinterReset; GetInfo("Initialize:",string); PrinterReset = string; break; case 8: restore(0,45,screen); DefaultPrinters(); break; case LEFT: case RIGHT: case ESC: case 0: finished = TRUE; break; } temp = ""; cursoroff(); cursor(0,0); } restore(0,45,screen); return(next); } /* Print() */ /**************************************************************** * Name: PrintPac() * * Synopsis: Prints range of documents underlining unknown * * words. * ****************************************************************/ int CurrentLine, CurrentPage; /* Current line and page number globally declared. */ PrintPac(int db, start, finish) { int i, j, k, key, error, number, file; int document, field; int key; char string[210]; int WordOffset; /* These variables are not global, */ int LineOffset; /* they are capitalized to make */ int LineLength; /* the program easier to read and */ text TextLine; /* understand. */ int dict, skip; /* Spell check b+tree's. */ /* Return if 0 documents found in search. */ if (count(db) <= 0) return(Message("No documents were located in this search.")); /* Ask the user to name the file for printer output. */ key = getfile('Print Destination (Use "prn" for printer)',"*.prn",string); /* */ /* Quit if they hit the Escape key. */ if (key == ESC) return; /* Try to open the user selected file, quit if an error occurs. */ if ((file = open(string,"w+")) == EOF) { beep(0,0); Message("Can't open the print file."); return; } /* Open the U.S. English dictionary used with all databases. */ if ((dict = btopen("WORDS.LST")) == EOF) { if ((dict = btopen("..\WORDS.LST")) == EOF) { if (getfile('Words.lst Main Dictionary',"*.LST",string) == CR) dict = btopen(string); } } /* Open up the local dictionary used for this database. */ skip = btopen(db.database+SkipExtension); /* Set local and global variables for tracking paging information. */ CurrentLine = 1; /* Current line on the printer. */ CurrentPage = 0; /* Current page on the printer. */ /* Print the first page header, then enter the printing loop. */ if (write(file,PrinterReset,len(PrinterReset)) < len(PrinterReset)) error = EOF; /* Start with the first document requested for print. */ puts(0,10,rep(' ',70),MenuColor_); if (start > finish) { i = start; start = finish; finish = i; } error = goto(db, start); document = 1; /* This is the loop which prints fields and underlines words. */ Header(db, file); while((error <> EOF) and (start <= finish)) { /* Display the progress in the status box. */ puts(0,10," Page"+str(CurrentPage,8,0),MenuColor_); for(k = 1; (error <> EOF) and (k <= db.fields); k = k + 1) for(field = 1; field <= db.fields; field = field + 1) if (FieldOrder[field] == k) { switch(db.type[field]) { case 'D': write(file,rep(' ',LeftMargin)+pad(db.name[field],'L',13)+" = ",16+LeftMargin); TextLine = dtoc(db->field); writeln(file,TextLine,len(TextLine)); CurrentLine = CurrentLine + 1; case 'N': write(file,rep(' ',LeftMargin)+pad(db.name[field],'L',13)+" = ",16+LeftMargin); TextLine = str(db->field,db.length[field],db.places[field],0); writeln(file,TextLine,len(TextLine)); CurrentLine = CurrentLine + 1; case 'T': write(file,rep(' ',LeftMargin)+pad(db.name[field],'L',13)+" = ",16+LeftMargin); TextLine = trim(db->field); if (writeln(file,TextLine,len(TextLine)) < len(TextLine)) { Message("Disk full or printer error."); error = EOF; } CurrentLine = CurrentLine + 1; case 'P': error = PrintParagraph(file, db, field, dict, skip); } /* Advance to the next page if the line limit has been reached. */ if (CurrentLine >= LinesPerPage - 2) Header(db, file); break; } /* Print a blank line between records */ /* and advance to the next record. */ writeln(file,"",0); if (error <> EOF) error = goto(db,start = start + 1); document = document + 1; /* Check for a user requested exit. */ while(keypress()) if ((key = getkey()) == ESC) error = EOF; } /* Eject the last page from the printer. */ write(file,chr(CR)+chr(12),2); close(file); btclose(skip); btclose(dict); return; } /* PrintPac() */ /**************************************************************** * Name: PrintParagraph * * Synopsis: Prints a paragraph for textpac mode, with words * * underlined that are not in the dictionaries. * ****************************************************************/ PrintParagraph(int file, db, field, dict, skip) { int i, j; int error; int line; /* Offset of the current line. */ int eol; /* Offset of the } of the current line. */ int l; /* Length of the current word. */ char string[80]; /* Stores the word for lookup(). */ char spaces[80]; /* Something to print for the left margin.*/ /* Wordwrap the field to fit the page. */ wrap(db->field,Width - (16+LeftMargin)); /* Print the field's title. */ spaces = rep(' ',LeftMargin)+pad(db.name[field],'L',13)+" : "; line = findline(db->field,1,l); while((i = line) and (error <> EOF)) { /* Print a left margin plus spaces for Paragraph field. */ write(file,spaces,16+LeftMargin); if (i == 1) spaces = rep(' ',16+LeftMargin); /* Calculate the } of the line offset. */ eol = i + l; while(i < eol) { if (l = wordlen(db,addr(db->field,i))) { memcpy(string,addr(db->field,i),l); string[l] = 0; if (LookUp(string, dict, skip)) { if (write(file, string, l) <> l) { error = EOF; Message("Disk full or printer error."); } } else { write(file,BoldOn,len(BoldOn)); if (write(file, addr(db->field,i), l) <> l) { error = EOF; Message("Disk full or printer error."); } write(file,BoldOff,len(BoldOff)); } /* Advance past this word, to the next. */ i = i + l; } else { writec(file,addr(db->field,i)); i = i + 1; } } /* This line is printed, advance to the next line. */ if (writeln(file,"",0) <= 0) { Message("Disk full or printer error."); error = EOF; } if ((CurrentLine = CurrentLine + 1) >= LinesPerPage - 2) Header(db, file); line = findnline(db->field, line, l); while(keypress()) if (getkey() == ESC) if (messageBox("Cancel printing?", "Spell Checker", MB_YESNO) == IDYES) return(EOF); } return(error); } /* PrintParagraph() */ /**************************************************************** * Name: Header * * Synopsis: Prints a page header for the textpac report. * ****************************************************************/ Header(int db, file) { int error; char string[250]; /* Eject the page if it isn't page 0. */ if (CurrentPage) write(file,chr(CR)+chr(12),2); /* Advance the page counter. */ CurrentPage = CurrentPage + 1; CurrentLine = 3; string = rep(' ',LeftMargin)+db.database + pad("Page: "+str(CurrentPage),'R',Width - (len(db.database) + LeftMargin)); writeln(file,string,len(string)); writeln(file,"",0); return(error); } /* Header() */ /**************************************************************** * Name: DefaultPrinters * * Synopsis: Set up strings for common printers. * ****************************************************************/ DefaultPrinters() { text p[12], screen; int next; p[00] = "Default Printers"; p[01] = "Plain Text Printer"; p[02] = "Epson"; p[03] = "Gemini Star"; p[04] = "HP Laserjet & Compatibles"; p[05] = "HP Deskjet"; p[06] = "IBM Proprinter"; p[07] = "Okidata"; p[08] = "NO PRINTER UNDERLINING"; screen = save(9,22,20,55); next = 1; while(next) switch(next = menu(9, 22, 20, 55, p, next)) { case 0: next = 0; case 1: /* Plain text setup strings. */ BoldOn = "<"; BoldOff = ">"; PrinterReset = ""; Width = 80; LeftMargin = 5; LinesPerPage = 60; next = 0; break; case 4: /* Hewlett Packard Laserjet setup strings. */ case 5: BoldOn = chr(27)+"&dD"; /* Turn underline on. */ BoldOff = chr(27)+"&d@"; /* Turn underline off. */ PrinterReset = chr(27)+"E"; Width = 80; LeftMargin = 5; LinesPerPage = 60; next = 0; break; case 3: /* Gemini Star in Epson emulation mode. */ case 7: /* Okidata printer in Epson emulation mode. */ case 2: /* Epson printers. */ BoldOn = chr(27)+"E"; /* Emphasized print on. */ BoldOff = chr(27)+"F"; /* Emphasized print off. */ PrinterReset = chr(27)+"@"; /* Printer reset. */ Width = 80; LeftMargin = 5; LinesPerPage = 60; next = 0; break; case 6: /* IBM Proprinter X24 and XL24. */ /*BoldOn = chr(27)+"-"+chr(1);*/ /* Underscore on. */ /*BoldOff = chr(27)+"-"+chr(0); *//* Underscore off. */ PrinterReset = ""; /* Printer reset. */ Width = 80; LeftMargin = 5; LinesPerPage = 60; next = 0; break; case 8: BoldOn = ""; /* Emphasized print on. */ BoldOff = ""; /* Emphasized print off. */ next = 0; break; } restore(9,22,screen); } /* Defaultprinters() */ /**************************************************************** * Name: GetInfo * * Synopsis: Helper routine to prompt user for information. * ****************************************************************/ GetInfo(text prompt; char string[]) { text screen; int key; char oldstring[sizeof(string)]; screen = save(0,0,0,79); scroll(0,10,0,79,0,0,MenuColor_); key = 0; puts(0,10,prompt,MenuColor_); oldstring = string; while((key <> CR) and (key <> ESC)) if ((key = getline(0,len(prompt)+11,sizeof(oldstring),oldstring,MenuColor_)) == F1) Message("Press [Enter] to finish or [Esc] to cancel."); if (key <> ESC) string = oldstring; restore(0,0,screen); } /* GetInfo() */ /**************************************************************** * Name: GetNInfo * * Synopsis: Helper routine to prompt user for numeric info. * ****************************************************************/ GetNInfo(text prompt; int number, max) { text screen; int key; int oldnumber; screen = save(0,0,0,79); scroll(0,10,0,79,0,0,MenuColor_); key = 0; puts(0,10,prompt,MenuColor_); oldnumber = number; while((key <> CR) and (key <> ESC)) if ((key = getnumber(0,len(prompt)+11,number,0,max,15,0,MenuColor_)) == F1) Message("Press [Enter] to finish or [Esc] to cancel."); if (key == ESC) number = oldnumber; restore(0,0,screen); return(number); } /* GetNInfo() */ /**************************************************************** * Name: LoadConfig * * Synopsis: Set global variables to default values, load * * values from configuration file if available. * * The variables are set to their default values * * if an error is encountered reading the file. * ****************************************************************/ LoadConfig() { char string[MAXSETUP+1]; int i, error; /* Read in the default configuration file if it exists. Read text variables into char strings as text variables cannot be read or written directly due to their variable length. */ if ((i = open(Path(program())+"spell.cfg","r")) <> EOF) { if (read(i,string,MAXSETUP) <> MAXSETUP) error = TRUE; BoldOn = string; if (read(i,string,MAXSETUP) <> MAXSETUP) error = TRUE; BoldOff = string; if (read(i,string,MAXSETUP) <> MAXSETUP) error = TRUE; PrinterReset = string; if (read(i,LinesPerPage,sizeof(LinesPerPage)) <> sizeof(LinesPerPage)) error = TRUE; if (read(i,Width,sizeof(Width)) <> sizeof(Width)) error = TRUE; if (read(i,LeftMargin,sizeof(LeftMargin)) <> sizeof(LeftMargin)) error = TRUE; close(i); } /* Assign default values if the above file read failed. */ if (error or (i == EOF)) { LinesPerPage = 60; /* Number of lines per page before eject. */ Width = 79; /* Width of page in columns. */ LeftMargin = 8; /* Assign a left margin for ring binders. */ PrinterReset = ""; BoldOn = "<"; BoldOff = ">"; } } /* LoadConfig() */ /**************************************************************** * Name: SaveConfig * * Synopsis: Save the global variables to file. * * Note: The values must be written and read in the same * * order by SaveConfig() and LoadConfig(). * ****************************************************************/ SaveConfig() { char string[MAXSETUP+1]; int i, error; /* Save variables to the default configuration file. Copy text variables into char strings as text variables cannot be read or written directly due to their variable length. */ if ((i = open(Path(program())+"spell.cfg","w+")) <> EOF) { string = BoldOn; if (write(i,string,MAXSETUP) <> MAXSETUP) error = TRUE; string = BoldOff; if (write(i,string,MAXSETUP) <> MAXSETUP) error = TRUE; string = PrinterReset; if (write(i,string,MAXSETUP) <> MAXSETUP) error = TRUE; if (write(i,LinesPerPage,sizeof(LinesPerPage)) <> sizeof(LinesPerPage)) error = TRUE; if (write(i,Width,sizeof(Width)) <> sizeof(Width)) error = TRUE; if (write(i,LeftMargin,sizeof(LeftMargin)) <> sizeof(LeftMargin)) error = TRUE; close(i); if (error) erase("spell.cfg"); } } /* SaveConfig() */ /**************************************************************** * Name: FileName * * Synopsis: Trims the path from the file name. * ****************************************************************/ FileName(text name) { int i; if (i = match(name,":",1)) name = substr(name,i+1); while(i = match(name,"\",1)) name = substr(name,i+1); return(name); } /**************************************************************** * Name: Path * * Synopsis: Returns the path up to the last \ and without * * the file's name. * ****************************************************************/ Path(text dosPath) { int i, j; if ((i = match(dosPath,":",1)) == 0) i = match(dosPath,"\",1); while(j = match(dosPath,"\",i+1)) i = j; return(substr(dosPath,1,i)); } /* Path() */ /**************************************************************** * Name: Message * * Synopsis: Displays error message and waits for key. * ****************************************************************/ Message(text message) { messageBox(message, "Spell Checker", MB_OK); } /* Message() */ /**************************************************************** * Name: initialize * * Synopsis: Set global variables to initial values * ****************************************************************/ text SkipExtension = ".SPL"; /* File extension for approved words. */ int LinesPerPage; /* Lines per page. */ text BoldOn, BoldOff; /* Printer escape sequences. */ int Width; /* Width of page, used by header. */ text PrinterReset; /* Escape sequence to reset printer. */ int LeftMargin; /* Left margin added to printed text. */ char FieldOrder[101]; /* Used to specify the fields to print.*/ char MAXSETUP = 20; /* Maximum characters in setup string. */ char ESC = 27, EOF = -1, CR = 13, LF = 10, CTRLENTER = LF, TRUE = 1, FALSE = 0; short F1 = 15104, F2 = 15360, F3 = 15616, F4 = 15872, F5 = 16128, F6 = 16384, F7 = 16640, F8 = 16896, F9 = 17152, F10 = 17408, LEFT = 19200, RIGHT = 19712, UP = 18432, DOWN = 20480, PGUP = 18688, PGDN = 20736, HOME = 18176, } = 20224; int FirstDocument, LastDocument, ActiveQuery; initialize() { cls(); cursoroff(); cursor(0,0); LoadConfig(); PMenu[00] = "Print"; PMenu[01] = "Print"; PMenu[02] = "Lines per Page"; PMenu[03] = "Width of Page"; PMenu[04] = "Margin"; PMenu[05] = "Underline Setup String"; PMenu[06] = "Normal Setup String"; PMenu[07] = "Initialization String"; PMenu[08] = "Default Printers"; MMenu[00] = "Spell Checker"; /* MMenu[01] = "Query..."; */ MMenu[01] = "Fields..."; MMenu[02] = "Edit Skip List"; MMenu[03] = "Clear Skip List"; MMenu[04] = "Go"; DMenu[00] = "Document Range"; DMenu[01] = "First..."; DMenu[02] = "Last..."; } /* Standard dialog button return values. */ int IDOK = 1; int IDCANCEL = 2; int IDABORT = 3; int IDRETRY = 4; int IDIGNORE = 5; int IDYES = 6; int IDNO = 7; /* MessageBox() display options. */ int MB_OK = 0; int MB_OKCANCEL = 1; int MB_ABORTRETRYIGNORE = 2; int MB_YESNOCANCEL = 3; int MB_YESNO = 4; int MB_RETRYCANCEL = 5; int MB_TYPEMASK = 15; int MB_ICONHAND = 16; int MB_ICONQUESTION = 32; int MB_ICONEXCLAMATION = 48; int MB_ICONASTERISK = 64; int MB_ICONMASK = 240; int MB_ICONINFORMATION = MB_ICONASTERISK; int MB_ICONSTOP = MB_ICONHAND; int MB_DEFBUTTON1 = 0; int MB_DEFBUTTON2 = 256; int MB_DEFBUTTON3 = 512; int MB_DEFMASK = 3840; int MB_APPLMODAL = 0; int MB_SYSTEMMODAL = 4096; int MB_TASKMODAL = 8192; int MB_NOFOCUS = 32768;