[comp.sources.misc] VI in TPU part 9/13

gregg@a.cs.okstate.edu@mandrill.CWRU.Edu (Gregg Wonderly) (10/13/87)

$ WRITE SYS$OUTPUT "Creating ""VI.8"""
$ CREATE VI.8
$ DECK/DOLLARS=$$EOD$$
    POSITION (pos);
    DELETE (buf);

    vi$message ("Key now unmapped!");
ENDPROCEDURE;

!
!   Show current keyboard mappings.
!
PROCEDURE vi$show_maps
    LOCAL
        com,
        key_type,
        keyn,
        key,
        bpos,
        npos,
        pos,
        buf;

    pos := MARK (NONE);
    buf := choice_buffer;

    POSITION (buf);
    ERASE (buf);

    key_type := vi$cmd_keys;
    COPY_TEXT ("COMMAND KEY MAPS:");
    SPLIT_LINE;
    LOOP
        keyn := GET_INFO (DEFINED_KEY, "first", key_type);
        LOOP
            EXITIF (keyn = 0);
            com := LOOKUP_KEY (keyn, COMMENT, key_type);

            IF (com = "active_macro") THEN
                key := vi$key_map_name (keyn);
                vi$global_var := 0;
                EXECUTE (COMPILE ("vi$global_var:=vi$$key_map_buf_"+
                                                            key+key_type));
                IF (vi$global_var <> 0) AND
                        (GET_INFO (vi$global_var, "TYPE") = BUFFER) THEN
                    key := vi$ascii_name (keyn);
                    COPY_TEXT (" "+key+SUBSTR ("   ", 1, 4-LENGTH(key))+'"');
                    npos := MARK (NONE);
                    POSITION (BEGINNING_OF (vi$global_var));
                    LOOP
                        keyn := CURRENT_LINE;
                        EXITIF (LENGTH (keyn) < 8);
                        bpos := MARK (NONE);
                        POSITION (npos);
                        COPY_TEXT (vi$ascii_name (INT(keyn)));
                        POSITION (bpos);
                        MOVE_VERTICAL (1);
                    ENDLOOP;
                    POSITION (npos);
                    COPY_TEXT ('"');
                    SPLIT_LINE;
                ENDIF;
            ENDIF;
            keyn := GET_INFO (DEFINED_KEY, "next", key_type);
        ENDLOOP;
        EXITIF (key_type = vi$edit_keys);
        key_type := vi$edit_keys;
        SPLIT_LINE;
        COPY_TEXT ("EDITING KEY MAPS:");
        SPLIT_LINE;
    ENDLOOP;

    APPEND_LINE;
    POSITION (BEGINNING_OF (buf));
    POSITION (pos);
    vi$show_list (buf,
        "                                 Current MAPPINGS" +
        "                           ",
        info_window);
    RETURN (0);

ENDPROCEDURE;

!
!   Generate a unique string based on a KEY_NAME value.
!
PROCEDURE vi$key_map_name (key)
    RETURN (SUBSTR(FAO("!XL", key),1,6));
ENDPROCEDURE;

!
!   Increment "i" until it is no longer indexing a blank or tab in "cmd".
!
PROCEDURE vi$skip_white (cmd, i)

    LOOP
        EXITIF i > LENGTH (cmd);
        EXITIF (INDEX ("    ", SUBSTR(cmd, i, 1)) = 0);
        i := i + 1;
    ENDLOOP;
ENDPROCEDURE;

!
!   Given a string, extract a line specification that is either absolute,
!   relative, or an RE pattern expression.
!
PROCEDURE vi$get_line_spec (idx, cmd)
    LOCAL
        ch,
        sch,
        num;

    num := -1;

    ch := SUBSTR (cmd, idx, 1);

    IF (ch = "/") OR (ch = "?") THEN
        idx := idx + 1;
        sch := ch;
        num := "";
        LOOP
            EXITIF (vi$parse_next_ch (idx, cmd, sch));
            EXITIF (LENGTH (cmd) < idx);
            ch := SUBSTR (cmd, idx, 1);
            IF (ch = "\") THEN
                num := num + SUBSTR (cmd, idx, 2);
                idx := idx + 1;
            ELSE
                num := num + ch;
            ENDIF;
            idx := idx + 1;
        ENDLOOP;

        IF (LENGTH (cmd) < idx - 1) THEN
            MESSAGE ("Oops, improper expression!");
            RETURN (-1);
        ENDIF;

        ch := SUBSTR (cmd, idx, 1);

        IF sch = "?" THEN
            SET (REVERSE, CURRENT_BUFFER);
        ELSE
            SET (FORWARD, CURRENT_BUFFER);
        ENDIF;

        num := vi$find_str (num, 0);

        IF (num <> 0) THEN
            num := BEGINNING_OF (num);
            POSITION (num);
            num := vi$cur_line_no;
        ELSE
            num := -1;
        ENDIF;
    ELSE
        LOOP
            ch := SUBSTR (cmd, idx, 1);
            EXITIF (INDEX (vi$_numeric_chars, ch) = 0);
            IF (num < 0) THEN
                num := INT (ch);
            ELSE
                num := num * 10 + INT (ch);
            ENDIF;
            idx := idx + 1;
        ENDLOOP;
    ENDIF;

    IF (ch = ".") THEN
        num := vi$cur_line_no;
        idx := idx + 1;
        IF (vi$parse_next_ch (idx, cmd, "+")) THEN
            num := num + vi$get_line_spec (idx, cmd);
        ENDIF;
    ELSE
        IF (ch = "$") THEN
            num := GET_INFO (CURRENT_BUFFER, "RECORD_COUNT");
            idx := idx + 1;
        ELSE
            IF (ch = "+") THEN
                num := num + vi$get_line_spec (idx, cmd);
            ENDIF;
        ENDIF;
    ENDIF;

    RETURN (num);
ENDPROCEDURE;

!
!   If the character at location "idx" in "cmd" is "try", then increment
!   "idx" and return TRUE, otherwise return FALSE.
!
PROCEDURE vi$parse_next_ch (idx, cmd, try)
    IF (SUBSTR (cmd, idx, 1) = try) THEN
        idx := idx + 1;
        RETURN (1);
    ENDIF;

    RETURN (0);
ENDPROCEDURE;

!
!   A function to get the string, in "cmd", that is spanned by the characters
!   in "mask".  "idx" is incremented to point past this string, and the string
!   is returned as the function value.
!
PROCEDURE vi$get_cmd_token (mask, cmd, idx)
    LOCAL
        token,
        ch;

    token := "";

    vi$skip_white (cmd, idx);

    LOOP
        EXITIF (idx > LENGTH (cmd));
        ch := SUBSTR (cmd, idx, 1);
        EXITIF (INDEX (mask, ch) = 0);
        token := token + ch;
        idx := idx + 1;
    ENDLOOP;

    RETURN (token);
ENDPROCEDURE;

!
!   A function to see if the string "token" is a lead substring of "cmd".
!
PROCEDURE vi$leading_str (token, cmd)
    RETURN ((token <> "") AND (INDEX (cmd, token) = 1));
ENDPROCEDURE;

!
!   A routine that looks for the first occurance of a character in
!   "seps", in "cmd", and then changes "idx" to reflect that locatation.
!   "separ" will contain the character in "seps" that was actually found.
!
PROCEDURE vi$skip_separ (cmd, idx, seps, separ)
    LOCAL
        nch,
        retstr;

    retstr := "";
    separ := "";
    vi$skip_white (cmd, idx);

    LOOP
        EXITIF (idx > LENGTH (cmd));
        nch := SUBSTR (cmd, idx, 1);
        idx := idx + 1;
        IF (INDEX (seps, nch) <> 0) OR (nch = " ") OR (nch = "  ") THEN
            separ := nch;
            RETURN (retstr);
        ENDIF;
        retstr := retstr + nch;
    ENDLOOP;
    RETURN (retstr);
ENDPROCEDURE;

!
!   A procedure that returns the characters occuring at index, "idx", and
!   after in the string "cmd".
!
PROCEDURE vi$rest_of_line (cmd, idx)
    RETURN (SUBSTR (cmd, idx, LENGTH (cmd)-idx + 1));
ENDPROCEDURE;

!
!  SET (INFORMATIONAL/SUCCESS) short procedures.
!
PROCEDURE vi$info_success_off vi$info_off; vi$success_off; ENDPROCEDURE;
PROCEDURE vi$info_success_on vi$info_on; vi$success_on; ENDPROCEDURE;
PROCEDURE vi$success_off SET (SUCCESS, OFF); ENDPROCEDURE;
PROCEDURE vi$success_on SET (SUCCESS, ON); ENDPROCEDURE;
PROCEDURE vi$info_off SET (INFORMATIONAL, OFF); ENDPROCEDURE;
PROCEDURE vi$info_on SET (INFORMATIONAL, ON); ENDPROCEDURE;

!
!   Called from vi$do_global to perform a substitution during a global command.
!
PROCEDURE vi$global_subs (cmd, nsubs)

    LOCAL
        idx,
        result_text,
        replace_text,
        hrange,
        ch,
        pos,
        spos,
        epos,
        lpos,
        source,
        scount,
        dest,
        query,
        global,
        replace,
        separ;

    idx := 1;

    separ := vi$next_char (cmd, idx);

    source := "";
    dest   := "";
    global := 0;
    query  := 0;

    LOOP
        IF (idx > LENGTH (cmd)) THEN
            vi$message ("Insufficent arguments!");
            RETURN (0);
        ENDIF;

        ch := SUBSTR (cmd, idx, 1);
        EXITIF ch = separ;
        source := source + ch;
        idx := idx + 1;
    ENDLOOP;

    idx := idx + 1;
    LOOP
        EXITIF idx > LENGTH (cmd);
        ch := SUBSTR (cmd, idx, 1);
        EXITIF ch = separ;
        dest := dest + ch;
        idx := idx + 1;
    ENDLOOP;

    idx := idx + 1;
    LOOP
        EXITIF idx > LENGTH (cmd);
        ch := SUBSTR (cmd, idx, 1);
        IF ch = "q" THEN
            query := 1;
        ELSE
            IF ch = "g" THEN
                global := 1;
            ELSE
                vi$message ("Unrecognized command qualifier '"+ch+"'");
                RETURN (0);
            ENDIF;
        ENDIF;
        idx := idx + 1;
    ENDLOOP;

    vi$replace_source := source;
    vi$replace_dest := dest;

    lpos := vi$perform_subs (source, dest, vi$cur_line_no,
                                                scount, global, query);
    nsubs := nsubs + scount;

    RETURN (lpos);
ENDPROCEDURE;
!
!   Called from vi$do_command to parse the rest of the command line,
!   this procedure then envokes lower level routines to perform the work
!   of a substitution command.
!
PROCEDURE vi$do_substitute (start_line, end_line, whole_range, idx, cmd)

    LOCAL
        result_text,
        replace_text,
        hrange,
        ch,
        pos,
        spos,
        epos,
        lpos,
        source,
        scount,
        dest,
        query,
        global,
        replace,
        separ;

    pos := MARK (NONE);
    POSITION (END_OF (whole_range));
    epos := MARK (NONE);
    POSITION (pos);

    separ := vi$next_char (cmd, idx);
    vi$replace_separ := separ;

    source := "";
    dest   := "";
    global := 0;
    query  := 0;

    MESSAGE ("");
    LOOP
        IF (idx > LENGTH (cmd)) THEN
            vi$message ("Insufficent arguments!");
            RETURN (1);
        ENDIF;

        ch := SUBSTR (cmd, idx, 1);
        EXITIF ch = separ;
        source := source + ch;
        idx := idx + 1;
    ENDLOOP;

    idx := idx + 1;
    LOOP
        EXITIF idx > LENGTH (cmd);
        ch := SUBSTR (cmd, idx, 1);
        EXITIF ch = separ;
        dest := dest + ch;
        idx := idx + 1;
    ENDLOOP;

    idx := idx + 1;
    LOOP
        EXITIF idx > LENGTH (cmd);
        ch := SUBSTR (cmd, idx, 1);
        IF ch = "q" THEN
            query := 1;
        ELSE
            IF ch = "g" THEN
                global := 1;
            ELSE
                vi$message ("Unrecognized command qualifier '"+ch+"'");
                RETURN (1);
            ENDIF;
        ENDIF;
        idx := idx + 1;
    ENDLOOP;

    POSITION (pos);
    vi$save_for_undo (whole_range, VI$LINE_MODE, 1);
    vi$move_to_line (start_line);

    IF MARK (NONE) <> BEGINNING_OF (CURRENT_BUFFER) THEN
        MOVE_HORIZONTAL (-1);
        spos := MARK (NONE);
        MOVE_HORIZONTAL (1);
    ELSE
        spos := 0;
    ENDIF;

    vi$replace_source := source;
    vi$replace_dest := dest;

    lpos := vi$perform_subs (source, dest, end_line, scount, global, query);

    IF (scount = 0) THEN
        vi$kill_undo;
        vi$undo_end := 0;
        POSITION (pos);
    ELSE
        vi$undo_end := epos;
        IF (spos = 0) THEN
            vi$undo_start := BEGINNING_OF (CURRENT_BUFFER);
        ELSE
            POSITION (spos);
            MOVE_HORIZONTAL (1);
            vi$undo_start := MARK (NONE);
        ENDIF;
        vi$pos_in_middle (lpos);
        MESSAGE (FAO ("!UL substitution!%S!", scount));
    ENDIF;

    RETURN (1);
ENDPROCEDURE;

!
!   Repeat the last substitute command that was issued at the ":" prompt.
!
!   The function mapped to '&'.
!
PROCEDURE vi$repeat_subs
    LOCAL
        scount,
        global,
        query,
        lpos,
        spos,
        pos,
        epos,
        here;

    IF (vi$replace_separ = 0) THEN
        vi$message ("No previous substitution!");
        RETURN;
    ENDIF;

    global := 0;
    query := 0;
    here := vi$cur_line_no;
    vi$save_for_undo (CURRENT_LINE, VI$LINE_MODE, 1);

    pos := MARK (NONE);
    MOVE_HORIZONTAL (-CURRENT_OFFSET);

    spos := vi$get_undo_start;

    MOVE_HORIZONTAL (LENGTH (CURRENT_LINE));
    IF (LENGTH (CURRENT_LINE) > 0) THEN
        MOVE_HORIZONTAL (-1);
    ENDIF;
    epos := MARK (NONE);
    POSITION (pos);

    lpos := vi$perform_subs (vi$replace_source, vi$replace_dest,
                                                here, scount, global, query);

    IF (scount = 0) THEN
        vi$kill_undo;
        vi$undo_end := 0;
    ELSE
        vi$undo_end := epos;
        vi$undo_start := vi$set_undo_start (spos);
        POSITION (lpos);
    ENDIF;

ENDPROCEDURE;

!
!   Perform a substitution from the current location to "end_line".
!   Use source as the search string, and dest as the substitution
!   spec.  "global" indicates whether or not all occurances on a line
!   are examined, and "query" indicates whether or not to prompt before
!   performing the substitution.  On return, "scount" will hold the
!   number of substitutions actually performed.
!
PROCEDURE vi$perform_subs (source, dest, end_line, scount, global, query)

    LOCAL
        result_text,
        replace_text,
        answer,
        fcnt,
        lpos,
        hrange,
        replace,
        fpos,
        quit_now,
        cwin,
        pos;

    SET (FORWARD, CURRENT_BUFFER);
    scount := 0;
    fcnt := 0;
    quit_now := 0;
    pos := MARK (NONE);

    LOOP
        fpos := vi$find_str (source, 1);
        EXITIF (fpos = 0);
        fcnt := fcnt + 1;
        POSITION (BEGINNING_OF (fpos));

        IF vi$cur_line_no > end_line THEN
            POSITION (pos);
            EXITIF (1);
        ENDIF;
        result_text := SUBSTR (fpos, 1, LENGTH (fpos));
        replace_text := vi$substitution (result_text, dest);
        POSITION (BEGINNING_OF (fpos));

        replace := 1;
        IF (query) THEN
            POSITION (BEGINNING_OF (fpos));
            hrange := CREATE_RANGE (BEGINNING_OF (fpos),
                                                    END_OF (fpos), REVERSE);
            cwin := GET_INFO (WINDOWS, "FIRST");
            LOOP
                EXITIF (cwin = 0);
                IF (GET_INFO (cwin, "VISIBLE")) THEN
                    UPDATE (cwin);
                ENDIF;
                cwin := GET_INFO (WINDOWS, "NEXT");
            ENDLOOP;

            answer := vi$read_line ("Replace y/n/a/q? ");

            CHANGE_CASE (answer, LOWER);
            IF (answer = "") OR (INDEX ("yes", answer) <> 1) THEN
                replace := 0;
            ENDIF;
            IF (INDEX ("quit", answer) = 1) THEN
                quit_now := 1;
            ENDIF;
            IF (INDEX ("all", answer) = 1) THEN
                query := 0;
            ENDIF;
        ENDIF;

        IF replace THEN

!           This is a hack necessary to fix TPU's pattern matching.
!           The length of the text match by only "line_begin" has
!           length == 1 instead of 0 as one would expect.

            IF (source <> "^") THEN
                ERASE_CHARACTER (LENGTH (result_text));
            ENDIF;
            COPY_TEXT (replace_text);
            pos := MARK (NONE);
            scount := scount + 1;
        ELSE
            MOVE_HORIZONTAL (1);
        ENDIF;

        IF NOT global THEN
            MOVE_HORIZONTAL (-CURRENT_OFFSET);
            EXITIF MARK (NONE) = END_OF (CURRENT_BUFFER);
            MOVE_VERTICAL (1);
        ENDIF;
        EXITIF quit_now;
    ENDLOOP;

    IF fcnt = 0 THEN
        MESSAGE ("string not found!");
    ENDIF;

    RETURN (pos);
ENDPROCEDURE;

!
!   Move horizontal, ignoring errors
!
PROCEDURE vi$move_horizontal (cnt)
    ON_ERROR
    ENDON_ERROR;

    MOVE_HORIZONTAL (cnt);
ENDPROCEDURE;

!
!   Move vertical, ignoring errors
!
PROCEDURE vi$move_vertical (cnt)
    ON_ERROR
    ENDON_ERROR;

    MOVE_VERTICAL (cnt);
ENDPROCEDURE;

!
!   Move to the indicated line number.
!
PROCEDURE vi$move_to_line (line_no)
    LOCAL
        pos;

    ON_ERROR
        POSITION (pos);
        RETURN (0);
    ENDON_ERROR;

    pos := MARK (NONE);
    POSITION (BEGINNING_OF (CURRENT_BUFFER));
    MOVE_VERTICAL (line_no - 1);

    RETURN (MARK (NONE));
ENDPROCEDURE;

!
!   Give a source string, and a "dest" substitution spec, perform the
!   RE style substitution, and return the resultant string.
!
PROCEDURE vi$substitution (source, dest)

    LOCAL
        cur_char,
        result,
        idx;

    idx := 0;
    result := "";

    LOOP
        EXITIF (idx > LENGTH(dest));

        cur_char := SUBSTR (dest, idx, 1);
        IF (cur_char = "&") THEN
            result := result + source;
            idx := idx + 1;
        ELSE
            IF (cur_char = '\') THEN
                cur_char := SUBSTR(dest, idx+1, 1);
                IF (INDEX ("123456789", cur_char) > 0) THEN
                    IF INT(cur_char) > 1 THEN
                        EXECUTE (COMPILE ("vi$glo_str := SUBSTR (p" +
                            cur_char +", LENGTH (o"+cur_char+")+1,512);"));
                    ELSE
                        EXECUTE (COMPILE ("vi$glo_str := SUBSTR (p" +
                            cur_char +", LENGTH (o"+cur_char+"),512);"));
                    ENDIF;
                    result := result + vi$glo_str;
                ELSE
                    result := result + "\" + cur_char;
                ENDIF;
                idx := idx + 2;
            ELSE
                result := result + cur_char;
                idx := idx + 1;
            ENDIF;
        ENDIF;
    ENDLOOP;

    RETURN (result);
ENDPROCEDURE;

!
!   Get the next character from a string at idx, and point past the character
!
PROCEDURE vi$next_char (cmd, idx)

    IF idx <= LENGTH (cmd) THEN
        idx := idx + 1;
        RETURN (SUBSTR (cmd, idx -1, 1));
    ENDIF;

    RETURN ("");
ENDPROCEDURE;

!
!  Process all set commands in the string cmd
!
PROCEDURE vi$set_commands (cmd, i)
    LOCAL
        err,
        separ,
        token_1;

    ON_ERROR
        RETURN;
    ENDON_ERROR;

    LOOP
        token_1 := vi$skip_separ (cmd, i, "=    ", separ);
        EDIT (token_1, COLLAPSE);

        EXITIF token_1 = "";

        err :=  vi$set_one (token_1, separ, cmd, i);
        EXITIF err;
    ENDLOOP;
    RETURN (err);
ENDPROCEDURE

!
!  Process a single set command and return success or failure.
!
PROCEDURE vi$set_one (token_1, separ, cmd, i)

    LOCAL
        val,
        errno,
        curwin,
        curbuf,
        buf,
        use_fortran,
        oldscrlen,
        npat,
        pstr,
        token_2;

    ON_ERROR
        errno := ERROR;
        MESSAGE ("ERROR at line: "+STR(ERROR_LINE)+", "+
                                call_user(vi$cu_getmsg,STR(errno)));
        RETURN (1);
    ENDON_ERROR;

    token_2 := "";

    IF (token_1 = "all") THEN
        vi$show_settings;
        RETURN (0);
    ENDIF;

    IF (token_1 = "tags") THEN
        vi$tag_files := vi$rest_of_line (cmd, i);
        i := LENGTH (cmd) + 1;
        RETURN (vi$load_tags);
    ENDIF;

    IF (token_1 = "notagcase") OR (token_1 = "notc") THEN
        vi$tag_case := NO_EXACT;
        RETURN (0);
    ENDIF;

    IF (token_1 = "tagcase") OR (token_1 = "tc") THEN
        vi$tag_case := EXACT;
        RETURN (0);
    ENDIF;

    IF (token_1 = "senddcl") THEN
        vi$send_dcl := 1;
        RETURN (0);
    ENDIF;

    IF (token_1 = "nosenddcl") THEN
        vi$send_dcl := 0;
        RETURN (0);
    ENDIF;

    IF (token_1 = "files") OR (token_1 = "file") THEN
        val := vi$expand_file_list (vi$rest_of_line (cmd, i));
        MESSAGE (FAO ("!UL file!%S selected", val, 0));
        RETURN (2);
    ENDIF;

    IF (token_1 = "notabs") THEN
        vi$use_tabs := 0;
        RETURN (0);
    ENDIF;

    IF (token_1 = "tabs") THEN
        vi$use_tabs := 1;
        RETURN (0);
    ENDIF;

    IF (token_1 = "write") OR (token_1 = "wr") THEN
        SET (NO_WRITE, CURRENT_BUFFER, OFF);
        vi$status_lines (CURRENT_BUFFER);
        RETURN (0);
    ENDIF;

    IF (token_1 = "nowrite") OR (token_1 = "nowr") THEN
        SET (NO_WRITE, CURRENT_BUFFER, ON);
        vi$status_lines (CURRENT_BUFFER);
        RETURN (0);
    ENDIF;

    IF (token_1 = "width") THEN
        token_2 := vi$skip_separ (cmd, i, "=    ", separ);
        val := INT (token_2);
        SET (WIDTH, CURRENT_WINDOW, val);
        vi$scr_width := val;
        RETURN (0);
    ENDIF;

    IF (token_1 = "window") THEN
        token_2 := vi$skip_separ (cmd, i, "=    ", separ);
        val := INT (token_2);
        RETURN (vi$do_set_window (val));
    ENDIF;

    IF (token_1 = "ts") OR (token_1 = "tabstops") THEN
        token_2 := vi$skip_separ (cmd, i, "=   ", separ);
        val := INT (token_2);
        SET (TAB_STOPS, CURRENT_BUFFER, val);
        vi$tab_amount := val;
        RETURN (0);
    ENDIF;

    IF (token_1 = "sw") OR (token_1 = "shiftwidth") then
        token_2 := vi$skip_separ (cmd, i, "=    ", separ);
        vi$shift_width := INT (token_2);
        RETURN (0);
    ENDIF;

    IF (token_1 = "noundomap") OR (token_1 = "noum") THEN
        vi$undo_map := 0;
        RETURN (0);
    ENDIF;

    IF (token_1 = "undomap") OR (token_1 = "um") THEN
        vi$undo_map := 1;
        RETURN (0);
    ENDIF;

    IF (token_1 = "scroll") THEN
        token_2 := vi$skip_separ (cmd, i, "=    ", separ);
        vi$how_much_scroll := INT (token_2);
        RETURN (0);
    ENDIF;

    IF (token_1 = "report") THEN
        token_2 := vi$skip_separ (cmd, i, "=    ", separ);
        vi$report := INT (token_2);
        RETURN (0);
    ENDIF;

    IF (token_1 = "aw") OR (token_1 = "autowrite") THEN
        vi$auto_write := 1;
        RETURN (0);
    ENDIF;

    IF (token_1 = "noaw") OR (token_1 = "noautowrite") THEN
        vi$auto_write := 0;
        RETURN (0);
    ENDIF;

    IF (token_1 = "noic") OR (token_1 = "noignorecase") THEN
        vi$ignore_case := EXACT;
        RETURN (0);
    ENDIF;

    IF (token_1 = "ic") OR (token_1 = "ignorecase") THEN
        vi$ignore_case := NO_EXACT;
        RETURN (0);
    ENDIF;

    IF (token_1 = "magic") THEN
        vi$magic := 1;
        RETURN (0);
    ENDIF;

    IF (token_1 = "nomagic") THEN
        vi$magic := 0;
        RETURN (0);
    ENDIF;

    IF (token_1 = "noerrorbells") OR (token_1 = "noeb") THEN
        vi$error_bells := 0;
        RETURN (0);
    ENDIF;

    IF (token_1 = "errorbells") OR (token_1 = "eb") THEN
        vi$error_bells := 1;
        RETURN (0);
    ENDIF;

    IF (token_1 = "nowrapscan") OR (token_1 = "nows") THEN
        vi$wrap_scan := 0;
        RETURN (0);
    ENDIF;

    IF (token_1 = "wrapscan") OR (token_1 = "ws") THEN
        vi$wrap_scan := 1;
        RETURN (0);
    ENDIF;

    IF (token_1 = "noupdate") THEN
        vi$min_update := 1;
        RETURN (0);
    ENDIF;

    IF (token_1 = "update") THEN
        vi$min_update := 0;
        RETURN (0);
    ENDIF;

    IF (token_1 = "noshowmode") OR (token_1 = "nosm") THEN
        vi$show_mode := 0;
        RETURN (0);
    ENDIF;

    IF (token_1 = "showmode") OR (token_1 = "sm") THEN
        vi$show_mode := 1;
        RETURN (0);
    ENDIF;

    IF (token_1 = "wrapmargin") OR (token_1 = "wm") THEN
        token_2 := vi$skip_separ (cmd, i, "=    ", separ);
        vi$wrap_margin := INT (token_2);
        RETURN (0);
    ENDIF;

    vi$para_str := "P p ";
    vi$para_pat := line_begin & (
                                    (".P" | ".p") |
                                    (LINE_END));
    IF (token_1 = "sections") OR (token_1 = "sect") THEN
        pstr := "LINE_BEGIN&(";
        use_fortran := 0;
        vi$sect_str := "";
        LOOP
            EXITIF (SUBSTR (cmd, i, 1) = " ") OR (i >= LENGTH (cmd));
            npat := SUBSTR (cmd, i, 2);
            vi$sect_str := vi$sect_str + npat;
            EDIT (npat, COLLAPSE);
            IF (npat = "+c") OR (npat = "+C") THEN
                pstr := pstr + '"{"';
            ELSE
                IF (npat = "+f") OR (npat = "+F") THEN
                    use_fortran := 1;
                    npat := "";
                ELSE
                    IF (npat = "+t") OR (npat = "+T") THEN
                        pstr := pstr + '"PROCEDURE"';
                    ELSE
                        pstr := pstr + '".' + npat + '"';
                    ENDIF;
                ENDIF;
            ENDIF;
            i := i + 2;
            EXITIF (SUBSTR (cmd, i, 1) = " ") OR (i >= LENGTH (cmd));
            IF (npat <> "") THEN
                pstr := pstr + "|";
            ENDIF;
        ENDLOOP;
        pstr := pstr + ")";
        IF (use_fortran) THEN
            pstr := '""&(("FUNCTION"|"SUBROUTINE")|('+ pstr + "))";
        ELSE
            pstr := '""&'+pstr;
        ENDIF;
        EXECUTE (COMPILE ("vi$sect_pat:="+pstr+";"));
        RETURN (0);
    ENDIF;

    IF (token_1 = "paragraphs") OR (token_1 = "para") THEN
        pstr := '""&LINE_BEGIN&(';
        vi$para_str := "";
        LOOP
            EXITIF (SUBSTR (cmd, i, 1) = " ") OR (i >= LENGTH (cmd));
            npat := SUBSTR (cmd, i, 2);
            vi$para_str := vi$para_str + npat;
            EDIT (npat, COLLAPSE);
            pstr := pstr + '".' + npat + '"';
            i := i + 2;
            EXITIF (SUBSTR (cmd, i, 1) = " ") OR (i >= LENGTH (cmd));
            IF (npat <> "") THEN
                pstr := pstr + "|";
            ENDIF;
        ENDLOOP;
        pstr := pstr + ")";
        EXECUTE (COMPILE ("vi$para_pat:="+pstr+";"));
        RETURN (0);
    ENDIF;

    IF (token_1 = "number") OR
            (token_1 = "optimize") OR
            (token_1 = "autoindent") OR
            (token_1 = "noautoprint") OR
            (token_1 = "novice") OR
            (token_1 = "slowopen") OR
            (token_1 = "beautify") OR
            (token_1 = "taglength") OR
            (token_1 = "directory") OR
            (token_1 = "noprompt") OR
            (token_1 = "edcompatible") OR
            (token_1 = "term") OR
            (token_1 = "noredraw") OR
            (token_1 = "terse") OR
            (token_1 = "flash") OR
            (token_1 = "noremap") OR
            (token_1 = "timeout") OR
            (token_1 = "hardtabs") OR
            (token_1 = "ttytype") OR
            (token_1 = "warn") OR
            (token_1 = "nowarn") OR
            (token_1 = "lisp") OR
            (token_1 = "list") OR
            (token_1 = "shell") OR
            (token_1 = "mesg") OR
            (token_1 = "nomesg") OR
            (token_1 = "showmatch") THEN
        vi$not_implemented (token_1);
        RETURN (1);
    ENDIF;

    vi$message ("Unrecognized option, use `set all' to see options.");
    RETURN (1);

ENDPROCEDURE;

!
!   Set the window length to the integer value passed.
!
PROCEDURE vi$do_set_window (len)
    LOCAL
        buf,
        curwin,
        curbuf;

    curwin := CURRENT_WINDOW;
    curbuf := CURRENT_BUFFER;

    IF (vi$prev_win (curwin) = 0) AND (vi$next_win (curwin) = 0)
                                            AND (NOT vi$in_occlusion) THEN
        IF len < 3 THEN
            len := 3;
        ENDIF;

        IF len > GET_INFO (SCREEN, "VISIBLE_LENGTH") THEN
            len := GET_INFO (SCREEN, "VISIBLE_LENGTH");
        ENDIF;

        oldscrlen := vi$scr_length;
        vi$scr_length := len;

        ADJUST_WINDOW (curwin, 0, vi$scr_length - oldscrlen);

        buf := GET_INFO (message_window, "BUFFER");
        UNMAP (message_window);
        DELETE (message_window);
        message_window := CREATE_WINDOW (vi$scr_length - 1, 2, ON);
        MAP (message_window, buf);
        SET (STATUS_LINE, message_window, NONE, "");
        ADJUST_WINDOW (message_window, 1, 0);

        DELETE (command_window);
        command_window := CREATE_WINDOW (vi$scr_length, 1, OFF);

        buf := GET_INFO (info_window, "BUFFER");
        DELETE (info_window);
        info_window := CREATE_WINDOW (1, vi$scr_length - 1, ON);
        SET (STATUS_LINE, info_window, NONE, "");

        SET (PROMPT_AREA, vi$scr_length, 1, REVERSE);

        POSITION (curbuf);
        POSITION (curwin);
        UNMAP (curwin);
        MAP (curwin, curbuf);
    ELSE
        MESSAGE (
        "Can't change length of screen while multiple windows visible!");
        RETURN (1);
    ENDIF;

    vi$how_much_scroll := vi$scr_length / 2;
    RETURN (0);
ENDPROCEDURE;

!
!   Show the current settings when ":set all" is issued.
!
PROCEDURE vi$show_settings
    LOCAL
        obuf,
        ic,
        ostat,
        ovid,
        buf;

    buf := vi$init_buffer ("$$vi_set_all$$", "");

    ostat := GET_INFO (CURRENT_WINDOW, "STATUS_LINE");
    IF (ostat = 0) THEN
        ostat := "";
    ENDIF;
    ovid := GET_INFO (CURRENT_WINDOW, "STATUS_VIDEO");
    IF (ovid = 0) THEN
        ovid := NONE;
    ENDIF;
    SET (STATUS_LINE, CURRENT_WINDOW, NONE, "");
    SET (STATUS_LINE, CURRENT_WINDOW, REVERSE,
        "                      Current settings of VI options");
    SET (EOB_TEXT, buf,
                "                      [Hit ENTER to continue editing]");
    obuf := CURRENT_BUFFER;
    POSITION (buf);

    IF vi$ignore_case = EXACT THEN
        ic := 2;
    ELSE
        ic := 0;
    ENDIF;

    COPY_TEXT (FAO (
    "!20<wrapmargin=!UL!>!20<tabstop=!UL!>!20<!ASmagic!>!20<!ASignorecase!>",
            vi$wrap_margin, vi$tab_amount,
            SUBSTR ("no", 1, (1-vi$magic)*2),
            SUBSTR ("no", 1, ic)));

    SPLIT_LINE;

    COPY_TEXT (FAO (
    "!20<shiftwidth=!UL!>!20<scroll=!UL!>!20<report=!UL!>!20<!ASautowrite!>",
            vi$shift_width, vi$how_much_scroll, vi$report,
            SUBSTR ("no", 1, (1-vi$auto_write)*2)));

    SPLIT_LINE;

    COPY_TEXT (FAO (
            "!20<!ASwrapscan!>!20<!ASupdate!>!20<!AStabs!>!20<!ASundomap!>",
            SUBSTR ("no", 1, (1-vi$wrap_scan)*2),
            SUBSTR ("no", 1, (vi$min_update)*2),
            SUBSTR ("no", 1, (1-vi$use_tabs)*2),
            SUBSTR ("no", 1, (1-vi$undo_map)*2)
        ));

    SPLIT_LINE;

    IF vi$tag_case = EXACT THEN
        ic := 0;
    ELSE
        ic := 2;
    ENDIF;

    COPY_TEXT (FAO (
            "!20<!AStagcase!>!20<window=!UL!>!20<width=!UL!>tags=!AS",
            SUBSTR ("no", 1, ic),
            GET_INFO (CURRENT_WINDOW, "VISIBLE_LENGTH"),
            GET_INFO (CURRENT_WINDOW, "WIDTH"),
            vi$tag_files
        ));

    SPLIT_LINE;

    COPY_TEXT (FAO (
            "!20<!ASerrorbells!>!20<paragraphs=!AS!>!20<sections=!AS!>"+
                "!20<!ASsenddcl!>",
            SUBSTR ("no", 1, (1-vi$error_bells)*2),
            vi$para_str,
            vi$sect_str,
            SUBSTR ("no", 1, (1-vi$send_dcl)*2)
        ));

    SPLIT_LINE;

    COPY_TEXT (FAO (
            "!20<!ASshowmode!>",
            SUBSTR ("no", 1, (1-vi$show_mode)*2)
        ));

    SPLIT_LINE;

    MAP (CURRENT_WINDOW, buf);
    UPDATE (CURRENT_WINDOW);
    LOOP
        EXITIF vi$read_a_key = RET_KEY;
    ENDLOOP;

    SET (STATUS_LINE, CURRENT_WINDOW, NONE, "");
    SET (STATUS_LINE, CURRENT_WINDOW, ovid, ostat);
    MAP (CURRENT_WINDOW, obuf);
    POSITION (obuf);
    DELETE (buf);
ENDPROCEDURE;

!
!   Function to say that a particular command is not implemented.
!
PROCEDURE vi$not_implemented (cmd)
    vi$message (cmd + " is not implemented!");
ENDPROCEDURE;

!
!   The function mapped to 't'.
!
PROCEDURE vi$_to_char (char_to_find)
    LOCAL
        char_val;

    char_val := char_to_find;
    vi$position (vi$to_char (char_val), 0);
ENDPROCEDURE;

!
!   Function performing task for 't'.
!
PROCEDURE vi$to_char (char_to_find)

    LOCAL
        act_count,
        pos,
        found;

    IF char_to_find = 0 THEN
        char_to_find := vi$read_char_to_find;
    ENDIF;

    vi$last_s_char := char_to_find;
    vi$last_s_func := "vi$to_char";

    pos := MARK(NONE);

    act_count := vi$cur_active_count;

    MOVE_HORIZONTAL (1);

    IF char_to_find <> ASCII(27) THEN
        found := 0;
        LOOP
            EXITIF (CURRENT_OFFSET >= LENGTH (vi$current_line));
            MOVE_HORIZONTAL (1);
            found := 1;
            IF (CURRENT_CHARACTER = char_to_find) THEN
                act_count := act_count - 1;
                EXITIF (act_count = 0);
            ENDIF;
            found := 0;
        ENDLOOP;

        IF (NOT found) THEN
            POSITION (pos);
            RETURN (0);
        ELSE
            vi$move_horizontal (-1);
        ENDIF;
    ENDIF;

    vi$yank_mode := VI$IN_LINE_MODE;
    RETURN (vi$retpos (pos));

ENDPROCEDURE;

!
!   The function mapped to 'T'.
!
PROCEDURE vi$_back_to_char (char_to_find)

    LOCAL
        char_val;

    char_val := char_to_find;
    vi$position (vi$back_to_char (char_val), 0);
ENDPROCEDURE;

!
!   Function performing task for 'T'.
!
PROCEDURE vi$back_to_char (char_to_find)

    LOCAL
        act_count,
        pos,
        found;

    IF char_to_find = 0 THEN
        char_to_find := vi$read_char_to_find;
    ENDIF;

    vi$last_s_char := char_to_find;
    vi$last_s_func := "vi$back_to_char";

    pos := MARK(NONE);

    IF (CURRENT_OFFSET = 0) THEN
        RETURN (0);
    ENDIF;

    vi$move_horizontal (-1);
    IF (CURRENT_CHARACTER <> char_to_find) THEN
        vi$move_horizontal (1);
    ENDIF;

    act_count := vi$cur_active_count;

    IF char_to_find <> ASCII(27) THEN
        found := 0;
        LOOP
            EXITIF (CURRENT_OFFSET = 0);
            vi$move_horizontal (-1);
            found := 1;
            IF (CURRENT_CHARACTER = char_to_find) THEN
                act_count := act_count - 1;
                EXITIF (act_count = 0);
            ENDIF;
            found := 0;
        ENDLOOP;

        IF (NOT found) THEN
            POSITION (pos);
            RETURN (0);
        ELSE
            MOVE_HORIZONTAL(1);
        ENDIF;
    ENDIF;

    vi$yank_mode := VI$IN_LINE_MODE;
    RETURN (vi$retpos (pos));
ENDPROCEDURE;

!
!   The function mapped to 'f'.
!
PROCEDURE vi$_find_char (char_to_find)

    LOCAL
        char_val;

    char_val := char_to_find;
    vi$position (vi$find_char (char_val), 0);
ENDPROCEDURE;

!
!   Function performing task for 'f'.
!
PROCEDURE vi$find_char (char_to_find)

    LOCAL
        act_count,
        pos,
        found;

    IF char_to_find = 0 THEN
        char_to_find := vi$read_char_to_find;
    ENDIF;

    vi$last_s_char := char_to_find;
    vi$last_s_func := "vi$find_char";

    act_count := vi$cur_active_count;

    IF char_to_find <> ASCII(27) THEN
        pos := MARK(NONE);
        found := 0;
        LOOP
            EXITIF (CURRENT_OFFSET >= LENGTH (vi$current_line));
            MOVE_HORIZONTAL (1);
            found := 1;
            IF (CURRENT_CHARACTER = char_to_find) THEN
                act_count := act_count - 1;
                EXITIF (act_count = 0);
            ENDIF;
            found := 0;
        ENDLOOP;

        IF (NOT found) THEN
            POSITION (pos);
            RETURN (0);
        ENDIF;
    ELSE
        RETURN (0);
    ENDIF;

    vi$yank_mode := VI$IN_LINE_MODE;
    RETURN (vi$retpos(pos));
ENDPROCEDURE;

!
!   The function mapped to 'F'.
!
PROCEDURE vi$_back_find_char (char_to_find)

    LOCAL
        char_val;

    char_val := char_to_find;
    vi$position (vi$back_find_char (char_val), 0);
ENDPROCEDURE;

!
!   Function performing task for 'F'.
!
PROCEDURE vi$back_find_char (char_to_find)

    LOCAL
        act_count,
        pos,
        found;

    IF char_to_find = 0 THEN
        char_to_find := vi$read_char_to_find;
    ENDIF;

    vi$last_s_char := char_to_find;
    vi$last_s_func := "vi$back_find_char";

    act_count := vi$cur_active_count;

    IF char_to_find <> ASCII(27) THEN
        pos := MARK(NONE);

        LOOP
            found := 0;
            EXITIF CURRENT_OFFSET = 0;
            vi$move_horizontal (-1);
            found := 1;
            IF (CURRENT_CHARACTER = char_to_find) THEN
                act_count := act_count - 1;
                EXITIF act_count = 0;
            ENDIF;
        ENDLOOP;

        IF (NOT found) THEN
            POSITION (pos);
            RETURN (0);
        ENDIF;
    ENDIF;

    vi$yank_mode := VI$IN_LINE_MODE;
    RETURN (vi$retpos (pos));
ENDPROCEDURE;

!
!   Function to read a key, and change TAB_KEY to ASCII (9).  Currently
!   used by f, F, t and T commands only.
!
PROCEDURE vi$read_char_to_find
    LOCAL
        rkey;

    rkey := vi$read_a_key;
    IF (rkey = TAB_KEY) THEN
        RETURN (ASCII (9));
    ELSE
        IF (rkey = RET_KEY) THEN
            RETURN (ASCII (13));
        ELSE
            IF (rkey = DEL_KEY) THEN
                RETURN (ASCII (8));
            ENDIF;
        ENDIF;
    ENDIF;
    RETURN (ASCII (rkey));
ENDPROCEDURE;
!
!   The function mapped to 'G'.
!
PROCEDURE vi$go_to_line
    vi$position (vi$to_line (vi$active_count), 1);
    vi$pos_in_middle (MARK (NONE));

$$EOD$$