             { --- Subroutines for finding and replacing substrings --- }

function number_in_string(var str:line_string; default:integer):integer;
var i,n,first,last:integer;
begin
first:=1;
while (first<=length(str)) and not (str[first] in digits) do first:=first+1;
if first>length(str) then number_in_string:=default
else
    begin
    last:=first+1;
    while (last<=length(str)) and (str[last] in digits) do last:=last+1;
    last:=last-1;
    Val( copy(str,first,last-first+1), n, i);
    number_in_string:=n;
    end;
end;

function match(pattern:line_string; var first,last:integer; upper,word:boolean):boolean;
var incr,i,l:integer;
    forwd,equal: boolean;
    pch,tch: char;
begin
forwd:=(first<=last); if forwd then incr:=1 else incr:=-1;
first:=first+incr; l:=length(pattern); equal:=false;
while (incr*first<=incr*last-l+1) and not equal do
    begin
    i:=0;
    repeat
        tch:=text[first+incr*i];
        if forwd then pch:=pattern[1+i] else pch:=pattern[l-i];
        if upper then begin tch:=UpCase(tch); pch:=UpCase(pch); end;
        equal:=(tch=pch);
        i:=i+1;
    until not equal or (i>=l);
    if equal and word then
        if forwd then
            equal:=not( ((first>TEXTSTART) and (text[first-1] in letters)) or
                        ((first+l<=text_end) and (text[first+l] in letters)) )
        else
            equal:=not( ((first<text_end) and (text[first+1] in letters)) or
                        ((first-l>=TEXTSTART) and (text[first-l] in letters)) );
    if not equal then first:=first+incr
           else if not forwd then first:=first-l+1;
    end;
match:=equal;
end; { match }

function replace_pattern:boolean;
var cr_count_patt,cr_count_repl,diff:integer;
    okay:boolean;
begin
cr_count_repl:=0;
for i:=1 to length(replacement) do if replacement[i]=CR then
    cr_count_repl:=cr_count_repl+1;
cr_count_patt:=0;
for i:=1 to length(pattern) do if pattern[i]=CR then
    cr_count_patt:=cr_count_patt+1;
diff:=length(pattern)-length(replacement);
if diff=0 then okay:=true
else if diff>0 then okay:=move_down(diff)
else begin
     okay:=move_up(diff);
     if not okay then prompt_for_esc('Text buffer full. Not replaced.');
     end;
if okay then
    for i:=1 to length(replacement) do text[index+i-1]:=replacement[i];
if (cr_count_patt>0) or (cr_count_repl>0) then restore_page(index,cx,cy)
else begin find_page(index,cx,cy); display_rest_page(index,cx,cy); end;
replace_pattern:=okay
end; { replace_pattern }

procedure find_pattern(new:boolean);
var i,first,last,count: integer;
    upper,word,global,veto,found,replaced: boolean;
    str2: string[2];
    str4: string[4];
begin
if new then prompt_for_string('Find: ',pattern);
if not empty_string(pattern) then
    begin
    if new then
        begin
        if replace_flag then
            begin
            prompt_for_string('Replace with: ',replacement);
            prompt_for_string('Options (B/G/N/U/W/-/n): ',options);
            end
        else prompt_for_string('Options (B/G/U/W/-/n): ',options);
        for i:=1 to length(options) do options[i]:=UpCase(options[i]);
        end;
    if replace_flag then veto:= not (Pos('N',options)>0);
    upper:= (Pos('U',options)>0);
    word := (Pos('W',options)>0);
    global := (Pos('G',options)>0);
    if global then begin first:=TEXTSTART; last:=text_end; end
    else
        begin
        first:=index;
        if (Pos('B',options)>0) or (Pos('-',options)>0)
            then last:=TEXTSTART else last:=text_end;
        count:=number_in_string(options,1);
        end;
    if global or (count>0) then
        begin
        i:=1;
        repeat
            found:=match(pattern,first,last,upper,word);
            if replace_flag and found then
                begin
                index:=first; find_page(index,cx,cy);
                if veto then prompt_for_YN('Replace',true) else yes_flag:=true;
                if yes_flag then replaced:=replace_pattern else replaced:=true;
                end;
            i:=i+1;
        until (not global and (i>count)) or not found or not replaced;
        if not found and not global then
            prompt_for_esc('Search string not found.')
        else begin index:=first; find_page(index,cx,cy); end;
        end;
    end;
end; { find_pattern }

procedure find;
begin replace_flag:=false; find_pattern(true); end;

procedure find_and_replace;
begin replace_flag:=true; find_pattern(true); end;

procedure repeat_last_find;
begin find_pattern(false); end;
