[comp.os.vms] Repost of EVE Regular Expression code.

gregg@a.cs.okstate.edu (Gregg Wonderly) (03/16/88)

I appologize to those of you who really don't want this again.  My
previous posting contained tabs which were promptly removed by
the first brain damaged mailer the artical passed through.  Some
lines were also wrapped :-(.  Anyway, here it is again.  For those
who do not know how to add this to EVE, you can do one of the following.

    1)  a.  Start EVE
        b.  "GET" re.tpu into a buffer
        c.  issue the command "EXTEND *"
        d.  issue the appropriate "SAVE_EXTENDED_TPU" command to put
            the resulting section file where you want it.
        e.  change your normal invocation of EDIT/TPU to include the
            /SECTION= qualifier with the value being the path to
            the section file that you created.  i.e. if you issue
            the command, "SAVE F$DISK:[GREGG.TPUSTUFF]MYEVE.GBL" then
            you would use /SECTION=F$DISK:[GREGG.TPUSTUFF]MYEVE.GBL.
            (.GBL is certainly shorter then .TPU$SECTION).

        *   This procedure is advantageous in that you do not have to
            wait for EVE to load and compile the extension each time
            you start it.

    2)  a.  Change your usual invocation of EVE to include the /COMMAND=
            qualifier with the value being the path to RE.TPU.

        *   This procedure is advantageous if you do not have the quota
            to support a TPU section file, but has the drawback that the
            startup of EVE will be a lot slower.

Gregg Wonderly
Department of Mathematics
Oklahoma State University

UUCP:      {cbosgd, ihnp4, rutgers}!okstate!nemo.math.okstate.edu!gregg
Internet:  gregg@NEMO.MATH.OKSTATE.EDU

===========================================================================
$!.............................................................................
$! VAX/VMS archive file created by VMS_SHAR V-5.03 07-Oct-1987
$! which was written by Michael Bednarek (U3369429@ucsvc.dn.mu.oz.au)
$! To unpack, simply save and execute (@) this file.
$!
$! This archive was created by GREGG
$! on Monday 14-MAR-1988 14:17:23.19
$!
$! It contains the following 2 files:
$! RE.RNO RE.TPU
$!=============================================================================
$ Set Symbol/Scope=(NoLocal,NoGlobal)
$ Version=F$GetSYI("VERSION") ! See what VMS version we have here:
$ If Version.ges."V4.4" then goto Version_OK
$ Write SYS$Output "Sorry, you are running VMS ",Version, -
                ", but this procedure requires V4.4 or higher."
$ Exit 44
$Version_OK: CR[0,8]=13
$ Pass_or_Failed="failed!,passed."
$ Goto Start
$Convert_File:
$ Read/Time_Out=0/Error=No_Error1/Prompt="creating ''File_is'" SYS$Command ddd
$No_Error1: Define/User_Mode SYS$Output NL:
$ Edit/TPU/NoSection/NoDisplay/Command=SYS$Input/Output='File_is' -
        VMS_SHAR_DUMMY.DUMMY
set(Informational, off); set (success, off);
f:=Get_Info(Command_Line,"File_Name"); b:=Create_Buffer("",f);
o:=Get_Info(Command_Line,"Output_File"); Set(Output_File,b,o);
Position(Beginning_of(b)); Loop x:=Erase_Character(1);
Loop ExitIf x<>"V"; Move_Vertical(1); x:=Erase_Character(1);
Append_Line; Move_Horizontal(-Current_Offset);
EndLoop; Move_Vertical(1); ExitIf Mark(None)=End_of(b)
EndLoop; Position(Beginning_of(b)); Loop
x:=Search("`",Forward,Exact); ExitIf x=0; Position(x);
Erase_Character(1); If Current_Character='`' then Move_Horizontal(1);
else Copy_Text(ASCII(INT(Erase_Character(3)))); EndIf; EndLoop; Exit;
$ Delete VMS_SHAR_DUMMY.DUMMY;*
$ Checksum 'File_is
$ Success=F$Element(Check_Sum_is.eq.CHECKSUM$CHECKSUM,",",Pass_or_Failed)+CR
$ Read/Time_Out=0/Error=No_Error2/Prompt=" CHECKSUM ''Success'" SYS$Command ddd
$No_Error2: Return
$Start:
$ File_is="RE.RNO"
$ Check_Sum_is=157000438
$ Copy SYS$Input VMS_SHAR_DUMMY.DUMMY
X.I-1
X3 FIND
X.br
XFIND
X.s
X.endif manual
XSearches for an occurrence of a pattern.  FIND uses a very powerful pattern
Xmatching mechanism known as a regular expression.  Regular expressions use
Xspecial characters to denote special patterns such as the beginning of the
Vline, and the end of the line.  The regular expressions recognized by FIND hav
Xe
Xthe following form.
X.s
X.lm +5
XThe character '.' represents any character.  Thus, the pattern ".t" matches
X"it", "at", "ot", "lt", and other strings of length 2 that end in a "t".
X.s
XThe character '^' as the first character of a pattern indicates that the
Xfollowing characters should be looked for only at the beginning of the line.
X.s
XThe character '$' as the last character of a pattern indicates that the
Vpreceeding characters should be looked for only at the end of a line.  Anywher
Xe
Xelse in the pattern it is taken literally.
X.s
XThe character '[' starts a list of characters that ends with a ']'.  The
Xcharacters between the '[' and ']' indicate all the possible characters that
Xcan occur ONCE at that position in the pattern.  Thus, the pattern "[ai]t"
Xmatches ONLY the strings "it" and "at".  If the first character following the
X'[' is a '^', then the complement of the set of characters that follows is
Xassumed.  Thus, the pattern "[^ia]t", never matches the strings "it" or "at".
XIt is possible to denote a sequence of characters by placing a '-' between two
Xcharacters.  Thus, "[0-9]" is short for "[0123456789]".
X.s
XThe character '*' is a multiplicative operator.  Its presence indicates that
Xthe preceeding character or [...] set might occur zero or more times.  Thus,
Xthe pattern "i[ai]*t" matches "it", "iat", "iit", "iaat", "iiit", "iait", and
Xall other strings starting with "i", ending with "t", and having only the
Xcharacters 'a' or 'i' between.
X.s
XBecause the characters '[', '.', '^', '$', and '*' are special, they must
Xbe preceed by a '\' if they are meant literally in a pattern.  Thus, '\'
Xitself must be indicated as "\\".
X.lm -5
X.s
XPress the FIND key and then enter the pattern from the main keyboard.  End the
Xstring by pressing the RETURN key.  To search for the previously specified
Xpattern, press the FIND key twice, or use the FIND NEXT key on the keypad
X(normally PF3).  You can press the FIND key, and then press the up-arrow
Xkey to edit the previously specified search string.
X.s
XFIND is case-insensitive if the string contains only lowercase letters; it is
Xcase-sensitive if the string contains any uppercase letters.
X.s
XThe direction of the search (Forward or Reverse) is determined by the current
Xdirection of the buffer, as shown in the status line at the bottom of the
Xbuffer.  If the pattern can be found only by searching in the opposite
Xdirection, GWEDIT asks you if you want to move the cursor in that direction.
X.s
VThe FIND command is bound to the FIND key, and the SHIFT-KEY, PF3 key sequence
X.
X.I-1
X3 REPLACE
X.br
XREPLACE
X.s
X.endif manual
XLets you repeatedly substitute one word or phrase for another
Xthroughout a buffer.  For example,
X.s
X.I+5
Xreplace good excellent
X.s
Xinstructs GWEDIT to replace "good" with "excellent" in the current
Xbuffer.
X.s
XEach occurrence of "good" within the current buffer will be highlighted in
Vreverse video, and you will be prompted for an action.  Typing "YES", "Y", "y"
X,
X"yes" or just pressing RETURN replaces this occurrence.  "NO", "no", "n", or
X"N", skips this occurrence.  "All" replaces this occurrence and all future
Voccurrences without any further prompting.  "Last" replaces this occurrence an
Xd
Xstops the REPLACE command.  "Quit" stops the REPLACE command without replacing
Xthis occurrence.
X.s
XIn order to replace multi-word phrases, put the phrases in
Xquotation marks.  For example,
X.s
X.I+5
Xreplace "the first one" "the second one"
X.s
VSpecial notation in the first string allows you to specify regular expressions
X.
XSee the FIND command for more information.  You may also specify groups of
Xcharacters in the first string to be placed in the second string when the
Vsubstitution occurs.  An '&' in the second string will cause all text that was
X found
Xby the search pattern to be placed in the resultant string.
X.s
X.lm+5
Xe.g.  REPLACE "go[ <TAB>]*" "foo$&"
X.s
X.lm-5
Xwill cause the string "foo$" to be prepended to all occurrances of the string
X"go" followed by zero or more space or tab characters.
X.s
XIt is also possible to pick portions of the original text to be kept in the
Xresultant text.
X.s
X.lm+5
Xe.g.  REPLACE "foo$\(go[ <TAB>]\)" "\1"
X.lm-5
X.s
Xwould cause the prefix "foo$" to be removed from all occurances of the string
X"foo$go" followed by zero or more space or tab characters.  The notation
Xshown is derived
Xfrom the EX(1) editor under UNIX.  Up to 9 sets of \( \) pairs may appear
Xinside the search string.  These are refered to by specifying the relative
Xnumber from left to right.  E.g. to swap two columns of text that occur at the
Xbeginning of a line, you might say
X.lm+5
X.s
XREPLACE "^\([^ <TAB>]*\)\([ <TAB>]*\)\([^ <TAB>]*\)" "\3\2\1"
X.lm -5
X.s
Xwhich delimits three groups of characters.  The first is a sequence of
Xcharacters which are not spaces or tabs, followed by a group of characters
Xwhich are spaces or tabs, followed, again, by a sequence of characters which
Xare not spaces or tabs.  By swapping field 3 with field 1, the columns will be
Xswapped.
$ GoSub Convert_File
$ File_is="RE.TPU"
$ Check_Sum_is=1898631914
$ Copy SYS$Input VMS_SHAR_DUMMY.DUMMY
X!
X! Top-level find command.  Calls eve$find, as does the replace command.
X! The two commands have slightly different requirements, so they both
X! call eve$find and pass it a parameter to indicate the caller.
X!
X! Parameters:
X!
X!   target          String to find - input
X
XPROCEDURE eve_find (target, compile_it)
X
X    LOCAL
X        comp_val;
X
X    comp_val := compile_it;
X
X    IF (GET_INFO (compile_it, "TYPE") = STRING) THEN
X        comp_val := 1;  ! Always compile when EVE_FIND is called by EVE_PARSE.
X    ENDIF;
X
X    eve$find (target, 0, comp_val, 0);
X
XENDPROCEDURE;
X`012
X!
X! Search for target in the current direction.  If not found in the
X! current direction look in the opposite direction, but do not go
X! there without prompting the user.  Search is case-insensitive if
X! target is all lowercase; otherwise is case-sensitive.
X! Returns range if target found, otherwise returns false.
X!
X! Parameters:
X!
X!   target          String to find - input
X!   replacing       If true, called by eve_replace; allow a
X!               match at current cursor position - input
X
XPROCEDURE eve$find (target, replacing, compile_it, do_parens)
X
X    LOCAL
X        new_target,             ! Local copy of target
X        lowercase_target,       ! Lowercase version of eve$x_target
X        start_find_key,         ! String describing key used to invoke find
X        old_str,
X        old_pos,
X        new_pat,
X        stop_find_key,          ! String describing key used after prompt
X        this_position,          ! Marker for current cursor position
X        how_exact,              ! Keyword to indicate case-sensitivity
X        find_range,             ! Range returned by search
X        other_direction,        ! Keyword for opposite direction
X        other_direction_string, ! String for message including other_direction
X        find_reply,             ! Reply to inquiry about changing direction
X        change_direction_key;   ! Keyword for key used to end find_reply
X
X    ON_ERROR
X        IF ERROR = TPU$_STRNOTFOUND THEN
X            find_range := 0;
X        ENDIF;
X    ENDON_ERROR;
X
X    start_find_key := eve$lookup_comment (LAST_KEY);
X
X    IF target <> eve$kt_null THEN
X        new_target := target;
X    ELSE
X        IF CURRENT_DIRECTION = FORWARD THEN
X            new_target := READ_LINE ("Forward Find: ");
X        ELSE
X            new_target := READ_LINE ("Reverse Find: ");
X        ENDIF;
X    ENDIF;
X
X    IF (compile_it = 0) AND (GET_INFO (new_target, "TYPE") = STRING) THEN
X        eve$x_orig_target := new_target;
X    ENDIF;
X
X    IF (new_target <> "") AND (GET_INFO (new_target, "TYPE") = STRING) AND
X                                                        (compile_it <> 0) THEN
X        eve$x_orig_target := new_target;
X        eve$x_paren_cnt := 0;
X        new_pat := eve$pattern_gen (new_target, eve$x_paren_cnt, do_parens);
X
X        IF (new_pat = "") THEN
X            RETURN (0);
X        ENDIF;
X
X        EXECUTE (COMPILE ("eve$x_find_pat := " + new_pat));
X        new_target := eve$x_find_pat;
X    ENDIF;
X
X    stop_find_key := eve$lookup_comment (LAST_KEY);
X
X    IF new_target = eve$kt_null THEN
X        IF ((start_find_key = "find") AND
X                ((stop_find_key = "find") OR (stop_find_key = "return"))) THEN
X
X            IF eve$x_target = eve$kt_null THEN
X                MESSAGE ("No previous target to find");
X                RETURN (0);
X            ELSE
X                IF GET_INFO (eve$x_orig_target, "TYPE") = STRING THEN
X                    MESSAGE (FAO ("Finding: !AS", eve$x_orig_target));
X                ELSE
X                    MESSAGE ("Finding previous target: ");
X                ENDIF;
X            ENDIF;
X        ELSE
X            MESSAGE ("Invalid terminator");
X            RETURN (0);
X        ENDIF;
X    ELSE
X        eve$x_target := new_target;
X        IF GET_INFO (eve$x_orig_target, "TYPE") = STRING THEN
X            MESSAGE (FAO ("Finding: !AS", eve$x_orig_target));
X        ELSE
X            MESSAGE ("Searching...");
X        ENDIF;
X    endif;
X
X    lowercase_target := eve$x_orig_target;
X
X    IF GET_INFO (lowercase_target, "TYPE") = STRING THEN
X        CHANGE_CASE (lowercase_target, LOWER);
X    ENDIF;
X
X    IF lowercase_target = eve$x_orig_target THEN
X        how_exact := NO_EXACT;
X    ELSE
X        how_exact := EXACT;
X    ENDIF;
X
X    this_position := MARK (NONE);
X
X    IF CURRENT_DIRECTION = FORWARD THEN
X        IF this_position <> END_OF (CURRENT_BUFFER) THEN
X            IF NOT replacing THEN
X                MOVE_HORIZONTAL (1);
X            ENDIF;
X            find_range := SEARCH (eve$x_target, FORWARD, how_exact);
X        ELSE
X            find_range := 0;
X        ENDIF;
X    ELSE
X        IF this_position <> BEGINNING_OF (CURRENT_BUFFER) THEN
X            MOVE_HORIZONTAL (-1);
X            find_range := SEARCH (eve$x_target, REVERSE, how_exact);
X        ELSE
X            find_range := 0;
X        ENDIF;
X    ENDIF;
X
X    IF find_range = 0 THEN
X        IF CURRENT_DIRECTION = FORWARD THEN
X            other_direction := REVERSE;
X            other_direction_string := "reverse";
X        ELSE
X            other_direction := FORWARD;
X            other_direction_string := "forward";
X        ENDIF;
X
X        POSITION (this_position);
X
X        IF other_direction = FORWARD THEN
X            IF this_position <> END_OF (CURRENT_BUFFER) THEN
X                MOVE_HORIZONTAL (1);
X                find_range := SEARCH (eve$x_target, FORWARD, how_exact);
X            ELSE
X                find_range := 0;
X            ENDIF;
X        ELSE
X            IF this_position <> BEGINNING_OF (CURRENT_BUFFER) THEN
X                MOVE_HORIZONTAL (-1);
X                find_range := SEARCH (eve$x_target, REVERSE, how_exact);
X            ELSE
X                find_range := 0;
X            ENDIF;
X        ENDIF;
X
X        IF find_range = 0 THEN
X            IF GET_INFO (eve$x_orig_target, "TYPE") = STRING THEN
X                MESSAGE (FAO ("Could not find: !AS", eve$x_orig_target));
X            ELSE
X                MESSAGE ("Could not find pattern!");
X            ENDIF;
X
X            POSITION (this_position);
X            RETURN (0);
X        ELSE
X            find_reply :=
X                READ_LINE (FAO ("Found in !AS direction.  Go there? ",
X                    other_direction_string));
X
X            ! Hitting return or do means yes; hitting another non-typing
X            ! key is probably a mistake, so interpret as no.
X
X            IF find_reply = eve$kt_null THEN
X                change_direction_key := eve$lookup_comment (LAST_KEY);
X
X                IF (change_direction_key = "return") OR
X                                        (change_direction_key = "do") THEN
X                    find_reply := "yes";
X                ELSE
X                    find_reply := "no";
X                ENDIF;
X            ELSE
X                CHANGE_CASE (find_reply, LOWER);
X            ENDIF;
X
X            IF SUBSTR ("yes", 1, LENGTH (find_reply)) = find_reply THEN
X                SET (other_direction, CURRENT_BUFFER);
X                eve$update_status_lines;
X                eve$position_in_middle (BEGINNING_OF (find_range));
X                RETURN (find_range);
X            ELSE
X                POSITION (this_position);
X                RETURN (0);
X            ENDIF;
X        ENDIF;
X    ELSE
X        eve$position_in_middle (BEGINNING_OF (find_range));
X        RETURN (find_range);
X    ENDIF;
X
X    MESSAGE (eve$kt_null);
XENDPROCEDURE;
X`012
X!
X!
X! TPU pattern generator.  Generates a pattern string from the passed
X! regular expression.
X!
XPROCEDURE eve$pattern_gen (pat, paren_cnt, do_parens)
X
X    LOCAL
X        first,      ! First pattern to be done
X        part_pat,
X        chno,
X        startchar,
X        haveany,
X        regular,
X        tstr,
X        endchar,
X        str_pat,
X        cur_pat,    ! The current pattern to be extracted
X        cur_char,   ! The current character in the regular
X                    ! expression being examined
X        new_pat,    ! The output pattern
X        pos;        ! The position within the regular
X                    ! expression string that we are examining
X                    ! currently
X
X    paren_cnt := 0;
X
X    IF ((INDEX (pat, "$") <> 0) OR (INDEX (pat, "[") <> 0) OR
X                    (INDEX (pat, "^") <> 0) OR (INDEX (pat, ".") <> 0) OR
X                        (INDEX (pat, "*") <> 0) OR (INDEX (pat, "\") <> 0) OR
X                        (INDEX (pat, '"') <> 0)) THEN
X        new_pat := "";
X    ELSE
X        new_pat := '"'+pat+'"';
X        RETURN (new_pat);
X    ENDIF;
X
X    pos := 1;
X
X    IF SUBSTR (pat, pos, 1) = "^" THEN
X        new_pat := "line_begin";
X        pos := pos + 1;
X    ENDIF;
X
X    LOOP
X        EXITIF (pos > LENGTH (pat));
X
X        regular := 0;
X        cur_pat := "";
X        cur_char := substr (pat, pos, 1);
X        pat_str := "";
X
X        IF (cur_char = "$") THEN
X            cur_pat := "line_end";
X            IF (pos < LENGTH (pat)) THEN
X                cur_pat := "'$'";
X            ENDIF;
X        ELSE
X            IF cur_char = "[" THEN
X                pos := pos + 1;
X
X                IF SUBSTR (pat, pos, 1) = "^" THEN
X                    pos := pos + 1;
X                    part_pat := "notany('";
X                ELSE
X                    part_pat := "any('";
X                ENDIF;
X
X                LOOP
X                    EXITIF pos > LENGTH (pat);
X                    EXITIF SUBSTR (pat, pos, 1) = "]";
X
X                    IF SUBSTR (pat, pos, 1) = "\" THEN
X                        pos := pos + 1;
X                        IF pos > LENGTH (pat) THEN
X                            MESSAGE ("Missing character after \");
X                            RETURN ("");
X                        ENDIF;
X                    ENDIF;
X
X                    startchar := SUBSTR (pat, pos, 1);
X                    pat_str := pat_str + startchar;
X
X                    IF (SUBSTR (pat, pos+1, 1) = '-') THEN
X                        pos := pos + 2;
X                        IF (pos >= LENGTH (pat)) THEN
X                            MESSAGE ("Missing character after '-'");
X                            RETURN ("");
X                        ENDIF;
X
X                        endchar := SUBSTR (pat, pos, 1);
X
X                        chno := 1;
X                        LOOP
X                            EXITIF (ASCII(chno) = startchar);
X                            chno := chno + 1;
X                        ENDLOOP;
X
X                        LOOP
X                            chno := chno + 1;
X                            IF (chno > 255) THEN
V                                MESSAGE ("Invalid character sequence for '-'")
X;
X                                RETURN ("");
X                            ENDIF;
X
X                            EXITIF (ASCII (chno-1) = endchar);
X                            pat_str := pat_str + ASCII (chno);
X                        ENDLOOP;
X                    ENDIF;
X                    pos := pos + 1;
X                ENDLOOP;
X
X                IF pat_str = "" THEN
X                    MESSAGE ("No text found between []");
X                    RETURN ("");
X                ENDIF;
X
X                IF (SUBSTR (pat, pos+1, 1) = "*") THEN
X                    IF (part_pat = "notany('") THEN
X                        cur_pat := cur_pat + "(scan('"+pat_str+"')|"""")";
X                    ELSE
X                        cur_pat := cur_pat + "(span('"+pat_str+"')|"""")";
X                    ENDIF;
X                    pos := pos + 1;
X                ELSE
X                    cur_pat := part_pat + pat_str + "')";
X                ENDIF;
X            ELSE
X
X                !
X                !   Process a single character character, either '\', '.',
X                !   or some literal character.  There might be a '*' following
X                !   a character, in which case that is also handled here.
X                !
X
X                tstr := '"';
X                haveany := 0;
X                regular := 1;
X
X                LOOP
X                    cur_char := SUBSTR (pat, pos, 1);
X                    EXITIF (INDEX ("$^[", cur_char) > 0);
X                    EXITIF (pos > LENGTH (pat));
X
X                    IF cur_char = "\" THEN
X                        pos := pos + 1;
X                        cur_char := SUBSTR (pat, pos, 1);
X                        IF (do_parens) THEN
X                            IF (cur_char = "(") THEN
X                                paren_cnt := paren_cnt + 1;
X                                IF tstr = '"' THEN
X                                    tstr := '""@o'+STR(paren_cnt)+'&"';
X                                ELSE
X                                    tstr := tstr + '"@o'+STR(paren_cnt)+'&"';
X                                ENDIF;
X                            ELSE
X                                IF (cur_char = ")") THEN
X                                    IF (paren_cnt = 0) THEN
X                                        MESSAGE (
X                                        FAO ("No previous ""\("" near: !AS",
X                                        SUBSTR (pat, pos, LENGTH(pat)-pos))
X                                        );
X                                        RETURN (0);
X                                    ENDIF;
X
X                                    IF tstr = '"' THEN
X                                        tstr := "@p"+STR(paren_cnt)+'&"';
X                                    ELSE
X                                        tstr := tstr + '"@p' +
X                                                    STR(paren_cnt)+'&"';
X                                    ENDIF;
X                                ELSE
X                                    tstr:=tstr+cur_char;
X                                ENDIF;
X                            ENDIF;
X                        ELSE
X                            tstr:=tstr+cur_char;
X                        ENDIF;
X                    ELSE
X                        IF (cur_char = ".") THEN
X                            cur_char := "longer_than_1";
X                        ENDIF;
X
X                        ! Check for zero or more
X
X                        IF (SUBSTR (pat, pos+1, 1) = '*') THEN
X                            pos := pos + 1;
X
X                            IF (LENGTH (cur_char) > 1) THEN
X                                cur_pat :=
X                                    "''&(span(eve$pch)|'')";
X                            ELSE
X                                cur_pat := "(span('"+cur_char+"')|"""")";
X                            ENDIF;
X                            tstr := tstr+'"'+"&"+cur_pat+"&"+'"';
X                            haveany := 0;
X                        ELSE
X                            IF (LENGTH (cur_char) > 1) THEN
X                                IF (haveany) THEN
X                                    tstr := tstr +'"'+"&"+"arb(1)"+"&"+'"';
X                                    haveany := 0;
X                                ELSE
V                                    IF (LENGTH (tstr)>0) and (tstr <> '"') THE
XN
V                                        tstr := tstr +'"'+"&"+"arb(1)"+"&"+'"'
X;
X                                    ELSE
X                                        tstr := "arb(1)"+"&"+'"';
X                                    ENDIF
X                                ENDIF;
X                            ELSE
X                                IF (cur_char = """") THEN
X                                    tstr := tstr + '""';
X                                    haveany := haveany + 2;
X                                ELSE
X                                    tstr := tstr + cur_char;
X                                    haveany := haveany + 1;
X                                ENDIF;
X                            ENDIF;
X                        ENDIF;
X                    ENDIF;
X                    pos := pos + 1;
X                ENDLOOP;
X                cur_pat := tstr + '"';
X                pos := pos - 1;
X            ENDIF;
X        ENDIF;
X
X        IF (regular) THEN
X            IF new_pat = "" THEN
X                new_pat := cur_pat;
X            ELSE
X                IF (LENGTH (tstr) > 1) THEN
X                    new_pat := new_pat + "&" + cur_pat;
X                ENDIF;
X            ENDIF;
X        ELSE
X            IF new_pat = "" THEN
X                new_pat := cur_pat;
X            ELSE
X                new_pat := new_pat + "&" + cur_pat;
X            ENDIF;
X        ENDIF;
X        pos := pos + 1;
X    ENDLOOP;
X
X    pos := INDEX (new_pat, "&");
X    LOOP
X        EXITIF (pos = 0) OR (pos > LENGTH (new_pat));
X        IF (SUBSTR (new_pat, pos, 3) = '&""') AND
X            (SUBSTR (new_pat, pos, 5) <> '&""""') THEN
X            new_pat := SUBSTR (new_pat, 1, pos-1) +
X                                                SUBSTR (new_pat, pos+3, 255);
X        ELSE
X            IF SUBSTR (new_pat, pos, 2) = '&@' THEN
X                new_pat := SUBSTR (new_pat, 1, pos-1) +
X                                                SUBSTR (new_pat, pos+1, 255);
X            ENDIF;
X        ENDIF;
X        pos := pos + 1;
X    ENDLOOP;
X
X    RETURN (new_pat);
XENDPROCEDURE;
X`012
X!
X!
X!
XPROCEDURE eve$do_substitution (source, dest)
X
X    LOCAL
X        cur_char,
X        result,
X        idx;
X
X    idx := 0;
X    result := "";
X
X    LOOP
X        EXITIF (idx > LENGTH(dest));
X
X        cur_char := SUBSTR (dest, idx, 1);
X        IF (cur_char = "&") THEN
X            result := result + source;
X            idx := idx + 1;
X        ELSE
X            IF (cur_char = '\') THEN
X                cur_char := SUBSTR(dest, idx+1, 1);
X                IF (INDEX (eve$x_digit_characters, cur_char) > 0) THEN
X                    EXECUTE (COMPILE ("eve$x_glo_str := SUBSTR (p" +
X                            cur_char +", LENGTH (o"+cur_char+")+1,512);"));
X                    result := result + eve$x_glo_str;
X                ELSE
X                    result := result + "\" + cur_char;
X                ENDIF;
X                idx := idx + 2;
X            ELSE
X                result := result + cur_char;
X                idx := idx + 1;
X            ENDIF;
X        ENDIF;
X    ENDLOOP;
X
X    RETURN (result);
XENDPROCEDURE;
X`012
X!
X! Search and replace procedure.  Case-sensitivity of search is
X! same as for the find command.  If case-insensitive, replacements
X! are done to match case of current occurrence.
X!
X! Parameters:
X!
X!   replace_parameter_1 Old string - input
X!   replace_parameter_2 New string - input
X
XPROCEDURE eve_replace (replace_parameter_1, replace_parameter_2)
X
X    LOCAL
X        replace_text,
X        source_text,
X        target,                 ! Local copy of replace_parameter_1
X        replacement,            ! Local copy of replace_parameter_2
X        this_buffer,            ! Current buffer
X        this_mode,              ! Keyword for current mode
X        lowercase_target,       ! Lowercase version of target string
X        lowercase_replacement,  ! Lowercase version of replacement string
X        uppercase_target,       ! Uppercase version of target string
X        uppercase_replacement,  ! Uppercase version of replacement string
X        capital_target,         ! Capitalized version of target string
X        capital_replacement,    ! Capitalized version of replacement string
X        how_exact,              ! Keyword to indicate case-sensitivity
X        replace_range,          ! Range of current occurrence
X        highlight_range,        ! Reverse-video version of replace_range
X        replace_action,         ! String reply to prompt
X        action_length,          ! Length of replace_action
X        asking,                 ! True unless "all" option has been chosen
X        this_occurrence,        ! String of replace_range
X        occurences;
X
X    this_buffer := CURRENT_BUFFER;
X    this_mode := GET_INFO (CURRENT_BUFFER, "MODE");
X    SET (INSERT, this_buffer);
X    asking := 1;
X
X    IF NOT (eve$prompt_string (replace_parameter_1, target,
X                                "Old string: ", "No string to replace")) THEN
X        RETURN;
X    ENDIF;
X
X    replacement := replace_parameter_2;
X
X    IF replacement = eve$kt_null THEN
X        replacement := READ_LINE ("New string: ");  ! empty string is ok here
X    ENDIF;
X
X    lowercase_target := target;
X
X    IF GET_INFO (lowercase_target, "TYPE") = STRING THEN
X        CHANGE_CASE (lowercase_target, LOWER);
X    ENDIF;
X
X    lowercase_replacement := replacement;
X    CHANGE_CASE (lowercase_replacement, LOWER);
X
X    IF (lowercase_target = target) AND
X                                    (lowercase_replacement = replacement) THEN
X        how_exact := NO_EXACT;
X        uppercase_target := target;
X        IF GET_INFO (uppercase_target, "TYPE") = STRING THEN
X            CHANGE_CASE (uppercase_target, UPPER);
X        ENDIF;
X
X        capital_target := target;
X
X        IF GET_INFO (capital_target, "TYPE") = STRING THEN
X            eve$capitalize_string (capital_target);
X        ENDIF;
X
X        uppercase_replacement := replacement;
X        CHANGE_CASE (uppercase_replacement, UPPER);
X        capital_replacement := replacement;
X        eve$capitalize_string (capital_replacement);
X    ELSE
X        how_exact := EXACT;
X    ENDIF;
X
X    occurrences := 0;
X
X    LOOP
X        replace_range := eve$find (target, 1, 1, 1);
X
X        EXITIF replace_range = 0;
X        highlight_range :=
X            CREATE_RANGE (BEGINNING_OF (replace_range),
X                                END_OF (replace_range), eve$x_highlighting);
X        POSITION (BEGINNING_OF (replace_range));
X        UPDATE (CURRENT_WINDOW);
X        source_text :=
X            CREATE_RANGE (BEGINNING_OF (replace_range),
X                                END_OF (replace_range), NONE);
X
X        source_text := SUBSTR (source_text, 1, LENGTH (source_text));
X
X        LOOP
X            IF asking THEN
X                replace_action :=
X                    READ_LINE ("Replace? Type yes, no, all, last, or quit: ");
X                CHANGE_CASE (replace_action, LOWER);
X            ELSE
X                replace_action := "yes";
X            ENDIF;
X
X            action_length := LENGTH (replace_action);
X            IF (replace_action = SUBSTR ("yes", 1, action_length)) OR
X                    (replace_action = SUBSTR ("all", 1, action_length)) OR
X                    (replace_action = SUBSTR ("last", 1, action_length)) OR
X                                                    (action_length = 0) THEN
X                highlight_range := 0;
X
X                IF how_exact = EXACT THEN
X                    replace_text := eve$do_substitution (
X                                                    source_text, replacement);
X                ELSE
X                    ! Make sure non-alphabetic target is replaced by lowercase
X
X                    IF this_occurrence = lowercase_target THEN
X                        replace_text := eve$do_substitution (
X                                        source_text, lowercase_replacement);
X                    ELSE
X                        IF this_occurrence = uppercase_target THEN
X                            replace_text := eve$do_substitution (
X                                        source_text, uppercase_replacement);
X                        ELSE
X                            IF this_occurrence = capital_target THEN
X                                replace_text := eve$do_substitution (
X                                        source_text, capital_replacement);
X                            ELSE
X                                replace_text := eve$do_substitution (
X                                        source_text, lowercase_replacement);
X                            ENDIF;
X                        ENDIF;
X                    ENDIF;
X                ENDIF;
X                this_occurrence := ERASE_CHARACTER (LENGTH (replace_range));
X                COPY_TEXT (replace_text);
X
X                IF CURRENT_DIRECTION = REVERSE THEN
X                    MOVE_HORIZONTAL (- LENGTH (replace_text));
X                ENDIF;
X
X                occurrences := occurrences + 1;
X                UPDATE (current_window);
X
X                IF (replace_action = SUBSTR ("all", 1, action_length)) AND
X                                                    (action_length > 0) THEN
X                    asking := 0;
X                    MESSAGE ("Replacing all occurrences...");
X                    SET (SCREEN_UPDATE, OFF);
X                ENDIF;
X                EXITIF 1;
X            ELSE
X                IF (replace_action = SUBSTR ("no", 1, action_length)) OR
X                    (replace_action = SUBSTR ("quit", 1, action_length)) THEN
X                    highlight_range := 0;
X                    IF CURRENT_DIRECTION = FORWARD THEN
X                        POSITION (END_OF (replace_range));
X                        MOVE_HORIZONTAL (1);
X                    ENDIF;
X                    UPDATE (current_window);
X                    EXITIF 1;
X                ENDIF;
X            ENDIF;
X        ENDLOOP;
X
X        EXITIF (action_length > 0) AND
X                    ((replace_action = SUBSTR ("quit", 1, action_length)) OR
X                    (replace_action = SUBSTR ("last", 1, action_length)));
X
X    ENDLOOP;
X
X    SET (SCREEN_UPDATE, ON);
X    MESSAGE (FAO ("Replaced !SL occurrence!%S", occurrences));
X    SET (this_mode, this_buffer);
X
XENDPROCEDURE;
X
X!
X!  Re and initialize some things.
X!
Xdefine_key ("eve_find ('',1)", pf1, " find", eve$x_vt100_keys);
Xdefine_key ("eve_find ('',1)", e1, " find", eve$x_standard_keys);
Xeve$arg2_find := eve$arg1_buffer;
Xeve$x_orig_target := eve$kt_null;
Xeve$x_paren_cnt := eve$kt_null;
Xeve$x_find_pat := eve$kt_null;
Xeve$x_glo_str := eve$kt_null;
X
Xeve$pch :=  ASCII(9); ! All printable characters.
X
Xi := 32;
XLOOP
X    EXITIF (i > 255);
X    eve$pch := eve$pch + ASCII (i);
X    i := i + 1;
XENDLOOP;
$ GoSub Convert_File
$ Exit