IMD 1.16: 1/06/2007 9:40:43 FOGCPM.151 --FOGCPM151ANYCODE DOC ANYCODE DOC-01-00 87 ANYCODE ASMCHGCHAR COMCHGCHAR DOC'CHGCHAR AQM !"#DBL COM$%&'DBL DOC(IXMAIN COM)*+,-./012345678IXMAIN COM9:;-CPM151 DOCIXMAIN PAS)<=>?@AIXDIAG PAS BCIXDOIT PASNDEFGHIJKLMIXDONE PASNOPQIXINIT PAS(RSTUVIXSTRG PAS+WXYZ[\IXTEXT PAS]^_`IXTREE PAS%abcdeIXWALK PASfghiINDEXER DOCjklmnopqrstuvwxyINDEXER DOCEz{|}~ABSTRACT IXMERGE COMpIXMERGE PASsThis is the disk name. .pl 88 0 ANYCODE Doug Hurst M IMPORTANԠ NOTE Th documentatio fo ANYCOD i base o a articl submitte t th newl revive "Portabl Companion magazin fo Osborn Compute Owners Th referenc ar Osborn oriented I reality th progra i mor WordSta tha Osborn oriented an i u an runnin o Heat an Zorb machine an i adaptabl t al WordStars Version 2.26 3.0 an 3.3X B absolutel certai tha yo carefull mak AL̠ substitution fo you versio o WordStar Ther ar severa throughout Questions comment an bug shoul b reporte t m a 680 Estrell Ave. Twentynin Palms C 92277 evenin (619 367-109 (evenings). PLEASE! Giv thi articl loo eve i i seem somewha complicated I i useful valuabl modificatio t WordSta an i quit simpl i yo wil tak i ste b step VALUE Make WordSta mor powerfu an tex file mor flexibl an portable ANYCOD doe thi withou enlargin th siz o th mai WordSta progra WS.COM Thi i ver importan point B keepin WS.CO confine t it "stock size i ma installe o reinstalle usin INSTALL.CO a wil withou affectin o havin t reinstal ANYCODE. INTRODUCTION. Th EPSO FX M (w/Graftrax an R series a wel a man othe brand o printers hav man mor mode o operatio availabl tha ca b accesse b normall installe WordStar eve i on use al th built-i an use define prin contro cod facilitie available No onl ca thes built-i an use define code NO kee pac wit th printer' capability onc installed the ar no alterabl withou usin DDT/SID.CO͠ o WordStar' INSTALL.COM Ther ar severa commerciall availabl program tha wil le yo sen al th contro code printe wil accept The diffe fro th on presente i thi articl i tha thi on i free Thi articl i als designe t serv th dua purpos o displayin ho muc untappe potentia ther i i standar WordStar USIN ANYCODE. Befor gettin int th codin an installatio o ANYCODE i i importan t se wha th outcom o you effort wil be Thi will i i hoped piqu you interes an kee yo fro givin up I yo regularl us WordSta wit CHARTEC o simila progra installed yo alread posses th functio tha ANYCOD wil perform You'r welcom t continu anyway A mentione earlier nic featur o ANYCOD i tha i doe no enlarg th siz o WS.CO a man simila commerciall availabl product do. Onc installed us o ANYCOD code ha th sam functiona effec a WordStar' prin contro codes Instea o embeddin the i th tex wit prin contro cod suc a ^PS however yo ente ANYCOD ver muc a th code actuall appea i you printer' user' manual ANYCOD codes however ar no invisibl t th editor I othe words whe yo plac ANYCODE' code i you text the ar treate jus lik norma tex b WordStar Thi mean the wil b include i suc function a wordwra an paragrap reformatting Fo thi reason i i suggeste tha yo ente al you text ge i formatte jus th wa yo want the g bac an inser th ANYCOD codes Thi ma forc yo t us th ^O (releas margin comman mor tha yo ar use t (som line wil exten fa beyon th margin) Thi shoul b o n concer t yo thoug becaus ANYCOD code ar non-printin jus lik WordStar' prin contro codes Th actua lin wil fi withi th margin i i di prio t cod insertion. ANYCOD code wil usuall tak on o th thre form show below jus a mos printer accep code i on o thes thre forms. straigh he code escap he code escap he cod traile cod traile cod 2.... traile cod n Belo ar ANYCOD example o eac typ fo EPSO printers: `0 Woul transmi 0F (tur condense prin mod on). ~4 Woul transmi ,45 (tur emphasize prin on). ~2D`0 Woul transmi ,2Dh,0 (tur o continuou underline). A furthe example shor sectio o text firs withou code an the wit ANYCOD code embedded. Dea Bob, Jus wante t le yo kno wa thrille wit th modifie BIO yo sen me I worke SUPER. Dea Bob, Jus wante t le yo kno wa ~34thrilled~3 wit th modifie ~2D`01BIOS~2D`0 yo sen me I worke `0ESUPER!`14. I th lowe exampl wit th embedde (agai EPSON codes ~3 an ~3 tur italic on/off ~2D`0 an ~2D`0 turn underlin on/of an `0 an `1 tur expande prin on/off. Notic ho th firs lin no extend pas th norma margin Again i WIL NO whe th printin actuall take plac sinc ANYCOD cod i non-printing Th tw lea character fo ANYCOD cod wer picke especiall fo th Osborn computer Yo wil no fin the o you keyboard T mak "~" typ ^? (contro ?) T plac "` o th screen typ ^+ (contro +) I yo haven' see th obviou advantag t sendin th printe code i th ANYCOD forma ove WordStar' code yet her ar two First yo ca sen an cod t th printer (Okay th cat ou o th bag no yo kno wher th nam o th progra cam from. Second sa yo tak WordSta an documen dis wit yo somewher (lik fro hom t work an want/nee t prin fil usin anothe compute (obviousl anothe Osborn o Osborn dis forma compatible) Yo prepare th documen o you Osborn whic i installe wit Prin Control t sen EPSO codes Th compute a work however i drivin NE printer Wit ANYCOD code embedde i th text wouldn' i b eas t us th Fin an Replac (^QA comman t fin th EPSO code an substitut NE codes Answe ye s w ca continue.  Ther ar fe cautions an yo ma a wel kno the now 1 Us al capita letter (`0A NO `0a)(an that' zer no o). 2 Eac he cod mus b tw digits ( entere a 05 entere a 0E etc.) 3 Du t spac limitation an desir o th par o th autho t kee ANYCOD confine t th MORPAT are (explaine later) n provision wer mad t b abl t prin th lead-i character an ~ I yo us eithe o thes character frequentl i you wor processin operations yo shoul conside pickin othe character tha yo d no us often Th comment t ANYCODE.AS belo indicat wher t mak substitution t th defaul lead-i characters. 4 Th MORPAT are mus b free Late i th articl DDT.CO i use t mak som modification t WordSta an chec t se tha th MORPAT are i clear I i i not ABORT 5 ANYCOD a installe belo i fo WordStar' installe t us th CP/ Primar Lis Device Yo ca chec thi whe yo firs boo WordSta an observ th sign-o messag (Als whe runnin INSTALL.CO o WordStar chec th labe CSWTCH: I mus b 0 fo ANYCOD t b guarantee t work) Her ar wrong the righ exampl o ANYCOD syntax. WRONG `FNo i th tim fo ...... th ai o thei countries.`12 Meanin t tur compresse prin on/off N zer betwee th an a beginnin o th sentence PROMISE Thi wil no work. RIGHT `0FNo i th tim fo ...... th ai o thei countries.`12 PROMISE Thi wil work. Follo th simpl rule lai ou above an yo wil hav n problem No fo th work. WHA D W MODIF AN WHER D TH MODIFICATION GO? MicroPr and/o Osborne i thei infinit wisdom decide t includ INSTALL.CO wit WordStar bu no t includ th complet termina an printe patc areas (Earl Osborn user' di receiv thi trea befor Osborn converte t th "improved referenc manuals) Luckily th autho wa abl t fin complet manua fo WordSta 3.0 A an rate ther i a are withi WordStar labele MORPAT tha contain 12 empt byte an exist fo th sol purpos o use define sub-routines ANYCOD wil b installe i thi MORPAT area (B enlargin th siz o WS.COM eve mor are ca b mad availabl fo use supplie code Stand-b t us som o i i late article. Wher i th MORPAT area Fo thre WordSta version know t hav ANYCOD successfull installed i reside a th addresse show below: WordSta Version 2.2 3. 3.3 02DE 02DE 02BBh TH COD AN ASSEMBLY. Th comment t th cod ma see littl crypti s thi narrativ explanatio i supplie i advance W wil modif on o WordStar' printe driver t diver al character t b sen t th CP/ Primar Lis Devic t ou ANYCOD subroutine Eac characte i firs checke t se i i i a ANYCOD lead-i character I i is i i stored fla i se an th nex characte i read I thi characte wasn' lead-i character i i checke t se i i i th firs o secon characte followin lead-i character I i wasn't i i sen ou t th primar lis devic normally I th lea characte i bein store an th fla i set i mean th curren characte i th firs characte followin th lea character Th fla i lowered an th characte processe t becom hal o th tota follow-o code The th las characte i read I to i checke t se i i i lead-i characte o i th firs o secon follow-o character I i i th secon follow-o character i i adde t th firs follow-o character an th resul sen t th lis devic (printer) I yo didn' understan mos o tha don' worr abou it  Understandin i no requiremen fo ANYCOD t work Her i th code B sur t us th "N non-documen mod i yo us WordStar. ANYCODE.AS - progra t allo printe code t b transmitte t EPSO o othe do matri printe fro withi WordSta text. ; Author D M Hurst Date Apri 23 1984 OR XXXX ;Wher XXX i 02DE fo W 2.26/3.0 ;an 02BB fo W 3.3 mysub cp '` ;i i cod character? ;yo ma substitut anothe ;characte i yo us the ; ofte i you tex jn nchec ;no s nex check st lea ;yes s stor temporarily mv a, ;ge i accumulator st f ;se f flag re ;ge anothe character ;withou printing ncheck cp '~ ;i i cod character ;yo ma substitut another ;characte i yo us th ; ofte i you tex  jn hchec ;no s chec i par of ;cod sequence st lea ;ye s stor temporaril mv a, ;ge i accumulator st f ;se f flag mv a,1b ; mean escap require s jm pou ;sen it. hcheck mo b, ;se inpu cha aside ld lea ;ge content o lead cp '` ;ar w i cod seq? ;NOTE i yo change thi code ;above chang i her too mo a, ;cha bac i accumulato j flch ;yes s chec i fla set mo b, ;no s cha bac t b ld lea ;chec lea again cp '~ ;ar w i cod seq? ;NOTE i yo change thi code ;above chang i her too ;cha bac i accumulato mo a, ;cha bac i accumulator j flch ;yes s chec i fla se jm pou ;no s prin cha normally flchk mo b, ;se cha asid again ld f ;ge f flag cp 00 ; i se set mo a, ;cha bac i accumulator j addsn ;no set s las cha i seq mo b, ;yes s se cha asid agai mv a,00 ;no s plac 00 i accumulator st f ;an lowe th flag mo a, ;cha bac i accumulato cp 39 ;i cod cha letter? j nolet ;no s neve min su 07 ;yes s mak i number nolet1 su 30 ;no dow i for ASCII rl ;firs cha i cod s rl ;mov lo bit t high rlc rlc an 0f0 ; lowe bits st firs ;stor it re ;g ge secon cha code addsnd mo b, ;se 2n cod cha aside mv a,0 ; th accumulator lx h,lea ;ge lea add pointe i hl mo m, ; ou th lead in ;u th hl mo m, ;als th flag mo a, ;ge cha back cp 39 ;wa 2n cod cha letter? j nolet ;s s ski it su 07 ;ye s mak i number nolet2 su 30 ;no dow eithe fro ASCII lx h,firs ;poin t firs cod cha addr mo b, ;mov actua cha t b ad ;ad t (resul i a) pout mv c, ;prin ful cod char mo e,a cal 5 re ;bac fo nex rea char ; lea d ;spac fo lea code f d ;fla space firs d ;stor firs cod while ;waitin fo 2n t pro- ;cess. end Havin entere ANYCODE.AS above i mus b assembled Plac you CP/ syste dis i driv an th dis containin ANYCODE.AS i driv B Ente th followin comman lin fro th A prompt: A>AS ANYCODE.BBB I ther ar n errors anothe ste complete Th autho ha successfull assemble thi cod exactl a i appear i thi article I fact th cod wa rea int th articl fro ANYCODE.AS usin WordStar' ^K command I ther ar error i assembly g bac an chec you typing I i di assembl withou error yo shoul b lef wit ANYCODE.ASM ANYCODE.HE an ANYCODE.PR o driv B D no us LOAD.CO an attemp t loa ANYCODE.HE int .CO file Thi i no necessar an woul serv n usefu purpose. INSTALLATION: Very ver simple Althoug havin al thes file presen isn' necessary plac WS.COM WSMSGS.OVR WSOVLY1.OVR MAILMRGE.OV (MRGEPRIN.OVR) DDT.CO an ANYCODE.HE o SYSGEN' dis i driv A (Singl densit user' leav th thre .OV program of fo no an ad the afte th installatio o ANYCOD an th deletio o DDT.CO an ANYCODE.HEX (Remembe ANYCODE.HE wa generate durin th assembl o ANYCODE.ASM) Whe yo hav performe thi task ente th following: Versio 2.2 Versio 3. Versio 3.30 A>DD WS.COMDD WS.COMDD WS.COM Yo shoul see: A>DD WS.CO A>DD WS.CO A>DD WS.CO DD Ver 2. DD Ver 2. DD Ver 2. NEX P NEX P NEX PC 400 010 3F0 010 460 010 - - - Th MORPAT are mus b clear So fro th "- promp typ th following: Versio 2.2 Versio 3. Versio 3.30 -d2de -R Now type: -d2bb (WS.COM 2.26 and 3.0 type -d2de) Yo shoul se somethin lik this (WS.CO 2.2 an 3. wil star a -d2de) 02BB FE 60 C2 23 01 .`.#. 02C0 32 92 01 3E 01 32 93 01 C9 FE 7E C2 35 01 32 92 2..>.2....~.5.2. 02D0 01 3E 01 32 93 01 3E 1B C3 8B 01 47 3A 92 01 FE .>.2..>....G:... 02E 6 7 C 4 0 4 3 9 0 F 7 7 C 4 0 C `x.L.G:...~x.L.. 02F0 8B 01 47 3A 93 01 fE 00 78 CA 70 01 47 3E 00 32 ..G:....x.p.G>.2 0300 93 01 78 FE 39 FA 64 01 D6 07 D6 30 07 07 07 07 ..X.9.D....0.... 031 E F 3 9 0 C 4 3 0 2 9 0 7 2 7 7 ..2...G>.!..w#wx 0320 FE 39 FA 81 01 D6 07 D6 30 21 94 01 46 80 C3 8B .9......0!..F... 0330 01 0e 05 5F CD 05 00 C9 00 00 00 00 00 00 00 00 ................ 0340 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0350 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0360 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ - Th cod fo ANYCOD no reside i th MORPAT are previousl occupie b al thos 00's. Th las modificatio insid WS.CO i t alte th printe drive t jum t ou sub-routin rathe tha simpl sen eac characte directl t th primar lis device W d thi b overwritin th assembl languag startin a th addres correspondin t you versio o WS.COM. Version 2.26 Version 3.0 Version 3.30 -a71d -a71d -a71c 071D call 2de 071D call 2de 071C call 2bb 0720 jmp 723 0720 jmp 723 071f jmp 722 0723 0723 0722  - - - To close out, type: -G0 or -^C Then type A>sav X WS.CO (Wher X i 6 fo versio 2.26 6 fo versio 3. an 6 fo versio 3.30) That' it Yo ma no eras ANYCODE.HE an DDT.CO fro th dis containin th WS.CO yo jus modified (Singl densit user' shoul PI WSMSGS.OVR WSOVLY1.OV an MAILMRGE.OV (MRGEPRIN.OVR bac ont th dis containin th modifie WS.COM. Belo i tes fil fo EPSO printers I yo hav differen printe type consul you user' manua an substitut simila workabl codes. ******************************************************************* `0FCompressed print`12 (All) `0EExpanded print`14 " ~45Emphasized~46 " ~34Italic print mode~35 (MX w/Graftrax ,FX, RX only) ~2D`01Continuous underline~2D`00 " ~53`01Sub~48~54`01script " ~53`00Super~48~54`01script " ******************************************************************* Doug Hurst Hi-Desert Osborne Group (HOGS) 6808 Estrella Ave. 29 Palms, CA 92277 (619) 367-1090 (evenings)  Yo ma no eras ANYCODE.HE an DDT.CO fro th dis containin th WS.CO yo jus modified (Singl densit user' shoul PI WSMSGS.OVR WSOVLY1.OV an MAILMRGE.OV (MRGEPRIN.OVR bac ont th dis containin th modifie WS.COM. Belo i tes fil fo EPSO printers I yo hav differen printe type consul you user' manua an substitut simila workabl codes. ******************************************************************* `0FCompressed print`12 (All) `0EExpanded print`14 " ~45Emphasized~46 " ~34Italic print mode~35 (MX w/Graftrax ,FX, RX only) ~2D`01Continuous underline~2D`00 " ~53`01Sub~48~54`01script " ~53`00Super~48~54`01sc ; ANYCODE.ASM - A program to allow printer codes to be ; transmitted to EPSON or other dot matrix ; printer from within WordStar text. ; ; Author: D. M. Hurst ; Date: March 3, 1984 ; ORG 02BBh ;WS 2.26/3.0 (02DEh) WS 3.3 (02BBh) ; mysub: cpi '`' ;is it a code character? ;you may sub " ` " if you use the ;" ` " often in your text jnz ncheck ;no, so next check sta lead ;yes, so store temporarily mvi a,1 ;get a 1 in accumulator sta fl ;set fl flag ret ;get another character ;without printing ncheck: cpi '~' ;is it other code character? jnz hcheck ;no, so check if part of ;code sequence sta lead ;yes so store temporarily mvi a,1 ;get a 1 in accumulator sta fl ;set fl flag mvi a,1bh ;~ means escape required so jmp pout ;send it. hcheck: mov b,a ;set input char aside lda lead ;get contents of lead cpi '`' ;are we in a code seq? mov a,b ;char back in accumulator jz flchk ;yes, so check if flag set mov b,a ;no, so char back to b lda lead ;check lead again cpi '~' ;are we in a code seq? mov a,b ;char back in accumulator jz flchk ;yes, so check if flag set jmp pout ;no, so print char normally flchk: mov b,a ;set char aside again lda fl ;get fl flag cpi 00h ;1 if set set mov a,b ;char back in accumulator jz addsnd ;not set, so last char in seq mov b,a ;yes, so set char aside again mvi a,00h ;no, so place 00h in accumulator sta fl ;and lower the flag mov a,b ;char back in accumulator cpi 39h ;is code char a letter? jm nolet1 ;no, so never mind sui 07h ;yes, so make it a number nolet1: sui 30h ;now down it form ASCII rlc ;first char in code so rlc ;move 4 low bits to high rlc rlc ani 0f0h ;0 4 lower bits sta first ;store it ret ;go get second char code addsnd: mov b,a ;set 2nd code char aside mvi a,00 ;0 the accumulator lxi h,lead ;get lead addr pointer in hl mov m,a ;0 out the lead inx h ;up the hl mov m,a ;also 0 the flag mov a,b ;get char back cpi 39h ;was 2nd code char a letter? jm nolet2 ;so so skip it sui 07h ;yes so make it a number nolet2: sui 30h ;now down either from ASCII lxi h,first ;point to first code char addr mov b,m ;move actual char to b add b ;add a to b (result in a) pout: mvi c,5 ;print full code char mov e,a call 5 ret ;back for next real char ; lead ds 1 ;space for lead code fl ds 1 ;flag space first ds 1 ;store first code while ;waiting for 2nd to pro- ;cess. end es, so make it a number nolet1: sui 30h ;now down it form ASCII rlc ;first char in code so rlc ;move 4 low bits to high rlc rlc ani 0f0h ;0 4 lower bits sta first ;store it ret ;go get second char code addsnd: mov b,a ;set 2nd code char aside mvi a,00 ;0 the accumulator lxi h,lead ;get lead addr pointer in hl mov m,a ;0 out the lead inx h ;up the hl mo*%!F>>#m !F>##&+~0 B !gw* x!g~!fw!"g#####(+~2f##~2g>2h\<>2}2~2!\!:f³:gw:f#¨"\*}#"}Ô%\K PARAMETERS NOT SPECIFIED OR IMPROPERLY SPECIFIED $NO FILE WITH THAT NAME FOUND ON DISK $FILE SUCCESSFULLY ALTERED $ CHGCHAR.COM-CHANGE CHARACTER PROGRAM ------------------------------------ written by Al de la Torre vice-pres. gold coast osborne users group (GCOG) nov 1983 I wrote this assembly language program originally to simplify data transfer between Supercalc 2 and Dbase II. The particular problem I encountered was the fact that Dbase II delimited character strings with single quotes. The SDI (super data interchange) program which comes with Supercalc 2 had no problem converting the data. The problem arose as a result of the single quote delimiters, which when used as the first character of a cell entry in Supercalc, repeated the data following it in every blank cell to the right. The obvious solution to this problem was to use the "find and replace" option in wordstar. This worked but necessitated swapping disks and going through the interminable wordstar "boot". Not to mention the problems encountered when the remaining available disk space is less than the size of the file being modified. I recommend placing the CHGCHAR.COM file on the Supercalc file disk. Its operation is outlined as follows: COMMAND LINE SYNTAX: -------------------- ASCII CHARACTERS: To use the program from the CP/M prompt type: CHGCHAR d:filename.typ A>B where d:filename.typ is any valid CP/M file name, A is the ascii letter you wish changed, > instucts the program to look for ascii characters, and B is the letter which will replace all occurences of A throughout the file. NOTE: there should be NO spaces between the parameters (A and B) and the > NON-ASCII CHARACTERS: I included the capability to find and replace any non-ascii character simply by specifying the two digit hexadecimal equivalent of the character using the following command line syntax: CHGCHAR d:filename.type 00#ff where everthing is the same as above with the exception of the exchange parameters specified, # instructs the program to look for hexadecimal characters, 00 can be any hex character in the range 00 to ff, and ff will be the replacement character (which will replace all occurences of the first parameter 00) it also can be any characterin the range 00 to ff NOTE: there should be NO spaces between the parameters and the # THIS PROGRAM DOES NOT PRODUCE A BACKUP FILE OF THE ORIGINAL!!!! This allows you to modify files of any size without worrying about whether or not there is enough disk space. SAMPLE COMMAND LINES: CHGCHAR SAMPLE.TXT '>" will change all single quotes ' to double quotes " CHGCHAR B:SAMPLE.TXT 0D#8D will change all "hard" carriage returns to "soft" carriage returns. This will allow you to reformat files with the ^B option This program uses only standard CP/M function calls and will therefore work   with both CP/M and CP/M+. (Works on Osborne 1 and Executive) The accompanying file chgchar.asm is the assembly language source code for this program. I heavily commented it to assist other begining assembly programmers like myself. If some area of the code needs clarification I'll try to explain it if you wish. H (305) 653-2871 W (305) 374-2730 ADDITIONAL NOTES: ----------------- Care must be taken when modifying ascii characters in a wordstar file created in "D" document mode. If a soft space has been inserted next to a letter, what has actually happened is that the high bit of that letter has been turned on or "set". For instance the normal hexadecimal representation of the letter A is 41. However if a soft space has been inserted the "A" will still appear normal when viewed with wordstar but will actually be represented by C1 hexadecimal. Therefore if you use the command line syntax CHGCHAR SAMPLE.TXT A>S these "soft space A's" will not be changed. There are two ways around this problem. First is to use the hexadecimal command mode and invoke the chgchar program twice the first time to change all normal letters, in this case: CHGCHAR SAMPLE.TXT 41#53. And the second time to change all high bit occurences of the letter, in this case: CHGCHAR SAMPLE.TXT C1#53. The second way around this problem is to strip every high bit using the Z option and piping the file to itself, then using the normal command line syntax. It should be noted that upper to lower case conversion of a particular character is not possible using the ascii command line syntax. The reason for this is that since the parameters are given from the CP/M command line; the CCP changes all characters to upper case before putting them where the chgchar program can use them. xadecimal. Therefore if you use the command line syntax CHGCHAR SAMPLE.TXT A>S these "soft space A's" will not be changed. TvBCHGCHAR.ASMO  !"#%$&'(*)+-,/.012435678;9:<>=?B@ADCFEGHIJoKMNLvf$Y6;wy? GVN%w5{z9msTyWI~N2ܻM9tϜ]In Tw<[+|yXح>a%|݃aIp.vFv0, .y8w ;8ֿ§98\Tw'vG滕T"[?dh˹c8u><T%pq= yn'$'䩞K`ᬟ㼫VS N즶V ytz.玼Twn;9{AxS݅ d+~5s|TحSt09Cx7<}b-fu*՞#gƤSž'vISr*ټ$K{&vr8lU߭<]nu3pk*ټ$+rVbw{ů~|wM .8gH jaI򻎕MAy{ۺ)CcV)aS]%ydnkI"u 5+}VoVR{ő thw]n?OAh5,n*%C軮갲ۻ@pduln1+%S]w,A_Kt_n{uS$ 1+MؼUJrTyWID+٫`R>HF~}++կ^qd+]+_v0 St;wv.CyaD ő?n[_&BYf 04EGDu=Ma@S]w(lVv;]|Ha@)]w^جv.ûw}聘-l|aID肄wMQ0)˷[7ev!sl*%]Vv;<9 3i4LaZ5 24{ Z}UyeHsl5g}?LsaI򻎕]w,q&ڭ2+Q]w+ڭ2O.Hxnß=JIkJ氬w}聘-l|aID肄wMQ0)˷[9+l_}n`R+kVmwMQ0)*sVvu 4Zqs|XaC4 ,t4!:d Ys| C9m s[$ :_Xnds黎5[HÒ?=pd{ݺ)2f =e s@5,n!eKGDe*ټ$kX޵xﲲ[7e@l swy b !OK<5=М^_UwdX Gk?rvG滦(8]M?]boUUkOsp\;~v{ b- 9#:v_;uv{Au hwdX睴v{Z\˜;n;g5EWnnJn{UUkϳER2s9v߿n=Okzc!8v{ێ[$OwMQ0)Gv2LJ%k}  v2K`ힿۡAؼUJ.(}UVv_sNIc!x%*v{4n˷Aw9 B&d+αyw>ުi+qҿ9 B2R]J5,nݔY`$uln1+%S$]w,?wۻ-[2gߵƒk$n'$~#"yr_1ͻRckJh4YM+~5skX`UNK]ByEjClWwݡDn[z9A+]Rkcy*Cm aws`Mw]7>`X2 C.ajջݶJIculV~YuQȟanJ}0,d@u:\$vvS3wۻ,Vxp-WCnwMQp,ۻRckc95qT9;F|uSa%;"ȟzQ=]+<%`X2AVwz}Uuݔ?]sʻcR[:siֆ,F\b3 !)[ <=A3p5#-h8BH <ȕxw*C1@xB;%]Hc@m6dq b<MɅ4 Wݠ BHqGgA"-hCW3nɃ\w{1ɻK= suXR k?rvG滦(8]M?]boUUkOsp\;~v{ b- 9#:v_;uv{Au hwdX睴v{Z\˜;n;g5EWnnJn{UUkϳER2s9v߿n=Okzc!8v{ێ[$OwMQ0)Gv2LJ%k}  v2K`ힿۡAؼUJ.(}UVv_sNIc!x%*v{4n˷Aw9 B&d+αyw>ުi+qҿ9 B2R]J5,nݔY`$uln1+%S$]w,?wۻ-[2gߵƒk$n'$~#"yr_1ͻRckJh4YM+~5skX`UNK]ByEjClWwݡDn[z9A+]Rkcy*Cm aws`Mw]7>`X2 C.ajջݶJIculV~YuQȟanJ}0,d@u:\$vvS3wۻ,Vxp-WCnwMQp,ۻRckc95qT9;F|uSa%;"ȟzQ=]+<%`X2AVwz}Uuݔ?]sʻcR[:siֆ,F\b3 !)[ <=A3p5#-h8BH <ȕxw*C1@xB;%]Hc@m6dq b<MɅ4 Wݠ BHqGgA"-hCW3nɃ\w{1ɻK=Okidata MicrolineX  $u @!Ü *Ü 17.1 CPI, at 6 LPI>U  =2 ͍U ~B 10 CPIp#z 17!9:l2:]!Ͱ Ͱ BU17.1 CPI, at 8 LPI`2!vn!85 Ͱ͎͓X Teeny Weeny80= =hL%9 1 $u @!Ü  12 CPI at 8 LPIÙ >U  8=2 ͍U ~ 0 CPIp#z 17!9:l2:]!Ͱ Ͱ BP6.7 CPI, 8 LPI`2!vn!05 Ͱ͎͓X 1 DBL Version 4.00, setup for a !  printer !->%>:mPb>2` !nͬ b"\ + 1!"V "X "Z ! 61#6 !^ :]!ڝ:T >2T   2|\! <;:T >2T !@2+ !@~x12 0> Print using @  YOUR CHOICE: %1 6 =o&))))))@~D> $   ~# M*\ | Start print on page: ʹ ʁ"\ :`  Last page to print: ʹ ʾ##"^ *\ #zq{q:` $ ! $  (Default = x  ): v ڌ:`  +  Printing: !\͗  Start Page: *\ #͟  Finish Page: *^ ͟  Page Format: ! x $  x Press Return when printer is ready  ·! 5 Stop/Resume Press Ctl-Q to Exit to System *\ |̏ 1! !~###_2c G_2e 7? _2g x++_2f ! :#==2d :d 2a G*V "X h _ʇ z __*V ~ ‡#"V : ʖoU -*V "Z *X "V ~:O> *V "X *Z "V ~ :e O:b /O> J ‡J :a <:\ 2U :f Oj :g Oj U :U !#̖ *X ~ #"X *V ~ '#"V *^ |8*Z ~R! eL Mode out of range! L Page formatL Page number!"\ "^ ]:` L Number out of range (0 <= N <= 255)#w:b <2b !w:c O2b h h X  !~2b Ww#~ \  8   X:b W>W> QC<^:b !wʹ ʥ ¥5 Resume$  ‹5 Stop! *V 8 *V ~# "V 6 T]~# *X .8 \ 6# 8  ~w# 8 *X |Ob"X :[ 2[ :W 2W :=> \ > U :a =2a > \ > _:U z > ! ͖ ͦ *^ +"^ *\ +"\ +|2\ !~#O~\ # œ ! 4~:60+~A © 61 }o|gҿ y > 0G_ʹ  ʹ  ~#  > $ $   ! -  Press "P" to  Print Press [Escape] to Exit to Menuo&'ͽ ͽ dͽ  ͽ }0 |{  (Default)   ʹ 2` _ 1- b!"\ :\ !#͖ !0͖ >2\ !@? C  y? +6   w#6  !   - ERROR! ... Please Retry  !͒ |7>~0:bk)))_#Ò /| ÷ v } ++}o~#a{_ File to print (D:FILENAME.TYP): !\h  6#6   :` 2 !h  ͗ Á !~+:D ~w## !ځ .i  I !ځ .\ !  !ځ  s #~!L Filename ~ʦ @ >:  #~ y>. ¨ Page 1 finished No File Page Height Page Width  Left Margin Right Margin Center Gap Top Margin Bottom Margin  > $ $   ! -  Press "P" to  Print Press [Escape] to Exit to Menuo&'ͽ ͽ dͽ  ͽ }0 |{  (Default)   ʹ 2` _ 1- b!"\ :\ !#͖ !0͖ >2\ !@? C  y? +6   w#6  !   - ERROR! ... Please Retry  !͒ |7>~0:bk)))_#Ò /| ÷ v } ++}o~#a{_ File to print (D:FILENAME.TYP): !\h  6#6   :` 2 !h  ͗ Á !~+:D ~w## !ځ .i  I !ځ .\ !  !ځ  s #~!L Filename ~ʦ @ >:  #~ y>. ¨ Page 1 finished No File Page Height Page Width   THIS PROGRAM PRINTS TEXT IN COLUMN FORM WITH COMPLETE FLEXIBILITY IN ASSIGNING COLUMN AND PRINT SIZE. --------------------- N.T.M PRINTS TEXT IN COLUMN FORM WITH COMPLETE FLEXIBILITY IN ASSIGNING COLUMN AND PRINT SIZE. ----------DBL $$$ ͫCopyright (C) 1985 BORLAND IncBZenithminal selectedPY ELMKqp~7#~= oͦkԄ!!"~#(}:$= +*!5!*!!:(2!5:(>2!!!:O::O:!*! !45(! +/ 0y0( d!k5!{5__o&  :(͠|(  *"x2y( >28!"9!! og2"">~22 9/4*9 Co&ͦͣ} [ (!e{ͦA8Q0G: x@!\w# (͂ ?(*( .( w^. ^!h6# (?( *( ͂( w#>?> w#ͦ 8 !ɿ .,;:=?*[]<>{}a{ |ʹ}ͽƐ'@'7||}>2ͯ*Bڨ  "og"2>2! ""*B"[Ru*"^#V#^#V#N#FO/o&9O/o&9!9(> (G!9 w#Eͺw}8' RB0 >' RqRR!+ Ͱ R!+ Ͱ r!+ Ͱ r!+ Ͱ r!# Ͱ r!+ Ͱ T]KB!z> S>))0 = | |̀̀DMgo>jB0 7?= H͒<z5a)a<z {0Gɯgo||~}||/g}/o#}o&K[xAJSJDM!b"!6J"DM'd } ) W _}8(8J`9{T]=o`9y ) >' ́ ͬ͗ }>' xˆ }} ˸T}ٕ(0D=z ,= ( ͒ 0%{ , 7 ?(8ͬ x ͆ - r 8˸x ͏  ,-xG}r }مM 9r .>#n0͒ { = - nx ͇ ,-(-˸G,-r }ٕ?M 9.> 8ͬ ?= u+-(>͆ 0ͬ ͆ 8 ?x ͇ , 78ƀ8ƀ8ox٨!دoGOW_gɷɷ|لg{ً_zيWyىOxوG|ٔg{ٛ_zٚWyٙOx٘Gxٸyٹzٺ{ٻ|ټx٨ xx( ?}ٽ }ցr <(r 7{ = |٤g{٣_z٢Wy١Ox٠G{ ͬ ́ }x>' ͬ}ƀ/ƀo -́ }0͎-́ ͎,}l˸ 8 4 ͗ x( - 8́ - 8,́ }l8;*!͗ ! >4ͬ͗ ͗ ͬ--- ́ ,,,-xGg?+2n*8t z~,->' x' ͘}. ͆́ , ! >4,͢- o&0%,͗ }gr }؉}颋.:}8c~I$I~L*͢ٷx˸ }0G,<},-(-́ !>I0 ͗͘ o8 ͆ >' m.`1pF,t6|!wS<.z}[|%FXc~ur1}͆ٯx(<˸ 8 !~J 0.O!>s 8 =  n s͗ ͆ .n 0 ͎-́ OT0 j oD,:j !I}袋.}8c~I$I~L! >ͬ͗ I× nn ͗ = ͆ nf^VNF!DLT\I!!53!r1!͒!> x #-= o˸x͆(- }(x>8(z ,z `iÃ!>' |r |̀>)=|(DMbo˸88x(0 8> ̀x(>-{(ay( z(>. ( {>E>+|(|Dg>-|/ 0:p# ~# +>0w#,-  60#} ˸}րogM| .(z = ~> x0w#xG%͇ %͇ ZJDM%͇ = _~65+~hìx-Sx9?+{Η@}|z z gZJDM0{ ,7}o˸? #yO!@9i&?  #?w#?/w#?w#!9! E9!!9~(+Fͺ!"9!(#>2*"| >"2:( Ͷ *w*6 !\$![ (ͦ( #:~CONTRMKBDLSTCAUXUSR>2i:*ˮ~0:*:(@q##pZ* :(  ~* < >26"!"""~>2""v>2>"!"ˮ(!~8>~O6~*"w(6(2(-()(6 (8 0 :(* y(~#+ (( 66 #6 #"*: y~o p .##~ͺ(.6w4._~ =*##55= *[R8*~#"= ͣ}== ͯ}͵}*#w+#~+>*~('k!0(ˮ]k!8ˮ!]~-#8~>27kˮw>O$6̃s #r$ͣ6̏ k ( (ˮ qk(ˮ ( k ˮ*O:~ ##~._q4((=ʦ==ʩ=ʬò*:4^q*##~6ͺ>2}*|(̓|( ̓6-#[RM8( G> A~#*'C! !TRUEFALSE!9N#Y~#( G~#> >    "~(kѻ(( !0 (ˮ!!>2Sz:0:*6##ww#w$w#w:  ##N#F*B>2w#w#[s#r> "~ͮ*-w#ww##> ͮÁ""~>2:ZR0 *4#4>2:ZR> *4 #4(> >22*f(/˦:G(##~++ :O x yC!ͺ Q*:G(##~._.͚f<\=<͚*##w ͮ +4 #4x >>2:G("ͮ"*nˮ*0 SZѷR8@* N#F#s#r, 0})jS\*##w+ N#FB ͮr+s>2!T]>)j)0 0= ]R!#]*^#V#N#F#^#V>2Ͱ:0:*6 #-Nw#Fwq#p#6#w#w#w"~Á>">!DM!":*B:!>(>2>">!"2"~ʰ*w#wx(9* :O *-4 #4!*4 #4 *-N#Fq#pV+^Bq#pSZѷR&* s#r$ s#rL <?*L!\  <( !\$>2>2L:>!(* \$\<(!3: [1ð\!(7"~> 2"S"Ns#FrB(Z#\: \<(?*"}K\! !*}#"}! x \* *>) 2""{_!"*nf}(HR0nf" ^VMDnfutqp*s#r*s#r"* uKB!0>' ~#fo{_"*R0RnfR0KqputsrNF( ^VNF^V*SutKqp R*R(~w~wnf ut"6#K*K*!""*NFy(* "*B0Cnf* [R*"*RS[s#r^#V""6#>O"w2x2*"!F"" &y*"*>2"*"!F"""!\*: Nr!~6go(\R*s#r_2x( s x(T]DMx(R0 U(͝O/o&9q# (!>F0#( ~ ( #]( ~ ( (#}(  i&T-a%â}ͦo*!~6o&|:2 2}:__zѯ2*|KB " z ^C User break+=  I/O Run-time error {ʹ, PC=*ͯNot enough memory Program aborted :ʎ'1!pd!J>üI I !,!M INDEX.INPo!!}29!"7!"5!E!!}o}24"!}24*4& ," !#}2+*+&}29 #" !*9&!͒Ea"*9&}2!}29/#!Ev"!}2/#!Eʣ"!ͩͫ ! }2!"7!"5/#*5!E"! }2*5!R"5/#!ͩ!D *&}2*&! ͒E#*7!"7/#!*7!ER"5*7*5"7! }2*&}2"*"& "!!:M INDEX.OUTo !: " !}2*&! Eʬ#!:ͺ #*&!E#!:i #!:ͺ*&"  "# d'!"*!E $!-y#*̀"!}2!!vzʺ$"!#*+)^#V"!0}2**ͥEʅ$*&!}2**R"T$*&!0Eʞ$!}2*&Eʱ$*&y#*+2$!0*y#"!!!+)!!}2!!+)^#V !s#r"!""!}2!}2**&+n&}2*&!}2*&!?! ]OEʁ%*&! R}2**&+*&s*&!}2*&!E6%"!""!*&+)^#V"!*^#VR"*!E"&**!s#r!}2*!ͥEʦ&*&! ER&!͟!*&!}2!*&+)!!*&+)^#V"!"*!s#r!}2*&!}2**&+n&}2*!"**+*&s*&!E)&*&*s!*R*s#r"!""!*n&+)^#V"*^#V"!}2**+n&}2**&+*&s*&!͒Eʹ'*!"*&!}2*&!Eb'"!""!*n&+)^#V"*^#V!R"!}2*&!}2**&+n&}2*!"**+n&}2*&*&͒*&!}oE(*&*&Eʣ(!}2(*&*&E(!}2(!}2*&"!""!*n&+)^#V"*^#V!R"!}2*&!}2**&+n&}2*!"**+n&}2*&*&͒*&!}oE)*&*&*&!}oEʸ)!}2)*&*&E)!}2)!}2*&"!h"z"|!*|n&+)^#V"p*|^#V!R"l!*zn&+)^#V"n*z^#V!R"j*l!"l*p*l+n&}2i*j!"j*n*j+n&}2h*i&*h&͒*i&!}oEY**i&*h&E*!}2~+*i&*h&E*!}2~+!}2~*~&h"!X"b"d!X!*X"Z*Z!)!s#r*Z!)!s#r*Z!!s*Z!!s*Z!s#r*d*Z%*b*Z %*Z!s#r*X"f*fX" !M"U*U"M*M!n&*M!n&E5,!*M!n&}2WO,!*M!n&}2W*W& M"!<"G"I*G"=*=!͒E-*I*= '}2<*<&R¾,*=!)^#V"=,R,,R,*=!)^#V"=*<&!*=!}oEʉ,*="K*K<"!("6"8*6!EI-*8":ò.!}2-!}2,*8 *6 )!Eʅ-!}2-!}2,*6".*.*-&)*8*.*-&)^#V-s#r*.*-&*.*-&)^#V+s!*.*-&n&*.*,&n&R*.*-&)^#V!n&*.*-&)^#V!n&͒}oEʦ.*.*-&)^#V"**.*-&)!s#r*.*-&!s*6**-"*ì.*6"***":*:("Q!"""$*$!%!*"^#V^,"*!E%/*$!+"**"^#V-*"s#r*"&*&Q" !""*n&!Eʌ/*n&!*s**n&+)*s#r " !"*n&!E/**n&+)^#V"*n&!R*s/!"* "!""!*s*"*!͒EN0**8/*!)^#V"0**͕/s#r*^#V"*" !"!}2*^#V!͒E1*^#V!)^#V!͒E0**^#V!)^#Vs#r!}21**͕/s#r*&Ew1*^#V!)^#V!͒Ew1*^#V*8/**^#V!)^#Vs#r1*^#V"* "!"""*!E1!"ù2*"** (}2*&R1*!)^#V"'2R2*!)^#V"'2R'2*&!*!}oE1*&!͒Ed2!"ù2**/"** (}2*&!͒Eʡ2*}0"*&!Er2*"*" !="E͛ ͛ͺ Error in line*!&ͺ of input --  *ERL3͛ͺ!term must be printable characters U4R3͛ͺterm must shorter than !A!&ͺ letters U4R3͛ͺ$found nondigit where number expected U4R3͛ͺpage number too large U4R'4͛ͺimproper range of numbers U4RU4͛ͺsubterms nested too deeply ͛  ="!+3"}22!}21*2&!-Eʡ4!}213"}22*2&!_! ]O}oE4!2!!+!+,! *2&!0R !+3"}22*2&!_! ]O}oE4*2&"!+,!E^5!2*1&Ey5!+,xG!+!+,";*;+" !!I!+!s!"G*!"!"3"}2*!"*&! ͒E5*!R"*&! ͒Eʲ5*&!E%6!"ß8*&}/o|/ǵ! !͛OEM6!2!}2"*"&!}2"!I*"&+*&s3"}2*"&!A*&! }o*&! *&!I*"&+n&}o}oET6*"&!A*&! ͒}oE6!2*&! E7*"&!}2"*"&!!I*"&!R+n&!,}oEh7*"&!R}2"!I*"&+!s*&! Eʗ73"}2{7*&! ͒Eʟ8*G!E7!G!*G" 7* !* ^#V" !* s#r*&"* j4s#r3"}2*&!-E]8* j4s#r* ^#V* ^#V͸ES8!23"}2u8* * ^#Vs#r*&́!,͑! ͑OEʜ83"}2u8×7 "!"!"**ͥEL9**E9!I!Ϳ."**Gs#r͒5I9*d!"d*d! E&9!2**ͨ8s#r*d!R"dÿ8*"*"!!}2!"d$͢!E9͛ͺ&[reading existing index INDEX.INP ...  !"͒5*ͨ8"f͛ͺ finished] :͛ͺ[no existing index document] !"f!}2" !!*dfzO:"͛ͺ. .  *#/: " !!!o&}2*&!͒Ea:*&!Eʦ:͛*&" *&}2*& "!͛!"! "!" " !}2͛!" *&!E;;: ""!"!}2́! ͑! ͑!-͑!0!9͛!X:}2*&!]O}oEʒ;*&:ë<*&!E<;:*&!R}2*&!E;!]́!͑&!*&!E!}2!*s#r*&""!""!!*"!*s#r**s#r**s#r*"*"!p"""!"u*^#V"s*s!E>***>s#r@!}2p*!*s^#VEF?**>"q*u!͒E"?*q*us#r2?**qs#r*s*qs#r!}2p?*!R*s^#VEʣ?*s"u*s^#V"s*s!Eʠ?**>*us#r!}2p?**s^#VE?*s*s#r**s^#VE?*s*s#r!}2p*p&E>p"A!X*b&*c&EE@;:*b&!R}2b@X"!P*`!͒E@*`!#'*c&}2b!*b&+n&!͒E@͛!*b&+n&" *b&!}2b@@͛!" !}2bP"!H*c&*b&E2A!#*c&+!*c&+n&s*c&!}2c@!#*c&+!s!}2bH"!@!#!%*m!!͒1"`N@@"!8!}0"`*`!͒EA!!*` (͒EA!"`N@8"!`"m:͛ͺterm:  !}2c!}2b!#*c&+!sX:}2d*d&R_B@*c&!E\B;:*c&!R}2c=C RxB@͛ =C R‘B@͛ =CR¼B@*b&!EʶBUAùB͇A=CRB@!}2b=C@*d&! *c&!A}oEC*d&:=C!#*c&+*d&s*c&!}2c!#*c&+!s*d&! *d&! }oE B*c&!EzC!}2d*d&}2o*o&`"!'"6*6^#V @}2'*'&!͒ED!#*6Ϳ.",*'&! EʜD:͛ͺpage:  !*$;}2'*'&!͒EʉD*'&! E/D**"(wD͛ͺ.. !($;}2'*(**EwD͛ͺ bad range, redo. C***(*,z>ÐD! }2'͛ D*d!"d*d! ED*'&:D*,͒C*d!R"d*'&!*d!}oEʣC'"!"$*$^#V"*!͒EEE*^#V"KE!"*!͒EE*^#V!*^#VͥEE*^#V*^#VEʹE**^#Vs#r*^#V*s#rE*"*^#V"KE*$^#V"*!͒EF!,y#! y#! y#*!͒EF*"*^#V!E\F**^#V!s#r*^#V*^#V͸EF*^#V#*^#V*^#VEF!-y#*^#V#*^#V"*!͒EF!,y#! y#F"! "*!##'*d!EʆG!#!+n&}2 * &!?! ]OE_G* &! R}2 * &*&&͒EʃG! y#* &}2&ûG!*dfzʻG}2 ! y#! y#! y#* &#ÑG!}2 !}2 !#* &+n&!͒EIH!#* &+n&}2 * &! ͒EH* &y#!}2 7H* &E0H* &y#!}2 * &!}2 G "7!"*!/"*!͒EH*F* E! y#*^#V!͒EH*d!"d*^#VRH*d!R"d!}0"qH7"!G#!"d*fRH!y#" !"͛ ͛ͺ[Out of string space!] *&EʰI͛ͺ[Trying to write output file.] H͛ͺ[Output file complete.]  "!}2͛ͺ [index aid, revision of 11/3/86] ͛ͺ%[copyright (c) 1986 David E. Cortesi] _9͛ͺ [initialized] ͛ !f͒CH͛ =!!,!EʣJ!!, !͛ͺ [normal end,!,!!yͺ storage left.] !,!E"*!##'*d!EʆG!#!+n&}2 * &!?! ]OE_G* &! R}2 * &*&&͒EʃG! y#* &}2&ûG!*dfzʻG}2 ! y#! y#! y#* &#ÑG!}2 !}2 !#* &+n&!͒EIH!#* &+n&}2 * &! ͒EH* &y#!}2 7H* &E0H* &y#!}2 * &!}2 G "7!"*!/"*!͒EH*F* E! y#*^#V!͒EH*d!"d*^#VRH*d!R"d!}0"qH7"!G#!"dprogram indexer; {$I+,R-,A-,B-} {==============================================================} { Book Indexing Assistant Program } { Turbo Pascal version of: november 3, 1986 } { Copyright (C) 1986 by, and inquiries by mail to: } { David E. Cortesi } { 2340 Tasso Street } { Palo Alto, CA 94301 } { All rights reserved. Distribution for profit forbidden } { without explicit permission. Distribution by individuals } { and nonprofit organizations permitted provided this notice } { is retained and author credit is given in printed materials. } { } { This program assists you in constructing an index for a book.} { See the accompanying documentation file for further info. } { Note the R- compile switch: the program OUGHT to work with } { R+ (range checking), I pulled off enough errors, but since } { they were mostly trivial I turned off checking so that if any} { are left they won't crash the user.  } { } { Main file. History: } { bugs fixed and merge program added 11/3/86 } { initial writing 5/31 -- 6/6/86 } {==============================================================} const null = 0; { the null, end-of-string } asciibel = 7; { names for ascii characters } asciibs = 8; asciitab = 9; asciilf = 10; asciicr = 13; asciieof = 26; asciiesc = 27; asciiblank = 32; asciidel = 127; alnumset : set of char = ['0'..'9','A'..'Z','a'..'z']; alphset : set of char = ['A'..'Z','a'..'z']; allowset : set of char = ['a'..'z']; digitset : set of char = ['0'..'9']; digsign : set of char = ['0'..'9','-']; maxnests = 9; { allows up to 9 levels of subterms } maxdepth = 20; { max depth of a tree (2^19th nodes) } strmax = 65; { max size of a string, 64 data plus 00} sblksize = 2046; { block size of a string buffer } sblkmax = 10; { allow up to 20K -- use more in MSDOS!} type strindex = 1..strmax; { indices over strings } strlength= 0..strmax; { lengths of strings } relation = (less,equal,more); { result of comparisons } nchar = 0..255; { numeric characters are bytes } str = packed array[strindex] of nchar; { independent string} stref = record { an indirect string is an } nb : 1..sblkmax; { index into SBLKPTRS and an } bo : 1..sblksize { offset into that strblock } end; ppage = ^page; page = record { page reference to a term: } next : ppage; { ..linked in a chain, with } num1, num2 : integer { ..one or a range of nums } end; sides = (left, right); { names of children of tree } tdepth = 0..maxdepth; { range of depths, nil = 0 } tree = ^node; node = record { one node of a binary tree } cnodes : array[sides] of tree; { children of this node } cdepth : array[sides] of tdepth; { ..and their depths } subtree : tree;  { subtree of sub-terms } term, { index term as typed, and } dict : stref; { upcase for dictionary comps } pages : ppage; { head of chain of page-refs } end; walk = record { status of a walk over a tree } top : tdepth; { index of top of stack } curr : tree; { node on which walk now steps } stack : array[1..maxdepth] of tree; {pending nodes } end; var maintree : tree; { root of the term-tree } indlevel : integer; workterm, workdict, scratch : str; initdone : boolean; space : real; procedure disaster(which:integer); forward; {==============================================================} { Sections of code out-of-line to allow compilation in CP/M } {==============================================================} {$I IXTEXT.PAS textfile I/O stuff } {$I IXSTRG.PAS support for string storage } {xI IXDIAG.PAS nullify the "$" when diag not needed} {$I IXTREE.PAS support for tree creation } {$I IXWALK.PAS logic of inorder tree walk } {$I IXINIT.PAS phase 0: initialize, load existing index } {$I IXDOIT.PAS phase 1: keyboard interaction } {$I IXDONE.PAS phase 2: writing the output index document } {------ only one disaster coded just now, running out of -----} {------ string block space in stput --------------------------} procedure disaster; begin writeln; writeln('[Out of string space!]'); if (initdone) then begin writeln('[Trying to write output file.]'); finish; writeln('[Output file complete.]') end; HALT end; {==============================================================} { The main program, at last..... } {==============================================================} begin initdone := false; writeln('[index aid, revision of 11/3/86]'); writeln('[copyright (c) 1986 David E. Cortesi]'); initall; writeln('[initialized]'); writeln; keyinto(maintree); finish; writeln; space := MemAvail; if (space < 0) then space := 65536.0 + space; writeln('[normal end,',space:6:0,' storage left.]'); end.  MemAvail; if (space < 0) then space := on } {$I IXDONE.PAS phase 2: writing the output index document } {------ only one disaster coded just now, running out of -----} {------ string block space in stput --------------------------} procedure disaster; begin writeln; writeln('[Out of string space!]'); if (initdone) then begin writeln('[Trying to write output file.]'); finish; writeln('[Output file complete.]') end; HALT end; {==============================================================} { The main program, at last..... } {==============================================================} begin initdone := false; writeln('[index aid, revision of 11/3/86]'); writeln('[copyright (c) 1986 David E. Cortesi]'); initall; writeln('[initialized]'); writeln; keyinto(maintree); finish; writeln; space := MemAvail; if (space < 0) then space := {==============================================================} { IXDIAG.PAS } {Copyright (C) 1986 David E. Cortesi--see notice in IXMAIN.PAS.} { This include file contains a diagnostic procedure that will } { display a binary tree of index terms. The call "diag(t)" -- } { where t is of type tree as defined in IXMAIN -- can be inser-} { ted temporarily for diagnostic purposes in the code. The } { include-command in IXMAIN should be disabled (by changing } { its dollar-sign to something else) unless a diag() call is } { actually in use. } { History: } { initial code 11/2/86 } {==============================================================} {-- recursive tree display ----} procedure tdiag(t:tree;n:tdepth); var j:strlength; begin if (t <> nil) then tdiag(t^.cnodes[right],n+1); for j := 0 to n-1 do write(' '); write('|-- '); if (t <> nil) then begin stget(t^.term,scratch); j := 1; while(scratch[j] <> null) do begin write(chr(scratch[j])); j := j+1; end end else write('*'); writeln; if (t <> nil) then tdiag(t^.cnodes[left],n+1); end; {--- sets up the recursive display, waits afterward ----} procedure diag(t:tree); var c : char; begin writeln; writeln('DIAGNOSTIC DISPLAY'); tdiag(t,0); writeln('press return'); readln(c) end; char;ostic purposes in the code. The } { include-command in IXMAIN should be disabled (by changing } { its dollar-sign to something else) unless a diag() call is } { actually in use. } { History: } { initial code 11/2/86 } {==============================================================} {-- recursive tree display ----} procedure tdiag(t:tree;n:tdepth); var j:strlength; begin if (t <> nil) then tdiag(t^.cnodes[right],n+1); for j := 0 to n-1 do write(' '); write('|-- '); if (t <> nil) then begin stget(t^.term,scratch); j := 1; while(scratch[j] <> null) do begin{==============================================================} { IXDOIT.PAS } {Copyright (C) 1986 David E. Cortesi--see notice in IXMAIN.PAS.} { This include section contains the user interaction. The user} { is prompted for a term at the current indentation level. } { A null entry terminates this level of the index -- and ends } { the program, at the outermost level. } { When entering a term, pressing ESCape causes us to locate } { the term as typed so far in the tree at this level, and to } { complete the term on the screen from the matching term that } { lexically least. Further presses of ESC bring up successive } { matching terms until the user accepts one (by pressing any } { key except ESC) or until no more matches are found. DEL } { makes us give up trying to match the term and remove the } { letters supplied from the candidate term. After a term is } { ended with either CR or LF, we insert or find it in the } { current tree.  } { Terminating a term with CR causes a prompt for a page } { number reference. We read the number digit by digit and do } { our own conversion to binary so as to avoid crashing with a } { pascal run-time error. Any signed 16-bit integer is allowed } { (previous versions of the program couldn't handle 0). } { Terminating the number with CR ends entry of the number. } { Ending it with LF instead makes us prompt with ".." and take } { another number (must be algebraically greater) to store as } { the end of a range of numbers. } { Terminating term-entry with LF not CR causes us to enter } { a subtree off the completed term. We enter the subtree of } { the entered term and prompt again at a deeper indent level. } { History: } { don't put a page item on a term when no page # given 11/3/86 } { initial code 6/3-5/86 } {==============================================================} var scan: walk; {------ indent the cursor for the current nesting level -------} procedure indent; var i : integer; begin for i := 1 to indlevel do write('. . ') end; {------ get one byte from the keyboard. bypass pascal and go -} {------ straight to DOS. Echo printables, don't echo ctls. ---} function readnc : nchar; var c : nchar; begin repeat c := bdos(6,-1) { CHECK: OK FOR MS-DOS???? } until (c<>0); if (c>31) then write(chr(c)); readnc := c; end; {------ implement destructive backspace -----------------------} procedure backspace; begin write(chr(asciibs),chr(asciiblank),chr(asciibs)) end; {------ reject a user keystroke, blanking if printable --------} procedure reject(c:nchar); begin write(chr(asciibel)); if (c>31) then backspace end; {------ get a valid page number keyed in and set its value in -} {------ PAGE. Return its delimiter as the result: asciieof ---} {------ if no page entered, or asciicr, or asciilf. Since we -} {------ deal direct with the user we give immediate feedback --} {------ when a number is too long or too large or whatever ----} function getpage(var page: integer) : nchar; const hyphen = 45; var c : nchar; { keyed character } s : set of 0..57; { set of currently-valid ones } x : array[0..5] of 0..57; { keys typed so far } j,k : 0..6; { indices over x } w : real; { accumulated value at end } begin j := 0; { nothing typed so far } s := [asciilf,asciicr,hyphen, 48..57]; { bytes we can take } repeat c := readnc; if not(c in s) then reject(c) else if (c = asciibs) then begin backspace; j := j-1; if (j=0) then s := s - [asciibs]; if (j=1) then s := s + [hyphen] end else if (c>asciicr) then begin if (j>5) then reject(c) else begin x[j] := c; j := j+1; s := s - [hyphen]; s := s + [asciibs] end end until ((c=asciicr)or(c=asciilf)); if (j > 0) then begin {nonnull page number} getpage := c; c := x[0]; k := 0; if (c = hyphen) then k := 1; w := 0.0; while (k 32767.0) then w := 32767.0; if (c = hyphen) then w := -w; page := trunc(w) end {of nonnull page} else begin {no page entered} getpage := asciieof; page := 0 end end; {------ make a page record for numbers I and J ----------------} function makepage(i,j:integer):ppage; var p : ppage; begin new(p); with p^ do begin next := nil; num1 := i; num2 := j end; makepage := p end; {------ Add the range of consecutive numbers from I to J to ---} {------ the chain of numbers off treenode T, economizing on ---} {------ PAGE structures. The method used keeps the chain in --} {------ numeric sequence but may produce overlapping values ---} {------ under some entry sequences, e.g. (3,8),(5,5)(12,12) ---} procedure putpage(i,j:integer; t: tree); var p,q,n : ppage; stop : boolean; begin p := nil; q := t^.pages; if (q = nil) then { new entry, no numbers yet } t^.pages := makepage(i,j) else begin stop := false; repeat if ((j+1) < q^.num1) then begin { (i,j) has to follow p^ and precede q^ } n := makepage(i,j); if (p <> nil) then p^.next := n else t^.pages := n; n^.next := q; stop := true end else if ((i-1) > q^.num2) then begin { (i,j) has to follow q^ ... } p := q; q := q^.next; if (q = nil) then begin { (i,j) goes at end of chain } p^.next := makepage(i,j); stop := true end end else begin { (i,j) overlaps or touches q^ } if (i < q^.num1) then q^.num1 := i; if (j > q^.num2) then q^.num2 := j; stop := true end until stop; end end; {------ fill in term string TRM from the keyboard in context --} {------ of tree T. Return a signal, asciieof, -cr, or -lf -----} {------ depending on the outcome, as the result. --------------} function getterm( t : tree) : nchar; var c : nchar; { current input keystroke } j : strindex; { indexes over current term in TERM } k : strlength; { indexes recalled term, or else 0 } p : tree; { ^tree of recalled term } {-- back up the screen to the original term --} procedure backup; begin while (k > j) do begin backspace; k := k-1 end end; {-- common finish of both starting and stepping a scan ----} procedure showtrm; begin if (p <> nil) then begin stget(p^.term,scratch); k := j; while (scratch[k] <> null) do begin write(chr(scratch[k])); k := k+1 end; end else begin write(chr(asciibel)); k := 0 end end; {-- accept the current scanned term for use, if any -------} procedure accept; begin while (j < k) do begin workterm[j] := scratch[j]; j := j+1 end; workterm[j] := null; k := 0 end; {-- set up for a treewalk over all terms that are an ------} {-- initial match to the entry so far. --------------------} procedure startscan; begin stucase(workterm,workdict); p := setscan(t,workdict,scan); showtrm end; {-- scan to the next equal term in the tree and show it ---} procedure stepscan; begin p := wstep(scan); if p<>nil then if (equal<>stbcmpa(workdict,p^.dict)) then p := nil; showtrm end; begin { the main Get Term procedure } indent; write('term: '); j := 1; k := 0; repeat workterm[j] := null; c := readnc; case c of asciibs : begin { destructive backspace } accept; { accept current match if any } if (j > 1) then begin backspace; j := j-1; end end; asciicr : begin { normal completion } accept; writeln end; asciilf : begin { complete, move on to subterm } accept; writeln end; asciiesc : begin { automatic scan for match } backup; { wipe rejected match if any } if (k=0) then startscan else stepscan end; asciidel : begin { cancel search for match } backup; k := 0 { no scan active } end; else begin { ordinary (?) character } accept; if (c asciieof) then begin {nonnull term entered} insnode := insert(workterm,t); if (c = asciicr) then begin {final term} 111: indent; write('page: '); c := getpage(p1); if (c <> asciieof) then begin {nonnull page#} if (c = asciicr) then p2 := p1 else begin {page# then lf entered} write('..'); c := getpage(p2); if (p2 < p1) then begin writeln(' bad range, redo.'); goto 111 end end;{page range} putpage(p1,p2,insnode) end {of entering page for this term} else c := asciicr; {null page #} writeln; end {of term ended in cr} else begin { user hit lf, wants to recurse } indlevel := indlevel+1; if (indlevel > maxnests) then reject(c) else keyinto(insnode^.subtree); indlevel := indlevel-1 end {of recursing to lower-level term on lf} end {of handling nonnull term} until (c = asciieof) or (indlevel>0) end; end {of recursing to lower-level term on lf} end {of handling nonnull term} untf (c <> asciieof) then begin {nonnull term entered} insnode := insert(workterm,t); if (c = asciicr) then begin {final term} 111: indent; write('page: '); c := getpage(p1); if (c <> asciieof) then begin {nonnull page#} if (c = asc{==============================================================} { IXDONE.PAS } {Copyright (C) 1986 David E. Cortesi--see notice in IXMAIN.PAS.} { This include file contains declarations and code related to } { writing the output file, INDEX.OUT in the current directory. } { See IXTEXT.PAS for support functions. } { } { History: } { append comma to term string when page numbers follow 11/3/86 } { initial writing 6/5/86 } {==============================================================} var initial : nchar; {------ Write a list of page-numbers. Since in collecting the -} {------ numbers we might have built overlapping ranges, we ----} {------ must first tidy the list, during which we discard some-} {------ page records forever ----------------------------------} procedure outpages(t : tree); var p, q : ppage; begin p := t^.pages; if (p <> nil) then q := p^.next else q := nil; while(q <> nil) do begin if ((p^.num2+1) >= q^.num1) then begin {range q^ can at most extend the range of p^ up} if (q^.num2 > p^.num2) then p^.num2 := q^.num2; p^.next := q^.next {forget that page record} end else p := q; q := q^.next end; p := t^.pages; if (p <> nil) then begin nextout(ord(',')); nextout(asciiblank); nextout(asciiblank) end; while (p <> nil) do begin with p^ do begin if (num1 = 0) then num1 := num1 + 1; if (num1 <= num2) then begin intwrite(num1); if (num1 < num2) then begin nextout(ord('-')); intwrite(num2) end end end; p := p^.next; if (p <> nil) then begin nextout(ord(',')); nextout(asciiblank) end end end; {------ write one term to the output, inserting a blank line --} {------ if its initial varies from the last and we are at the -} {------ outermost level. Dump extra/leading/trailing blanks. -} procedure outterm(t : tree); var c : nchar; i : tdepth; j : strindex; s : boolean; begin stget(t^.term,workterm); if (indlevel = 0) then begin c := workterm[1]; if (chr(c) in allowset) then c := c-32; if (c <> initial) then begin nextout(asciicr); initial := c end end else for i := 1 to indlevel do begin nextout(asciiblank); nextout(asciiblank); nextout(asciiblank) end; j := 1; s := false; while (workterm[j] <> null) do begin c := workterm[j]; if (c <> asciiblank) then begin nextout(c); s := true; {ok to print next space} end else begin if (s) then nextout(c); s := false {omit further spaces} end; j := j+1 end end; {------ Display all the terms in a (sub)tree to the output ----} {------ by walking all its nodes. Where a term has a subtree -} {------ recurse to write it. ----------------------------------} procedure outtree(t : tree); var position : walk; p : tree; begin p := initwalk(t,position); while (p <> nil) do begin outterm(p); outpages(p); nextout(asciicr); if (p^.subtree <> nil) then begin indlevel := indlevel+1; outtree(p^.subtree); indlevel := indlevel-1 end; p := wstep(position) end end; {------ write the whole tree to the output --------------------} procedure finish; begin initout; indlevel := 0; outtree(maintree); nextout(asciieof) end; -----------} prohile (workterm[j] <> null) do begin c := workterm[j]; if (c <> asciiblank) then begin nextout(c); s := true; {ok to print next space} end else begin if (s) then nextout(c); s := false {omit further spaces} end; j := j+1 end end; {------ Display all the terms in a (sub)tree to the output ----} {------ by walking all its nodes. Where a term has a subtree -} {------ recurse to write it. ----------------------------------} procedure outtree(t : tree); var position : walk; p : tree; begin p := initwalk(t,position); while (p <> nil) do begin outterm(p); outpages(p); n{==============================================================} { IXINIT.PAS } {Copyright (C) 1986 David E. Cortesi--see notice in IXMAIN.PAS.} { This include file directs initializing, primarily reading an } { an existing index document to initialize the trees. } { The output file is, and the input is expected to be, an } { index document as made by this program. Blank lines aside, } { every line of an index document is (must be) composed of: } { * a MARGIN of zero or more spaces } { * a TERM of printable characters } { * two (or more) spaces to mark the end of the term } { * zero or more NUMBERS or ranges 1-9 } { The numbers may be signed, and may have spaces and commas } { between them. } { History: } { minor cleanup 11/4/86 } { drop trailing comma on input term 11/2/86 } { initial code 6/2/86 } {==============================================================} var lineno : integer; { line numbers for diagnostics } margin : integer; { margin of last-in or next-out term } interm : str; { term last-in or next-out } pagref : ppage; { list of "page" structures in/out } {------ diagnose an error in the input file and abort ---------} procedure badfile(e:integer); begin writeln; write('Error in line',lineno:5,' of input -- '); case e of 1 : write('term must be printable characters'); 2 : write('term must shorter than ',strmax,' letters'); 3 : write('found nondigit where number expected'); 4 : write('page number too large'); 5 : write('improper range of numbers'); 6 : write('subterms nested too deeply'); end; writeln; HALT end; {------ read and collect one signed integer, assuming that ----} {------ there should be a number coming next from nextinch ----} function nextpage:integer; var n : nchar; s : boolean; x : real; begin n := nextinch; s := false; if (n = ord('-')) then begin s := true; n := nextinch; end; if (not (chr(n) in digitset)) then badfile(3); x := 0.0; repeat x := (x * 10.0) + (n - ord('0')); n := nextinch until (not (chr(n) in digitset)); pushinch(n); if (x > 32767.0) then badfile(4); if (s) then x := -x; nextpage := trunc(x) end; {------ read and collect one line of input file ---------------} procedure nextline; var j : strlength; p : ppage; n : nchar; begin interm[1] := null; pagref := NIL; repeat lineno := lineno + 1; margin := 0; repeat n := nextinch; margin := margin + 1 until (n <> asciiblank); margin := margin - 1 { note size of margin } until (n <> asciicr); { discard null lines } if (n = asciieof) then margin := -1 { note eof } else begin if (not n in [asciiblank..asciidel]) then badfile(1); j := 0; repeat j := j + 1; interm[j] := n; n := nextinch until ( (j=strmax) or (n < asciiblank) or ((n = asciiblank) and (n = interm[j])) ); if ((j=strmax) and (n<>asciiblank)) then badfile(2); if (n = asciicr) then j := j+1; if ( (j > 1) and (interm[j-1] = ord(',')) ) then j := j-1; interm[j] := null; while (n = asciiblank) do n := nextinch; while (n <> asciicr) do begin if (pagref = nil) then begin new(pagref); p := pagref end else begin new(p^.next); p := p^.next end; p^.next := nil; pushinch(n); p^.num1 := nextpage; n := nextinch; if (n = ord('-')) then begin {range X-Y} p^.num2 := nextpage; if (p^.num2 <= p^.num1) then badfile(5); n := nextinch end else p^.num2 := p^.num1; while (chr(n) in [',',' ']) do n := nextinch; end end end; {------ read consecutive lines of existing index at margin ---} {------ level M and make a binary tree of them. When the ----} {------ margin moves in, recurse to build a subtree. When ---} {------ it moves back out, stop. -----------------------------} function initree(m: integer): tree; var t,p : tree; begin  t := nil; while (margin >= m) do begin if (margin = m) { true first time for sure } then begin p := insert(interm,t); p^.pages := pagref; nextline end else begin indlevel := indlevel + 1; if (indlevel > maxnests) then badfile(6); p^.subtree := initree(margin); indlevel := indlevel - 1; end end; initree := t end; {------ control initialization sequence, calling initializers -} {------ in other "modules" as they exist ----------------------} procedure initall; begin initdone := false; indlevel := 0; initstrg; { set up string storage } if (initinp) then begin write('[reading existing index INDEX.INP ... '); lineno := 0; nextline; maintree := initree(margin); writeln('finished]') end else begin writeln('[no existing index document]'); maintree := nil end; initdone := true end; ) end else begin writeln('[no existing index document]'); maintree := nil end; initdo{==============================================================} { IXSTRG.PAS } {Copyright (C) 1986 David E. Cortesi--see notice in IXMAIN.PAS.} { } { This include-file contains declarations and code related to } { the implementation of dynamically-allocated strings. The } { method chosen uses strictly standard Pascal facilities, and } { was chosen because nonstandard stuff varies between CP/M and } { MSDOS versions of Turbo Pascal, and between Turbo and other } { Pascal systems (yes, Virginia, there are some). It's kinda } { clunky, but it works good enuff for what this program needs. } { } { We define an INDEPENDENT string as a character array of the } { max size in which the string data are kept, left justified } { and bounded by a null (like in C). } { } { A string BLOCK is a 2K block holding a bunch of strings in } { compact form, without padding to the max length for each. } { Blocks are allocated as needed, whenever the current one is } { full, up to a limit set by SBLKMAX. } { } { Once stowed in a block, a string may be referred to using a } { STREF, an index to its block's address plus its offset in } { that block. } { History: } { initial code 6/2/86 } {==============================================================} type strblock = record { string block has count of } free : 0..sblksize; { ..bytes free in block and } data : array[1..sblksize] of nchar { ..space for bytes } end; var sblkptrs : array[1..sblkmax] of ^strblock;{ blocks of bytes} sblkcnt : 0..sblkmax; { how many blocks are active } {------ initialize string storage -----------------------------} procedure initstrg; begin new (sblkptrs[1]); sblkcnt := 1; sblkptrs[1]^.free := sblksize; end; {------ make a copy of a string with alphas forced to upcase --} {------ and punctuation deleted, so it can be compared in -----} {------ "dictionary sequence." --------------------------------} procedure stucase(var a,b:str); var aj, bj : strindex; n: nchar; begin aj := 1; bj := 1; repeat n := a[aj]; aj := aj+1; if (chr(n) in allowset) then n := n-32; b[bj] := n; bj := bj+1; until (n=null) end; {------ stow independent string A in a string block and fill --} {------ in its STREF, allocate new string block as needed -----} procedure stput(var a:str; var b:stref); var bp : ^strblock; j : strlength; k : 0..sblksize; n : nchar; begin bp := sblkptrs[sblkcnt]; { ^latest string buffer } k := sblksize - bp^.free; { index of next free byte -1 } if(k < sblksize) then b.bo := k+1;{ note start in stref } j := 0; { index start of string } repeat { copy bytes from str to block until null copied } if (k >= sblksize) then begin { ooops, off end of block, start over in a new one } if (sblkcnt = sblkmax) then { uh-oh } disaster(1); sblkcnt := sblkcnt + 1; new(sblkptrs[sblkcnt]); bp := sblkptrs[sblkcnt]; k := 0; b.bo := 1; j := 0 end; j := j + 1; n := a[j]; k := k + 1; bp^.data[k] := n; until (n = null); b.nb := sblkcnt; { complete the stref } bp^.free := sblksize - k; end; {------ retrieve stowed string into independent array --------} procedure stget(var b:stref; var a:str); var bp : ^strblock; j : strindex; k : 1..sblksize; n : nchar; begin bp := sblkptrs[b.nb]; { point to the buffer page } k := b.bo; { ..and offset into it } j := 1; repeat { copy bytes until the null } n := bp^.data[k]; a[j] := n; if (n<>null) then begin k := k+1; j := j+1 end until (n=null); end; {------ EXACT compare of string A to stowed string B, so a ----} {------ leading substring is found to be "less" ---------------} function stbcmpx(var a:str; var b:stref) : relation; var bp : ^strblock; j : strlength; k : 0..sblksize; x,y : nchar; begin bp := sblkptrs[b.nb]; k := b.bo - 1; j := 0; repeat j := j + 1; x := a[j]; k := k + 1; y := bp^.data[k]; until ((x<>y) or (x=null)); if (x=y) then stbcmpx := equal else if xy) or (x=null)); if ((x=y) or (x=null)) then stbcmpa := equal else if (xy) or (x=null)); if (x=y) then sbbcmpx := equal else if xy) or (x=null)); if ((x=y) or (x=null)) then stbcmpa := equal else if (x null) then begin n := pushed; pushed := null end else if eof(inpfile) then n := asciieof else if eoln(inpfile) then begin readln(inpfile); n := asciicr; column := 0; blanks := 0 end else if (blanks > 0) then begin n := asciiblank; blanks := blanks - 1; end else begin read(inpfile,c); n := ord(c); if (n <> asciitab) then column := column + 1 else begin blanks := 7 - (column mod 8); column := column + blanks; n := asciiblank end end; nextinch := n end; {------ open the output file ----------------------------------} procedure initout; begin assign(outfile,'INDEX.OUT'); rewrite(outfile); end; {------ write a byte to the output - this is ONLY output ------} procedure nextout(n:nchar); begin if (n = asciicr) then writeln(outfile) else if (n = asciieof) then close(outfile) else write(outfile,chr(n)) end; {------ write an integer without a leading blank --------------} procedure intwrite(x : integer); const czero = 48; powten : array[1..4] of integer = (10,100,1000,10000); var k, p : integer; d : nchar; z : boolean; begin if (x < 0) then begin nextout(ord('-')); x := abs(x) end; z := false; { n.b. integer division is VERY slow in 8-bit turbo } { yes, slower than 5 adds and 5 subtracts } for k := 4 downto 1 do begin p := powten[k]; d := czero; while (x >= p) do begin d := d + 1; x := x - p end; if (d > czero) then z := true; if (z) then nextout(d) end; nextout(czero+x) end;  + 1; x := x - p end; if (d > czero) then z := true; if (z) tUT'); rewrite(outfile); end; {------ write a byte to the output - this is ONLY output ------} procedure nextout(n:nchar); begin if (n = asciicr) then writeln(outfile) else if (n = asciieof) then close(outfile) else write(outfile,chr(n)) end; {------ write an integer without a leading blank --------------} procedure intwrite(x : integer); const czero = 48; powten : array[1..4] of integer = (10,100,1000,10000); var k, p : integer; d : nchar; z : boolean; begin if (x < 0) then begin nextout(ord('-')); x := abs(x) end; z := false; { n.b. integer division {==============================================================} { IXTREE.PAS } {Copyright (C) 1986 David E. Cortesi--see notice in IXMAIN.PAS.} { } { This include-file contains declarations and code related to } { the implementation of nested trees of index terms. A tree } { is a network of dynamically-allocated nodes in the structure } { of a binary tree. Terms are only inserted to the tree, no } { deletions are supported. However, the tree is kept balanced } { under insertions using a recursive method of my own. This } { dynamic rebalancing keeps the tree optimal (time to locate a } { node is never worse than log2 N) regardless of the order in } { which terms are entered. This in turn permits loading of the} { tree directly from a previous index text file, even though } { the terms in an index document are in ascending sequence. } { } { Each tree node represents one index term. The term itself is } { stored two ways, as typed and in dictionary-comparison form, } { a form that compares "Half Way" equal to "halfway". A node } { anchors a sorted chain of page-numbers, and may hold the root} { of an independent sub-tree of sub-terms. The tree is ordered } { so that all terms in cnodes[left] are less than, and all in } { cnodes[right] are greater, than this term. } { History: } { fix bug that caused loop/crash when adding C to tree with } { items A,B,D,E,F -- 11/2/86 } {==============================================================} {------ create a new tree node to hold the term in string A ---} {------ and its dictionary form, string D, returing address ---} function makenode(var a, d : str) : tree; var t : tree; begin new(t); with t^ do begin cnodes[left] := nil; cnodes[right] := nil; cdepth[left] := 0; cdepth[right] := 0; subtree := nil; stput(a,term); stput(d,dict); pages := nil; end; makenode := t end; {------ return the depth of tree T: one more than the larger --} {------ of the depths of its children -------------------------} function depthof(t: tree):tdepth; begin with t^ do if (cdepth[left] > cdepth[right]) then depthof := 1 + cdepth[left] else depthof := 1 + cdepth[right] end; {------ look for the term in string A in the tree T. Return ---} {------ the addr of the found node, or NIL if not found. This--} {------ routine is quick because nonrecursive. ----------------} function lookup(var d: str; {in} t : tree) : tree; var p : tree; r : relation; begin p := t; if (p <> nil) then repeat r := stbcmpx(d,p^.dict); case r of less: p := p^.cnodes[left]; equal: ; more: p := p^.cnodes[right]; end until ((r = equal) or (p = nil)); lookup := p end; {------ insert the tree N into the tree T. this is used when -} {------ a term was not found by lookup and must be put into --} {------ the tree as a node. The result of the function is NOT-} {------ the inserted node, but the NEW TOP NODE of the tree ---} {------ after rebalancing it: either T or its n-side child. ---} function tinsert( n, {into} t : tree) : tree; var s,o : sides; p,q : tree; begin if (t = nil) then tinsert := n else begin s := right; { assume adding in increasing order } o := left; if (sbbcmpx(n^.dict,t^.dict) = less) then begin s := left; o := right end; with t^ do begin cnodes[s] := tinsert(n,cnodes[s]); cdepth[s] := depthof(cnodes[s]); if (1 < (cdepth[s]-cdepth[o])) {tree is unbalanced toward side s but there's no point in } {attempting balance at the current level when subtree s } {is itself in perfect balance -- defer to higher level } {unless subtree s is unbalanced and might improve. } and (cnodes[s]^.cdepth[left] <> cnodes[s]^.cdepth[right]) then begin {rebalance tree by snipping off the parent at this level } {and its side-o subtree and reinserting that tree into its} {former child, subtree s. } p := cnodes[s]; cnodes[s] := nil; cdepth[s] := 0; p := tinsert(t, {into} p) end else p := t end; tinsert := p end end; {------ look for term A in tree T and, if it isn't found, put -} {------ it there, rebalancing the tree as needed. Return -----} {------ the address of the node found or inserted. -----------} function insert(var a: str; {into} var t : tree) : tree; var d: str; p: tree; begin stucase(a,d); p := lookup(d,t); if (p = nil) then begin {insert it} p := makenode(a,d); t := tinsert(p,t) end; insert := p end; kup(d,t); if (p = nil) then begin {insert it} p := {is itself in perfect balance -- defer to higher level } {unless subtree s is unbalanced and might improve. } and (cnodes[s]^.cdepth[left] <> cnodes[s]^.cdepth[right]) then begin {rebalance tree by snipping off the parent at this level } {and its side-o subtree and reinserting that tree into its} {former child, subtree s. } p := cnodes[s];{-------------------------------------------------------------} { IXWALK.PAS } {Copyright (C) 1986 David E. Cortesi--see notice in IXMAIN.PAS.} { } { This include file contains the code for tree-walking. The } { "term recall" feature of the program (see IX.DOC) works by } { finding the lexically-least term matching some argument, } { and performing an in-order walk of the tree from that point.} { During output we walk all nodes of all trees. } { The usual algorithm for a walk is recursive (see J&W 11.5), } { which is not convenient for this program. During output, } { there will be walks going on in main and subtrees, so the } { walk position is remembered in a dynamic record. } { History: } { initial code 6/2/86 } {-------------------------------------------------------------} {------ push node T onto the walk-stack for later use ---------} procedure wpush(t : tree; var w : walk); begin if (w.top < maxdepth) then begin w.top := w.top + 1; w.stack[w.top] := t end end; {------ pop the top node of the walk-stack --------------------} function wpop(var w : walk) : tree; begin if (w.top > 0) then begin wpop := w.stack[w.top]; w.top := w.top - 1 end else wpop := nil end; {------ initialize to walk a tree T by clearing the stack ----} {------ and pointing to the top node, then descending to the -} {------ lowest, leftmost node and returning it --------------} function initwalk(t : tree; var w : walk) : tree; var p : tree; begin w.top := 0; p := t; while (p <> nil) do begin wpush(p,w); p := p^.cnodes[left] end; w.curr := wpop(w); initwalk := w.curr end; {------ step to the next node of a tree in lexical order. ----} {------ return that next node as result, and save it as the --} {------ current step. Return nil at end of tree. ------------} function wstep(var w : walk) : tree; var wleft : boolean; begin wleft := false; { have handled w.curr and its left subtree} if (w.curr <> nil) then begin if (w.curr^.cnodes[right] <> nil) then begin { it has a right subtree, so enter it and.. } w.curr := w.curr^.cnodes[right]; wleft := true { ..then plunge down again } end else { nowhere to go but up } w.curr := wpop(w) end; if (wleft) then { plunge down to the left } while (w.curr^.cnodes[left] <> nil) do begin wpush(w.curr,w); w.curr := w.curr^.cnodes[left] end; wstep := w.curr end; {------ Given a partial term (upcased) in D and a tree T, -----} {------ set up so that a series of WSTEP calls will return all } {------ nodes initially equal to A. Return the first such, or } {------ nil if the tree is null or has no terms matching A. ---} function setscan(t : tree; var d : str; var w : walk) : tree; var p : tree; r : relation; quit : boolean; begin if (t = nil) then setscan := nil { no matches possible } else begin { first, find any part-equal node at all } p := t; repeat r := stbcmpa(d,p^.dict); case r of less : p := p^.cnodes[left]; more : p := p^.cnodes[right]; equal : ; end until (r = equal) or (p = nil); if (r <> equal) then setscan := nil { no match a-tall } else begin {-- node P is the highest match, none above it in the tree match, but there may be lesser and greater matches in its left and right children. Set up to walk the tree of which P is the root. InitWalk will return the least leaf of P and walking from there to the first match to A will give the least match to A (which may be the one in P) --} p := initwalk(p,w); repeat r := stbcmpa(d,p^.dict); if (r <> equal) then p := wstep(w) until (r = equal); setscan := p end end end; := stbcmpa(d,p^.dict); if (r <> equal) then p := wstep(w) until (r = equan if (t = nil) then setscan := nil { no matches possible } else begin { first, find any part-equal node at all }  The INDEXER Program Copyright (C) 1986 David E. Cortesi 2340 Tasso St. Palo Alto CA 94301 Compuserve 72155,450 MCI Mail DCORTESI (rarely checked) GEnie Mail DCORTESI (rarely checked) All rights reserved. Distribution for profit forbidden without explicit permission. Free distribution or distribution by nonprofit organizations permitted provided this notice is retained and author credit is given in printed materials. ====== HISTORICAL INTRODUCTION This program is an aid to indexing a book. The first version I wrote was in Ithaca Intersystem's Pascal/Z and donated to the Pascal/Z user's group, from which in time it reached SIG/M disk number 94 and also the FOG library. IF YOU HAVE THAT VERSION, (source in a single file INDEXER.PAS) GET RID OF IT; IT WILL CRASH ON YOU AND LOSE YOUR WORK!!!! In 1985 I converted that program to compile under Borland's Turbo Pascal for some people in Seattle. At the same time I made a version that would compile under Microsoft Pascal version 3.0 on the PC and gave it to some people at Microsoft. Those versions work pretty well (at least, nobody's complained), but this one supercedes both. This version is much more flexible and the code is a lot simpler and more readable (we all learn as we go along, though slowly). It's for Turbo Pascal and should work on a PC, but has NOT been tested on one by the author. Under CP/M, owing to Borland's peculiar storage management, you must compile the program in your own machine to get maximum benefit. The delivered COM file is set up for a machine with a TPA of only 48K (the CP/M CCP loads at C000h, the program overlays it up to C7F0h). The M)ain source file is IXMAIN; it includes all the others as it compiles. Select O)ptions and C)om file, and set the largest E)nd address your machine allows (the CCP plus 7f0h). ====== THE INDEXING PROBLEM An index is an indispensable part of any non-fiction book. Even a poor index can give better access to a book than a table of contents can, while a good index increases the utility of a book many times. However, indexing a book is a skilled job. The indexer must understand the subject of the book well, so as to know what the important topics are. The indexer must read the book and comprehend it, so as to know when a reference to a topic is important enough to merit an index entry. The indexer must understand the intended use, and users, of the book well, so as to know the terms that they will expect to find in the index. In the traditional, manual method of indexing, the indexer sets up a file of 4x5 (index!) cards, one for each topic to be indexed. Then he/she reads the book and, wherever a topic appears, writes the page number on its card. The sorted cards provide the material for typing the index. An index of sorts can be built automatically by sorting the words of a document file, eliminating duplicates, and appending the page numbers on which the words appeared. However, such an index refers only to words; it does not index topics. It works only to the extent that the topics of the book are fully expressed in single words (and to the extent that the words were used consistently). It is completely unselective, showing every reference no matter how trivial. There can be no provision for sub-entries under a major topic, nor for multiple entries for a topic under different terms. All automatic methods fail if the book is to be typeset, because they assume the numbers of the book as printed by computer. The page numbers of the typeset work will be different, so the machine-made index will have to be heavily edited at least. And of course, automatic methods can't work when the book isn't available in machine-readable form -- as when you want to index a software manual for your own use. The Indexer program is a machine aid for a human indexer. It does the job of the pile of index cards, and it makes the finished index automatically, as a disk file that can be edited or printed. ====== PROGRAM OPERATIONS When it starts operation, the Indexer loads a working document, an index in progress, if it can find one. Whether it does or not, it then prompts you for "terms" and "pages" and stores these until you indicate that you're finished. Then it writes the completed index as a text file to disk and ends. Let's look at this process in detail. The Indexer is a compiled program, and as such is a command of the operating system, CP/M or MSDOS. The name of the command is is IXMAIN, and it takes no parameters, so the command looks like A>ixmain The first thing the command does is to look for an existing file of index data, a file named INDEX.INP in the default disk (current directory, MSDOS). If it does not find that file, you will see these results: A>ixmain [index aid, revision of 11/3/86] [copyright(C) David E. Cortesi 1986] [no existing index document] [initialized] term: If it does find a file -- which would be the saved contents of an index you are working on -- you would see instead A>ixmain [index aid, revision of 11/3/86] [copyright(C) David E. Cortesi 1986] [reading existing index INDEX.INP ... finished] [initialized] term: Depending on the size of the existing index, there'd be a pause of some seconds before that word "... finished]" appeared. Once initialized, the program prompts for a "term." A "term" is a word or phrase you want to index. A term may contain spaces and any printable characters. You may use the backspace key to correct typing errors. You usually signal the end of the term with the Return key. Here's one: term: frammis page: ^---- return pressed there Indexer finds or adds the term to its list in storage, then prompts for a "page." It expects the page number of a reference to the term just entered. It will accept any integer from -32767 to 32767. You may only type a leading minus sign and digits here; if you type anything else the program rejects the keystroke with a beep. If you give it a page number greater than 32767, it will reduce it to 32767 without warning you. Suppose "frammis" is used on page 32: term: frammis page: 32 term: ^----- return ends the number The program has inserted the page number in its numerical list of page numbers entered for this term, "frammis," and it's now ready for another term. Suppose that was all you wanted to do with the index at this time (perhaps the phone rang). Pressing only Return at the main "term:" prompt ends the run: term: ^---- return pressed here [normal end, 27044 storage left.] A> The program has written the complete index as a printable text file to the file INDEX.OUT in the default disk (current direc- tory in MSDOS). You may work on it with the system's usual utilities, or edit it with an editor: A>type index.out frammis, 32 A> In particular, if you want the index as you have it so far to be available next time, you must make it INDEX.INP, using A>pip index.inp=index.out in CP/M, or A>copy index.out index.inp in MSDOS. The file INDEX.INP is the existing index-in-progress that the program will read when next it initializes itself. ====== INDEXING WITH A BATCH FILE You'd probably rather think of the index of a book in terms of that book's name, and keep the file with related ones, rather than calling it INDEX.INP and keeping it with this program. You might even have indexes in progress for more than one book at once. Well, all these things can be done with quite a simple batch command file. In CP/M it looks like this: A>type index.sub pip index.inp=$1.INX ixmain era $1.BAK ren $1.BAK=$1.INX pip $1.INX=index.out[v] ...and a typical use of it might look like A>submit index b:mybook Under MSDOS it might look like this: C:\indexer\>type index.bat copy %1.INX index.inp ixmain erase %1.BAK rename %1.INX %1.BAK copy index.out %1.INX and in use it might look like this: C:\indexer\>index b:\backmatter\mybook In either case, the aim is to keep a book's index file as a file called (name-of-book).INX on some drive or subdirectory with the rest of the book's files. When you want to index, the batch commands make a copy of the index under the name INDEX.INP and afterward, put the updated index back. You should be able to adapt these examples to your disk arrangement and methods. ====== RANGES OF PAGES When Indexer stores a page number, it attempts to economize on storage and document size by packing adjacent line numbers into hyphenated ranges. For example, if you have entered references to "frammis" on pages 9, 11, and later 10, you will find in the output file frammis, 9-11 instead of individual page numbers 9, 10, and 11. You may enter ranges of page numbers yourself. If you know that "frammis" is discussed on pages 193 to 212 inclusive, you need not make all 19 separate entries (as you did in older versions of this program). Instead, enter the first page number, 193, but end it with Linefeed instead of Return. The program will prompt with two dots and await the second number of the range: term: frammis page: 193..212 ^------linefeed pressed there term: The second number of the pair must be equal to or greater than the first one (a range of, say, 6..6 is taken as merely the single number 6). If the second number is less, the program will snap at you and make you enter both numbers over again. ====== USING SUB-TERMS Indexer allows any term to have sub-terms and sub-sub-terms to a depth of nine. Subterms are very useful in indexes; they allow you to group subtopics under a main topic entry. Some readers will look for a topic under its general term; others will look for it under a very specific term. For example, a good index to a Pascal manual might index the CASE statement two or even three ways: ... CASE statement, 27 ELSE allowed, 4, 69 speed of vs. IF, 43 ... ELSE clause, 27 with CASE, 4, 69 ... statement types assignment, 25 BEGIN, 32 CASE, 27 ... To Indexer, each group of subterms is a little index of its own. Its terms are stored and sorted, and their page numbers are collected, just as is done for the main terms. When it prepares the output file, Indexer indents 3 spaces at each subterm level. To enter a subterm, you first enter its main term. But instead of ending the main term by pressing Return, you end it by pressing LineFeed (or Control-J, or Control-Return on the IBM keyboard). Indexer then prompts for a ". . term," indenting one level on the screen. Enter the subterm just as you would a main term. End it, too, with LineFeed if you are entering a sub-subterm. When you eventually end a term with Return, you will be prompted for a page number as usual. That page number will be associated with the subterm, not with its superior term(s). Let's enter a couple: term: frammis <---linefeed used here to enter subterm . . term: purchase price <---return indicates last term . . page: 17 term: frammis <---linefeed here . . term: components of <---linefeed again, sub-subterm . . . . term: stripped screws <---now a return . . . . page: 1432 term: ====== TERM RECALL Typing all these terms is tedious, but Indexer has a feature that can save a lot of the labor. The feature is called Term Recall, and it serves two purposes. You recall a term by typing some of its initial letters, then pressing the Escape key. Indexer searches its list of terms for the alphabetically-lowest one that matches the initial letters you have typed to that point. It then completes the term on the screen by typing the remaining letters of that term. If that is the term you wanted to recall, you may then press Return or LineFeed exactly as if you had typed the whole term yourself. Or you can modify the term as it appears then by backspacing and retyping part of it. If the term is not the one you want, just press Escape again. INDEXER will wipe out the letters it supplied, find the next term in alphabetic order, and show its final letters. If you keep pressing Escape, you will be shown all the terms that match the initial letters you typed. When there are no more, INDEXER beeps the console alarm. If you decide that you don't want any of the recalled terms, press the Delete key. INDEXER will restore the line to just the characters you had typed initially. Term Recall can save a lot of typing. It also provides a way to review the terms you have defined so far. Press Escape without typing any initial letters at all; INDEXER will complete your non-existent entry by showing you the first of all the terms it has, and will step through all the terms as you press Escape. Term Recall may be used at any level of sub-terms. To see what subterms you have under some main term, enter the main term, ending it with Linefeed. Then use Escape to recall the subterms that are stored under that main term. ====== CORRECTING ERRORS -- THE INDEX INPUT FILE The Indexer provides no way to delete a term or a page number, and no way to edit either one after you've entered it. This was a serious problem in older versions of the program. In this version, however, the saved index-in-progress is a text file, and you may correct errors by editing it. As discussed earlier, the program's output is a text file, and when it starts up it reads a copy of its previous output in order to initialize itself for more work. But you are free to edit the program's output file before feeding it back in, and that means you are free to correct index errors, even add new terms, with your regular text editor, between runs of the indexer. You needn't even keep the entries in alphabetical order; the program will put them back in order as it reads in the file at the start of a new session. However, the program is very picky about the format of the lines it reads from its existing index document. Basically it expects the document to be pretty much the way it wrote it as output! It ignores blank lines, but all others must have this form: (margin) (term) (spaces) (number list) Taking those in order: the (margin) is zero or more spaces, and the program takes the number of spaces as its clue to terms and subterms. When the margin gets wider from one term to the next, the program assumes that the more-indented term is a subterm of the term in the previous line. When you add terms to the text file, be sure to align them precisely relative to the terms above and below them, or you may end up with levels of subterm you didn't intend. The (term) is any sequence of printable characters. SINGLE blanks may appear in it as word-spaces. But the program takes the appearance of TWO OR MORE spaces to be (spaces), the signal that marks the end of (term) and the start of (number list). This rule leads to some minor problems we'll discuss below. The (number list) is a list of zero or more numbers in the form the program writes it during output: single integers or pairs of them with a hyphen between, separated by commas and spaces. What this means is that you must treat an index in progress as a "non-document" file for word processing purposes. Nor should you put into it any commentary or text other than index lines until you are quite finished indexing. ====== MERGING INDEX FILES The program can store quite a large index (a few hundred terms), an adequate capacity for small projects. A message at the end of each run gives you some indication of how full storage was; if it drops below 5,000 you may begin to worry about space running out. If you do begin to run out, divide the work. Begin to build a second index for some separable part of the book. You might divide the book by parts or chapters, or you might choose some class of terms (names of people for example, or a class of technical terms such as medical or botanical latinisms or computer words), a class that can be indexed separately. (Choose the division right and you might even be able to divide the work of indexing between two people with separate computers!) After dividing the index work, begin working on two (or more) separate index files, exactly as you previously worked on a single file. When the work is complete, you may merge separate index files with the IXMERGE program. It performs a merger of two index documents to form a single larger one. Since it does this a line at a time, it suffers no storage restrictions. The command (under CP/M) is A>ixmerge addfile where "addfile" is the filename of an index document as prepared by ixmain. The program reads two files: "addfile" and INDEX.INP. It merges them into the single file, INDEX.OUT, which may then be copied to some other name. If there are more files to be merged, simply rename INDEX.OUT to INDEX.INP and repeat the merge for the next file. The only limit to the final size is disk capacity. Suppose you have split the work into three, building separate indexes as PART1.INX, PART2.INX, and NAMES.INX for proper names. Here is a CP/M batch file that would build the full index. A>type mrge.sub pip index.inp=b:part1.inx ixmerge b:part2.inx era index.inp ren index.inp=index.out ixmerge b:names.inx pip b:full.inx=index.out[v] And here is how it might look under MSDOS: C:\indexer\>type index.bat copy b:\backmatter\mybook\part1.inx index.inp ixmerge b:\backmatter\mybook\part2.inx erase index.inp rename index.out index.inp ixmerge b:\backmatter\mybook\names.inx copy index.out b:\backmatter\mybook\full.inx In either case, merger would be the final step before printing. If more entries were needed, you'd use ixmain on the partial files, then merge again. ====== SUGGESTIONS FOR USE The more planning you do beforehand the better your index will be (so what else is new?). But it's true! Start by marking up a copy of the book. Read through it carefully with a pencil and a highlighting pen in hand. Mark every term to be indexed, and note in the margins where a topic should be entered under more than one term. One reason for planning is to avoid inconsistent spellings. The Indexer ignores the case of letters in looking up terms, so that "Indexer" and "indexer" are the same term to it. However, it does compare all punctuation and white space literally, as you typed it -- so "halfway" and "half way" are DIFFERENT terms. Consistency of punctuation is equally important when using IXMERGE. It ignores letter case when comparing terms, but like IXMAIN it compares whitespace and punctuation naively, and minor differences of punctuation can lead to extra entries. Further on the matter of spaces: during input, IXMAIN takes the terms just as you type them, and alphabetizes naively. If you insert multiple spaces, they are stored, so that not only is "half way" not the same as "halfway," the term "half way" is different still. But during output, the program compresses multiple spaces to single ones, so as to ensure that its output file meets its own requirements for input as discussed above. If you must include multiple spaces in a term, enter some other character to stand for them -- the sharp, #, is conventional. An even odder thing affects leading spaces. You ought never to type a leading space on a term, but if you do it, the program will store it as typed. The leading space collates ahead of all other initial letters, so that term will show up first during term recall and out of sequence in the output. But during output, the program strips leading spaces so as to avoid an incorrect margin in the input file. Then, when the working index is read back on the next run, the misplaced term will be stored correctly, and will now show up in its right collating order! Sometimes you want a term to appear without page numbers. This is often true of main terms with many subterms; you use the main term only as a heading and put all the page numbers on its subterms. The way to do this is simply to press return when the program issues the "page:" prompt. The program does not allow for Roman numeral page numbers. If you must index Roman-numeralled pages, I suggest you enter them as their decimal equivalents but with a minus-sign: enter vii as -7. Minus numbers sort ahead of positive ones, and you can find them with a global search on (space)(hyphen) and fix them when the index is done. Sometimes you may want to distinguish one page number among many -- say you want the primary reference to a term to print in bold type, or a reference to an illustration to be italicized. The program doesn't allow for extraneous text with page numbers. One clumsy way to deal with this is to enter distinguished page numbers as large negative numbers, so that, say, -20318 stands for page 318 in bold face, and -30318 means page 318 in italics. Then you fix these by tedious hand labor in the final document. Some books have two-part page numbers, CC-PP where CC is a chapter number. If no chapter has more than 999 pages, and if there aren't more than 31 chapters, just enter these as the chapter number times 1000 plus the page: 2025 for 2-25. These even collate in the right sequence. The program doesn't make specific allowance for "see" entries, as in "Frammis: see Imaginary Implements." But such a string would just be another term, and if you don't enter a page number for it, it'll stand in the output. Alternatively, you could set up another range of phony numbers, with -9991 standing for "see Imaginary Implements," and have yet another job to do in the final edit. Ordinary terms are short, just a word or a very short phrase. After all, indexes are normally printed two columns to a page! Thus the 64-byte limit on terms is no real limit at all. But if you use terms near that length, and also use multiple levels of subterms, you'll find that at the 3rd subterm level, the long indentation of the prompt causes your long term to wrap on an 80-column screen. Indexer doesn't know or care how wide the screen is, and won't be bothered by this if you aren't. ====== TECHNICAL DETAILS Indexer stores terms as character strings. I chose to do my own strings rather than use the compiler's string type, so that the program could be ported to other compilers. Since most term storage is allocated dynamically, and the string type is always allocated for its maximal length, and most terms are much shorter than the 64 bytes allowed, keeping every term as an independent string would waste storage. Except when they are being entered or written, terms are stored compactly in blocks of 2048 bytes, and referenced by a block number and an offset index. If the program tries to allocate a string block and there isn't room, the program will crash with a runtime error, too bad. However, the number of 2K blocks it will try to use in all is set by the constant SBLKMAX in IXMAIN.PAS. If it uses them all up, it will save work in progress and end with a message, so keep SBLKMAX to a size that is less than the available heap space. Terms have to be stored in mixed case for output but compared in all-caps. For speed, every term is stored twice, once as typed and once all-cap. Even so, SBLKMAX = 10, or 20K for strings, allows a BIG index. But by all means allow more in MSDOS if you like. Terms are stored in a binary tree, and subterms under a term are stored in a tree dangling from the superior term's node. When a term is inserted, the tree is rebalanced as needed, so terms may be entered in any sequence and the tree stays optimal. The algorithm for dynamic rebalancing is my own, and is more notable for simplicity than for efficiency. Page numbers are stored in an ordered chain anchored in the term's node. Chain links are allocated only as needed, new numbers being merged into existing ranges when possible. However there is no attempt to compact a chain: entering 3 then 5 then 4 produces the range 3-4 and the lone page 5. Overlaps can develop in some circumstances, so that the chain shows a range of 8-15 and another of 12-16. These are tidied up at output. Term recall and output both depend on an "in-order" tree walk that is implemented with nonrecursive code. During output it is necessary to interrupt the walk over main terms to walk trees of subterms, so the WALK structure holds the record of the present position of a tree-walk. k over main terms to walk trees of subterms, so tode. When a term is inserted, the tree is rebalanced as needed, so terms may be entered in any sequence and the tree stays optimal. The algorithm for dynamic rebalancing is my own, and is more notable for simplicity than for efficiency. Page numbers are stored in an ordered chain anchored in the term's node. Chain links are allocated only as needed, new numbers being mer INDEX AID PACKAGE This is a package of software that aids the preparation of an index for a book. The package supersedes all prior versions of this code that have made their way into the public domain. The documentation is in INDEXER.DOC, and the executable programs are IXMAIN.COM and IXMERGE.COM. The remaining files are the source code, meant to compile under Turbo Pascal on CP/M (although few changes should be needed for use in MSDOS). David E. Cortesi 11/20/86 compile under Turbo IXSTRG PAS+WXYZ[\IXTEXT PAS]^_`IXTREE PAS%abcdeIXWALK PASfghiINDEXER DOCjklmnopqrstuvwxyINDEXER DOCEz{|}~ABSTRACT$$$ ͫCopyright (C) 1985 BORLAND IncBZenithminal selectedPY ELMKqp~7#~= oͦkԄ!!"~#(}:$= +*!5!*!!:(2!5:(>2!!!:O::O:!*! !45(! +/ 0y0( d!k5!{5__o&  :(͠|(  *"x2y( >28!"9!! og2"">~22 9/4*9 Co&ͦͣ} [ (!e{ͦA8Q0G: x@!\w# (͂ ?(*( .( w^. ^!h6# (?( *( ͂( w#>?> w#ͦ 8 !ɿ .,;:=?*[]<>{}a{ |ʹ}ͽƐ'@'7||}>2ͯ*Bڨ  "og"2>2! ""*B"[Ru*"^#V#^#V#N#FO/o&9O/o&9!9(> (G!9 w#Eͺw}8' RB0 >' RqRR!+ Ͱ R!+ Ͱ r!+ Ͱ r!+ Ͱ r!# Ͱ r!+ Ͱ T]KB!z> S>))0 = | |̀̀DMgo>jB0 7?= H͒<z5a)a<z {0Gɯgo||~}||/g}/o#}o&K[xAJSJDM!b"!6J"DM'd } ) W _}8(8J`9{T]=o`9y ) >' ́ ͬ͗ }>' xˆ }} ˸T}ٕ(0D=z ,= ( ͒ 0%{ , 7 ?(8ͬ x ͆ - r 8˸x ͏  ,-xG}r }مM 9r .>#n0͒ { = - nx ͇ ,-(-˸G,-r }ٕ?M 9.> 8ͬ ?= u+-(>͆ 0ͬ ͆ 8 ?x ͇ , 78ƀ8ƀ8ox٨!دoGOW_gɷɷ|لg{ً_zيWyىOxوG|ٔg{ٛ_zٚWyٙOx٘Gxٸyٹzٺ{ٻ|ټx٨ xx( ?}ٽ }ցr <(r 7{ = |٤g{٣_z٢Wy١Ox٠G{ ͬ ́ }x>' ͬ}ƀ/ƀo -́ }0͎-́ ͎,}l˸ 8 4 ͗ x( - 8́ - 8,́ }l8;*!͗ ! >4ͬ͗ ͗ ͬ--- ́ ,,,-xGg?+2n*8t z~,->' x' ͘}. ͆́ , ! >4,͢- o&0%,͗ }gr }؉}颋.:}8c~I$I~L*͢ٷx˸ }0G,<},-(-́ !>I0 ͗͘ o8 ͆ >' m.`1pF,t6|!wS<.z}[|%FXc~ur1}͆ٯx(<˸ 8 !~J 0.O!>s 8 =  n s͗ ͆ .n 0 ͎-́ OT0 j oD,:j !I}袋.}8c~I$I~L! >ͬ͗ I× nn ͗ = ͆ nf^VNF!DLT\I!!53!r1!͒!> x #-= o˸x͆(- }(x>8(z ,z `iÃ!>' |r |̀>)=|(DMbo˸88x(0 8> ̀x(>-{(ay( z(>. ( {>E>+|(|Dg>-|/ 0:p# ~# +>0w#,-  60#}˸}րogM| .(z = ~> x0w#xG%͇ %͇ ZJDM%͇ = _~65+~hìx-Sx9?+{Η@}|z z gZJDM0{ ,7}o˸? #yO!@9i&?  #?w#?/w#?w#!9! E9!!9~(+Fͺ!"9!(#>2*"| >"2:( Ͷ *w*6 !\$![ (ͦ( #:~CONTRMKBDLSTCAUXUSR>2i:*ˮ~0:*:(@q##pZ* :(  ~* < >26"!"""~>2""v>2>"!"ˮ(!~8>~O6~*"w(6(2(-()(6 (8 0 :(* y(~#+ (( 66 #6 #"*: y~o p .##~ͺ(.6w4._~ =*##55= *[R8*~#"= ͣ}== ͯ}͵}*#w+#~+>*~('k!0(ˮ]k!8ˮ!]~-#8~>27kˮw>O$6̃s #r$ͣ6̏ k ( (ˮ qk(ˮ ( k ˮ*O:~ ##~._q4((=ʦ==ʩ=ʬò*:4^q*##~6ͺ>2}*|(̓|( ̓6-#[RM8( G> A~#*'C! !TRUEFALSE!9N#Y~#( G~#> >    "~(kѻ(( !0 (ˮ!!>2Sz:0:*6##ww#w$w#w:  ##N#F*B>2w#w#[s#r> "~ͮ*-w#ww##> ͮÁ""~>2:ZR0 *4#4>2:ZR> *4 #4(> >22*f(/˦:G(##~++ :O x yC!ͺ Q*:G(##~._.͚f<\=<͚*##w ͮ +4 #4x >>2:G("ͮ"*nˮ*0 SZѷR8@* N#F#s#r, 0})jS\*##w+ N#FB ͮr+s>2!T]>)j)0 0= ]R!#]*^#V#N#F#^#V>2Ͱ:0:*6 #-Nw#Fwq#p#6#w#w#w"~Á>">!DM!":*B:!>(>2>">!"2"~ʰ*w#wx(9* :O *-4 #4!*4 #4 *-N#Fq#pV+^Bq#pSZѷR&* s#r$ s#rL <?*L!\  <( !\$>2>2L:>!(* \$\<(!3: [1ð\!(7"~> 2"S"Ns#FrB(Z#\: \<(?*"}K\! !*}#"}! x \* *>) 2""{_!"*nf}(HR0nf" ^VMDnfutqp*s#r*s#r"* uKB!0>' ~#fo{_"*R0RnfR0KqputsrNF( ^VNF^V*SutKqp R*R(~w~wnf ut"6#K*K*!""*NFy(* "*B0Cnf* [R*"*RS[s#r^#V""6#>O"w2x2*"!F"" &y*"*>2"*"!F"""!\*: Nr!~6go(\R*s#r_2x( s x(T]DMx(R0 U(͝O/o&9q# (!>F0#( ~ ( #]( ~ ( (#}(  i&T-a%â}ͦo*!~6o&|:2 2}:__zѯ2*|KB " z ^C User break+=  I/O Run-time error {ʹ, PC=*ͯNot enough memory Program aborted :ʎ'1!d!8>Ö6!"!"G*G!:o*G!Eʌ!*G}2IÓ!!}2I*"*G*s#r*!s*!s#r*!s#r*!s#r* !:*I&" !"}2**&s " !"*"*n&!͒E"*n&}2*!s#*^#VEʘ"!}2#*^#VE"*^#Vͩͫ ! }2*!s#r*!s#r#*^#V!E$#! }2**^#V!Rs#r#*^#Vͩ!D *&}2*&! ͒Es#**^#V!s#r#*!*^#V!ERs#r**^#V*^#Vs#r! }2*&}2*& "!!]M INDEX.OUTo !] !}2J" !}2*&! EM$!]ͺ |$*&!Ek$!]i |$!]ͺ*&"  " !""͛ ͛ͺError in line *^#V!&ͺ of file * :!ͪ ͛ͺ... *R@%͛ͺterm must shorter than !c!&ͺ letters m&Rx%͛ͺ!term must be printable characters m&R³%͛ͺ$found nondigit where number expected m&R%͛ͺpage number too large m&R&͛ͺimproper range of numbers m&R;&͛ͺtoo many page numbers m&Rm&͛ͺreading past eof (logic error) ͛ͺ"... output file may be incomplete. !$͛  "& d'!x"*!E&!-$*̀"!}2x!!vzʼ'"|!&*|+)^#V"z!0}2y*!EY'*y&!}2y**zR"('*!Eʇ'**z"*y&!R}2y*y&!0Eʠ'!}2x*x&Eʳ'*y&$*|+'!0*$x"!j"t"v*v!+n&!E (!*tͅ$*v!+n&! ͒El(*v!+n&*J&͒El(*J&!͒E[(! $*v!+n&}2J!}2k*v*k&+n&$*k&!}2k*v*k&+n&!*k&!c}oEs(*v^#Vn&!͒Eʱ)*v^#V"l!,$! $! $!*ln&fzʱ)}2j*j&!E9)!,$! $*l*j&+)^#V͸&*l*j&+)^#V*l*j&+)^#VEʦ)!-$*l*j&+)^#V͸&*j&#)! $j"!Z"e"g!}2\*\&!}2\*g*\&+n&}2[*e*\&+n&}2Z*[&*Z&͒*[&!}oE)*[&*Z&EV*!}2i{**[&*Z&Et*!}2i{*!}2i*i&Z" !P}2X*X&!  ]OE**X&! R}2Y**X&}2Y*Y& P"!<"L*L2"}2C*C&!-}2B*B&E"+*L2"}2C*C&!! ]O}oEH+!*Lͅ$!!<!<,! *C&!0R !<*L2"}2C*C&!! ]O}oEX+*C&*L"!<,!E+!*Lͅ$*B&E ,!<,xG!<!<,"N*N<"!+"8":*8c!+n&!Ea,!*:ͅ$*:*:^#V!s#r!}2/*:2"}2-*-&! E,*/&!}2/*8c*/&+*-&s*8*/&+*-&s*-&! ͒*/&!c}oEʆ,*/&!cE!-!*:ͅ$*-&! ͒Ea,*-&!E-!!c!Rfzʘ-}2/*8*/&+!s*8c*/&+!s*/&#Y-*8!c+!s*8c!c+!sì0*/&!}2/*8*/&+*-&s*8c*/&+*-&͊*s*:2"}2-*-&! *8*/&+n&! }o*-&! }o*/&!c}oE-*-&! ͒*-&! ͒}oEʎ.!*:ͅ$*/&!c*-&! ͒}oEʽ.!*:ͅ$*-&! E.*/&!}2/*8*/&!R+n&!,E/*/&!R}2/*8*/&+!s*8c*/&+!s*8^#V"0!*0s!}2.*-&! E|/*:2"}2-\/*-&! ͒Eʙ0*-&*:"*:*"+*0*.&+)*+s#r*:2"}2-*-&!-E0*:*"+*+*0*.&+)^#V͸E0!*:ͅ$*:2"}2-*0*.&+)*+s#r*-&! *-&!,}oEj0*:2"}2-80*.&!}2.*.&!cEʖ0!*:ͅ$|/*.&!R*0s+"!"%"'")!}2!}2!}2*&*)n&͸*&*'n&͸}oE3*)*&+)^#V*'*&+)^#V͸Eʀ2*%*&+)*)*&+)^#Vs#r*)*&+)^#V*'*&+)^#VE1*%*&+)*)*&+)^#Vs#rn2*)*&+)^#V*'*&+)^#VE32*%*&+)*'*&+)^#Vs#r_2*%*&+)*)*&+)^#Vs#r*&!}2*&!}2ü3*%*&+)*'*&+)^#Vs#r*'*&+)^#V*)*&+)^#VE3*%*&+)*'*&+)^#Vs#rí3*'*&+)^#V*)*&+)^#VEr3*%*&+)*)*&+)^#Vs#rÞ3*%*&+)*'*&+)^#Vs#r*&!}2*&!}2*&!}20*&*)n&͸E\4*%*&+)*)*&+)^#Vs#r*%*&+)*)*&+)^#Vs#r*&!}2*&!}23*&*'n&͸E4*%*&+)*'*&+)^#Vs#r*%*&+)*'*&+)^#Vs#r*&!}2*&!}2\4*&!R*%s " !!*u#,!K*s#,*u!+n&!*s!+n&!}oE'6*uc*sc)}2*&R—5*u!'!*u#,$6R6*u^#V*s^#V*m͵0*u*ms#r*u!'*u*qs#r!*u#,!K*s#,$6R$6*s!K'!K*s#,%5*u!+n&!EZ6*u!'!*u#,'6*s!+n&!Eʍ6*s!K'!K*s#,Z6 "͛ͺ+[index document merge, version of 11/20/86] ͛ͺ%[copyright (c) 1986 David E. Cortesi] ͛!͒Eʭ7͛ͺ$Command form is: IXMERGE addfilename ͛ͺ/Document "addfilename" is merged with INDEX.INP ͛ͺ to form combined file INDEX.OUT. !M INDEX.INP!?!E8͛ͺ)file INDEX.INP doesn't exist or is empty. ! !}!K?!EY8͛ͺfile !}!ͪͺ doesn't exist or is empty. #!u!!s!!q!!o!!m!*u*qs#r*s*os#r*uc!+!s*sc!+!s5!$ !s!!q!!o!program ixmerge; {$I+,R-,A-,B-} {==============================================================} { Book Indexing Assistant, merge utility } {Copyright (C) 1986 David E. Cortesi--see notice in IXMAIN.PAS } { } { This utility merges two index documents as prepared by the } { IXMAIN program -- see it and INDEXER.DOC for further info. } { } { The essential insight that simplifies this program is that it} { doesn't have to make any provision for sub-indexes at any } { level. All it has to do is preserve the initial blanks that } { are used to indent subterms, and include them in comparisons.} { Then subterms always compare "less" versus their own (or any)} { superior terms. Thus once a term-line in a file F has been } { chosen for output because it is less than the current line of} { the other file, any subterms it may have will necessarily } { compare less to the other file as well, and will follow their} { superior to the output automatically. When two lines are } { equal both files are advanced and, if both files had subterms} { for that term, they will be compared and merged. If only one} { file had subterms for that term, they'll all compare "less" } { to the unindented next line of the other file as before. } { } { History: } { insert traps against endless loop & simplify mergem()11/20/86} { fix bug in pagemrg() 11/20/86 } { fix pointer-initialization bugs 11/5/86 } { comments and minor cleanup 11/4/86 } { initial writing 11/3/86 } {==============================================================} const null = 0; { the null, end-of-string } asciibel = 7; { names for ascii characters } asciitab = 9; asciicr = 13; asciieof = 26; asciiblank = 32; asciidel = 127; allowset : set of char = ['a'..'z']; digitset : set of char = ['0'..'9']; maxnests = 9; { allows up to 9 levels of subterms } strmax = 65; { max size of a string, 64 data plus 00} trmmax = 99; {at least strmax+3*maxnests to allow for the indentation spaces, which are retained } maxpages = 99; { most page number (ranges) per term } type relation = (less,equal,more); { result of comparisons } nchar = 0..255; { numeric characters are bytes } trmindex = 1..trmmax; { indices over strings } trmcount = 0..trmmax; { lengths of strings } trm = packed array[trmindex] of nchar; { term as string} pagindex = 1..maxpages; { indices over page numbers } pagcount = 0..maxpages; { counts of page numbers } pages = record { a record of a term's pages } np : pagcount; p1,p2 : array[pagindex] of integer; end; pageptr = ^pages; { pointer to one of those } line = record { record of one file line } t,u : trm; { term as given and uppercase } p : pageptr; { ^list of page numbers } end; lineptr = ^line; filespec = string[127]; filer = record { info about one textfile } pf : ^text; { address of file } pushed : nchar; { pushed-back byte } lineno, { line number for diagnostics } column, { logical column in line } blanks : integer; { blanks owed for last tab seen } fn : filespec; end; var lineA, lineB : lineptr; pageA, pageB, pageM : pageptr; finA, finB, fout : text; { two infiles and an out } frA, frB: filer; {two infiles with associated vars } initial : nchar; {control-break during output } {------ open an input file, return true if any problems ------} function initinp(var f:text; fs:filespec; var fr: filer): boolean; begin {$I- check own I/O results } assign(f,fs); reset(f); if (0 = IOresult) then initinp := eof(f) else initinp := true; with fr do begin pf := Ptr(Addr(f)); pushed := null; lineno := 0; column := 0; blanks := 0; fn := fs; end end; {$I+ back to compiler checks on I/O } {------ push back one byte of input, and damn Borland! for -----} {------ making this necessary by leaving out get/put i/o ------} procedure pushbak(n:nchar; var f:filer); begin f.pushed := n end; {------ return the next byte from the text file inpfile -------} function nextinp(var f: filer) : nchar; var c : char; n : nchar; begin with f do begin if (pushed <> null) then begin n := pushed; pushed := null end else if eof(pf^) then n := asciieof else if eoln(pf^) then begin readln(pf^); n := asciicr; column := 0; blanks := 0 end else if (blanks > 0) then begin n := asciiblank; blanks := blanks - 1; end else begin read(pf^,c); n := ord(c); if (n <> asciitab) then column := column + 1 else begin blanks := 7 - (column mod 8); column := column + blanks; n := asciiblank end end end; {with} nextinp := n end; {------ open the output file ----------------------------------} procedure initout; begin assign(fout,'INDEX.OUT'); rewrite(fout); initial := null; end; {------ write a byte to the output - this is ONLY output ------} procedure nextout(n:nchar); begin if (n = asciicr) then writeln(fout) else if (n = asciieof) then close(fout) else write(fout,chr(n)) end; {------ diagnose an error in the input file and abort ---------} procedure badfile(e:integer; var f: filer); begin writeln; writeln('Error in line ',f.lineno:1,' of file ',f.fn); write('...'); case e of 1 : write('term must shorter than ',trmmax,' letters'); 2 : write('term must be printable characters'); 3 : write('found nondigit where number expected'); 4 : write('page number too large'); 5 : write('improper range of numbers'); 6 : write('too many page numbers'); 7 : write('reading past eof (logic error)'); end; writeln('... output file may be incomplete.'); nextout(asciieof); { close output file } writeln; HALT end; {------ write an integer without a leading blank --------------} procedure intwrite(x : integer); const czero = 48; powten : array[1..4] of integer = (10,100,1000,10000); var k, p : integer; d : nchar; z : boolean; begin if (x < 0) then begin nextout(ord('-')); x := abs(x) end; z := false; { n.b. integer division is VERY slow in 8-bit turbo } { yes, slower than 5 adds and 5 subtracts } for k := 4 downto 1 do begin p := powten[k]; d := czero; while (x > 0) do begin d := d + 1; x := x - p end; if (x < 0) then begin x := x + p; d := d - 1 end; if (d > czero) then z := true; if (z) then nextout(d) end; nextout(czero+x) end; {---- write the contents of one line to the output file, ------} {---- first the term and then the list of numbers if any. -----} {---- Insert a null line if this is a change of initials at ---} {---- the left margin. ----------------------------------------} procedure putline(var l:line; var f:filer); var j : trmindex; n : pagcount; begin if (l.t[1] = asciidel) then badfile(7,f); if (l.t[1] <> asciiblank) then { chk only at level 0 } if (l.t[1] <> initial) then begin { is control brk } if (initial <> null) then { isn't first time } nextout(asciicr); initial := l.t[1] end; j := 1; repeat nextout(l.t[j]); j := j+1 until ((l.t[j] = null) or (j = trmmax)); if (l.p^.np <> 0) then with l.p^ do begin nextout(ord(',')); nextout(asciiblank); nextout(asciiblank); for n := 1 to np do begin if (n > 1) then begin nextout(ord(',')); nextout(asciiblank) end; intwrite(p1[n]); if (p2[n] > p1[n]) then begin nextout(ord('-')); intwrite(p2[n]) end end {for} end; {with} nextout(asciicr) end; {--- compare two terms returning less, equal, or more -------} function trmcomp(var ta,tb: trm) : relation; var j : trmcount; a,b : nchar; begin j := 0; repeat j := j+1; a := ta[j]; b := tb[j] until ((a <> b) or (a = null)); if (a < b) then trmcomp := less else if (a > b) then trmcomp := more else trmcomp := equal end; {--- return uppercase form of a letter ------------------------} function upcase(n:nchar):nchar; begin if (chr(n) in allowset) then upcase := n-32 else upcase := n end; {------ read and collect one signed integer, assuming that ----} {------ there should be a number coming next from nextinch ----} function getpage(var f: filer):integer; var n : nchar; s : boolean; x : real; begin n := nextinp(f); s := n = ord('-'); if (s) then n := nextinp(f); if (not (chr(n) in digitset)) then badfile(3,f); x := 0.0; repeat x := (x * 10.0) + (n - ord('0')); n := nextinp(f); until (not (chr(n) in digitset)); pushbak(n,f); if (x > 32767.0) then badfile(4,f); if (s) then x := -x; getpage := trunc(x) end; {--- get a line of text from the given index document and -----} {--- separate it into a term-string "and series of binary ------} {--- page numbers. store the term in lowercase and uppercase -} {--- forms for quick comparisons. Store eof as a "term" of ---} {--- asciidel so as to compare high to all legitimate terms ---} procedure getline(var f: filer; var l:line); var j: trmcount; k:pagcount; n: nchar; i : integer; begin if (l.u[1] = asciidel) then badfile(7,f); repeat { read and skip null lines. stop on nonnull term or eof } f.lineno := f.lineno + 1; j := 0; repeat {read and store initial blanks up to term, cr, or eof} n := nextinp(f); if (n = asciiblank) then begin j := j+1; l.u[j] := n; l.t[j] := n end until ((n <> asciiblank) or (j = trmmax)); if (j = trmmax) then badfile(1,f); { here n is either cr, eof, or a graphic } until (n <> asciicr); { loop on n=cr case } { here n is either eof or a graphic } if (n = asciieof) then begin { create 'infinity' at eof} for j := 1 to trmmax-1 do begin l.t[j] := asciidel; l.u[j] := asciidel end; l.t[trmmax] := null; l.u[trmmax] := null end else begin { n contains first byte of term, j is count of blanks } repeat j := j+1; l.t[j] := n; l.u[j] := upcase(n); n := nextinp(f) until ( ((n = asciiblank) and (l.t[j] = asciiblank)) or (n < asciiblank) or (j = trmmax)); if ((n <> asciiblank) and (n <> asciicr)) then badfile(2,f); { unknown control character } if ((j = trmmax) and (n <> asciiblank)) then badfile(1,f); { line too long } if (n = asciicr) then j := j+1; if (l.t[j-1] = ord(',')) then j := j-1; l.t[j] := null; {terminate the terms} l.u[j] := null; with l.p^ do begin np := 0; k := 1; while (n = asciiblank) do n := nextinp(f); while (n <> asciicr) do begin { n is neither blank nor cr, s.b. digit } pushbak(n,f); { put it back for getpage } i := getpage(f); p1[k] := i; n := nextinp(f); if (n = ord('-')) then begin i := getpage(f); if (i <= p1[k]) then badfile(5,f); n := nextinp(f) end; p2[k] := i; { n = byte after last digit: cr, comma, or blank } while ( (n = asciiblank) or (n = ord(',')) ) do n := nextinp(f); k := k + 1; if (k = maxpages) then badfile(6,f) end; np := k - 1 end {with} end { else not end of file } end; {--- merge two arrays of page numbers to make a third, allowing } {--- for ranges of page numbers. -------------------------------} procedure pagemrg(var a,b,m: pages); var i,j,k :pagcount; begin i := 1; j := 1; k := 1; while ( (i <= a.np) and (j <= b.np) ) do begin if (a.p1[i] <= b.p1[j]) then begin m.p1[k] := a.p1[i]; if (a.p2[i] < b.p1[j]) then {a less & disjoint} m.p2[k] := a.p2[i] else begin { a less or equal but not disjoint } if (a.p2[i] < b.p2[j]) then m.p2[k] := b.p2[j] else m.p2[k] := a.p2[i]; j := j+1 end; i := i + 1 end else begin { b.p1 < a.p1 } m.p1[k] := b.p1[j]; if (b.p2[j] < a.p1[i]) then { b less & disjoint} m.p2[k] := b.p2[j] else begin { b less but not disjoint } if (b.p2[j] < a.p2[i]) then m.p2[k] := a.p2[i] else m.p2[k] := b.p2[j]; i := i + 1 end; j := j + 1 end; k := k + 1 end; while (i <= a.np) do begin m.p1[k] := a.p1[i]; m.p2[k] := a.p2[i]; k := k + 1; i := i + 1 end; while (j <= b.np) do begin m.p1[k] := b.p1[j]; m.p2[k] := b.p2[j]; k := k + 1; j := j + 1 end; m.np := k-1; end; {-- merge the two input files to the output file --------------} procedure mergem; var r: relation; begin getline(frA,lineA^); getline(frB,lineB^); while ((lineA^.t[1] < asciidel) and (lineB^.t[1] < asciidel)) do begin r := trmcomp(lineA^.u,lineB^.u); case r of less: begin putline(lineA^,frA); getline(frA,lineA^) end; equal: begin pagemrg(lineA^.p^,lineB^.p^,pageM^); lineA^.p := pageM; putline(lineA^,frA); lineA^.p := pageA; getline(frA,lineA^); getline(frB,lineB^) end; more: begin putline(lineB^,frB); getline(frB,lineB^) end end {case} end {while}; while (lineA^.t[1] < asciidel) do begin putline(lineA^,frA); getline(frA,lineA^) end; while (lineB^.t[1] < asciidel) do begin putline(lineB^,frB); getline(frB,lineB^) end end; {==============================================================} { The main program, at last..... } {==============================================================} begin writeln('[index document merge, version of 11/20/86]'); writeln('[copyright (c) 1986 David E. Cortesi]'); if (Paramcount <> 1) then begin writeln('Command form is: IXMERGE addfilename'); writeln('Document "addfilename" is merged with INDEX.INP'); writeln('to form combined file INDEX.OUT.'); Halt end; if (initinp(finA,'INDEX.INP',frA)) then begin writeln('file INDEX.INP doesn''t exist or is empty.'); Halt end; if (initinp(finB,Para#mStr(1),frB)) then begin writeln('file ',ParamStr(1), ' doesn''t exist or is empty.'); Halt end; initout; new(lineA); new(lineB); new(pageA); new(pageB); new(pageM); lineA^.p := pageA; lineB^.p := pageB; lineA^.u[1] := null; lineB^.u[1] := null; mergem; nextout(asciieof); {close output file} end. ===========================} { The main program, at last..... } {==============================================================} begin writeln('[index document merge, version of 11/20/86]'); writeln('[copyright (c) 1986 David E. Cortesi]'); if (Paramcount <> 1) then begin writeln('Command form is: IXMERGE addfilename'); writeln('Document "addfilename" is merged with INDEX.INP'); writeln('to form combined file INDEX.OUT.'); Halt end; if (initinp(finA,'INDEX.INP',frA)) then begin writeln('file INDEX.INP doesn''t exist or is empty.'); Halt end; if (initinp(finB,Para This is the release date of the disk. IXSTRG PAS WIXTEXT PAS ] IXTREE PAS aIXWALK PAS fINDEXER DOC jbNDEXER DOC z"ABSTRACT IXMERGE COM 8IXMERGE PAS 9IXDIAG .PAS 82 41 1408 11 IXDOIT .PAS 98 22 9984 78 IXDONE .PAS EF 02 3456 27 IXINIT .PAS BA 1D 5120 40 IXSTRG .PAS CC E9 5504 43 IXTEXT .PAS 65 E1 3456 27 IXTREE .PAS CF 6E 4736 37 IXWALK .PAS E0 39 3968 31 INDEXER .DOC 41 96 25216 197 ABSTRACT. 7F 29 512 4 IXMERGE .COM F3 F0 14336 112 IXMERGE .PAS 8C 99 14720 115  Fog Library Disk FOG-CPM.151 Copyright (1987) by Fog International Computer Users Group to the extent not copyrighted by the original author for the exclusive use and enjoyment of its members. Any reproduction or distribution for profit or personal gain is strictly forbidden. For information, contact FOG, P. O. Box 3474, Daly City, CA. 94015-0474. as part of the description of a file indicates that the program is distributed on a "try first, pay if you like it" basis. If you find the program(s) meet your need, please refer to the author's documentation for information on becoming a registered user. Only by registering and paying for the programs you like and use will the authors of such programs continue development. Often, more complete documentation, additional modules, and new releases are available only to registered users. Word processing and WordStar companion utilities. Filename Description -01-00 .87 This is the release date of the disk. -CPM151 .DOC This is the description of the disk contents. ANYCODE .ASM 0A39 3K [ANYCODE 1 of 2] ASseMbler source code for patch which allows you to send any character to your printer from within WordStar. ANYCODE .DOC 932C 20K [ANYCODE 2 of 2] CHGCHAR .COM 8957 1K [Change Character 1 of 3] Will change any character in a file to any other character. Works with ASCII or hexidecimal characters. Squeezed ASseMbler source code is included. CHGCHAR .DOC C2DC 5K [Change Character 2 of 3] CHGCHAR .AQM 13C4 4K [Change Character 3 of 3] DBL .COM D7E3 4K [Double 1 of 2] Prints your text in column format and gives you control over format and print size. DBL .DOC 4385 1K [Double 2 of 2] IXMAIN .COM B29F 19K [Indexer 1 of 14] Program to help you index a book. Includes module which merges two index files and Turbo Pascal source code. IXMAIN .PAS EAEE 6K [Indexer 2 of 14] IXDIAG .PAS 8241 2K [Indexer 3 of 14] IXDOIT .PAS 9822 10K [Indexer 4 of 14] IXDONE .PAS EF02 4K [Indexer 5 of 14] IXINIT .PAS BA1D 5K [Indexer 6 of 14] IXSTRG .PAS CCE9 6K [Indexer 7 of 14] IXTEXT .PAS 65E1 4K [Indexer 8 of 14] IXTREE .PAS CF6E 5K [Indexer 9 of 14] IXWALK .PAS E039 4K [Indexer 10 of 14] INDEXER .DOC 4196 25K [Indexer 11 of 14] ABSTRACT. 7F29 1K [Indexer 12 of 14] IXMERGE .COM F3F0 14K [Indexer 13 of 14] IXMERGE .PAS 8C99 15K [Indexer 14 of 14]  [ANYCODE 2 of 2] CHGCHAR .COM 8957 1K [Change Character 1 of 3] Will change any character in a file to any other character. Works with ASCII or hexidecimal characters$%&'