SWAGOLX.EXE (c) 1993 GDSOFT ALL RIGHTS RESERVED 00019 SEARCH/FIND/REPLACE ROUTINES 1 05-28-9313:46ALL SWAG SUPPORT TEAM BMFIND.PAS IMPORT 27 π Hi, Andy:ππ ...Here's a demo program of the Boyer-Moore search algorithm.ππ The basic idea is to first create a Boyer-Moore index-tableπ for the string you want to search for, and then call theπ BMsearch routine. *Remember* to turn-off "range-checking"π {$R-} in your finished program, otherwise the BMSearch willπ take 3-4 times longer than it should.ππ (* Public-domain demo of Boyer-Moore search algorithm. *)π (* Guy McLoughlin - May 1, 1993. *)πprogram DemoBMSearch;ππ (* Boyer-Moore index-table data definition. *)πtypeπ BMTable = array[0..127] of byte;ππ (***** Create a Boyer-Moore index-table to search with. *)π (* *)π procedure Create_BMTable({input } Pattern : string;π {update} var BMT : BMTable);π varπ Index : byte;π beginπ fillchar(BMT, sizeof(BMT), length(Pattern));π for Index := 1 to length(Pattern) doπ BMT[ord(Pattern[Index])] := (length(Pattern) - Index)π end; (* Create_BMTable. *)ππ (***** Boyer-Moore Search function. Returns 0 if string is not *)π (* found. Returns 65,535 if BufferSize is too large. *)π (* ie: Greater than 65,520 bytes. *)π (* *)π function BMsearch({input } var Buffer;π BuffSize : word;π var BMT : BMTable;π Pattern : string) : {output} word;π varπ Buffer2 : array[1..65520] of char absolute Buffer;π Index1,π Index2,π PatSize : word;π beginπ if (BuffSize > 65520) thenπ beginπ BMsearch := $FFFF;π exitπ end;π PatSize := length(Pattern);π Index1 := PatSize;π Index2 := PatSize;π repeatπ if (Buffer2[Index1] = Pattern[Index2]) thenπ beginπ dec(Index1);π dec(Index2)π endπ elseπ beginπ if (succ(PatSize - Index2) > (BMT[ord(Buffer2[Index1])])) thenπ inc(Index1, succ(PatSize - Index2))π elseπ inc(Index1, BMT[ord(Buffer2[Index1])]);π Index2 := PatSizeπ end;π until (Index2 < 1) or (Index1 > BuffSize);π if (Index1 > BuffSize) thenπ BMsearch := 0π elseπ BMsearch := succ(Index1)π end; (* BMsearch. *)ππtypeπ arby_64K = array[1..65520] of byte;ππvarπ Index : word;π st_Temp : string[10];π Buffer : ^arby_64K;π BMT : BMTable;ππBEGINπ new(Buffer);π fillchar(Buffer^, sizeof(Buffer^), 0);π st_Temp := 'Gumby';π move(st_Temp[1], Buffer^[65516], length(st_Temp));π Create_BMTable(st_Temp, BMT);π Index := BMSearch(Buffer^, sizeof(Buffer^), BMT, st_Temp);π writeln(st_Temp, ' found at offset ', Index)πEND.π - Guyπ---π ■ DeLuxe²/386 1.25 #5060 ■π * Rose Media, Toronto, Canada : 416-733-2285π * PostLink(tm) v1.04 ROSE (#1047) : RelayNet(tm)ππ 2 05-28-9313:46ALL SWAG SUPPORT TEAM BMSEARCH.PAS IMPORT 31 { Default Compiler Directives}π{$S-,R-,V-,I-,N-,B-,F-}ππ{$IFNDEF Ver40}π {Allow overlays}π {$F+,O-,X+,A-}π{$ENDIF}ππUNIT SEARCH;ππINTERFACEππfunction SearchBuffer(var Buffer; BufLength : Word;π var Match; MatLength : Word) : Word;π {-Search through Buffer for Match. BufLength is length of range to search.π MatLength is length of string to match. Returns number of bytes searchedπ to find Match, $FFFF if not found.}ππIMPLEMENTATIONππ function SearchBuffer(var Buffer; BufLength : Word;π var Match; MatLength : Word) : Word;π {-Search through Buffer for Match. BufLength is length of range to search.π MatLength is length of string to match. Returns number of bytes searchedπ to find Match, $FFFF if not found.}π beginπ inline(π $1E/ {PUSH DS ;Save DS}π $FC/ {CLD ;Go forward}π $C4/$7E/<Buffer/ {LES DI,[BP+<Buffer] ;ES:DI => Buffer}π $89/$FB/ {MOV BX,DI ;BX = Ofs(Buffer)}π $8B/$4E/<BufLength/ {MOV CX,[BP+<BufLength] ;CX = Length of range to scan}π $8B/$56/<MatLength/ {MOV DX,[BP+<MatLength] ;DX = Length of match string}π $85/$D2/ {TEST DX,DX ;Length(Match) = 0?}π $74/$24/ {JZ Error ;If so, we're done}π $C5/$76/<Match/ {LDS SI,[BP+<Match] ;DS:SI => Match buffer}π $AC/ {LODSB ;AL = Match[1]; DS:SI => Match[2]}π $4A/ {DEC DX ;DX = MatLength-1}π $29/$D1/ {SUB CX,DX ;CX = BufLength-(MatLength-1)}π $76/$1B/ {JBE Error ;Error if BufLength is less}π {;Search for first character in Match}π {Next:}π $F2/$AE/ {REPNE SCASB ;Search forward for Match[1]}π $75/$17/ {JNE Error ;Done if not found}π $85/$D2/ {TEST DX,DX ;If Length = 1 (DX = 0) ...}π $74/$0C/ {JZ Found ; the "string" was found}π {;Search for remainder of Match}π $51/ {PUSH CX ;Save CX}π $57/ {PUSH DI ;Save DI}π $56/ {PUSH SI ;Save SI}π $89/$D1/ {MOV CX,DX ;CX = Length(Match) - 1}π $F3/$A6/ {REPE CMPSB ;Does rest of string match?}π $5E/ {POP SI ;Restore SI}π $5F/ {POP DI ;Restore DI}π $59/ {POP CX ;Restore CX}π $75/$EC/ {JNE Next ;Try again if no match}π {;Calculate number of bytes searched and return}π {Found:}π $4F/ {DEC DI ;DX = Offset where found}π $89/$F8/ {MOV AX,DI ;AX = Offset where found}π $29/$D8/ {SUB AX,BX ;Subtract starting offset}π $EB/$03/ {JMP SHORT SDone ;Done}π {;Match was not found}π {Error:}π $31/$C0/ {XOR AX,AX ;Return $FFFF}π $48/ {DEC AX}π {SDone:}π $1F/ {POP DS ;Restore DS}π $89/$46/<SearchBuffer); {MOV [BP+<Search],AX ;Set func result}π end;ππEND. 3 05-28-9313:46ALL SWAG SUPPORT TEAM BMSRCH.PAS IMPORT 30 {$A+,B-,D-,E+,F-,I-,L-,N-,O-,R-,S-,V-}πUnit BMSrch;ππInterfaceππTypeπ Btable = Array[0..255] of Byte;ππProcedure BMMakeTable(Var s; Var t : Btable);πFunction BMSearch(Var buff; size : Word; Bt: Btable; Var st): Word;πFunction BMSearchUC(Var buff; size : Word; Bt: Btable; Var st): Word;ππImplementationππProcedure BMMakeTable(Var s; Var t : Btable);π { Makes a Boyer-Moore search table. s = the search String t = the table }π Varπ st : Btable Absolute s;π slen: Byte Absolute s;π x : Byte;π beginπ FillChar(t,sizeof(t),slen);π For x := slen downto 1 doπ if (t[st[x]] = slen) thenπ t[st[x]] := slen - xπ end;ππFunction BMSearch(Var buff; size : Word; Bt: Btable; Var st): Word;π { Not quite a standard Boyer-Moore algorithm search routine }π { To use: pass buff as a dereferenced Pointer to the buffer}π { st is the String being searched For }π { size is the size of the buffer }π { If st is not found, returns $ffff }π Varπ buffer : Array[0..65519] of Byte Absolute buff;π s : Array[0..255] of Byte Absolute st;π len : Byte Absolute st;π s1 : String Absolute st;π s2 : String;π numb,π x : Word;π found : Boolean;π beginπ s2[0] := chr(len); { sets the length to that of the search String }π found := False; π numb := pred(len);π While (not found) and (numb < (size - len)) do beginπ if buffer[numb] = ord(s1[len]) then { partial match } beginπ if buffer[numb-pred(len)] = ord(s1[1]) then { less partial! } beginπ move(buffer[numb-pred(len)],s2[1],len);π found := s1 = s2; { if = it is a complete match }π BMSearch := numb - pred(len); { will stick unless not found }π end;π inc(numb); { bump by one Char - match is irrelevant }π endπ elseπ inc(numb,Bt[buffer[numb]]);π end;π if not found thenπ BMSearch := $ffff;π end; { BMSearch }ππ πFunction BMSearchUC(Var buff; size : Word; Bt: Btable; Var st): Word;π { Not quite a standard Boyer-Moore algorithm search routine }π { To use: pass buff as a dereferenced Pointer to the buffer}π { st is the String being searched For }π { size is the size of the buffer }π { If st is not found, returns $ffff }π Varπ buffer : Array[0..65519] of Byte Absolute buff;π chbuff : Array[0..65519] of Char Absolute buff;π s : Array[0..255] of Byte Absolute st;π len : Byte Absolute st;π s1 : String Absolute st;π s2 : String;π numb,π x : Word;π found : Boolean;π beginπ s2[0] := chr(len); { sets the length to that of the search String }π found := False; π numb := pred(len);π While (not found) and (numb < (size - len)) do beginπ if UpCase(chbuff[numb]) = s1[len] then { partial match } beginπ if UpCase(chbuff[numb-pred(len)]) = s1[1] then { less partial! } beginπ move(buffer[numb-pred(len)],s2[1],len);π For x := 1 to length(s2) doπ s2[x] := UpCase(s2[x]);π found := s1 = s2; { if = it is a complete match }π BMSearchUC := numb - pred(len); { will stick unless not found }π end;π inc(numb); { bump by one Char - match is irrelevant }π endπ elseπ inc(numb,Bt[ord(UpCase(chbuff[numb]))]);π end;π if not found thenπ BMSearchUC := $ffff;π end; { BMSearchUC }ππend.π 4 05-28-9313:46ALL SWAG SUPPORT TEAM BOYER.PAS IMPORT 29 π (* Public-domain demo of Boyer-Moore search algorithm. *)π (* Guy McLoughlin - May 2, 1993. *)πprogram DemoBMSearch;πππ (* Boyer-Moore index-table data definition. *)πtypeπ BMTable = array[0..255] of byte;πππ (***** Create a Boyer-Moore index-table to search with. *)π (* *)π procedure Create_BMTable({output} var BMT : BMTable;π {input } Pattern : string;π ExactCase : boolean);π varπ Index : byte;π beginπ fillchar(BMT, sizeof(BMT), length(Pattern));π if NOT ExactCase thenπ for Index := 1 to length(Pattern) doπ Pattern[Index] := upcase(Pattern[Index]);π for Index := 1 to length(Pattern) doπ BMT[ord(Pattern[Index])] := (length(Pattern) - Index)π end; (* Create_BMTable. *)πππ (***** Boyer-Moore Search function. Returns 0 if string is not *)π (* found. Returns 65,535 if BufferSize is too large. *)π (* ie: Greater than 65,520 bytes. *)π (* *)π function BMsearch({input } var BMT : BMTable;π var Buffer;π BuffSize : word;π Pattern : string;π ExactCase : boolean) : {output} word;π varπ Buffer2 : array[1..65520] of char absolute Buffer;π Index1,π Index2,π PatSize : word;π beginπ if (BuffSize > 65520) thenπ beginπ BMsearch := $FFFF;π exitπ end;π PatSize := length(Pattern);π if NOT ExactCase thenπ beginπ for Index1 := 1 to BuffSize doπ if (Buffer2[Index1] > #96)π and (Buffer2[Index1] < #123) thenπ dec(Buffer2[Index1], 32);π for Index1 := 1 to length(Pattern) doπ Pattern[Index1] := upcase(Pattern[Index1])π end;π Index1 := PatSize;π Index2 := PatSize;π repeatπ if (Buffer2[Index1] = Pattern[Index2]) thenπ beginπ dec(Index1);π dec(Index2)π endπ elseπ beginπ if (succ(PatSize - Index2) > (BMT[ord(Buffer2[Index1])])) thenπ inc(Index1, succ(PatSize - Index2))π elseπ inc(Index1, BMT[ord(Buffer2[Index1])]);π Index2 := PatSizeπ end;π until (Index2 < 1) or (Index1 > BuffSize);π if (Index1 > BuffSize) thenπ BMsearch := 0π elseπ BMsearch := succ(Index1)π end; (* BMsearch. *)ππtypeπ arby_64K = array[1..65520] of byte;ππvarπ Index : word;π st_Temp : string[20];π Buffer : ^arby_64K;π BMT : BMTable;ππBEGINπ new(Buffer);π fillchar(Buffer^, sizeof(Buffer^), 0);π st_Temp := 'aBcDeFgHiJkLmNoPqRsT';π move(st_Temp[1], Buffer^[65501], length(st_Temp));π st_Temp := 'AbCdEfGhIjKlMnOpQrSt';π Create_BMTable(BMT, st_Temp, false);π Index := BMSearch(BMT, Buffer^, sizeof(Buffer^), st_Temp, false);π writeln(st_Temp, ' found at offset ', Index)πEND.π - Guyπ---π ■ DeLuxe²/386 1.25 #5060 ■π * Rose Media, Toronto, Canada : 416-733-2285π * PostLink(tm) v1.04 ROSE (#1047) : RelayNet(tm)ππ 5 05-28-9313:46ALL SWAG SUPPORT TEAM CHGE.PAS IMPORT 103 Program Chge;ππ{ Copyright 1990 Trevor J Carlsen Version 1.06 24-07-90 }π{ This Program may be used and distributed as if it was in the Public Domain}π{ With the following exceptions: }π{ 1. If you alter it in any way, the copyright notice must not be }π{ changed. }π{ 2. If you use code excerpts in your own Programs, due credit must be }π{ given, along With a copyright notice - }π{ "Parts Copyright 1990 Trevor J Carlsen" }π{ 3. No Charge may be made For any Program using code from this Program.}ππ{ Changes (or deletes) a String in any File. If an .EXE or .COM File then }π{ the change must be of a similar length inorder to retain the executable }π{ integrity. }ππ{ If you find this Program useful here is the author's contact address - }ππ{ Trevor J Carlsen }π{ PO Box 568 }π{ Port Hedland Western Australia 6721 }π{ Voice 61 [0]91 72 2026 }π{ Data 61 [0]91 72 2569 }ππUsesπ BmSrch,π Dos;ππConstπ space = #32;π quote = #34;π comma = #44;π copyright1 = 'CHGE - version 1.06 Copyright 1989,1990 Trevor Carlsen';π copyright2 = 'All rights reserved.';ππVarπ dirinfo : SearchRec; { Dos }π f : File;π FDir : DirStr; { Dos }π mask,π fname,π oldstr,π newstr : String;π oldlen : Byte Absolute oldstr;π newlen : Byte Absolute newstr;π changes : Word;π time : LongInt Absolute $0000:$046C;π start : LongInt;ππFunction ElapsedTime(start : LongInt): Real;π beginπ ElapsedTime := (time - start) / 18.2;π end; { ElapsedTime }ππProcedure ReportError(e : Byte);πbeginπ Writeln('CHGE [path]Filename searchstr replacementstr|NUL');π Writeln(' eg: CHGE c:\autoexec.bat "color" "colour"');π Writeln(' CHGE c:\autoexec.bat 12 13,10,13,10,13,10,13,10');π Writeln(' CHGE c:\wp\test.txt "Trevor" NUL');π Writeln;π Writeln('The first example will change every occurrence of the Word "color" to "colour"');π Writeln('The second will replace every formfeed Character (ascii 12) With 4 sets of');π Writeln('carriage return/linefeed combinations and the third will delete every');π Writeln('occurrence of "Trevor"');π Writeln('The prime requirements are:');π Writeln(' There MUST always be exactly three space delimiters on the command line -');π Writeln(' one between the Program name and the Filename, one between the Filename and');π Writeln(' the search String and another between the search String and the replacement');π Writeln(' String. Any other spaces may ONLY occur between quote Characters.');π Writeln(' The Program will not permit you to change the length of an .EXE or .COM File,');π Writeln(' therefore the replacement String MUST be the same length as the String');π Writeln(' that it is replacing in these cases.');π Writeln;π Writeln(' If using ascii codes, each ascii Character must be separated from another');π Writeln(' by a comma. The same rule applies to spaces as above - three required - no');π Writeln(' more - no less. If just deleting the NUL must not be in quotes.');π halt(e);πend; { ReportError }ππFunction StUpCase(Str : String) : String;πVarπ Count : Integer;πbeginπ For Count := 1 to Length(Str) doπ Str[Count] := UpCase(Str[Count]);π StUpCase := Str;πend;ππProcedure ParseCommandLine;πVarπ parstr, { contains the command line }π temp : String;π len : Byte Absolute parstr; { the length Byte For parstr }π tlen : Byte Absolute temp; { the length Byte For temp }π CommaPos,π QuotePos,π SpacePos,π chval : Byte;π error : Integer;π DName : NameStr;π DExt : ExtStr;ππ Function right(Var s; n : Byte): String;{ Returns the n right portion of s }π Varπ st : String Absolute s;π len: Byte Absolute s;π beginπ if n >= len thenπ right := stπ elseπ right := copy(st,succ(len)-n,n);π end; { right }ππbeginπ parstr := String(ptr(PrefixSeg,$80)^); { Get the command line }π if parstr[1] = space thenπ delete(parstr,1,1); { First Character is usually a space }π SpacePos := pos(space,parstr);π if SpacePos = 0 then { No spaces }π ReportError(1);π mask := StUpCase(copy(parstr,1,pred(SpacePos)));π FSplit(mask,Fdir,DName,DExt); { To enable the directory to be kept }π delete(parstr,1,SpacePos);π QuotePos := pos(quote,parstr);π if QuotePos <> 0 then begin { quotes - so must be quoted Text }π if parstr[1] <> quote then { so first Char must be quote }π ReportError(2);π delete(parstr,1,1); { get rid of the first quote }π QuotePos := pos(quote,parstr); { and find the next quote }ππ if QuotePos = 0 then { no more - so it is an error }π ReportError(3);π oldstr := copy(parstr,1,pred(QuotePos));{ search String now defined }π if parstr[QuotePos+1] <> space then { must be space between }π ReportError(1);π delete(parstr,1,succ(QuotePos)); { the quotes - else error }π if parstr[1] <> quote then begin { may be a delete }π tlen := 3;π move(parstr[1],temp[1],3);π if temp <> 'NUL' then { is not a delete }π ReportError(4) { must be quote after space or NUL }π elseπ newlen := 0; { is a delete - so nul the replacement }π endπ else beginπ delete(parstr,1,1); { get rid of the quote }π QuotePos := pos(quote,parstr); { find next quote For end of String }π if QuotePos = 0 then { None? - then error }π ReportError(5);π newstr := copy(parstr,1,pred(QuotePos));{ Replacement String defined }π end;π endπ else begin { must be using ascii codes }π oldlen := 0;π SpacePos := pos(space,parstr); { Find end of search Characters }π if SpacePos = 0 then { No space - so error }π ReportError(6);π temp := copy(parstr,1,SpacePos-1);π delete(parstr,1,SpacePos); { get rid of the search Characters }π CommaPos := pos(comma,temp); { find first comma }π if CommaPos = 0 then { No comma - so only one ascii code }π CommaPos := succ(tlen);π Repeat { create the search String }π val(copy(temp,1,CommaPos-1),chval,error); { convert to a numeral and }π if error <> 0 then { if there is an error bomb out }π ReportError(7);π inc(oldlen);π oldstr[oldlen] := Char(chval);{ add latest Char to the search String }π delete(temp,1,CommaPos);π CommaPos := pos(comma,temp);π if CommaPos = 0 thenπ CommaPos := succ(tlen);π Until tlen = 0;π newlen := 0;π CommaPos := pos(comma,parstr);π if CommaPos = 0 thenπ CommaPos := succ(len);π Repeat { create the replacement String }π val(copy(parstr,1,pred(CommaPos)),chval,error);π if error <> 0 then { must be ascii code }π ReportError(8);π inc(newlen);π newstr[newlen] := Char(chval);π delete(parstr,1,CommaPos);π CommaPos := pos(comma,parstr);π if CommaPos = 0 then CommaPos := len+1;π Until len = 0;π end; { else }π if ((right(mask,3) = 'COM') or (right(mask,3) = 'EXE')) andπ (newlen <> oldlen) thenπ ReportError(16);πend; { ParseCommandLine }ππFunction OpenFile(fn : String): Boolean;π beginπ assign(f,fn);π {$I-} reset(f,1); {$I+}π OpenFile := IOResult = 0;π end; { OpenFile }ππProcedure CloseFile;π beginπ {$I-}π truncate(f);π Close(f);π if IOResult <> 0 then; { dummy call to IOResult }π {$I+}π end; { CloseFile }ππProcedure ChangeFile(Var chge : Word);π Constπ bufflen = 65000; { This is the limit For BMSearch }π searchlen = bufflen - 1000; { Allow space For extra Characters in }π Type { the replacement String }π buffer = Array[0..pred(bufflen)] of Byte;π buffptr = ^buffer;π Varπ table : BTable; { Boyer-Moore search table }π old, { Pointer to old buffer }π nu : buffptr; { Pointer to new buffer }π count,π result,π oldpos,π newpos : Word;π oldfpos,π newfpos : LongInt;π finished : Boolean;ππ Procedure AllocateMemory(Var p; size : Word);π Varπ buff : Pointer Absolute p;π beginπ if MaxAvail >= size thenπ GetMem(buff,size)π else beginπ Writeln('Insufficient memory available.');π halt(10);π end;π end; { AllocateMemory }ππ beginπ oldfpos := 0; newfpos := 0;π chge := 0;π AllocateMemory(old,searchlen);π AllocateMemory(nu,bufflen); { make room on the heap For the buffers }π BMMakeTable(oldstr,table); { Create a Boyer-Moore search table }π {$I-}π BlockRead(f,old^,searchlen,result); { Fill old buffer }π oldfpos := FilePos(f);π {$I+}π if IOResult <> 0 then beginπ CloseFile; ReportError(11);π end;π Repeatπ oldpos := 0; newpos := 0; count := 0;π finished := (result < searchlen); { if buffer<>full then no more reads }π Repeat { Do a BM search For search String }π count := BMSearch(old^[oldpos],result-oldpos,table,oldstr);π if count = $FFFF then begin { search String not found so copy rest }π move(old^[oldpos],nu^[newpos],result-oldpos); { of buffer to new }π inc(newpos,result-oldpos); { buffer and update the buffer markers }π inc(oldpos,result-oldpos);π endπ else begin { search String found }π if count <> 0 then begin { not at position one in the buffer }π move(old^[oldpos],nu^[newpos],count);{ transfer everything prior }π inc(oldpos,count); { to the search String to new buffer }π inc(newpos,count); { and update the buffer markers }π end;π move(newstr[1],nu^[newpos],newlen); { copy the replacement String }π inc(oldpos,oldlen); { to the new buffer and update the buffer }π inc(newpos,newlen); { markers }π inc(chge);π end;π Until oldpos >= result; { keep going Until end of buffer }π if not finished then begin { Fill 'er up again For another round }π {$I-}π seek(f,oldfpos);π BlockRead(f,old^,searchlen,result);π oldfpos := FilePos(f);π {$I+}π if IOResult <> 0 then beginπ CloseFile; ReportError(13);π end; { if IOResult }π end; { if not finished }π {$I-}π seek(f,newfpos);π BlockWrite(f,nu^,newpos); { Write new buffer to File }π newfpos := FilePos(f);π {$I+}π if IOResult <> 0 then beginπ CloseFile; ReportError(12);π end;π Until finished;π FreeMem(old, searchlen); FreeMem(nu,bufflen);π end; { ChangeFiles }ππProcedure Find_and_change_all_Files;π Varπ Filefound : Boolean;ππ Function padstr(ch : Char; len : Byte): String;π π Varπ temp : String;π π beginπ FillChar(temp[1],len,ch);π temp[0] := chr(len);π padstr := temp;π end; { padstr }ππ beginπ Filefound := False;π FindFirst(mask,AnyFile,dirinfo);π While DosError = 0 do beginπ Filefound := True;π start := time;π fname := FDir + dirinfo.name;π if OpenFile(fname) then beginπ Write(fname,PadStr(space,30-length(fname)),FileSize(f):7,' ');π ChangeFile(changes);π CloseFile;π if changes = 0 thenπ Writelnπ elseπ Writeln('Made ',changes,' changes in ',ElapsedTime(start):4:2,' seconds.')π endπ elseπ Writeln('Unable to process ',fname);π FindNext(dirinfo);π end; { While DosError = 0 }π if not Filefound thenπ Writeln('No Files found.');π end; { Find_and_change_all_Files }ππbegin { main }π Writeln(copyright1);π Writeln(copyright2);π ParseCommandLine;π Find_and_change_all_Files;πend.ππ 6 05-28-9313:46ALL SWAG SUPPORT TEAM FINDDATA.PAS IMPORT 11 { Following is some code I've thrown together <!>, which has to find aπsequence of 4 Characters in a large buffer - non-Text data. The bufferπis 4096 Characters, and the sequence(s) I'm searching For could beπanywhere in it, and may be found numerous times. I suspect this code isπpretty inefficient, but I can't think of anything better. (Yep, this isπto work With the ZIP directory at the end of the File...)π So, I'm looking For a better way to code this process. I know thatπPos won't work, so this brute-Force is what I came up with. Anythingπbetter? Thanks...π}πConst CFHS : String[4] = 'PK'#01#02; { CENTRAL_File_HEADER_SIGNATURE }π ECDS : String[4] = 'PK'#05#06; { end_CENTRAL_DIRECtoRY_SIGNATURE }πVar S4 : String[4];π FOUND : Boolean;π QUIT : Boolean; { "end" sentinel encountered }πbeginπ FETCH_NAME; Assign (F,F1); Reset (F,1); C := 1; HSize := 0;π FSize := FileSize(F);π I := FSize-BSize; { Compute point to start read }π Seek (F,I); BlockRead (F,BUFF,BSize,RES); { ZIP central directory }π S4[0] := #4; C := 0;π Repeatπ FOUND := False; { search For CENTRAL_File_HEADER_SIGNATURE }π Repeatπ Inc (C); Move (BUFF[C],S4[1],4); FOUND := S4 = CFHS;π QUIT := S4 = ECDS;π Until FOUND or QUIT;πend. 7 05-28-9313:46ALL SWAG SUPPORT TEAM FINDDUPL.PAS IMPORT 23 {πTRAVIS GRIGGSππ> I have one question For you in return: could you send the currentπ> source code of your Program, or could you otherwise describe whatπ> your input Text File Characterizations are (how big can the File be,π> how long can the lines be, do you scan each line, or only taglines,ππHere's the code. Don't worry about the structure of it. I know it is bad butπthis was a quick and dirty little util I wrote up that I needed. Have fun Withπit and try to speed it up. And whoever else wants to help have fun!ππI hope this compiles I took out some stuff that would display a little pictureπof a sWord and show the version and product name. I also tried DJ's idea ofπthe buffer of 65535 but it said the structure was too large. So I used 64512.π}πUses Crt;πTypeπ BBT = Array[0..64512] of Char;ππVarπ BUFF : ^BBT;π TheFile,π logFile : Text;π Looking,π TempStr : String[80];π Numoflines,π F, J, Point : LongInt;π Divi, Multi : Real;ππProcedure Stop;πbeginπ Close(TheFile);π Close(LogFile);π Halt(1);πend;ππProcedure CommandError(Err: Byte);πbeginπ TextColor(10);π Case Err Ofπ 2 : WriteLn('You must specify a File on the command line.');π 3 : WriteLn('Can''t find "', ParamStr(1),'"');π 4 : WriteLn('Too many open Files to open ', ParamStr(1));π 5 : WriteLn('Error in reading ', ParamStr(1));π end; { end total Case }π WriteLn;π Halt(1);πend; { end Procedure }ππbeginπ if Paramcount < 1 Thenπ CommandError(2);π ClrScr;π Assign(TheFile,ParamStr(1));π New(BUFF);π SetTextBuf(TheFile,BUFF^);π Assign(LogFile,'FINDDUPE.LOG');π ReWrite(LogFile);π Reset(TheFile);π Case IoResult Ofπ 2 : CommandError(3);π 4 : CommandError(4);π 3,5..162 : CommandError(5);π end;π While not EOF(TheFile) Doπ beginπ Readln(TheFile);π Inc(Numoflines);π end;π Writeln('There are ',Numoflines,' lines in this File.');π Writeln;π Writeln('Duplicate lines are being written to FINDDUPE.LOG');π Writeln;π Writeln('Press any key to stop the search For duplicate lines');π Point := 0;π Reset(TheFile);π While Point <> Numoflines Doπ beginπ GotoXY(1, 7);π if Point <> 0 Thenπ beginπ Divi := Point / Numoflines;π Multi := Divi * 100;π WriteLn(Multi : 3 : 2, '% Completed');π end;π Reset(TheFile);π if Point <> 0 Thenπ For J := 1 to Point Doπ Readln(TheFile);π Readln(TheFile,Looking);π Reset(TheFile);π Inc(Point);π For F := 1 to Numoflines Doπ beginπ if KeyPressed thenπ Stop;π Readln(TheFile, TempStr);π if (Point <> F) and (TempStr = Looking) Thenπ Writeln(LogFile,Looking);π end;π end;π GotoXY(1, 7);π Writeln('100.00% Completed');π Close(TheFile);π Close(LogFile);πend.π 8 05-28-9313:46ALL SWAG SUPPORT TEAM FINDTEXT.PAS IMPORT 11 {π> I need help on making a Search Procedure in TURBO PASCAL.π> what I want it to do is to open the contents in a Text Fileπ> search For a given String. and diplay that Record or line With thatπ> given String!!!ππHere is a Program that will search a Text File and display the linesπof Text With the search String in it.π}ππProgram Search;πTypeπ BigString = String[132];πVarπ FileName: String[14];π FileVar: Text;π LineNumber: Integer;π OneLine, Temporary, SubString: BigString;ππ{ Make all Chars in S upper case}πProcedure UpperCase(Var S: BigString);πVarπ I: Integer;πbeginπ For I := 1 to Length(S) doπ S[I] := Upcase(S[I]);πend;ππbeginπ Write('Search what Text File? ');π Readln(FileName);π Assign(FileVar, FileName);π Repeatπ Writeln;π Reset(FileVar);π Write('Search for? (Enter to quit) ');π Readln(SubString);π if Length(SubString) > 0 thenπ beginπ UpperCase(SubString);π LineNumber := 0;π While not Eof(FileVar) doπ beginπ Readln(FileVar, OneLine);π Inc(LineNumber);π Temporary := OneLine;π UpperCase(Temporary);π if Pos(SubString, Temporary) >0π Then Writeln(LineNumber:3, ': ', OneLine)π endπ endπ Until Length(SubString) = 0πend.π 9 05-28-9313:46ALL SWAG SUPPORT TEAM NEXTCHAR.PAS IMPORT 13 {πDuncan Murdochππ>varπ> TextFile: Text;π> NextChar: Char;π>...π>beginπ>...π> with TextRec(TextFile) do NextChar:= Buffer[BufPos];ππCareful! This is unreliable, because the Buffer could be empty. You shouldπcheck that there's something there, and fill it if not.ππHere's my NextChar routine. BTW, I don't like the DOS unit's declaration ofπTextRec, so I wrote my own.π}ππtypeπ IOFunc = function(var Source:text): Integer;π TTextBuf = array[0..127] of char;π PTextBuf = ^TTextBuf;π TextRec = recordπ Handle: Word;π Mode: Word;π BufSize: Word;π Private: Word;π BufPos: Word;π BufEnd: Word;π BufPtr: PTextBuf;π OpenFunc: IOFunc;π InOutFunc: IOFunc;π FlushFunc: IOFunc;π CloseFunc: IOFunc;π UserData: array[1..16] of Byte;π Name: array[0..79] of Char;π Buffer: TTextBuf;π end;ππfunction NextChar(var Source: text):char;πbeginπ NextChar := chr(0); { This is the default value in case ofπ error }π with TextRec(Source) doπ beginπ if BufPos >= BufEnd thenπ { Buffer empty; need to fill it }π InOutRes := InOutFunc(Source); { This sets the System errorπ variable InOutRes; other thanπ that, it ignores errors. }π NextChar := BufPtr^[BufPos] { A test here of whether aπ a character was availableπ would be a good idea }π end;πend;π 10 05-28-9313:46ALL SWAG SUPPORT TEAM NICECODE.PAS IMPORT 113 (*π>Does anyone know of a utility Program that will apply some sort ofπ>reasonable structuring to a pascal source File?ππI'm not sure if it's what you want, but the source For a PascalπreFormatter, etc, was entered in the Fidonet PASCAL ProgrammingπCompetition, and came third (I came second!!).ππAs you can see by the File dates, this is a very recent thing andπsince it is Nearly too late I toyed With the idea of just keeping itπto myself. It certainly is not an example of inspired Programming.πBut then, I thought, if everyone felt that way you'd have nothing toπchose from and even if this is not a prize winner, mayby someoneπelse will find it useful.ππSo here it is... not extensively tested, but I couldn't find anyπbugs. Used Pretty to reFormat itself and it still Compiled andπworked. Anyway, the only possible use is to another Turbo PascalπProgrammer who shouldn't have any difficult modifying to suitπhimself. They'd probably do that anyway since the output representsπmy own peculiar notion as to what a readable Format should be.ππ'Pretty Printers' date back to the earliest Computer days andπVariations existed For just about any language. However, I've beenπunable to find a current one For Turbo Pascal.ππHere's what this one does:ππPretty With no parameters generates a syntax message.ππInput is scanned line-by-line, Word-by-Word and Byte-by-Byte. Anyπidentifiers recognized as part of TP's language are replaced byπmixed Case (in a style which _I_ like). Someone else can editπConstants Borland1 through Borland5 and TP3. (Why TP3 later.) Theπfirst one on a line is capitalized anyway.ππA fallout of this is to use selected ones to determine indentationπin increments of 'IndentSpcs' which I arbitrarily set to 3. Changeπif you like. Indentation is incremented whenever one of theπ'IndentIDs' appears and decremented With 'UnindentIDs' (surprise!).ππSingle indents are also provided For 'SectionIDs' (Const, Type,πUses, Var) and For 'NestIDs' (Procedure Function) to make these moreπvisible. White space is what does it, right?ππOn the other hand, no attempt is made to affect white space in theπvertical direction. Since that generally stays the way youπoriginate it.ππAny '{', '(' or '''' (Single quote) detected during the line scanπtrigger a 'skipit' mode which moves the enclosed stuff directly toπoutput, unmodified. With one exception. {Comments} which begin aπline are aligned to the left margin (where I like to see Compilerπdirectives and one line Procedure/Function explanations). Otherπ{Comments} which begin/end on the same line are shifted so the '}'πaligns at the (80th column) right margin. I think this makes themπmore visible than when snuggled up to a semi-colon and getting themπaway from the code makes it more legible, too.ππand it did look better originally when it used some of my personalπUnits. Hastily modified to stand alone. There are, no doubt, someπobvious ways the Programming can be improved (you would probablyπhave used some nice hash tables to look up key Words) but, as I say,πI thought I would be the only one using this and speed in this Caseπis not all that important.ππWith one exception. Something I worked up For an earlierπapplication and may be worth looking at -- 'LowCase'.ππIt will Compile With TP4-TP5.5 and probably TP6 (if it stillπsupports Inline). I included TP3 stuff because some of the oldπsoftware I was looking at was written in it. and it recognizesπUnits in a clumsy sort of way.ππSwitching to chat mode here. if you're Really busy, you can skip theπfollowing.ππThis thing actually began as a 'Case-converter'. I was trying toπavoid re-inventing some wheels by re-working some old Pascal sourceπdating back to the late 70's and 80's. Upper Case Programs became aπ'standard' back in the days when you talked to main frames through aπteleType machine, which has no lower Case. Sadly, this persistedπlong after it was no longer necessary and I find thoseπall-upper-Case Programs almost unreadable. That is I can't findπwhat I'm looking For. They were making me crazy. (BTW I suspectπsome of this has to do With why Pascal has UpCase but no LoCase.)ππI stole the orginal LowCase included here from someone who had doneπthe intuitive thing -- first test For 'A', then For 'Z'. Changingπto an initial test For 'Z' does two things. A whopping 164 of theπ255 possible Characters can be eliminated With just one test and,πsince ordinary Text consists of mostly lower Case, these will beπpassed over rapidly.ππWhen you received this you thought, "Who the heck is Art Weller? Iπdon't remember him on the Pascal Echo." Right. I'm a 'lurker'!πBeen reading the echo since beFore it had a moderator. (Now we haveπan excellent one. Thank you.) I have a machine on a timer whichπcalls the BBS each morning to read and store several echos which Iπread later. Rarely get inspired enough to call back and enter aπdiscussion. Things usually get resolved nicely without me. Iπespecially don't want to get involved in such as the 'Goto' wars.πBut I monitor the better discussions to enhance my TP skills.ππI'm not Really a Programmer (no Formal training, that is --πComputers hadn't been invented when I was in school!), but anπengineer. I'm retired from White Sands Missile Range where I wasπChief of Plans and Programs For (mumble, mumble) years. Iπself-taught myself Computers when folks from our Analysis andπComputation Directorate started using jargon on me. I did that wellπenough to later help Write a book For people who wanted to convertπfrom BASIC to Pascal then after "retiring" was an editor For a smallπComputer magazine (68 Micro-Journal).ππIn summary, if you think this worth sharing With others I'll beπpleased enough even without a prize. not even sure it will getπthere in time. Snail-Mail, you know.π*)ππProgram Pretty;π{A 'Pretty Printer' For Turbo Pascal Programs}π{ This Program converts Turbo Pascal identifiers in a source code File toπ mixed Case and indents the code.π Released into Public Domain June, 1992 on an 'AS IS' basis. Enjoy at yourπ own risk.π Art Wellerπ 3217 Pagosa Courtπ El Paso, Texas 79904π U. S. A.π Ph. (915) 755-2516}ππ{Usesπ Strings;}ππConstπ IndentSpcs = 3;ππ Borland1 =π ' Absolute Addr and ArcTan Array Assign AuxInptr AuxOutptr BDos begin Bios '+π ' BlockRead BlockWrite Boolean Buflen Byte Case Chain Char Chr Close ClrEol '+π ' ClrScr Color Concat Const Copy Cos Delay Delete DelLine Dispose div do ';π Borland2 =π ' Downto Draw else end Eof Eoln Erase Execute Exp External False File '+π ' FilePos FileSize FillChar Flush For Forward Frac Freemem Function Getmem '+π ' Goto GotoXY Halt HeapPtr Hi HighVideo HiRes if Implementation in Inline ';π Borland3 =π ' Input Insert InsLine Int Integer Interface Intr IOResult KeyPressed '+π ' Label Length Ln Lo LowVideo Lst Mark MaxAvail Maxint Mem MemAvail Memw Mod '+π ' Move New Nil NormVideo not Odd of Ofs or Ord Output Overlay Packed ';π Borland4 =π ' Pallette Pi Plot Port Pos Pred Procedure Program Ptr Random Randomize Read '+π ' ReadLn Real Record Release Rename Repeat Reset ReWrite Round Seek Seg Set '+π ' Shl Shr Sin SizeOf Sound Sqr Sqrt Str String Succ Swap Text then to ';π Borland5 =π ' True Trunc Type Unit Until UpCase Uses UsrOutPtr Val Var While Window With '+π ' Write WriteLn xor ';π TP3 =π ' AUX CONinPTR CON CONOUTPTR ConstPTR CrtEXIT CrtinIT ERRorPTR Kbd '+π ' LStoUTPTR TRM USR USRinPTR ';ππ IndentIDs = ' begin Case Const Record Repeat Type Uses Var ';π UnIndentIDs = ' end Until ';π SectionIDs = ' Const Type Uses Var ';π endSection = ' begin Const Uses Var Function Implementation Interface '+π ' Procedure Type Unit ';π NestIDs = ' Function Procedure Unit ';ππ IDAlphas = ['a'..'z', '1'..'0', '_'];ππVarπ Indent,π endPend,π Pending,π UnitFlag : Boolean;π NestLevel,π NestIndent,π IndentNext,π IndentNow,π Pntr, LineNum : Integer;π IDs,π InFile,π OutFile,π ProgWrd,π ProgLine : String;π Idents,π OutID : Array [1..5] of String;π f1, f2 : Text;ππFunction LowCase(Ch: Char): Char;πbeginπ Inline(π $8A/$86/>Ch/ { mov al,>Ch[bp] ;Char to check}π $3C/$5A/ { cmp al,'Z' }π $7F/$06/ { jg Done }π $3C/$41/ { cmp al,'A' }π $7C/$02/ { jl Done }π $0C/$20/ { or al,$20 }π $88/$86/>LowCase); {Done :mov >LowCase[bp],al }πend;ππFunction LowCaseStr(InStr : String): String;πVarπ i : Integer;π len: Byte Absolute InStr;πbeginπ LowCaseStr[0] := Chr(len);π For i := 1 to len doπ LowCaseStr[i] := LowCase(InStr[i]);πend;ππFunction Blanks(Count: Byte): String; {return String of 'Count' spaces}πVarπ Result: String;πbeginπ FillChar(Result[1], Count+1, ' ');π Result[0] := Chr(Count);π Blanks := Result;πend;ππProcedure StripLeading(Var Str: String); {remove all leading spaces}πbeginπ While (Str[1] = #32) and (length(Str) > 0) doπ Delete(Str,1,1);πend;ππProcedure Initialize;πbeginπ IDs := IndentIDs + UnIndentIDs + endSection;π OutID[1] := Borland1;π Idents[1] := LowCaseStr(OutID[1]);π OutID[2] := Borland2;π Idents[2] := LowCaseStr(OutID[2]);π OutID[3] := Borland3;π Idents[3] := LowCaseStr(OutID[3]);π OutID[4] := Borland4;π Idents[4] := LowCaseStr(OutID[4]);π OutID[5] := Borland5 + TP3;π Idents[5] := LowCaseStr(OutID[5]);π Pending := False;π UnitFlag := False;π IndentNext := 0;π IndentNow := 0;π LineNum := 0;π NestIndent := 0;π NestLevel := 0;πend;ππProcedure Greeting;πbeginπ Writeln;π Writeln('Pascal Program Indenter');π Writeln; Writeln;π Writeln('SYNTAX: INDENT InputFile OutPutFile');π Writeln(' INDENT InputFile > OutPut');π Writeln; Writeln;π Halt(0);πend;ππProcedure OpenFiles;πbeginπ if paramcount <> 0 thenπ beginπ InFile := ParamStr(1);π if (pos('.', InFile) = 0) thenπ InFile := InFile + '.pas';π OutFile := Paramstr(2);π endπ elseπ Greeting;π Assign(f1, InFile);π Reset(f1);π Assign(f2, OutFile);π ReWrite(f2);πend;ππProcedure GetWord;πVarπ i,π index,π TmpPtr,π WrdPos : Integer;ππ Procedure DecIndent;π beginπ if (IndentNext > IndentNow) then {begin/end on same line}π Dec(IndentNext)π elseπ if IndentNow > 0 thenπ dec(IndentNow);π IndentNext := IndentNow; {next line, too}π end;ππbeginπ ProgWrd := ' ';π TmpPtr := Pntr;ππ While (LowCase(ProgLine[Pntr]) in IDAlphas) {Convert checked For LCase alpha}π and (Pntr <= length(ProgLine)) doπ beginπ ProgWrd := ProgWrd + LowCase(ProgLine[Pntr]);π Inc(Pntr);π end;ππ ProgWrd := ProgWrd+' '; {surrounded With blanks to make it unique!}π index := 0;ππ Repeat; {is it a Turbo Pascal Word?}π inc(index);π WrdPos := Pos(ProgWrd, Idents[index]);π Until (WrdPos <> 0) or (index = 5);ππ if WrdPos <> 0 then {found a Pascal Word}π beginπ Move(OutID[index][WrdPos+1], ProgLine[TmpPtr], Length(ProgWrd)-2);π if TmpPtr = 1 thenπ ProgLine[1] := UpCase(ProgLine[1]);ππ if Pos(ProgWrd, IDs) <> 0 then {only checked if a Pascal Word ^}π beginπ if Pos(ProgWrd, endSection) <> 0 then {this includes "SectionIDs"}π begin {and "NestIDs"}π if (pos(ProgWrd, NestIDs) <> 0) thenπ beginπ if ProgWrd = ' Unit ' thenπ UnitFlag := True;π if not UnitFlag thenπ inc(NestLevel);π end;π if Pending thenπ DecIndent;π Pending := Pos(ProgWrd, SectionIDs) <> 0;π if ProgWrd = ' Implementation ' thenπ UnitFlag := False;π end;π if Pos(ProgWrd, IndentIDs) <> 0 thenπ inc(IndentNext); {Indent 1 level}π if Pos(ProgWrd, UnIndentIDs) <> 0 thenπ beginπ DecIndent; {Unindent 1 level}π if (IndentNow = 0) and (NestLevel > 0) thenπ dec(NestLevel);π end;π if NestLevel > 1 thenπ NestIndent := 1;π end;π end;πend;ππProcedure Convert;ππ Procedure OutLine;π Varπ Tabs : String[40];π beginπ Tabs := Blanks((IndentNow+NestIndent) * IndentSpcs);π if ProgLine[1] = '{' thenπ Writeln(f2, ProgLine)π elseπ Writeln(f2, Tabs, ProgLine);π IndentNow := IndentNext; { get ready For next line }π if NestLevel < 2 thenπ NestIndent := 0;π end;ππ Procedure Skipto(SearchChar: Char);π beginπ Repeatπ if pntr > Length(ProgLine) thenπ beginπ OutLine;π Readln(f1, ProgLine); {get another line}π Pntr := 0;π end;π Inc(pntr);π Until (ProgLine[pntr] = SearchChar) or Eof(f1);π end;ππ Procedure MoveComments;π Varπ TmpIndent : Integer;π beginπ if (ProgLine[1] = '{') or (ProgLine[Pntr+1] = '$') thenπ beginπ Skipto('}');π Exit;π end;π TmpIndent := (IndentNow+NestIndent) * IndentSpcs;π While Length(ProgLine) < 80-TmpIndent doπ Insert(' ', ProgLine, Pntr);π While (pos('}', ProgLine) > 80-TmpIndent) and (pos(' {', ProgLine) > 1) doπ beginπ Delete(ProgLine, Pos(' {', ProgLine), 1);π Dec(Pntr);π end;π Skipto('}');π end;ππbeginπ While not Eof(f1) doπ beginπ Readln(f1, ProgLine);π StripLeading(ProgLine);π if Length(ProgLine) = 0 thenπ Writeln(f2)π elseπ beginπ Pntr := 1;π Repeatπ Case LowCase(ProgLine[pntr]) ofπ 'a'..'z','_' : GetWord;π '{' : MoveComments;π '(' : Skipto(')');π #39 : Skipto(#39) {Single quote}π end;π Inc(pntr)π Until (pntr >= length(ProgLine));π OutLine;π end;π end; { While }π Close(f1); Close(f2);πend;ππbeginπ Initialize;π OpenFiles;π Convert;πend.π 11 05-28-9313:46ALL SWAG SUPPORT TEAM SEARCH.PAS IMPORT 101 Program search; π{$A+,B-,D-,E+,F-,I+,L-,N-,O-,R-,S-,V-}π{$M 16384,0,655360}πππ { Copyright 1990 Trevor J Carlsen Version 1.05 24-07-90 }π { This Program may be used and distributed as if it was in the Public Domain}π { With the following exceptions: }π { 1. if you alter it in any way, the copyright notice must not be }π { changed. }π { 2. if you use code excerpts in your own Programs, due credit must be }π { given, along With a copyright notice - }π { "Parts Copyright 1990 Trevor J Carlsen" }π { 3. No Charge may be made For any Program using code from this Program.} ππ { SEARCH will scan a File or group of Files and report on all occurrences }π { of a particular String or group of Characters. if found the search String }π { will be displayed along With the 79 Characters preceding it and the 79 }π { Characters following the line it is in. Wild cards may be used in the }π { Filenames to be searched. }π π { if you find this Program useful here is the author's contact address - } π π { Trevor J Carlsen } π { PO Box 568 } π { Port Hedland Western Australia 6721 } π { Voice 61 [0]91 72 2026 } π { Data 61 [0]91 72 2569 } πππ πUsesπ Dos,π tpString, { Turbo Power's String handling library. Procedures and }π { Functions used from this Unit are - }π { BMSearch THESE ARE in THE SOURCE\MISC DIRECtoRY }π { BMSearchUC }π { BMMakeTable }π { StUpCase }π tctimer; { A little timing routine - not needed if lines (**) removed. }π πConstπ bufflen = 65000; { Do not increase this buffer size . Ok to decrease. }π searchlen = bufflen;π copyright1 = 'SEARCH - version 1.05 Copyright 1990 Trevor Carlsen';π copyright2 = 'All rights reserved.';ππTypeπ str79 = String[79];π bufferType = Array[0..bufflen] of Byte;π buffptr = ^bufferType;ππConstπ space = #32;π quote = #34;π comma = #44;π CaseSensitive : Boolean = True; { default is a Case sensitive search }πVarπ table : BTable; { Boyer-Moore search table }π buffer : buffptr; { Pointer to new buffer }π f : File;π DisplayStr : Array[0..3] of str79;π Filename,π SrchStr : String;π Slen : Byte Absolute SrchStr;π πProcedure Asc2Str(Var s, ns; max: Byte);ππ { Converts an Array of asciiz Characters to a turbo String }π { For speed the Variable st is effectively global and it is thereFore }π { vitally important that max is no larger than the ns unTyped parameter }π { Failure to ensure this can result in unpredictable Program behaviour }π π Var stArray : Array[0..255] of Byte Absolute s;π st : String Absolute ns;π len : Byte Absolute st;π π beginπ move(stArray[0],st[1],max);π len := max;π end; { Asc2Str }ππProcedure ReportError(e : Byte);π { Displays a simple instruction screen in the event of insufficient }π { parameters or certain other errors }π beginπ Writeln('SYNTAX:');π Writeln('SEARCH [-c] [path]Filename searchstr');π Writeln(' eg: SEARCH c:\comm\telix\salt.doc "color"');π Writeln(' or');π Writeln(' SEARCH c:\comm\telix\salt.doc 13,10,13,10,13,10,13,10');π Writeln(' or');π Writeln(' SEARCH -c c:\*.* "MicroSoft"');π Writeln;π Writeln('if the -c option is used then a Case insensitive search is used.');π Writeln('When used the -c option must be the first parameter.');π halt(e);π end; { ReportError }ππProcedure ParseCommandLine;π { This Procedure is Really the key to everything as it parses the command }π { line to determine what the String being searched For is. Because the }π { wanted String can be entered in literal Form or in ascii codes this will }π { disect and determine the method used. }π π Varπ parstr : String; { contains the command line }π len : Byte Absolute parstr;{ will contain the length of cmd line }π cpos, qpos,π spos, chval : Byte;π error : Integer;π π begin { ParseCommandLine}π parstr := String(ptr(PrefixSeg,$80)^); { Get the command line }π if parstr[1] = space thenπ delete(parstr,1,1); { if the first Character is a space get rid of it }π spos := pos(space,parstr); { find the first space }π if spos = 0 then { No spaces which must be an error }π ReportError(1); π π Filename := StUpCase(copy(parstr,1,spos-1)); { Filename used as a temp }π if pos('-C',Filename) = 1 then begin { Case insensitive search required }π CaseSensitive := False;π delete(parstr,1,spos); { Get rid of the used portion }π end; { if pos('-C' }π spos := pos(space,parstr); { find next space }π if spos = 0 then { No spaces which must be an error }π ReportError(1); π Filename := StUpCase(copy(parstr,1,spos-1)); { Get the File mask }π delete(parstr,1,spos); { Get rid of the used portion }π π qpos := pos(quote,parstr); { look For the first quote Char }π if qpos <> 0 then begin { quote Char found - so must be quoted Text }π if parstr[1] <> quote then ReportError(2); { first Char must be quote }π delete(parstr,1,1); { get rid of the first quote }π qpos := pos(quote,parstr); { and find the next quote }π if qpos = 0 then ReportError(3); { no more quotes - so it is an error }π SrchStr := copy(parstr,1,qpos-1); { search String now defined }π end { if qpos <> 0 }π π else begin { must be using ascii codes }π Slen := 0; π cpos := pos(comma,parstr); { find first comma }π if cpos = 0 then cpos := succ(len);{ No comma - so only one ascii code }π Repeat { create the search String }π val(copy(parstr,1,pred(cpos)),chval,error);π if error <> 0 then ReportError(7); { there is an error so bomb out }π inc(Slen);π SrchStr[Slen] := Char(chval); { add Char to the search String }π delete(parstr,1,cpos); { get rid of used portion of parstr }π cpos := pos(comma,parstr); { find the next comma }π if cpos = 0 then cpos := succ(len); { no more commas so last Char }π Until len = 0; { Until whole of command line is processed }π end; { else}π π if not CaseSensitive then { change the Search String to upper Case }π SrchStr := StUpCase(SrchStr);π end; { ParseCommandLine }ππFunction OpenFile(ofn : String): Boolean; { open a File For BlockRead/Write }π Varπ error : Word;π begin { OpenFile}π assign(f,ofn);π {$I-} reset(f,1); {$I+}π error := Ioresult;π if error <> 0 thenπ Writeln('Cannot open ',ofn);π OpenFile := error = 0;π end; { OpenFile }ππProcedure CloseFile;π beginπ {$I-}π Close(f);π if Ioresult <> 0 then; { don't worry too much if an error occurs here }π {$I+}π end; { CloseFile }ππProcedure SearchFile(Var Filename: String);π { Reads a File into the buffer and then searches that buffer For the wanted}π { String or Characters. }π Varπ x,y,π count,π result,π bufferpos : Word;π abspos : LongInt;π finished : Boolean;π π begin { SearchFile}π BMMakeTable(SrchStr,table); { Create a Boyer-Moore search table }π new(buffer); { make room on the heap For the buffers }π {$I-} BlockRead(f,buffer^,searchlen,result); {$I+} { Fill buffer buffer }π if Ioresult <> 0 then begin { error occurred While reading the File }π CloseFile;π ReportError(11);π end; { if Ioresult }π abspos := 0; { Initialise the Absolute File position marker }π Repeatπ bufferpos := 0; { position marker in current buffer }π count := 0; { offset from search starting point }π finished := (result < searchlen); { if buffer <> full no more reads }π π Repeat { Do a BM search For search String }π if CaseSensitive then { do a Case sensitive search }π count:=BMSearch(buffer^[bufferpos],result-bufferpos,table,SrchStr)π else { do a Case insensitive search }π count:=BMSearchUC(buffer^[bufferpos],result-bufferpos,table,SrchStr);π π if count <> $FFFF then begin { search String found }π inc(bufferpos,count); { starting point of SrchStr in buffer }π DisplayStr[0] := HexL(abspos+bufferpos) + { hex and decimal pos }π Form(' @######',(abspos+bufferpos) * 1.0);π if bufferpos > 79 then { there is a line available beFore }π Asc2Str(buffer^[bufferpos - 79],DisplayStr[1],79)π else { no line available beFore the found }π DisplayStr[1] := ''; { position so null the String }π if (bufferpos + 79) < result then { at least 79 Chars can be }π Asc2Str(buffer^[bufferpos],DisplayStr[2],79) { displayed }π else { only display what is left in buffer }π Asc2Str(buffer^[bufferpos],DisplayStr[2],result - bufferpos);π if (bufferpos + 158) < result then { display the line following }π Asc2Str(buffer^[bufferpos + 79],DisplayStr[3],79)π else { no line following the found String }π DisplayStr[3] := ''; { so null the display String }π Writeln;π Writeln(DisplayStr[0],' ',Filename);{ display the File locations }π π For x := 1 to 3 do beginπ For y := 1 to length(DisplayStr[x]) do{ filter out non-printables}π if ord(DisplayStr[x][y]) < 32 then DisplayStr[x][y] := '.';π if length(DisplayStr[x]) <> 0 then { only display Strings With }π Writeln(DisplayStr[x]); { valid content }π end; { For x }π π inc(bufferpos,Slen); { no need to check buffer in found st }π end; { if count <> $ffff }π π Until (bufferpos >= (result-length(SrchStr))) or (count = $ffff);π π if not finished then begin { Fill 'er up again For another round }π inc(abspos,result - Slen); { create overlap so no String missed }π {$I-} seek(f,abspos);π BlockRead(f,buffer^,searchlen,result); {$I+}π if Ioresult <> 0 then beginπ CloseFile;π ReportError(13);π end;π end; { if not finished}π Until finished;π dispose(buffer);π end; { SearchFile }ππProcedure SearchForFiles;π Varπ dirinfo : SearchRec;π FullName: PathStr;π DirName : DirStr;π FName : NameStr;π ExtName : ExtStr;π found : Boolean;π beginπ FindFirst(Filename,AnyFile,dirinfo);π found := DosError = 0;π if not found then beginπ Writeln('Cannot find ',Filename);π ReportError(255);π end;π FSplit(Filename,DirName,FName,ExtName);π While found do beginπ if (dirinfo.Attr and 24) = 0 then beginπ FullName := DirName + dirinfo.name;π if OpenFile(FullName) then beginπ SearchFile(FullName);π CloseFile;π end;π end;π FindNext(dirinfo);π found := DosError = 0;π end;π end; { SearchForFiles }ππbegin { main}π (**) StartTimer;π Writeln(copyright1);π Writeln(copyright2);π ParseCommandLine;π SearchForFiles;π (**) WriteElapsedTime;πend.ππ 12 05-28-9313:46ALL SWAG SUPPORT TEAM STRPOS.PAS IMPORT 23 π Hi, Andy:ππ ...Just for fun I also threw together a "PosSearch" routineπ that uses the built-in TP "POS" function. It actually performsπ better than I thought it would, as it takes a string longer thanπ 15 characters before it starts to become slower than the Boyer-π Moore function I just posted. (ie: PosSearch is faster than theπ Boyer-Moore routine for strings that are smaller than 16 chars)π Here's a demo program of the "PosSearch" search routine I putπ together. *Remember* to turn-off "range-checking" {$R-} in yourπ finished program, otherwise the PosSearch will take longer thanπ it should to execute.ππ (* Public-domain Search routine, using the standard TP *)π (* POS function. Guy McLoughlin - May 1, 1993. *)πprogram DemoPosSearch;πππ (***** PosSearch function. Returns 0 if string is not found. *)π (* Returns 65,535 if BufferSize is too large. *)π (* ie: Greater than 65,520 bytes. *)π (* *)π function PosSearch({input } var Buffer;π BuffSize : word;π Pattern : string) : {output} word;π typeπ arwo_2 = array[1..2] of word;π arch_255 = array[1..255] of char;π varπ po_Buffer : ^arch_255;π by_Temp,π by_IncSize : byte;π wo_Index : word;π beginπ if (BuffSize > 65520) thenπ beginπ PosSearch := $FFFF;π exitπ end;π wo_Index := 0;π by_IncSize := (255 - pred(length(Pattern)));π po_Buffer := addr(Buffer);π repeatπ by_Temp := pos(Pattern, po_Buffer^);π if (by_Temp = 0) thenπ beginπ inc(wo_Index, by_IncSize);π inc(arwo_2(po_Buffer)[1], by_IncSize)π endπ elseπ inc(wo_Index, by_Temp)π until (by_Temp <> 0) or (wo_Index > BuffSize);π if (by_Temp = 0) thenπ PosSearch := 0π elseπ PosSearch := wo_Indexπ end; (* PosSearch. *)πππtypeπ arby_64K = array[1..65520] of byte;ππvarπ Index : word;π st_Temp : string[20];π Buffer : ^arby_64K;ππBEGINπ new(Buffer);π fillchar(Buffer^, sizeof(Buffer^), 0);π st_Temp := '12345678901234567890';π move(st_Temp[1], Buffer^[65501], length(st_Temp));π Index := PosSearch(Buffer^, sizeof(Buffer^), st_Temp);π writeln(st_Temp, ' found at offset ', Index)πEND.ππ - Guyπ---π ■ DeLuxe²/386 1.25 #5060 ■π * Rose Media, Toronto, Canada : 416-733-2285π * PostLink(tm) v1.04 ROSE (#1047) : RelayNet(tm)ππ 13 05-28-9313:46ALL SWAG SUPPORT TEAM STSEARCH.PAS IMPORT 16 ┌─┬─────────────── Andy Stewart ───────────────┬─╖π│o│ Can someone tell/show me how to write a procedure that │o║π│o│ will take a string input and search for it in a textfile │o║π╘═╧══════════════════════════════════════════════════════════╧═╝π{ Simple example for a straight forward search routine }πvarπ f : text;π buf : array[0..maxint] of char;π line : word;π pattern,s,t : string;ππ{ Corrected version of routine from turbo techniques }πfunction uppercase (strg:string):string; assembler;πASMπ push dsπ lds si,strgπ les di,@resultπ cldπ lodsbπ stosbπ xor ch,chπ mov cl,alπ jcxz @doneπ @more:π lodsbπ cmp al,'a'π jb @noπ cmp al,'z'π ja @noπ sub al,20hπ @no:π stosbπ loop @moreπ @done:π pop dsπEND;ππ{ If you want the above routine in pascalπfunction uppercase (strg : string) : string;π var i : integer;π beginπ for i := 1 to length(strg) do strg[i] := upcase(strg[i]);π uppercase := strg;π end;π}ππprocedure search4pattern;π beginπ readln(f,s);π inc(line);π t := uppercase(s);π if pos(pattern,t) > 0π then writeln(line:5,' ',s);π end;ππbeginπ Line := 0;π if paramcount < 2 then exit;π pattern := paramstr(2);π pattern := uppercase(pattern);π assign(f,paramstr(1));π settextbuf(f,buf);π {$I-} reset(f); {$I+}π if ioresult = 0π then beginπ while not eof(f) do search4pattern;π close(f);π endπ else writeln('File not found');πend.π---π ■ Tags τ Us ■ Abandon the search for truth: settle on a good fantasy.π * Suburban Software - Home of King of the Board(tm) - 708-636-6694π * PostLink(tm) v1.05 SUBSOFT (#715) : RelayNet(tm) Hubππ 14 05-28-9313:46ALL SWAG SUPPORT TEAM SYMTAB1.PAS IMPORT 69 SYMBOL TABLEππ All Compilers and interpreters must maintain a data structureπ called the SYMBOL TABLE. This is where all the inFormation aboutπ the Programs symbols are kept. Maintaining a well-organizedπ symbol table is a skill all Compiler Writers must master.ππ As a Compiler parses a source Program, it relies on the symbolπ table to provide inFormation about each identifier (such asπ Variables and Constants) - it must be able to access and updateπ inFormation about each identifier and do so quickly - otherwiseπ the process is slowed or produces incorrect results.ππ No matter what inFormation is kept, or how the table is organizedπ certain operations are fundamental to a symbol tables operation.ππ You ENTER inFormation about about an identifier into the table byπ *creating* and entry.ππ You SEARCH the table to look up an identifier's entry and makeπ available the inFormation in that entry.ππ You UPDATE the entry to modify stored inFormation.ππ There can be only one entry per identifier in the symbol table,π so you must first search the table beFore making a new entry.ππ TABLE ORGANIZATIONππ There are many different ways to handle symbol tables: Arrays,π linked lists, hash tables...but since the most common operationsπ perFormed on a symbol table are searching it For existing entriesπ it makes perfect sense to implement it as a BINARY TREE.ππ Each NODE in the TREE contains and entry, and points to two otherπ nodes. The *values* of the nodes on the subtree to the left areπ always LESS than the parent node, While the subtree to the rightπ is always MORE than the parent. This makes searching sortedπ binary trees very efficient.ππ Inserting new nodes is as easy as searching the tree: if theπ value you want to insert is LESS than the current node, searchπ the node to the left. If it is MORE, search the tree to the right.π Keep doing this recursively Until an empty node is found, thenπ insert the value into that node.ππ NITTY-GRITTYππ Now that we've covered some background on the table, here's aπ recap on the symbol table Type defs. For those that missed themπ in the first message, or didn't save them:ππTypeπ sptr = ^String; { useful For minimum-size allocation }ππ DEFN_KEY = (UNDEFINED,π Const_DEFN, Type_DEFN, Var_DEFN, FIELD_DEFN,π VALPARM_DEFN, VarPARM_DEFN,π PROG_DEFN, PROC_DEFN, FUNC_DEFNπ );ππ ROUTINE_KEY = (rkDECLARED, rkForWARD,π rkREAD, rkREADLN, rkWrite, rkWriteLN,π rkABS, rkARCTAN, rkCHR, rkCOS, rkEOF, rkEOLN,π rkEXP, rkLN, rkODD, rkORD, rkPRED, rkROUND,π rkSIN, rkSQR, rkSQRT, rkSUCC, rkTRUNCπ );ππ RTN_BLOCK = Record {info about routine declarations}π key :ROUTINE_KEY;π parm_count,π total_parm_size,π total_local_size :Word;π parms, locals,π local_symtab :SYMTAB_PTR; {symbol tables of routine}π code_segment :sptr; {interpreter}π end;ππ DTA_BLOCK = Recordπ offset :Word;π Record_idp :SYMTAB_PTR;π end;ππ INFO_REC = Recordπ Case Byte ofπ 0:(Constant :VALUE); { literal value }π 1:(routine :RTN_BLOCK); { identifier is routine }π 2:(data :DTA_BLOCK); { identifier is data }π end;ππ DEFN_REC = Recordπ key :DEFN_KEY; { what is identifier? }π info :INFO_REC; { stuff about identifier }π end;ππ SYMTAB_PTR = ^SYMTAB_NODE;π SYMTAB_NODE = Record {actual tree node}π left, right :SYMTAB_PTR; {Pointers to left and right subtrees}π next :SYMTAB_PTR; {For chaining a node}π name :sptr; {identifier name String}π level, {nesting level}π co_index :Integer; {code Label index}π defn :DEFN_REC; {definition info}π end; { Record }ππ EXCERCISE #1ππ Implement a symbol table SEARCH routine, and a symbol table ENTERπ routine. Both routines must accept a Pointer to the root of theπ tree, and the name of the identifier you are working With, andπ must return a Pointer to the node that was found in the searchπ routine, or enters in the enter routine. If no node was found, orπ entered, the routines must return NIL.ππ The resulting symbol table should be a sorted tree.ππππ│ Implement a symbol table SEARCH routine, and a symbol table ENTERπ│ routine. Both routines must accept a Pointer to the root of theπ│ tree, and the name of the identifier you are working with, andπ│ must return a Pointer to the node that was found in the searchπ│ routine, or enters in the enter routine. If no node was found, orπ│ entered, the routines must return NIL.π│ The resulting symbol table should be a sorted tree.ππππFunction Enter(root: SymTab_Ptr; PidStr: spstr): SymTab_Ptr;π{ - inserts a new indetifier String PidStr in the symol table. }π{ - nil is returned if duplicate identifier is found. }πVarπ Ptemp: SymTab_Ptr;πbeginπ if (root <> nil) then { not a terminal node }π if (PidStr = root^.name) thenπ beginπ Enter := nil;π Exitπ endπ else { recursive insertion calls to either left or right sub-tree }π if (PidStr > root^.name) thenπ Enter(root^.right, PidStr)π elseπ Enter(root^.left, PidStr)π else { a terminal node }π beginπ new(Ptemp); { create a new tree leaf node }π Ptemp^.name := PidStr;π Ptemp^.left := nil;π Ptemp^.right := nilπ endπend; { Enter }πππFunction Search(root: SymTab_Ptr; PidStr: spstr): SymTab_Ptr;π{ - search For a certain identifier String PidStr in the symbol table. }π{ - returns nil if search faild. }πbeginπ While (root <> nil) and (PidStr <> root^.name) doπ if (PidStr > root^.name) then { search the right sub-tree }π root := root^.rightπ elseπ if (PidStr < root^.name) thenπ root := root^.left; { search the left sub-tree }π Search := root { return the node }πend;ππ{===========================================================================}ππComment:π What made you choose BINARY trees over AVL trees? With binary trees,π the structure may become degenerate (unbalanced) and, the routines forπ searching and insertion becomes inefficient.ππ>Comment:π> What made you choose BINARY trees over AVL trees? With binary trees,π> the structure may become degenerate (unbalanced) and, the routines forπ> searching and insertion becomes inefficient.ππ Glad you could join us!ππ I chose a binary tree because it's simple and easy to Write, alsoπ a degenerate tree isn't much of a concern, simply because it'sπ intended to hold only identifiers and Constants, not everyπ statement. :)ππ As long as it sorts the data as it inserts, it will work. Thisπ isn't, after all, a graduate "course". The intention is to teachπ people how compilers work and show interested parties how toπ understand and Write their own, if they're interested. This isπ YOUR compiler you're writing, if you want to implement an AVLπ tree, go ahead!ππ>Function Search(root: SymTab_Ptr; PidStr: spstr): SymTab_Ptr;ππ This works. It's efficient and does the job.ππ>Function Enter(root: SymTab_Ptr; PidStr: spstr): SymTab_Ptr;ππ> else { recursive insertion calls to either left or right sub-tree }π> if (PidStr > root^.name) thenπ> Enter(root^.right, PidStr)π> elseπ> Enter(root^.left, PidStr)ππ Note: recursive calls shouldn't be necessary in this Function.π You can search the table the same way you did With Search, andπ you don't run the risk of running out of stack space. Procedureπ calls can also be exensive, slowing down the Program too muchπ especially if a lot of symbols are searched.ππ> else { a terminal node }π> beginπ> new(Ptemp); { create a new tree leaf node }π> Ptemp^.name := PidStr;π> Ptemp^.left := nil;π> Ptemp^.right := nilπ> endπ>end; { Enter }ππ Please note that there is a lot of data that will be have toπ added to this section over time, as an identifier could beπ ANYTHING from a ConstANT to a Program identifier.ππ That isn't too important right now, as we're just getting startedπ on the symbol table but suggest you add the following lines, forπ use later:ππ Ptemp^.info := NIL;π Ptemp^.defn.key := UNDEFINED;π Ptemp^.level := 0; {recursion level}π Ptemp^.Label_index := 0; {Label # to be used in code output}π 15 05-28-9313:46ALL SWAG SUPPORT TEAM SYMTAB2.PAS IMPORT 27 LARRY HADLEYππ Errata: include an "info" Pointer field in the SYMTAB_NODEπ structure in the previous post.ππ USING THE SYMBOL TABLE - A CROSS REFERENCERππ A cross-reference is a listing of a Programs identifiers inπ alphabetical order:ππPage 1 hello.pas April 08 1993 19:03π 1 0: Program hello (output);π 2 0: Var i:Integer;π 3 0: beginπ 4 0: For i := 1 to 10 doπ 5 0: beginπ 6 0: WriteLn('Hello world.');π 7 0: end;π 8 0: end.ππCross Referenceπ---------------ππhello 1ππi 2 4ππInteger 2ππouput 1ππWriteln 6ππ As shown above, alongside each identifier's name are the sourceπ line numbers that contain the identifier. (This is useful forπ tracking where they're used)ππ A cross-referencer reads the source File and looks forπ identifiers, using the scanner you've built previously. The firstπ time a particular identifier is found, it is inserted in theπ symbol tree along With it's line number. Subsequent appearances ofπ the same identifier update the symbol tree With an additional lineπ number appended to the list of line numbers.ππ As soon as the Program is completely scanned, all the identifierπ names and their line numbers are printed.ππ Use the INFO field of SYMTAB_NODE to point to a LINKED LIST ofπ line numbers.ππ The main loop should scan For tokens Until it finds a period, orπ Exits With an "Unexpected end of File" error. For each identifier,π search the symbol table to see if their were any previousπ instances of the identifier. If it is not found, then this must beπ the first time it is used so we can call the "enter" Function toπ create a new node.ππ Then, whether a new node was actually created or not, we call aπ Function to add the line number to the queue of line numbersπ attached to the node's "info" field. Finally, when the scannerπ loop terminates, we call a printing Function which traverses theπ tree from left to right to print the sorted tree - and all theπ line numbers in the linked list attached to each node.ππ Note that a recursive call to itself is probably the easiest wayπ to do this, since _all_ the nodes of the tree are being accessed,π not just one.ππ Types you will need:ππTypeπ pLINENUMS = ^LINENUM_NODE;π LINENUM_NODE = Recordπ next :pLINENUMS;π line :Integer;π end;ππ pLINE_HEADER = ^LINENUM_HDR;π LINENUM_HDR = Recordπ first, last :pLINENUMS;π end;ππ EXCERCISE #1ππ Write a cross referencer, as above. Text it With an assortment ofπ pascal sourceFiles.ππ ADVANCED EXCERCISEππ Note that the symbol table above converts all identifier names toπ lower case. What would be needed to reWrite the scanner/xrefπ Program to preserve case? ReWrite the xref Program to do so. (noteπ that Pascal compilers are Case insensitive, so the symbol table -ππ For compatibility - must compare lower case)ππ "BRAIN TEASERS"ππ 1. What would be necessary to reWrite the symbol table as a hashπ table?ππ 2. If an identifier appears more than once in a line, lineπ numbers will appear more than once in the listing. Fix xref toπ recognize duplicate occurences of line numbers in node-lists.ππ -----------------------------------------------------------------ππ Next: Pascal source cruncher.π 16 05-28-9313:46ALL SWAG SUPPORT TEAM TAGLINES.PAS IMPORT 42 { BOB SWARTππHere it is, all new and much faster. I used an internal binary tree to manageπthe taglines. You can store up to the available RAM in taglines:π}ππ{$A+,B-,D-,E-,F-,G-,I-,L-,N-,O-,P-,Q-,R-,S+,T-,V-,X-}π{$M 16384,0,655360}πUsesπ Crt;πTypeπ TBuffer = Array[0..$4000] of Char;ππConstπ Title = 'TagLines 0.2 by Bob Swart For Travis Griggs'#13#10;π Usage = 'Usage: TagLines inFile outFile'#13#10#13#10+π ' Taglines will remove dupicate lines from inFile.'#13#10+π ' Resulting Text is placed in outFile.'#13#10;ππ NumLines: LongInt = 0; { total number of lines in InFile }π NmLdiv80: LongInt = 0; { NumLines div 80, For 'progress' }π CurrentL: LongInt = 0; { current lineno read from InFile }ππTypeπ String80 = String[80];ππ PBinTree = ^TBinTree;π TBinTree = Recordπ Info: String80;π left,right: PBinTreeπ end;ππVarπ InBuf,π OutBuf : TBuffer;π InFile,π OutFile : Text;π TagLine : String80;π Root,π Current,π Prev : PBinTree;π i : Integer;π SaveExit : Pointer;πππFunction CompStr(Var Name1,Name2: String): Integer; Assembler;π{ Author: drs. Robert E. Swartπ}πAsmπ push DSπ lds SI,Name1 { ds:si pts to Name1 }π les DI,Name2 { es:di pts to Name2 }π cldπ lodsb { get String1 length in AL }π mov AH,ES:[DI] { get String2 length in AH }π inc DIπ mov BX,AX { save both lengths in BX }π xor CX,CX { clear cx }π mov CL,AL { get String1 length in CX }π cmp CL,AH { equal to String2 length? }π jb @Len { CX stores minimum length }π mov CL,AH { of String1 and String2 }π @Len: jcxz @Exit { quit if null }ππ @Loop: lodsb { String1[i] in AL }π mov AH,ES:[DI] { String2[i] in AH }π cmp AL,AH { compare Str1 to Str2 }π jne @Not { loop if equal }π inc DIπ loop @Loop { go do next Char }π jmp @Exit { Strings OK, Length also? }ππ @Not: mov BX,AX { BL = AL = String1[i],π BH = AH = String2[i] }π @Exit: xor AX,AXπ cmp BL,BH { length or contents comp }π je @Equal { 1 = 2: return 0 }π jb @Lower { 1 < 2: return -1 }π inc AX { 1 > 2: return 1 }π inc AXπ @Lower: dec AXπ @Equal: pop DSπend {CompStr};ππProcedure Stop; Far;πbeginπ ExitProc := SaveExit;π Close(InFile);π Close(OutFile);πend {Stop};πππbeginπ Writeln(Title);π if Paramcount <> 2 thenπ beginπ Writeln(Usage);π Haltπ end;ππ Assign(InFile,ParamStr(1));π SetTextBuf(InFile,InBuf);π Reset(InFile);π if IOResult <> 0 thenπ beginπ WriteLn('Error: could not open ', ParamStr(1));π Halt(1)π end;ππ Assign(OutFile,ParamStr(2));π SetTextBuf(OutFile,OutBuf);π Reset(OutFile);π if IOResult = 0 thenπ beginπ WriteLn('Error: File ', ParamStr(2),' already exists');π Halt(2)π end;ππ ReWrite(OutFile);π if IOResult <> 0 thenπ beginπ WriteLn('Error: could not create ', ParamStr(2));π Halt(3)π end;ππ SaveExit := ExitProc;π ExitProc := @Stop;ππ While not eof(InFile) doπ beginπ readln(InFile);π Inc(NumLines);π end;π Writeln('There are ',NumLines,' lines in this File.'#13#10);π Writeln('Press any key to stop the search For duplicate lines');π NmLdiv80 := NumLines div 80;ππ Root := nil;π reset(InFile);π While CurrentL <> NumLines doπ beginπ if KeyPressed thenπ Halt { calls Stop };π Inc(CurrentL);π if (CurrentL and NmLdiv80) = 0 thenπ Write('#');π readln(InFile,TagLine);ππ if root = nil then { first TagLine }π beginπ New(Root);π Root^.left := nil;π Root^.right := nil;π Root^.Info := TagLine;π Writeln(OutFile,tagLine)π endπ else { binary search For TagLine }π beginπ Current := Root;π Repeatπ Prev := Current;π i := CompStr(Current^.Info,TagLine);π if i > 0 thenπ Current := Current^.leftπ elseπ if i < 0 thenπ Current := Current^.rightπ Until (i = 0) or (Current = nil);ππ if i <> 0 then { TagLine not found }π beginπ New(Current);π Current^.left := nil;π Current^.right := nil;π Current^.Info := TagLine;ππ if i > 0 thenπ Prev^.left := Current { Current before Prev }π elseπ Prev^.right := Current { Current after Prev };π Writeln(OutFile,TagLine)π endπ endπ end;π Writeln(#13#10'100% Completed, result is in File ',ParamStr(2))π { close is done by Stop }πend.ππ{π> I also tried DJ's idea of the buffer of 65535 but it said the structureπ> was too large. So I used 64512.πAlways try to use a multiple of 4K, because the hard disk 'eats' space in theseπchunks. Reading/Writing in these chunks goes a lot faster that way.π} 17 08-27-9320:20ALL RUFUS HENDON Fast Boyer-Moore Search IMPORT 114 } { BOYERMO2.PAS (23 January 1988) (Rufus S. Hendon) }ππ{ This Unit provides facilities For searching a Text For a target usingπ the Boyer-Moore search method. The routine is based on Don Strenczewilk'sπ Implementation of a Variant form of the Boyer-Moore method (his case-π insensitive version B1, available on CompuServe in File BLINE.ARC inπ Borland BPROGA Data Library 4, uploaded 21 August 1987). In addition toπ repackaging his routine as a Turbo Pascal 4.0 Unit, I have modified itπ (1) to provide protection against endless loops that in the originalπ version can arise due to wrap-around of the index used to scan the Textπ when the the length of the Text approaches the maximum (65521 Characters)π allowed by Turbo Pascal 4.0 For Arrays of Type Char and (2) to improveπ efficiency slightly by removing three instructions (a PUSH, a MOV, and aπ POP) from the comparison loop.π The Text to be searched must be stored in an Array of Type Char or anπ equivalent user-defined Type. The lower bound of the Array must be 1.π The target For which the Text is to be searched must be of Type String.π The Program must also provide a Variable For the storage of the shiftπ table used by the Boyer-Moore method when it searches the Text. Thisπ Variable must provide 256 Bytes of storage; it can, For example, be aπ Variable of Type Array[Char] of Byte. The target Variable and the shift-π table Variable must be in the same segment: they must both be globalπ Variables (located in the data segment) or both local Variables (storedπ in the stack segment).π Whenever the Text is to be searched For a new target, the Program mustπ call MAKE_BOYER_MOORE_TABLE to create the shift table For the target.π Thereafter the Text can be searched For the target by invokingπ BOYER_MOORE_SEARCH, specifying as arguments the target and its shiftπ table as well as the position in the Text where the search is to begin.π if the Program maintains multiple target Variables and a separate shiftπ table and starting-position Variable For each target, searches forπ occurrences of the Various targets can be underway simultaneously.π In a call to BOYER_MOORE_SEARCH, the argument associated With theπ parameter START determines the position in the Text With which the searchπ begins. To search the entire Text, the Function would be invoked Withπ START = 1. The Function scans the Text beginning from the START positionπ For the first subString that matches the target specified by the Variableπ associated With the parameter TARGET, using the shift table stored in theπ Variable associated With the parameter TABLE. if such a subString isπ found, the Function returns the position (Array subscript) of the initialπ Character of the matching subString; since the Array is required to haveπ 1 as its lower bound, the position returned after a successful searchπ will always be greater than 0. if the Function fails to find a matchingπ subString, it returns 0. (if the requirement that the TARGET and TABLEπ Variables be in the same segment is violated, the Function also returnsπ 0.)π When it is required that all occurrences in the Text of a given targetπ be found, BOYER_MOORE_SEARCH would be invoked in a loop, in which theπ START argument would initially have the value of 1; thereafter, afterπ every successful search, the START argument would be reset to theπ position returned by the Function plus 1. The loop would terminate whenπ the Function reported failure. The loop would have a general structureπ similar to this:ππ item := [the target String];π make_Boyer_Moore_table(item,shift_table);π scan_beginning := 1;π search_Text_length := length(search_Text);π Repeatπ i := Boyer_Moore_search(search_Text,scan_beginning,search_Text_length,π item,shift_table);π if i > 0 then beginπ [do whatever processing is required when the search is successful];π scan_beginning := i+1π endπ Until i = 0ππ Note that if the Text Array can only be referred to by means of aπ Pointer, as will be the Case if the Array is allocated in the heap byπ means of the NEW Procedure, the Pointer, when used as the first argumentπ of BOYER_MOORE_SEARCH, must be dereferenced by writing '^' after it. If,π For example, TextPTR is a Pointer to the Text Array, the call to theπ search Function in the loop just given would take this form:ππ i := Boyer_Moore_search(Textptr^,scan_beginning,search_Text_length,π item,shift_table);π }π{============================================================================}πUnit BOYERMO2;π{============================================================================}πInterfaceππProcedure MAKE_BOYER_MOORE_TABLE(Var target: String; Var table);π{ TARGET is the target String For which a Text is to be searched. Theπ shift table For the target String is Constructed in TABLE, which must beπ a Variable providing 256 Bytes of storage, e.g. a Variable declared asπ Array[Char] of Byte. }ππFunction BOYER_MOORE_SEARCH(Var Text_Array; start, Text_length: Word;π Var target: String; Var table): Word;π{ Text_Array is an Array of Characters in which a Text is stored; theπ Text begins in Text_Array[1] and is Text_LENGTH Characters long. TARGETπ must either be the same Variable used as parameter TARGET in an earlierπ call to MAKE_BOYER_MOORE_TABLE or another Variable With the same value.π TABLE must be the Variable that was used as parameter TABLE in the sameπ call to MAKE_BOYER_MOORE_TABLE. TARGET and TABLE must be in the sameπ segment, i.e. they must both be global Variables or both local Variables.π A Boyer-Moore search is performed on the Text in Text_Array, beginningπ With the Character in position START and using shift table TABLE, forπ the first subString that matches TARGET. if a match is found, theπ position of the first Character of the matching subString is returned.π Otherwise 0 is returned. A Function value of 0 is also returned if TABLEπ and TARGET are not in the same segment. }π{============================================================================}πImplementationππConstπ copy: String = '';πVarπ table: Array[Char] of Byte;π{****************************************************************************}πProcedure MAKE_BOYER_MOORE_TABLE(Var target: String; Var table);π{ TARGET is the target String For which a Text is to be searched. Theπ shift table For the target String is Constructed in TABLE, which must beπ a Variable providing 256 Bytes of storage, e.g. a Variable declared asπ Array[Char] of Byte. }πbegin { MAKE_BOYER_MOORE_TABLE }π Inlineπ ($1E/ { push ds }π $C5/$76/<target/ { lds si,[bp+target] }π $89/$F3/ { mov bx,si }π $8A/$04/ { mov al, [si] }π $88/$C4/ { mov ah,al }π $B9/$80/$00/ { mov cx,$0080 }π $C4/$7E/<table/ { les di,[bp+table] }π $89/$FA/ { mov dx,di }π $FC/ { cld }π $F2/$AB/ { rep stosw }π $89/$DE/ { mov si,bx }π $89/$D7/ { mov di,dx }π $46/ { inc si }π $98/ { cbw }π $3C/$01/ { cmp al,1 }π $7E/$13/ { jle done }π $48/ { dec ax }π $88/$E1/ { mov cl,ah }π $88/$E7/ { mov bh,ah }π $8A/$1C/ { next: mov bl,[si] }π $89/$C2/ { mov dx,ax }π $29/$CA/ { sub dx,cx }π $88/$11/ { mov [bx+di],dl }π $46/ { inc si }π $41/ { inc cx }π $39/$C1/ { cmp cx,ax }π $75/$F2/ { jne next }π $1F) { done: pop ds }πend; { MAKE_BOYER_MOORE_TABLE }ππ{****************************************************************************}πFunction BOYER_MOORE_SEARCH(Var Text_Array; start, Text_length: Word;π Var target: String; Var table): Word;π{ Text_Array is an Array of Characters in which a Text is stored; theπ Text begins in Text_Array[1] and is Text_LENGTH Characters long. TARGETπ must either be the same Variable used as parameter TARGET in an earlierπ call to MAKE_BOYER_MOORE_TABLE or another Variable With the same value.π TABLE must be the Variable that was used as parameter TABLE in the sameπ call to MAKE_BOYER_MOORE_TABLE. TARGET and TABLE must be in the sameπ segment, i.e. they must both be global Variables or both local Variables.π A Boyer-Moore search is performed on the Text in Text_Array, beginningπ With the Character in position START and using shift table TABLE, forπ the first subString that matches TARGET. if a match is found, theπ position of the first Character of the matching subString is returned.π Otherwise 0 is returned. A Function value of 0 is also returned if TABLEπ and TARGET are not in the same segment. }πbegin { BOYER_MOORE_SEARCH }π Inlineπ ($1E/ { push ds }π $33/$C0/ { xor ax,ax }π $C5/$5E/<table/ { lds bx,[bp+table] } { if TABLE and }π $8C/$D9/ { mov cx,ds } { TARGET are in }π $C5/$76/<target/ { lds si,[bp+target] } { different }π $8C/$DA/ { mov dx,ds } { segments, re- }π $3B/$D1/ { cmp dx,cx } { port failure }π $75/$76/ { jne notfound2 } { at once }π $8A/$F4/ { mov dh,ah }π $8A/$14/ { mov dl,[si] }π $80/$FA/$01/ { cmp dl,1 }π $7F/$1F/ { jg boyer }π $7C/$6B/ { jl notfound2 }π $8A/$44/$01/ { mov al,[si+1] }π $8B/$56/<start/ { mov dx,[bp+start] }π $4A/ { dec dx }π $8B/$4E/<Text_length/ { mov cx,[bp+Text_length] }π $2B/$CA/ { sub cx,dx }π $C4/$7E/<Text_Array/ { les di,[bp+Text_Array] }π $8B/$DF/ { mov bx,di }π $03/$FA/ { add di,dx }π $FC/ { cld }π $F2/$AE/ { repne scasb }π $75/$53/ { jne notfound2 }π $97/ { xchg ax,di }π $2B/$C3/ { sub ax,bx }π $EB/$50/ { jmp short Exit }π $FE/$CA/ { boyer: dec dl }π $03/$F2/ { add si,dx }π $C4/$7E/<Text_Array/ { les di,[bp+Text_Array] }π $8B/$CF/ { mov cx,di }π $03/$4E/<Text_length/ { add cx,[bp+Text_length] }π $49/ { dec cx }π $4F/ { dec di }π $03/$7E/<start/ { add di,[bp+start] }π $03/$FA/ { add di,dx }π $8A/$74/$01/ { mov dh,[si+1] }π $55/ { push bp }π $8B/$E9/ { mov bp,cx }π $8A/$EC/ { mov ch,ah }π $FD/ { std }π $EB/$05/ { jmp short comp }π $D7/ { nexttable: xlat }π $03/$F8/ { add di,ax }π $72/$2A/ { jc notfound }π $3B/$EF/ { comp: cmp bp,di }π $72/$26/ { jb notfound }π $26/$8A/$05/ { mov al,es:[di] }π $3A/$F0/ { cmp dh,al }π $75/$F0/ { jne nexttable }π $4F/ { dec di }π $8A/$CA/ { mov cl,dl }π $F3/$A6/ { repe cmpsb }π $74/$0D/ { je found }π $8A/$C2/ { mov al,dl }π $2B/$C1/ { sub ax,cx }π $03/$F8/ { add di,ax }π $47/ { inc di }π $03/$F0/ { add si,ax }π $8A/$C6/ { mov al,dh }π $EB/$DC/ { jmp short nexttable }π $5D/ { found: pop bp }π $C4/$46/<Text_Array/ { les ax,[bp+Text_Array] }π $97/ { xchg ax,di }π $2B/$C7/ { sub ax,di }π $40/ { inc ax }π $40/ { inc ax }π $EB/$03/ { jmp short Exit }π $5D/ { notfound: pop bp }π $32/$C0/ { notfound2: xor al,al }π $89/$46/$FE/ { Exit: mov [bp-2],ax }π $FC/ { cld }π $1F) { pop ds }πend; { BOYER_MOORE_SEARCH }π{****************************************************************************}πend.ππ 18 10-28-9311:36ALL GUY MCLOUGHLIN Position Search IMPORT 26 } {===========================================================================πDate: 10-07-93 (13:12)πFrom: GUY MCLOUGHLINπSubj: Pos-Search Demoπ---------------------------------------------------------------------------}ππ {.$DEFINE DebugMode}ππ {$IFDEF DebugMode}ππ {$A+,B-,D+,E-,F-,I+,L+,N-,O-,R+,S+,V+}π {$M 4096,65536,65536}ππ {$ELSE}ππ {$A+,B-,D-,E-,F-,I-,L-,N-,O-,R-,S-,V-}π {$M 4096,65536,65536}ππ {$ENDIF}ππ (* Public-domain Search routine, using the standard TP *)π (* POS function. Guy McLoughlin - May 16, 1993. *)πprogram DemoPosSearch;πππ (***** Force alphabetical characters to uppercase. *)π (* *)π procedure UpCaseData({input } var Data;π wo_Size : word); far; assembler;π asmπ push dsπ cldπ lds si, Dataπ mov di, siπ mov cx, wo_Sizeπ xor ah, ahππ @L1:π jcxz @ENDπ lodsbπ cmp al, 'a'π jb @L2π cmp al, 'z'π ja @L2π sub al, 20hππ @L2:π stosbπ loop @L1ππ @END:π pop dsππ end; (* UpCaseData. *)πππ (***** PosSearch function. Returns 0 if string is not found. *)π (* Returns 65,535 if BufferSize is too large. *)π (* ie: Greater than 65,520 bytes. *)π (* *)π function PosSearch({input } var Buffer;π BuffSize : word;π Pattern : string;π ExactCase : boolean) : {output} word;π typeπ arwo_2 = array[1..2] of word;π arch_255 = array[1..255] of char;π varπ po_Buffer : ^arch_255;π by_Temp,π by_IncSize : byte;π wo_Index : word;π beginπ if (BuffSize > 65520) thenπ beginπ PosSearch := $FFFF;π exitπ end;π by_IncSize := (255 - pred(length(Pattern)));π po_Buffer := addr(Buffer);π if NOT ExactCase thenπ beginπ UpCaseData(po_Buffer^, BuffSize);π for wo_Index := 1 to length(Pattern) doπ Pattern[wo_Index] := upcase(Pattern[wo_Index])π end;ππ wo_Index := 0;π repeatπ by_Temp := pos(Pattern, po_Buffer^);π if (by_Temp = 0) thenπ beginπ inc(wo_Index, by_IncSize);π inc(arwo_2(po_Buffer)[1], by_IncSize)π endπ elseπ inc(wo_Index, by_Temp)π until (by_Temp <> 0) or (wo_Index > BuffSize);π if (by_Temp = 0) thenπ PosSearch := 0π elseπ PosSearch := wo_Indexπ end; (* PosSearch. *)πππtypeπ arby_64K = array[1..65520] of byte;ππvarπ Index : word;π st_Temp : string[20];π Buffer : ^arby_64K;ππBEGINπ new(Buffer);π fillchar(Buffer^, sizeof(Buffer^), 0);π st_Temp := 'aBcDeFgHiJkLmNoPqRsT';π move(st_Temp[1], Buffer^[65501], length(st_Temp));π st_Temp := 'AbCdEfGhIjKlMnOpQrSt';π Index := PosSearch(Buffer^, sizeof(Buffer^), st_Temp, false);π writeln(st_Temp, ' found at offset ', Index)πEND.ππ 19 11-21-9309:26ALL COSTAS MENICO VERY FAST Boyer-Moore IMPORT 55 } {π The originial benchmark program was to demonstrate the speed differenceπ between the POS() in Turbo Pascal 4 or 5 brute-forceπ and the Boyer-Moore method function POSBM()π Program author: Costas Menicoππ Call: posbm(pat,buf,buflen);π or if you are using a string buffer:π posbm(pat,s[1],length(s));π}ππprogram bufSearch;ππusesπ dos, crt;πππ{$F+}πfunction posbm(pat:string; var buf; buflen:word):word; EXTERNAL;π{$L BM.OBJ}π{$F-}ππfunction bruteForce(var such:string; var buf; buflen:word):word; ASSEMBLER;πASMπ cldπ push dsπ les di,bufπ mov cx,buflenπ jcxz @@30π lds si,suchπ mov al,[si]π or al,alπ je @@30π xor ah,ahπ cmp ax,cxπ ja @@30π mov bx,siπ dec cxπ @@10:π mov si,bxπ lodswπ xchg al,ah { AH=Stringlänge, AL=Suchchar }π repne scasbπ jne @@30π dec ahπ or ah,ahπ je @@20ππ inc cx { CX++ nach rep... }π xchg cx,axπ mov cl,chπ xor ch,chπ mov dx,diπ repe cmpsbπ mov di,dxπ mov cx,axπ loopne @@10π @@20:π mov ax,buflenπ sub ax,cxπ dec axπ jmp @@40π @@30:π xor ax,axπ @@40:π pop dsπend;ππππprocedure showtime(s : string; t : registers);ππbeginπ writeln(s, ' Hrs:', t.ch, ' Min:', t.cl, ' Sec:', t.dh, ' Milsec:', t.dl);πend;ππvarπ pat : string;π i,π j : integer;π start,π finish : registers;π arr : array[1..4096] of char;ππconstπ longloop = 5000;ππbeginπ clrscr;π randomize;π for i := 1 to 4096 doπ arr[i] := chr(random(255)+1);ππ move(arr[4090],pat[1],5); pat[0]:=#5;ππ writeln('Search using Brute-Force Method <please wait>');π start.ah := $2C;π msdos(start);π for j := 1 to longloop doπ i := bruteForce(pat,arr,4096);π finish.ah := $2C;π msdos(finish);π showtime('Start ', start);π showtime('Finish ', finish);π writeln('Pattern found at position ', i);π writeln;π writeln('Search using Boyer-Moore Method <please wait>');π start.ah := $2C;π msdos(start);π for j := 1 to longloop doπ i := posbm(pat, arr,4096);π finish.ah := $2C;π msdos(finish);π showtime('Start ', start);π showtime('Finish ', finish);π writeln('Pattern found at position ', i);π writeln;π writeln('Done ... Press Enter');π readln;πend.ππ{ -------------------------- XX34 OBJECT CODE ----------------------- }π{ ------------------------- CUT OUT AND SAVE AS BM.XX ------------------}π{ ------------------------ USE XX3401 D BM.XX ------------------------}ππ*XX3401-000392-050693--68--85-03573----------BM.OBJ--1-OF--1πU-M+32AuL3--IoB-H3l-IopQEYoiEJBBYcUU++++53FpQa7j623nQqJhMalZQW+UJaJmπQqZjPW+n9X8NW-k+ECbfXgIO32AuL3--IoB-H3l-IopQEYoiEJBB+sU1+21dH7M0++-cπW+A+E84IZUM+-2BDF2J3a+Q+OCQ++U2-1d+A+++--J-DIo7B++++rMU2+20W+N4Uuk+-π++-JUSkA+Mjg5X9YzAKq4+4AbUM-f+f+REDdjU09m6Z4+6aq-+53hVE-X7s8+Mi42U29πk5I1uO6+WIM0WPM6+MDt+LIPlPM2+On2jUU-Wos0weto+ya1+6jrUys0uqyEXLs2XB8Cπkcd4+6fUiM++wuj3hUE-XJs2Wos+GMjRXKs2AiGgWzW60y9tf6jsW+i9uwKq0+4BTUG9πJU78WoM+G19zzGjEQXE1w6cQBcc-0g-pwMjSWos+l9s2+Iw1yTCaR+ms+E0BTUG9wn9zπuxK9lgKq0+2flUI0+Cg0Aw1w5sjZUQEA+Jr80U-fWU6++5E+π***** END OF XX-BLOCK *****ππ{ -------------------------- ASSEMBLER CODE ------------------------- }π{ ------------------------- CUT OUT AND SAVE AS BM.AMS ------------------}π{ ------------------------ USE TASM TO ASSEMBLE ------------------------}ππ; filename: BM.ASMπ; fast search routine to search strings in ARRAYS OF CHARSπ; function in Turbo Pascal >= 4. Based on the Boyer-Moore algorithm.π; program author: Costas Menico.π; Very small modifications for using an ARRAY OF CHAR buffer instead ofπ; a string made by Jochen Magnus in May 93.π; declare as follows:π; {$F+}π; {$L BM.OBJ}π; function posbm(pat:string; var buffer; buflen:word):WORD; external;π; call as follows from Turbo 4..7:π; location := posbm(pat, buf, buflen);π; call for a search in a string typed buffer:π; location := posbm(pat, str[1], length(str));πππskiparrlength equ 256ππ; function work stackππdstk strucπpatlen dw ?πstrlen dw ?πskiparr db skiparrlength dup(?)πpattxt dd 0πstrtxt dd 0πdstk endsππ; total stack (callers plus work stack)ππcstk strucπourdata db size dstk dup(?)πbpsave dw 0πretaddr dd 0πparamlen dw 0 ; JOπstraddr dd 0πpataddr dd 0πcstk endsππparamsize equ size pataddr+size straddr +size paramlen ; +2 JOππcode segment para publicπ assume cs:codeππ; entry point to posbm functionππposbm proc farπ public posbmππ push bpπ sub sp, size dstkπ mov bp, spπ push dsπ xor ah, ahπ cldππ; get and save the length and address of the patternππ lds si, [bp.pataddr]π mov word ptr [bp.pattxt][2], dsπ lodsbπ or al, alπ jne notnullpπ jmp nomatchππnotnullp:π mov cx, axπ mov [bp.patlen], axπ mov word ptr [bp.pattxt], siππ; get and save the length and address of the string textππ lds si, [bp.straddr]π mov word ptr [bp.strtxt][2], dsπ mov ax,[bp.paramlen] ; JOπ or ax,ax ; JOπ jne notnullsπ jmp nomatchππnotnulls:π mov [bp.strlen], axπ mov word ptr [bp.strtxt], siπ cmp cx, 1π jne do_boyer_mooreπ lds si, [bp.pattxt]π lodsbπ les di, [bp.strtxt]π mov cx, [bp.strlen]π repne scasbπ jz match1π jmp nomatchππmatch1:π mov si, diπ sub si, 2π jmp exactmatchππdo_boyer_moore:ππ; fill the ASCII character skiparray with theπ; length of the patternππ lea di, [bp.skiparr]π mov dx, ssπ mov es, dxπ mov al, byte ptr [bp.patlen]π mov ah, alπ mov cx, skiparrlength/2π rep stoswππ; replace in the ASCII skiparray the correspondingπ; character offset from the end of the pattern minus 1ππ lds si, [bp.pattxt]π lea bx, [bp.skiparr]π mov cx, [bp.patlen]π dec cxπ mov bx, bpπ lea bp, [bp.skiparr]π xor ah, ahππfill_skiparray:π lodsbπ mov di, axπ mov [bp+di], clπ loop fill_skiparrayπ lodsbπ mov di, axπ mov [bp+di], clπ mov bp, bxππ; now initialize our pattern and string text pointers toπ; start searchingππ lds si, [bp.strtxt]π lea di, [bp.skiparr]π mov dx, [bp.strlen]π dec dxπ mov ax, [bp.patlen]π dec axπ xor bh, bhπ stdππ; get character from text. use the character as an indexπ; into the skiparray, looking for a skip value of 0.π; if found, execute a brute-force search on the patternππsearchlast:π sub dx, axπ jc nomatchπ add si, axπ mov bl, [si]π mov al, ss:[di+bx]π or al, alπ jne searchlastππ; we have a possible match, thereforeπ; do the reverse brute-force compareππ mov bx, siπ mov cx, [bp.patlen]π les di, [bp.pattxt]π dec diπ add di, cxπ repe cmpsbπ je exactmatchπ mov ax, 1π lea di, [bp.skiparr]π mov si, bxπ xor bh, bhπ jmp short searchlastππexactmatch:π mov ax, siπ lds si, [bp.strtxt]π sub ax, siπ add ax, 2π jmp short endsearchππnomatch:π xor ax, axππendsearch:π cldπ pop dsπ mov sp, bpπ add sp, size dstkπ pop bpπ ret paramsizeπposbm endpππcode endsπ endπ{----------------------- END OF ASSEMBLER CODE -------------------------}