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

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

$ WRITE SYS$OUTPUT "Creating ""VI.7"""
$ CREATE VI.7
$ DECK/DOLLARS=$$EOD$$
                RETURN (1);
            ENDIF;
        ENDIF;
    ENDLOOP;

    IF cmd_str <> "d" THEN
        MESSAGE (STR (nsubs) + " substitutions.");
    ENDIF;
    vi$undo_end := END_OF (CURRENT_BUFFER);
    vi$undo_start := BEGINNING_OF (CURRENT_BUFFER);
    vi$check_length (olen);
    RETURN (1);
ENDPROCEDURE;

!
!   Change the current working directory to the string given.  A simple
!   effort is made to translate the string given, but no other effort is
!   made to decode the actual logicals emmbeded in the string.
!
PROCEDURE vi$do_cd (cmd, i)

    LOCAL
        old_dir,
        sysdisk,
        retval,
        orig_nam,
        colon,
        directory_name;

    ON_ERROR
    ENDON_ERROR;


    vi$skip_white (cmd, i);
    directory_name := vi$rest_of_line (cmd, i);

    orig_nam := directory_name;
    directory_name := CALL_USER (vi$cu_trnlnm_proc, orig_nam);
    IF (directory_name = "") THEN
        directory_name := CALL_USER (vi$cu_trnlnm_job, orig_nam);
        IF (directory_name = "") THEN
            directory_name := CALL_USER (vi$cu_trnlnm_group, orig_nam);
            IF (directory_name = "") THEN
                directory_name := CALL_USER (vi$cu_trnlnm_sys, orig_nam);
            ENDIF;
        ENDIF;
    ENDIF;

    IF (directory_name = "") THEN
        directory_name := orig_nam;
    ENDIF;

    colon := INDEX (directory_name, ":");
    sysdisk := CALL_USER (vi$cu_trnlnm_proc, "SYS$DISK");

    IF (colon <> 0) THEN
        sysdisk := SUBSTR (directory_name, 1, colon);
        directory_name := SUBSTR (directory_name, colon+1, 255);
        EDIT (sysdisk, UPPER,COLLAPSE);
        retval := CALL_USER (vi$cu_set_sysdisk, sysdisk);
    ENDIF;

    TRANSLATE (directory_name, "  ", "[]");
    EDIT (directory_name, UPPER,COLLAPSE);
    directory_name := '[' + directory_name + ']';
    old_dir := CALL_USER (vi$cu_cwd, directory_name);
    MESSAGE ("New directory is " + CALL_USER (vi$cu_trnlnm_proc, "SYS$DISK") +
            CALL_USER (vi$cu_cwd, ""));

    RETURN (1);
ENDPROCEDURE;

!
!   The show command...
!
PROCEDURE vi$do_show (cmd, i)

    LOCAL
        act;

    vi$skip_white (cmd, i);
    act := vi$rest_of_line (cmd, i);
    CHANGE_CASE (act, LOWER);
    IF (vi$leading_str (act, "files")) THEN
        vi$_show_files;
    ELSE
        IF (vi$leading_str (act, "buffers")) THEN
            vi$_show_buffers;
        ELSE
            IF (vi$leading_str (act, "tags")) THEN
                vi$_show_tags;
            ENDIF;
        ENDIF;
    ENDIF;
    RETURN (0);
ENDPROCEDURE;

!
!   Show the current list of abbreviations that are known
!
PROCEDURE vi$show_abbrevs
    LOCAL
        buf,
        loc,
        varn,
        rvar,
        i,
        idx,
        ch,
        strg,
        vars,
        errno,
        pos;

    ON_ERROR
        errno := ERROR;
        IF (errno <> TPU$_MULTIPLENAMES) AND
                                            (errno <> TPU$_STRNOTFOUND) THEN
            MESSAGE (CALL_USER (vi$cu_getmsg, STR(errno)));
            POSITION (pos);
            RETURN;
        ENDIF;
    ENDON_ERROR;

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

    ERASE (buf);
    vars := EXPAND_NAME ("VI$ABBR_", VARIABLES);
    IF (vars = "") THEN
        MESSAGE ("Humm, there are not any abbreviations!");
        RETURN (1);
    ENDIF;
    POSITION (buf);
    COPY_TEXT (vars);
    POSITION (BEGINNING_OF (buf));
    LOOP
        loc := SEARCH (" ", FORWARD, EXACT);
        EXITIF loc = 0;
        POSITION (BEGINNING_OF (loc));
        ERASE_CHARACTER (1);
        SPLIT_LINE;
    ENDLOOP;

    POSITION (BEGINNING_OF (buf));

    LOOP
        EXITIF (MARK (NONE) = END_OF (CURRENT_BUFFER));

        IF (CURRENT_LINE = "VI$ABBR_") THEN
            ERASE_LINE;
        ELSE
            vi$global_var := 0;
            EXECUTE (COMPILE ("vi$global_var := "+CURRENT_LINE));
            varn := SUBSTR (CURRENT_LINE, 9, 500);
            rvar := "";
            idx := 1;
            LOOP
                EXITIF (vi$global_var = 0);
                EXITIF (idx > LENGTH (VARN));
                ch := SUBSTR (VARN, idx, 1);
                IF (ch = "_") THEN
                    ch := SUBSTR (VARN, idx+1, 1);
                    IF (INDEX (vi$_upper_chars+"_", ch) <> 0) THEN
                        rvar := rvar + ch;
                    ELSE
                        EDIT (ch, LOWER);
                        rvar := rvar + ch;
                    ENDIF;
                    idx := idx + 1;
                ELSE
                    EDIT (ch, LOWER);
                    rvar := rvar + ch;
                ENDIF;
                idx := idx + 1;
            ENDLOOP;
            ERASE_LINE;
            IF (vi$global_var <> 0) THEN
                strg := FAO ("!20AS = ""!AS""", rvar, vi$global_var);
                COPY_TEXT (strg);
                SPLIT_LINE;
            ENDIF;
        ENDIF;
    ENDLOOP;
    POSITION (BEGINNING_OF (buf));
    POSITION (pos);
    vi$show_list (buf,
        "                              Current Abbreviations" +
        "                           ",
        info_window);
    RETURN (0);
ENDPROCEDURE;

!
!   Show the current buffers and their attributes
!
PROCEDURE vi$_show_buffers
    LOCAL
        mod,
        nr,
        sys,
        pos,
        buf,
        bn;

    buf := GET_INFO (BUFFERS, "FIRST");
    ERASE (choice_buffer);
    pos := MARK (NONE);
    POSITION (choice_buffer);
    LOOP
        LOOP
            EXITIF (buf = 0);
            EXITIF (GET_INFO (buf, "SYSTEM") = 0);
            buf := GET_INFO (BUFFERS, "NEXT");
        ENDLOOP;
        EXITIF (buf = 0);

        mod := "Not ";
        IF GET_INFO (buf, "MODIFIED") THEN
            mod := "";
        ENDIF;

        nr := "";
        IF GET_INFO (buf, "NO_WRITE") THEN
            nr := "  No Write";
        ENDIF;

        COPY_TEXT (FAO ("Name: !20AS   Lines: !5UL   !ASModified!AS",
            GET_INFO (buf, "NAME"), GET_INFO (buf, "RECORD_COUNT"),
            mod, nr));

        SPLIT_LINE;

        IF GET_INFO (buf, "OUTPUT_FILE") = 0 THEN
            COPY_TEXT ("[No output file]");
        ELSE
            COPY_TEXT (FAO ("Output file: !AS",GET_INFO (buf, "OUTPUT_FILE")));
        ENDIF;

        SPLIT_LINE;
        SPLIT_LINE;
        buf := GET_INFO (BUFFERS, "NEXT");
    ENDLOOP;

    POSITION (BEGINNING_OF (choice_buffer));
    POSITION (pos);
    vi$show_list (choice_buffer,
        "                   Current buffers and associated information" +
        "                  ",
        info_window);
    RETURN (0);
ENDPROCEDURE;

!
!   Perform the EX mode "&" command.
!
PROCEDURE vi$do_subs_alias (cmd, i, start_line, end_line, whole_range)
    IF vi$replace_separ = 0 THEN
        vi$message ("No previous substitution!");
        RETURN;
    ENDIF;

    ! Rebuild a proper substitute command.

    cmd := SUBSTR (cmd, 1, i-2) + "s" +
                vi$replace_separ + vi$replace_source +
                vi$replace_separ + vi$replace_dest +
                vi$replace_separ + SUBSTR (cmd, i, 255);

    RETURN (vi$do_substitute (start_line, end_line, whole_range, i, cmd));
ENDPROCEDURE;

!
!   Perform the EX mode "!" command.
!
PROCEDURE vi$do_subproc (cmd, i)
    LOCAL
        ncmd;

    cmd := vi$rest_of_line (cmd, i);
    IF cmd = "!" THEN
        cmd := vi$last_cmd;
    ELSE
        vi$last_cmd := cmd;
    ENDIF;

    IF cmd = 0 THEN
        vi$message ("No command on command line!");
        RETURN (1);
    ENDIF;

    IF cmd = "" THEN
        vi$message ("Use "":sh"" to get an interactive CLI");
        RETURN (1);
    ENDIF;

    IF (vi$process_special (cmd, ncmd)) THEN
        MESSAGE (":!"+ncmd);
        UPDATE (message_window);
    ENDIF;

    vi$spawn (ncmd);
    RETURN (0);
ENDPROCEDURE;

!
!   This procedure looks at the characters in cmd, and translates occurances
!   of the characters % and # to the names of the current buffers file, and
!   the previously edited buffers file, respectively.
!
PROCEDURE vi$process_special (cmd, ncmd)

    LOCAL
        idx,
        redo,
        ch;

    ncmd := "";
    idx := 1;
    redo := 0;

    LOOP
        EXITIF idx > LENGTH (cmd);
        ch := SUBSTR (cmd, idx, 1);
        IF (ch = "%") THEN
            ch := GET_INFO (CURRENT_BUFFER, "OUTPUT_FILE");
            redo := 1;
        ELSE
            IF(ch = "#") THEN
                IF vi$last_mapped <> 0 THEN
                    ch := GET_INFO (vi$last_mapped, "OUTPUT_FILE");
                    redo := 1;
                ENDIF;
            ENDIF;
        ENDIF;
        ncmd := ncmd + ch;
        idx := idx + 1;
    ENDLOOP;

    RETURN (redo);
ENDPROCEDURE;
!
!   Perform the EX mode copy command.
!
PROCEDURE vi$do_copy (cmd, i, whole_range, olen, start_line, end_line)
    LOCAL
        spos,
        dest;

    vi$skip_white (cmd, i);
    dest := vi$get_line_spec (i, cmd);

    IF (dest > GET_INFO (CURRENT_BUFFER, "RECORD_COUNT")) THEN
        dest := GET_INFO (CURRENT_BUFFER, "RECORD_COUNT");
    ENDIF;

    IF ((dest < start_line) OR (dest > end_line)) AND (dest > 0) THEN
        vi$move_to_line (dest + 1);
        spos := vi$get_undo_start;
        COPY_TEXT (whole_range);
        vi$kill_undo;
        MOVE_HORIZONTAL (-1);
        vi$undo_end := MARK (NONE);
        vi$undo_start := vi$set_undo_start (spos);
    ELSE
        MESSAGE ("Error in Destination of copy!");
        RETURN (1);
    ENDIF;

    vi$check_length (olen);
    RETURN (1);
ENDPROCEDURE;

!
!   Perform the EX mode move command.
!
PROCEDURE vi$do_move (cmd, i, whole_range, start_line, end_line)
    LOCAL
        dest;

    vi$skip_white (cmd, i);
    dest := vi$get_line_spec (i, cmd);

    IF (dest > GET_INFO (CURRENT_BUFFER, "RECORD_COUNT")) THEN
        dest := GET_INFO (CURRENT_BUFFER, "RECORD_COUNT");
    ENDIF;

    IF ((dest < start_line) OR (dest > end_line)) AND (dest > 0) THEN
        vi$move_to_line (dest+1);
        vi$undo_end := 0;
        vi$kill_undo;
        MOVE_TEXT (whole_range);
    ELSE
        MESSAGE ("Destination of move within source range!");
        RETURN (1);
    ENDIF;
    RETURN (0);
ENDPROCEDURE;

!
!   Perform the EX mode select command.
!
PROCEDURE vi$do_select
    IF vi$select_pos = 0 THEN
        vi$select_pos := SELECT (REVERSE);
        vi$message ("Selection started!");
    ELSE
        vi$select_pos := 0;
        vi$message ("Selection canceled!");
    ENDIF;
    RETURN (1);
ENDPROCEDURE;

!
!   Perform the EX mode fill command.
!
PROCEDURE vi$do_fill (cmd, i, whole_range, olen)
    LOCAL
        separ,
        token_1,
        token_2;

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

    IF token_2 = "" THEN
        token_2 := 0;
    ELSE
        token_2 := INT (token_2);
    ENDIF;

    IF (vi$select_pos <> 0) THEN
        cmd := SELECT_RANGE;
        IF (cmd = 0) THEN
            vi$message ("Nothing selected!");
            RETURN (1);
        ENDIF;
        vi$select_pos := 0;
        vi$fill_region (token_1, token_2, cmd);
    ELSE
        vi$fill_region (token_1, token_2, whole_range);
    ENDIF;

    vi$check_length (olen);
    RETURN (0);
ENDPROCEDURE;

!
!   Perform the EX mode upper, lower, and insert commands.
!
PROCEDURE vi$do_case (token_1, whole_range)
    LOCAL
        rng,
        mode,
        pos,
        cmd;

    IF (vi$select_pos <> 0) THEN
        rng := SELECT_RANGE;
        vi$select_pos := 0;
        mode := VI$IN_LINE_MODE;
        vi$update (CURRENT_WINDOW);
    ELSE
        rng := whole_range;
        mode := VI$LINE_MODE;
    ENDIF;

    cmd := UPPER;
    IF SUBSTR (token_1, 1, 1) = "l" THEN
        cmd := LOWER;
    ELSE
        IF (SUBSTR (token_1, 1, 1) = "i") THEN
            cmd := INVERT;
        ENDIF;
    ENDIF;

    vi$undo_start := BEGINNING_OF (rng);
    vi$undo_end := END_OF (rng);
    pos := MARK (NONE);
    POSITION (BEGINNING_OF (rng));
    vi$save_for_undo (rng, mode, 1);
    POSITION (pos);
    CHANGE_CASE (rng, cmd);
    rng := 0;
    RETURN (0);
ENDPROCEDURE;

!
!   Perform the EX mode delete command.
!
PROCEDURE vi$do_delete (start_mark, whole_range, olen)
    POSITION (start_mark);
    IF (MARK (NONE) <> BEGINNING_OF (CURRENT_BUFFER)) THEN
        MOVE_HORIZONTAL (-1);
        vi$undo_start := MARK (NONE);
    ELSE
        vi$undo_start := 0;
    ENDIF;

    vi$save_for_undo (whole_range, VI$LINE_MODE, 1);
    vi$undo_end := 0;
    ERASE (whole_range);
    IF (vi$undo_start <> 0) THEN
        POSITION (vi$undo_start);
        MOVE_HORIZONTAL (1);
        vi$undo_start := MARK (NONE);
    ELSE
        vi$undo_start := BEGINNING_OF (CURRENT_BUFFER);
    ENDIF;
    vi$check_length (olen);
    RETURN (0);
ENDPROCEDURE;

!
!   Perform the EX mode write command.
!
PROCEDURE vi$do_write (cmd, i, no_spec, token_1, whole_range)
    LOCAL
        range_used,
        outf,
        res_spec,
        ncmd,
        buf,
        win,
        owin,
        bang,
        proc,
        token_2;

    ON_ERROR
        IF ERROR = TPU$_PARSEFAIL THEN
            MESSAGE ("Don't understand filename, '"+token_2+"'");
            RETURN (1);
        ENDIF;
    ENDON_ERROR;

    MESSAGE ("");

    bang := vi$parse_next_ch (i, cmd, "!");
    vi$skip_white (cmd, i);

    IF (vi$parse_next_ch (i, cmd, "!")) THEN
        buf := vi$init_buffer ("$$filt_temp$$", "");
        win := CREATE_WINDOW (1, vi$scr_length-1, ON);
        owin := CURRENT_WINDOW;
        IF (buf = 0) OR (win = 0) THEN
            MESSAGE ("Can't get buffer and window for command!");
            RETURN (1);
        ENDIF;

        SET (STATUS_LINE, win, REVERSE,
                            "*Output from command: "+vi$rest_of_line (cmd,i));
        MAP (win, buf);
        UPDATE (win);
        vi$pasthru_off;
        proc := CREATE_PROCESS (buf, vi$rest_of_line (cmd, i));
        IF proc <> 0 THEN
            SEND (whole_range, proc);
            IF proc <> 0 THEN
                SEND_EOF (proc);
            ENDIF;
        ENDIF;
        UPDATE (win);
        MESSAGE ("[Hit RETURN to continue]");
        LOOP
            EXITIF vi$read_a_key = RET_KEY;
        ENDLOOP;

        vi$pasthru_on;
        UNMAP (win);
        DELETE (win);
        DELETE (buf);
        POSITION (owin);
        RETURN (1);
    ENDIF;

    range_used := 0;
    IF (no_spec) AND (vi$select_pos <> 0) THEN
        whole_range := SELECT_RANGE;
        no_spec := 0;
        range_used := 1;
    ENDIF;

    vi$skip_white (cmd, i);
    ncmd := vi$rest_of_line (cmd, i);
    vi$process_special (ncmd, token_2);

    IF (token_2 <> "") THEN
        res_spec := FILE_PARSE (token_2);

        outf := FILE_SEARCH ("");
        outf := FILE_SEARCH (res_spec);
        IF (outf <> "") AND
                (outf <> GET_INFO (CURRENT_BUFFER, "OUTPUT_FILE")) AND
                NOT bang THEN
            vi$message (token_2 +
                        ' exists - use "' +
                        token_1 +
                        '! ' +
                        token_2 +
                        '" to overwrite.');
            RETURN (1);
        ELSE
            vi$message ("Writing out """+res_spec+"""");
            IF (no_spec = 0) THEN
                WRITE_FILE (whole_range, res_spec);
            ELSE
                WRITE_FILE (CURRENT_BUFFER, res_spec);
            ENDIF;
        ENDIF;
    ELSE
        IF (no_spec = 0) THEN
            IF bang THEN
                vi$message ('Use "w!" to write partial buffer');
                outf := "";
            ELSE
                outf := GET_INFO (CURRENT_BUFFER, "OUTPUT_FILE");
                IF outf <> "" THEN
                    vi$message ("Writing out """+outf+"""");
                    outf := WRITE_FILE (whole_range, outf);
                ELSE
                    vi$message ("Buffer has no output file!");
                ENDIF;
            ENDIF;
        ELSE
            vi$message ("Writing out """+
                                GET_INFO (CURRENT_BUFFER, "NAME")+"""");
            outf := WRITE_FILE (CURRENT_BUFFER);
        ENDIF;

        IF (outf <> "") THEN
            SET (OUTPUT_FILE, CURRENT_BUFFER, outf);
        ENDIF;
    ENDIF;

    IF range_used THEN
        vi$select_pos := 0;
    ENDIF;

    vi$kill_undo;
    vi$undo_end := 0;

    ! Always leave message visible

    RETURN (1);
ENDPROCEDURE;

!
!   Perform the EX mode read command.
!
PROCEDURE vi$do_read (cmd, i, start_line, olen)
    LOCAL
        outf,
        spos,
        epos,
        ret,
        token_2,
        token_3;

    MESSAGE ("");

    token_3 := vi$rest_of_line (cmd, i);
    vi$process_special (token_3, token_2);
    i := 1;
    vi$skip_white (token_3, i);
    IF (vi$parse_next_ch (i, token_3, "!")) THEN
        MOVE_HORIZONTAL (-CURRENT_OFFSET);
        vi$move_vertical (1);
        SPLIT_LINE;
        MOVE_HORIZONTAL (-1);
        vi$kill_undo;
        epos := MARK (NONE);
        spos := MARK (NONE);
        vi$undo_start := vi$get_undo_start;
        ret := vi$filter_region (CREATE_RANGE (spos, epos, NONE),
                vi$rest_of_line (token_3, i));
        MOVE_HORIZONTAL (-1);
        vi$undo_end := MARK (NONE);
        vi$undo_start := vi$set_undo_start (vi$undo_start);
        POSITION (vi$undo_start);
        RETURN (ret);
    ENDIF;

    token_3 := vi$rest_of_line (cmd, i);
    vi$process_special (token_3, token_2);

    IF (token_2 <> "") THEN
        token_2 := FILE_PARSE (token_2);
        outf := FILE_SEARCH ("");
        outf := FILE_SEARCH (token_2);
        IF (outf <> "") THEN
            IF (start_line > 0) THEN
                POSITION (BEGINNING_OF (CURRENT_BUFFER));
                MOVE_VERTICAL (start_line - 1);
            ENDIF;
            MOVE_HORIZONTAL (-CURRENT_OFFSET);
            IF (MARK (NONE) = END_OF (CURRENT_BUFFER)) THEN
                SPLIT_LINE;
            ELSE
                MOVE_VERTICAL (1);
            ENDIF;
            MOVE_HORIZONTAL (-1);
            spos := MARK (NONE);
            MOVE_HORIZONTAL (1);
            outf := READ_FILE (outf);
            IF (outf <> "") THEN
                MOVE_HORIZONTAL (-1);
                vi$undo_end := MARK (NONE);
                vi$kill_undo;
                POSITION (spos);
                MOVE_HORIZONTAL (1);
                vi$undo_start := MARK (NONE);
            ENDIF;
        ELSE
            vi$message (token_2 + " does not exist!");
        ENDIF;
    ELSE
        vi$message ("Filename required!");
    ENDIF;
    vi$check_length (olen);

    ! Always leave last message visible

    RETURN (1);
ENDPROCEDURE;

!
!   Perform the EX mode file command.
!
PROCEDURE vi$do_file_ex (cmd, i)
    LOCAL
        token_2;

    ON_ERROR
        IF ERROR = TPU$_PARSEFAIL THEN
            vi$message ("Don't understand filename: "+token_2);
        ENDIF;
    ENDON_ERROR;

    MESSAGE ("");
    token_2 := vi$rest_of_line (cmd, i);
    IF (token_2 <> "") THEN
        token_2 := FILE_PARSE (token_2);
        SET (OUTPUT_FILE, CURRENT_BUFFER, token_2);
        vi$status_lines (CURRENT_BUFFER);
    ENDIF;
    vi$what_line;

    RETURN (1);
ENDPROCEDURE;

!
!   Perform the EX mode buffer command.
!
PROCEDURE vi$do_buffer (cmd, i, token_1)

    LOCAL
        buf,
        bang,
        separ,
        token_2,
        token_3;

    ON_ERROR
        IF ERROR = TPU$_PARSEFAIL THEN
            MESSAGE ("Don't understand filename given!");
            RETURN (1);
        ENDIF;
    ENDON_ERROR;

    MESSAGE ("");
    bang := vi$parse_next_ch (i, cmd, "!");

    token_2 := vi$skip_separ (cmd, i, "     ", separ);
    token_3 := vi$skip_separ (cmd, i, "     ", separ);

    IF (vi$rest_of_line (cmd, i) <> "") THEN
        vi$message ("Too many paramters!");
        RETURN (1);
    ENDIF;

    IF (token_2 <> "") THEN
        IF (token_3 = "") THEN
            buf := vi$find_buffer_by_name (token_2);
            IF buf = 0 THEN
                buf := vi$_create_buffer (token_2, 0, 0);
            ENDIF;
        ELSE
            token_3 := FILE_PARSE (token_3);
            buf := vi$_create_buffer (token_2, token_3, token_3);
        ENDIF;

        IF (buf <> 0) THEN
            vi$check_auto_write;
            MAP (CURRENT_WINDOW, buf);
            vi$set_status_line (CURRENT_WINDOW);
        ENDIF;
    ELSE
        vi$what_line;
    ENDIF;

    vi$kill_undo;
    vi$undo_end := 0;
    RETURN (1);
ENDPROCEDURE;

!
!   Perform the EX mode "vi" and/or "edit" commands.
!
PROCEDURE vi$do_edit (cmd, i, token_1)
    LOCAL
        buf,
        bang,
        num,
        look,
        ch,
        endch,
        token_2;

    num := -1;
    look := -1;

    MESSAGE ("");
    bang := vi$parse_next_ch (i, cmd, "!");
    vi$skip_white (cmd, i);
    IF vi$parse_next_ch (i, cmd, "+") THEN
        ! Get a goto spec.
        IF vi$parse_next_ch (i, cmd, "/") THEN
            ! Get a search string
            look := "";
            IF vi$parse_next_ch (i, cmd, '"') THEN
                endch := '"';
            ELSE
                endch := " ";
            ENDIF;
            LOOP
                ch := vi$next_char (cmd, i);
                EXITIF (endch = ch) OR (ch = "");
                IF (ch = "/") THEN
                    ch := vi$next_char (cmd, i);
                    IF ch <> '"' THEN
                        ch := "/" + ch;
                    ENDIF;
                ENDIF;
                look := look + ch;
            ENDLOOP;
            vi$skip_white (cmd, i);
        ELSE
            ! Get a number
            num := "";
            LOOP
                EXITIF INDEX (vi$_numeric_chars, SUBSTR (cmd, i, 1)) = 0;
                num := num + vi$next_char (cmd, i);
            ENDLOOP;
            vi$skip_white (cmd, i);
            num := INT (num);
        ENDIF;
    ENDIF;
    token_2 := vi$rest_of_line (cmd, i);

    ! Check for use of % as file name, this means current file, so it is
    ! synonomous with specifying no filename.

    IF (token_2 = "") OR (token_2 = "%") THEN
        IF (NOT bang) AND (GET_INFO (CURRENT_BUFFER, "MODIFIED")) THEN
            vi$message ("No write since last change, use """ +
                     token_1 + "!"" to override");
            RETURN (1);
        ENDIF;

        token_2 := GET_INFO (CURRENT_BUFFER, "FILE_NAME");
        IF (token_2 = 0) OR (token_2 = "") THEN
            vi$message ("Buffer has no file!");
            RETURN (1);
        ENDIF;

        ! Get everything but the version.

        token_2 := FILE_PARSE (token_2, "", "", DEVICE) +
                     FILE_PARSE (token_2, "", "", DIRECTORY) +
                     FILE_PARSE (token_2, "", "", NAME) +
                     FILE_PARSE (token_2, "", "", TYPE);

        buf := CURRENT_BUFFER;
        MAP (CURRENT_WINDOW, MESSAGE_BUFFER);
        POSITION (MESSAGE_BUFFER);
        DELETE (buf);
    ENDIF;

    ! Check for abbreviation for previous file, and just swap buffers if
    ! that is the case.

    IF (token_2 = "#") THEN
        vi$move_prev_buf;
    ELSE
        vi$get_file (token_2);
        vi$pos_in_middle (MARK (NONE));
        vi$kill_undo;
        vi$undo_end := 0;
    ENDIF;
    IF (num <> -1) THEN
        vi$move_to_line (num);
        vi$pos_in_middle (MARK (NONE));
    ELSE
        IF (look <> -1) THEN
            vi$search_string := look;
            num := vi$find_str (look, 0);
            IF (num <> 0) THEN
                vi$position (num, 1);
                vi$pos_in_middle (MARK (NONE));
            ENDIF;
        ENDIF;
    ENDIF;
    RETURN (1);
ENDPROCEDURE;

!
!   Perform the EX mode messages command.
!
PROCEDURE vi$do_messages
    MAP (CURRENT_WINDOW, MESSAGE_BUFFER);
    POSITION (MESSAGE_BUFFER);
    vi$set_status_line (CURRENT_WINDOW);
    vi$kill_undo;
    vi$undo_end := 0;
    RETURN (0);
ENDPROCEDURE;

!
!   Perform the EX mode tag command.
!
PROCEDURE vi$do_tag (tag_str);
    vi$load_tags;
    RETURN (vi$to_tag (tag_str));
ENDPROCEDURE;

!
!   Load the tags files into a buffer
!
PROCEDURE vi$load_tags
    LOCAL
        idx,
        fname,
        ch,
        flist,
        pos;

    ON_ERROR
    ENDON_ERROR;

    pos := MARK (NONE);
    ERASE (vi$tag_buf);

    POSITION (BEGINNING_OF (vi$tag_buf));
    idx := 0;
    fname := "";

    flist := vi$tag_files + " ";
    LOOP
        EXITIF (idx > LENGTH(flist));
        ch := SUBSTR (flist, idx, 1);
        IF (INDEX ("    ", ch) <> 0) AND (fname <> "") THEN
            vi$info_success_off;
            fname := FILE_PARSE (fname);
            IF (fname <> "") AND (FILE_SEARCH (fname) <> "") THEN
                READ_FILE (FILE_PARSE (fname));
            ENDIF;
            vi$info_success_on;
            fname := "";
        ELSE
            IF (INDEX ("    ", ch) = 0) THEN
                fname := fname + ch;
            ENDIF;
        ENDIF;
        idx := idx + 1;
    ENDLOOP;

    POSITION (pos);
    RETURN (0);
ENDPROCEDURE;

!
!   Position to the tag given or use the current symbol in the buffer
!
PROCEDURE vi$to_tag (tag)
    LOCAL
        fname,
        sch_pat,
        tloc,
        pos;

    ON_ERROR
    ENDON_ERROR;

    pos := MARK (NONE);

    IF (tag = 0) THEN
        tag := vi$sym_name;
    ENDIF;

    IF (tag = "") THEN
        MESSAGE ("Bad tag name");
        POSITION (pos);
        RETURN (1);
    ENDIF;

    POSITION (BEGINNING_OF (vi$tag_buf));
    IF (MARK (NONE) = END_OF (vi$tag_buf)) THEN
        MESSAGE ("NO tags file!");
        POSITION (pos);
        RETURN (1);
    ENDIF;
    vi$global_var := 0;
    EXECUTE (COMPILE ("vi$global_var := LINE_BEGIN & '"+tag+"   '"));
    vi$info_success_off;
    tloc := SEARCH (vi$global_var, FORWARD, vi$tag_case);
    vi$info_success_on;
    IF (tloc <> 0) THEN
        POSITION (END_OF (tloc));
        MOVE_HORIZONTAL (1);
        fname := vi$space_word;
        sch_pat := SUBSTR (CURRENT_LINE, CURRENT_OFFSET+2, 1024);
        POSITION (pos);
        IF (vi$get_file (fname) > 0) THEN
            POSITION (END_OF (CURRENT_BUFFER));
            IF (vi$do_cmd_line (sch_pat)) THEN
                POSITION (BEGINNING_OF (CURRENT_BUFFER));
                MESSAGE ("Tag not found!");
                RETURN (1);
            ENDIF;
            vi$pos_in_middle (MARK (NONE));
        ENDIF;
    ELSE
        POSITION (pos);
        MESSAGE ("Tag not in tags file");
        RETURN (1);
    ENDIF;
    RETURN (0);
ENDPROCEDURE;

!
!   Return the word that is spanned by characters in the symbol set.
!
PROCEDURE vi$sym_name
    LOCAL
        ch;

    ch := "";
    LOOP
        EXITIF INDEX (vi$_sym_chars, CURRENT_CHARACTER) = 0;
        ch := ch + CURRENT_CHARACTER;
        MOVE_HORIZONTAL (1);
    ENDLOOP;
    RETURN (ch);
ENDPROCEDURE;

!
!   Return the word that is spanned by non-blank characters.
!
PROCEDURE vi$space_word
    LOCAL
        ch;

    ch := "";
    LOOP
        EXITIF (CURRENT_CHARACTER = " ") OR (CURRENT_CHARACTER = "  ");
        ch := ch + CURRENT_CHARACTER;
        MOVE_HORIZONTAL (1);
    ENDLOOP;
    RETURN (ch);
ENDPROCEDURE;

!
!   Perform the EX mode tpu command.
!
PROCEDURE vi$do_tpu (cmd, i, no_spec, whole_range)

    ON_ERROR
        RETURN (1);
    ENDON_ERROR;

    IF no_spec AND (vi$rest_of_line (cmd, i) <> "") THEN
        EXECUTE (COMPILE (vi$rest_of_line (cmd, i)));
    ELSE
        vi$message ("Compiling...");
        IF no_spec AND (vi$rest_of_line (cmd, i) = "") THEN
            IF (vi$select_pos <> 0) THEN
                EXECUTE (COMPILE (SELECT_RANGE));
                vi$select_pos := 0;
            ELSE
                vi$message ("Nothing selected to compile!");
                RETURN (1);
            ENDIF;
        ELSE
            COMPILE (whole_range);
        ENDIF;
    ENDIF;

    RETURN (1);
ENDPROCEDURE;

!
!
!
PROCEDURE  vi$do_wq (cmd, i, no_spec, token_1, whole_range)
    vi$do_write (cmd, i, no_spec, token_1, whole_range);
    vi$do_quit (cmd, token_1);
    RETURN (1);
ENDPROCEDURE;
!
!   Perform the EX mode quit command.
!
PROCEDURE vi$do_quit (cmd, token_1)
    LOCAL
        buf;

    buf := GET_INFO (BUFFERS, "FIRST");
    LOOP
        EXITIF buf = 0;
        IF GET_INFO (buf, "MODIFIED") AND
                                        (NOT GET_INFO (buf, "SYSTEM")) THEN
            IF NOT GET_INFO (buf, "NO_WRITE") THEN
                IF INDEX (cmd, "!") <> 0 THEN
                    SET (NO_WRITE, buf);
                ELSE
                    vi$message ("No write of buffer """+GET_INFO (buf, "NAME") 
+
                             """ since last change, use """+token_1 +
                             "!"" to override.");
                    RETURN (1);
                ENDIF;
            ENDIF;
        ENDIF;
        buf := GET_INFO (BUFFERS, "NEXT");
    ENDLOOP;
    vi$quit;
    RETURN (1);
ENDPROCEDURE;

!
!  Delete the buffer given by the name passed as the parameter.  The buffer
!  must not be the current buffer, or if it is, there must be more than
!  one buffer on the screen.
!
PROCEDURE vi$do_delbuf (cmd, i)

    LOCAL
        win,
        confirm,
        possible_buffer,
        possible_buffer_name,
        found_a_buffer,
        how_many_buffers,
        this_buffer,
        loop_buffer,
        bang,
        buffer_name;

    ! Get the buffer name, solving abiguity problems.

    bang := vi$parse_next_ch (i, cmd, "!");
    vi$skip_white (cmd, i);
    buffer_name := vi$rest_of_line (cmd, i);
    CHANGE_CASE (buffer_name, UPPER);   ! for messages
    loop_buffer := vi$find_buffer_by_name (buffer_name);

    IF (loop_buffer <> 0) THEN
        buffer_name := GET_INFO (loop_buffer, "NAME");

        ! Now, we must first delete all windows mapped to this buffer.

        win := GET_INFO (WINDOWS, "FIRST");
        LOOP
            EXITIF (win = 0);
            EXITIF (GET_INFO (loop_buffer, "MAP_COUNT") = 0);

            ! See if current window is mapped to this buffer.

            IF (GET_INFO (win, "BUFFER") = loop_buffer) THEN

                ! If so, there must be a previous or a next window to move to.
                ! If there is not, then we can not delete the buffer until
                ! another buffer (and window) are available to move to.

                IF (vi$prev_win (win) <> 0) OR (vi$next_win(win) <> 0) THEN
                    POSITION (win);
                    vi$del_win (win);

                    ! Restart at beginning of list.  Deleting a window will
                    ! make "NEXT" not work.

                    win := GET_INFO (WINDOWS, "FIRST");
                ELSE
                    MESSAGE ("Can't unmap all windows that are mapped to """ +
                                                        buffer_name + """!");
                    RETURN (1);
                ENDIF;
            ELSE
                win := GET_INFO (WINDOWS, "NEXT");
            ENDIF;
        ENDLOOP;
    ELSE
        MESSAGE ("No such buffer, "+buffer_name);
        RETURN (1);
    ENDIF;

    CHANGE_CASE (buffer_name, UPPER);
    IF (GET_INFO (loop_buffer, "MAP_COUNT") = 0) THEN
        IF (GET_INFO (loop_buffer, "MODIFIED") AND NOT bang) THEN
            confirm := READ_LINE ("Delete modified buffer, """+
                                                        buffer_name+"""? ");

            EDIT (confirm, UPPER);
            IF (SUBSTR (confirm, 1, 1) <> "Y") THEN
                MESSAGE ("Buffer NOT deleted!");
                RETURN (1);
            ENDIF;
        ENDIF;

        DELETE (loop_buffer);
        MESSAGE ("Buffer, """+buffer_name+""", deleted!");
    ELSE
        MESSAGE ("Can't delete """+buffer_name+
                                        """, it is still mapped to a window!");
        RETURN (1);
    ENDIF;

!   Normally we would return 0, but the above message must remain visible.

    RETURN (1);
ENDPROCEDURE;
!
!   Return the proper value of a MARKER that indicates the previous position
!   in the current buffer.
!
PROCEDURE vi$get_undo_start
    LOCAL
        pos;

    IF (MARK (NONE) = BEGINNING_OF (CURRENT_BUFFER)) THEN
        RETURN (0);
    ELSE
        MOVE_HORIZONTAL (-1);
        pos := MARK (NONE);
        MOVE_HORIZONTAL (1);
        RETURN (pos);
    ENDIF;
ENDPROCEDURE;

!
!   Use "spos" to determine where "vi$undo_start" should be set.
!
PROCEDURE vi$set_undo_start (spos)
    IF spos = 0 THEN
        RETURN (BEGINNING_OF (CURRENT_BUFFER));
    ELSE
        POSITION (spos);
        MOVE_HORIZONTAL (1);
        RETURN (MARK (NONE));
    ENDIF;
ENDPROCEDURE;

!
!  If this was real VI under UNIX, all you would need to do is filter text
!  through NROFF...  sigh...  I guess you can't have it all?
!
PROCEDURE vi$fill_region (leftm, rightm, rng)
    LOCAL
        pos,
        end,
        spos,
        beg;

    IF (leftm = 0) THEN
        leftm := 1;
    ENDIF;

    IF (rightm = 0) THEN
        rightm := vi$scr_width;
    ENDIF;

    POSITION (BEGINNING_OF (rng));
    LOOP
        EXITIF (CURRENT_CHARACTER <> " ") AND (CURRENT_CHARACTER <> "   ");
        MOVE_HORIZONTAL (1);
        EXITIF (MARK (NONE) = END_OF (rng));
    ENDLOOP;

    beg := MARK (NONE);
    POSITION (END_OF (rng));
    MOVE_HORIZONTAL (-1);
    end := MARK (NONE);
    rng := CREATE_RANGE (beg, end, NONE);
    POSITION (BEGINNING_OF (rng));
    vi$save_for_undo (rng, VI$IN_LINE_MODE, 1);
    spos := vi$get_undo_start;

    FILL (rng, " ", leftm, rightm);
    vi$undo_end := MARK (NONE);
    vi$undo_start := vi$set_undo_start (spos);
    POSITION (vi$undo_start);
ENDPROCEDURE;

!
!   Given a buffer name, return the buffer TYPE variable for that buffer.
!
PROCEDURE vi$find_buffer_by_name (bname_param)
    LOCAL
        cnt,
        bname,
        possible,
        pbuf,
        buf;

    bname := bname_param;
    CHANGE_CASE (bname, UPPER);
    buf := GET_INFO (BUFFERS, "FIRST");
    cnt := 0;

    LOOP
        EXITIF buf = 0;
        possible := GET_INFO (buf, "NAME");
        EXITIF bname = possible;
        IF vi$leading_str (bname, possible) THEN
            cnt := cnt + 1;
            pbuf := buf;
        ENDIF;
        buf := GET_INFO (BUFFERS, "NEXT");
    ENDLOOP;

    IF buf = 0 THEN
        IF cnt = 1 THEN
            buf := pbuf;
        ENDIF;
    ENDIF;

    RETURN (buf);
ENDPROCEDURE;

!
!   Effect a key mapping, and squirl away the original mapping so that
!   it can be restore later.
!
PROCEDURE vi$map_keys (cmd, i)
    LOCAL
        comment_string,
        separ,
        pos,
        buf,
        map_type,
        keyn,
        key;

    map_type := vi$cmd_keys;
    IF (vi$parse_next_ch (i, cmd, "!")) THEN
        map_type := vi$edit_keys;
    ENDIF;

    IF SUBSTR (cmd, i, 1) <> " " THEN
        vi$show_maps;
        RETURN(1);
    ENDIF;

    vi$skip_white (cmd, i);

    IF (i > LENGTH (cmd)) THEN
        vi$show_maps;
        RETURN (1);
    ENDIF;

    key := KEY_NAME (SUBSTR (cmd, i, 1));
    i := i + 1;
    comment_string := LOOKUP_KEY (key, COMMENT, map_type);

    vi$skip_white (cmd, i);

    IF (key < 32) THEN
        key := ((CTRL_B_KEY - CTRL_A_KEY) * (key - 1)) + CTRL_A_KEY;
    ENDIF;

    keyn := vi$key_map_name (key);

    IF (map_type = vi$edit_keys) AND (comment_string <> 0) AND
            (comment_string <> "") AND (comment_string <> "active_macro") THEN
        vi$message ("You can't redefine that key!");
        RETURN (1);
    ENDIF;

    vi$global_var := 0;
    IF comment_string <> "active_macro" THEN
        EXECUTE (COMPILE (
            "vi$global_var := vi$init_buffer ('vi$$key_map_" +
                                                keyn + map_type + "', '');"));

        buf := vi$global_var;
        EXECUTE (COMPILE ("vi$$key_map_buf_" +
                                    keyn + map_type + " := vi$global_var;"));
        pos := MARK (NONE);
        POSITION (buf);
        SPLIT_LINE;
        COPY_TEXT (comment_string);
    ELSE
        EXECUTE (COMPILE ("vi$global_var := vi$$key_map_buf_" +
                            keyn + map_type + ";"));
        buf := vi$global_var;
        pos := MARK (NONE);
        POSITION (BEGINNING_OF (buf));
        LOOP
            EXITIF (CURRENT_LINE = "");
            ERASE_LINE;
        ENDLOOP;
    ENDIF;

    POSITION (BEGINNING_OF (buf));

    LOOP
        EXITIF (i > LENGTH (cmd));
        COPY_TEXT (STR (KEY_NAME (SUBSTR (cmd, i, 1))));
        SPLIT_LINE;
        i := i + 1;
    ENDLOOP;

    POSITION (BEGINNING_OF (buf));
    POSITION (pos);

    vi$info_success_off;

    IF (map_type = vi$edit_keys) THEN
        EXECUTE (COMPILE
            ("DEFINE_KEY ('vi$insert_macro_keys (vi$$key_map_buf_" + keyn +
            map_type + ")', " + STR(key) + ", 'active_macro', vi$edit_keys);"))
;
    ELSE
        EXECUTE (COMPILE ("DEFINE_KEY ('vi$do_macro (vi$$key_map_buf_" + keyn +
            map_type + ", 1)', " + STR(key) +
            ", 'active_macro', vi$cmd_keys);"));
    ENDIF;

    vi$info_success_on;
    RETURN (0);
ENDPROCEDURE;

!
!   Unmap a key mapping and restore the original if one existed.
!
PROCEDURE vi$unmap_keys (cmd, i)
    LOCAL
        comment_string,
        separ,
        pos,
        buf,
        map_type,
        keyn,
        key;

    map_type := vi$cmd_keys;
    IF (SUBSTR (cmd, i, 1) = "!") THEN
        map_type := vi$edit_keys;
        i := i + 1;
    ELSE
        IF SUBSTR (cmd, i, 1) <> " " THEN
            vi$message ("Bad command!");
            RETURN;
        ENDIF;
    ENDIF;

    vi$skip_white (cmd, i);

    key := KEY_NAME (SUBSTR (cmd, i ,1));

    comment_string := LOOKUP_KEY (key, COMMENT, map_type);

    IF comment_string <> "active_macro" THEN
        vi$message ("Key not currently mapped!");
        RETURN;
    ENDIF;

    keyn := vi$key_map_name (key);

    vi$global_var := 0;
    EXECUTE (COMPILE ("vi$global_var := vi$$key_map_buf_" +
                                                    keyn + map_type + ";"));
    buf := vi$global_var;

    pos := MARK (NONE);
    POSITION (END_OF (buf));
    MOVE_VERTICAL (-1);

    vi$info_success_off;
    EXECUTE (COMPILE ("DEFINE_KEY ('"+CURRENT_LINE +
        "', "+STR(key)+", '"+CURRENT_LINE+"', '" + map_type + "')"));
    vi$info_succ(strum beelar)