home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume4 / vms-vi-2 / part14 < prev    next >
Encoding:
Internet Message Format  |  1989-02-03  |  39.2 KB

  1. Path: xanth!mcnc!gatech!cwjcc!hal!ncoast!allbery
  2. From: gregg@a.cs.okstate.edu (Gregg Wonderly)
  3. Newsgroups: comp.sources.misc
  4. Subject: v04i105: TPUVI for VMS part 14 of 17
  5. Message-ID: <8809210934.AA06271@uunet.UU.NET>
  6. Date: 27 Sep 88 22:18:30 GMT
  7. Sender: allbery@ncoast.UUCP
  8. Reply-To: gregg@a.cs.okstate.edu (Gregg Wonderly)
  9. Lines: 1503
  10. Approved: allbery@ncoast.UUCP
  11.  
  12. Posting-number: Volume 4, Issue 105
  13. Submitted-by: "Gregg Wonderly" <gregg@a.cs.okstate.edu>
  14. Archive-name: vms-vi-2/Part14
  15.  
  16. $ WRITE SYS$OUTPUT "Creating ""VI.10"""
  17. $ CREATE VI.10
  18. $ DECK/DOLLARS=$$EOD$$
  19.         coff;
  20.  
  21.     coff := CURRENT_OFFSET;
  22.     cline := vi$cur_line_no;
  23.  
  24.     IF (LENGTH (vi$current_line) > 0) THEN
  25.         IF (CURRENT_OFFSET < LENGTH(vi$current_line)) THEN
  26.             MOVE_HORIZONTAL (1);
  27.         ENDIF;
  28.     ENDIF;
  29.     vi$insert_here;
  30.     vi$undo_offset := coff;
  31.     vi$undo_line := cline;
  32.  
  33. ENDPROCEDURE;
  34.  
  35. !
  36. !  A do nothing function
  37. !
  38. PROCEDURE vi$_dummy
  39. ENDPROCEDURE;
  40.  
  41. !
  42. !  Do the command line input processing
  43. !
  44. PROCEDURE vi$while_not_esc
  45.  
  46.     LOCAL
  47.         max_mark,
  48.         start_pos,
  49.         max_col;
  50.  
  51.     max_col := CURRENT_OFFSET;
  52.     start_pos := max_col;
  53.     max_mark := MARK(NONE);
  54.     vi$update (CURRENT_WINDOW);
  55.  
  56.     RETURN (vi$line_edit (max_col, start_pos, max_mark, 0));
  57. ENDPROCEDURE;
  58.  
  59. !
  60. !   Insert text into the buffer using standard VI insertion.
  61. !   Used by CHANGE, APPEND, INSERT, and REPLACE functions.
  62. !
  63. PROCEDURE vi$line_edit (max_col, start_pos, max_mark, replace)
  64.  
  65.     LOCAL
  66.         chcnt,
  67.         offset,
  68.         seen_eol,
  69.         col,
  70.         cnt,
  71.         tabstops,
  72.         current_mark,
  73.         desc,
  74.         start_ins,
  75.         ins_text,
  76.         should_wrap,
  77.         abbrs,
  78.         rchar,
  79.         abbrlen,
  80.         cabbr,
  81.         cmode,
  82.         pos,
  83.         did_ai,
  84.         in_char;
  85.  
  86.     ON_ERROR
  87.     ENDON_ERROR;
  88.  
  89.     ! If show mode is in effect the show the mode.
  90.  
  91.     IF (vi$show_mode) THEN
  92.         vi$mess_select (BOLD);
  93.         MESSAGE (FAO ("!7*  INSERT"));
  94.         vi$mess_select (REVERSE);
  95.     ENDIF;
  96.  
  97.     chcnt := 0;
  98.     seen_eol := 0;
  99.  
  100.     ! Get the list of current abbreviation variable names.
  101.  
  102.     abbrs := EXPAND_NAME ("vi$abbr_", VARIABLES) + " ";
  103.  
  104.     cabbr := "";
  105.     abbrlen := 0;
  106.  
  107.     ! Now decide whether we are entering from a change or replace command
  108.     ! verses an insert or append command.  If it is change or replace, then
  109.     ! we must set the buffer to overstrike so that we can type over things
  110.     ! until we get to the right marker, max_col.
  111.  
  112.     SET (INSERT, CURRENT_BUFFER);
  113.     IF (max_col > CURRENT_OFFSET) OR (replace <> 0) THEN
  114.         SET (OVERSTRIKE, CURRENT_BUFFER);
  115.     ENDIF;
  116.  
  117.     ! Save the starting position for repeat_last_typed_text.
  118.  
  119.     start_ins := MARK (NONE);
  120.  
  121.     ! Add the initial auto indent margin.
  122.  
  123.     chcnt := vi$do_auto_indent(0);
  124.     did_ai := (chcnt <> 0);
  125.     IF (did_ai) THEN
  126.         max_col := CURRENT_OFFSET;
  127.         max_mark := MARK (NONE);
  128.     ENDIF;
  129.  
  130.     LOOP        ! Until escape is pressed.
  131.         LOOP    ! Until we are not reinserting previously typed text.
  132.             in_char := vi$read_a_key;
  133.             desc := LOOKUP_KEY (KEY_NAME (in_char), COMMENT, vi$edit_keys);
  134.             IF (desc = "entab") THEN
  135.                 IF (vi$auto_indent = 0) THEN
  136.                     EXITIF (1);
  137.                 ENDIF;
  138.                 vi$do_entab;
  139.                 max_col := CURRENT_OFFSET;
  140.                 max_mark := MARK (NONE);
  141.             ELSE
  142.                 IF (desc = "detab") THEN
  143.                     IF (vi$auto_indent = 0) THEN
  144.                         EXITIF (1);
  145.                     ENDIF;
  146.                     vi$do_detab;
  147.                     max_col := CURRENT_OFFSET;
  148.                     max_mark := MARK (NONE);
  149.                 ELSE
  150.                     EXITIF (desc <> "reinsert");
  151.  
  152.                     IF max_mark <> MARK (NONE) THEN
  153.                         current_mark := MARK (NONE);
  154.                         POSITION (max_mark);
  155.                         MOVE_HORIZONTAL (-1);
  156.  
  157.                         ERASE (CREATE_RANGE (MARK (NONE), current_mark, NONE));
  158.                     ENDIF;
  159.  
  160.                     SET (INSERT, CURRENT_BUFFER);
  161.                     COPY_TEXT (vi$last_insert);
  162.                     APPEND_LINE;
  163.  
  164.                     max_col := CURRENT_OFFSET;
  165.                     start_pos := CURRENT_OFFSET;
  166.                     max_mark := MARK(NONE);
  167.                     chcnt := chcnt + 1;
  168.                 ENDIF;
  169.             ENDIF;
  170.         ENDLOOP;
  171.  
  172.         ! Out when escape is pressed.
  173.  
  174.         EXITIF desc = "escape";
  175.  
  176.         ! Catch maps.
  177.  
  178.         IF (desc = "active_macro") THEN
  179.             EXECUTE (LOOKUP_KEY (KEY_NAME (in_char), PROGRAM, vi$edit_keys));
  180.         ELSE
  181.  
  182.             ! If this is a typing key....
  183.  
  184.             IF (desc <> "eol") AND (desc <> "bword") AND (desc <> "bs") THEN
  185.  
  186.                 ! Check if :set wm is in effect, and we are at the right margin.
  187.  
  188.                 should_wrap := (vi$wrap_margin <> 0) AND
  189.                             ((CURRENT_OFFSET + vi$wrap_margin) > vi$scr_width);
  190.  
  191.                 ! If we should do line wrapping.
  192.  
  193.                 IF (should_wrap) THEN
  194.  
  195.                     ! Backup over the last word.
  196.  
  197.                     offset := 0;
  198.                     MOVE_HORIZONTAL (-1);
  199.  
  200.                     LOOP
  201.                         EXITIF (CURRENT_OFFSET = 0);
  202.                         EXITIF (INDEX (vi$_space_tab, CURRENT_CHARACTER) <> 0);
  203.                         MOVE_HORIZONTAL (-1);
  204.                         offset := offset + 1;
  205.                     ENDLOOP;
  206.  
  207.                     ! Trim off the white space.
  208.  
  209.                     IF (offset <> 0) THEN
  210.                         ERASE_CHARACTER (1);
  211.                         LOOP
  212.                             EXITIF (CURRENT_OFFSET = 0);
  213.                             MOVE_HORIZONTAL (-1);
  214.                             EXITIF (
  215.                                 INDEX (vi$_space_tab, CURRENT_CHARACTER) = 0);
  216.                             ERASE_CHARACTER (1);
  217.                         ENDLOOP;
  218.                     ENDIF;
  219.  
  220.                     ! Split the line at the proper place, and reset the
  221.                     ! markers.
  222.  
  223.                     IF (CURRENT_OFFSET <> 0) THEN
  224.                         MOVE_HORIZONTAL (1);
  225.                         SPLIT_LINE;
  226.                         max_col := CURRENT_OFFSET;
  227.                         start_pos := CURRENT_OFFSET;
  228.                         max_mark := MARK(NONE);
  229.                         MOVE_HORIZONTAL (offset);
  230.                     ELSE
  231.                         MOVE_HORIZONTAL (offset);
  232.                         SPLIT_LINE;
  233.                         max_col := CURRENT_OFFSET;
  234.                         start_pos := CURRENT_OFFSET;
  235.                         max_mark := MARK(NONE);
  236.                     ENDIF;
  237.  
  238.                     ! After spliting, put in the left margin.
  239.  
  240.                     did_ai := (vi$do_auto_indent(1) <> 0);
  241.                 ENDIF;
  242.  
  243.                 ! Make sure the window is up to date.
  244.  
  245.                 vi$update (CURRENT_WINDOW);
  246.  
  247.                 ! If the key was ^V then read another.
  248.  
  249.                 IF desc = "vquote" THEN
  250.                     COPY_TEXT ("^");
  251.                     MOVE_HORIZONTAL (-1);
  252.                     vi$update (CURRENT_WINDOW);
  253.                     in_char := vi$read_a_key;
  254.                     IF (GET_INFO (CURRENT_BUFFER, "MODE") = INSERT) THEN
  255.                         ERASE_CHARACTER (1);
  256.                     ENDIF;
  257.                 ENDIF;
  258.  
  259.                 ! Insert a tab?
  260.  
  261.                 IF in_char = TAB_KEY THEN
  262.  
  263.                     ! Check for a completed abbreviation.
  264.  
  265.                     vi$abbr (abbrs, 0, cabbr, abbrlen);
  266.  
  267.                     ! Check whether to use a tab or expand to spaces.
  268.  
  269.                     IF (vi$use_tabs = 1) THEN
  270.                         COPY_TEXT (ASCII (9));
  271.                     ELSE
  272.                         cnt := 0;
  273.                         col := GET_INFO (SCREEN, "CURRENT_COLUMN");
  274.                         tabstops := GET_INFO (CURRENT_BUFFER, "TAB_STOPS");
  275.  
  276.                         IF (GET_INFO (tabstops, "TYPE") <> STRING) THEN
  277.                             LOOP
  278.                                 EXITIF (col - ((col / tabstops) *
  279.                                                             tabstops) = 0);
  280.                                 cnt := cnt + 1;
  281.                                 col := col + 1;
  282.                             ENDLOOP;
  283.  
  284.                             chcnt := chcnt + cnt;
  285.                             LOOP
  286.                                 EXITIF (cnt < 0);
  287.                                 IF (CURRENT_OFFSET = max_col) AND
  288.                                                 ((replace = 0) OR seen_eol) THEN
  289.                                     SET (INSERT, CURRENT_BUFFER);
  290.                                 ELSE
  291.                                     IF CURRENT_OFFSET > max_col THEN
  292.                                         max_col := CURRENT_OFFSET;
  293.                                         max_mark := MARK (NONE);;
  294.                                     ENDIF;
  295.                                 ENDIF;
  296.                                 COPY_TEXT (" ");
  297.                                 cnt := cnt - 1;
  298.                             ENDLOOP
  299.                         ELSE
  300.  
  301.                             ! Give up on windows with weird tab stops.
  302.  
  303.                             COPY_TEXT (ASCII (9));
  304.                         ENDIF;
  305.                     ENDIF;
  306.                     chcnt := chcnt + 1;
  307.                 ELSE
  308.  
  309.                     ! If it is a CONTROL key, then normalize the value to be
  310.                     ! 1-26.
  311.  
  312.                     in_char := INT (in_char);
  313.                     IF (in_char <= INT(CTRL_Z_KEY)) AND
  314.                                     (in_char >= INT(CTRL_A_KEY)) THEN
  315.                         in_char := (in_char - INT(CTRL_A_KEY)) /
  316.                                     (INT(CTRL_B_KEY) - INT(CTRL_A_KEY)) + 1;
  317.                     ENDIF;
  318.  
  319.                     ! Get the character we really want to insert.
  320.  
  321.                     rchar := vi$ascii(in_char);
  322.  
  323.                     ! If the character is a word separator, then check to see
  324.                     ! if an abbreviation preceeded this key.
  325.  
  326.                     IF (INDEX (vi$_ws, rchar) <> 0) THEN
  327.                         chcnt := chcnt + vi$abbr (abbrs, rchar, cabbr, abbrlen);
  328.                     ELSE
  329.  
  330.                         ! Otherwise put the character into the buffer.
  331.  
  332.                         COPY_TEXT (rchar);
  333.  
  334.                         ! Add the current character to the string that is
  335.                         ! going to contain the trailing portion of the variable
  336.                         ! name for the abbreviation.
  337.  
  338.                         IF (INDEX(vi$_upper_chars, rchar) <> 0) THEN
  339.                             cabbr := cabbr + "_";
  340.                         ENDIF;
  341.                         cabbr := cabbr + rchar;
  342.                         abbrlen := abbrlen + 1;
  343.  
  344.                         ! Count the number of characters typed in.
  345.  
  346.                         chcnt := chcnt + 1;
  347.                     ENDIF;
  348.                 ENDIF;
  349.  
  350.                 ! See if time to make the transition from OVERSTRIKE to
  351.                 ! INSERT modes.
  352.  
  353.                 IF (CURRENT_OFFSET = max_col) AND
  354.                                     ((replace = 0) OR seen_eol) THEN
  355.                     SET (INSERT, CURRENT_BUFFER);
  356.                 ELSE
  357.  
  358.                     ! Move the indicators up when necessary.
  359.  
  360.                     IF CURRENT_OFFSET > max_col THEN
  361.                         max_col := CURRENT_OFFSET;
  362.                         max_mark := MARK (NONE);
  363.                     ENDIF;
  364.                 ENDIF;
  365.             ELSE
  366.  
  367.                 ! Check for a backspace.
  368.  
  369.                 IF desc = "bs" THEN
  370.  
  371.                     ! If it is possible to backspace.
  372.  
  373.                     IF start_pos < CURRENT_OFFSET THEN
  374.  
  375.                         ! Delete backspace and the character before it in
  376.                         ! the key buffer that is remembering all of the
  377.                         ! keystrokes typed.
  378.  
  379.                         vi$del_a_key;
  380.                         vi$del_a_key;
  381.  
  382.                         ! Transition back to overstrike.
  383.  
  384.                         SET (OVERSTRIKE, CURRENT_BUFFER);
  385.  
  386.                         ! Backspace on the screen, and decrement char count.
  387.  
  388.                         MOVE_HORIZONTAL (-1);
  389.                         chcnt := chcnt - 1;
  390.                     ENDIF;
  391.                 ELSE
  392.  
  393.                     ! Check for RETURN.
  394.  
  395.                     IF desc = "eol" THEN
  396.  
  397.                         ! If not up to the max_mark, then there is trailing
  398.                         ! text to erase, so do that first.
  399.  
  400.                         IF (max_mark <> MARK (NONE)) AND (replace = 0) THEN
  401.                             current_mark := MARK (NONE);
  402.                             POSITION (max_mark);
  403.                             MOVE_HORIZONTAL (-1);
  404.                             ERASE (CREATE_RANGE (MARK (NONE),
  405.                                                         current_mark, NONE));
  406.                         ENDIF;
  407.  
  408.                         ! Now check for an abbreviation, and inc the count..
  409.  
  410.                         chcnt := vi$abbr (abbrs, 0, cabbr, abbrlen) + 1;
  411.  
  412.                         ! Split the line
  413.  
  414.                         SPLIT_LINE;
  415.  
  416.                         ! Set flag for REPLACE so that we do not write over
  417.                         ! unreplaced, but overstruck text.
  418.  
  419.                         seen_eol := 1;
  420.  
  421.                         ! Check for the DCL buffer activity
  422.  
  423.                         IF (CURRENT_BUFFER = vi$dcl_buf) AND (vi$send_dcl) THEN
  424.                             MOVE_VERTICAL (-1);
  425.                             vi$send_to_dcl (CURRENT_LINE);
  426.                             MOVE_VERTICAL (1);
  427.                         ENDIF;
  428.  
  429.                         ! Update all of the indicators and transition to
  430.                         ! INSERT mode.
  431.  
  432.                         max_col := CURRENT_OFFSET;
  433.                         start_pos := CURRENT_OFFSET;
  434.                         max_mark := MARK(NONE);
  435.                         SET (INSERT, CURRENT_BUFFER);
  436.  
  437.                         ! Add left margin if needed.
  438.  
  439.                         did_ai := (vi$do_auto_indent(1) <> 0);
  440.  
  441.                         ! End of input if DCL buffer and flag set.
  442.  
  443.                         IF (CURRENT_BUFFER = vi$dcl_buf) AND (vi$send_dcl) THEN
  444.                             EXITIF (1);
  445.                         ENDIF;
  446.                     ELSE
  447.  
  448.                         ! Check for CTRL-W, backup over word.
  449.  
  450.                         IF (desc = "bword") THEN
  451.  
  452.                             ! Backup over whitespace.
  453.  
  454.                             LOOP
  455.                                 EXITIF start_pos = CURRENT_OFFSET;
  456.                                 MOVE_HORIZONTAL (-1);
  457.                                 chcnt := chcnt - 1;
  458.                                 EXITIF (INDEX (vi$_space_tab,
  459.                                                     CURRENT_CHARACTER) = 0);
  460.                                 SET (OVERSTRIKE, CURRENT_BUFFER);
  461.                             ENDLOOP;
  462.  
  463.                             ! Backup over nonblank chars.
  464.  
  465.                             LOOP
  466.                                 EXITIF start_pos = CURRENT_OFFSET;
  467.                                 SET (OVERSTRIKE, CURRENT_BUFFER);
  468.                                 IF (INDEX (vi$_space_tab,
  469.                                             CURRENT_CHARACTER) <> 0) THEN
  470.                                     chcnt := chcnt + 1;
  471.                                     MOVE_HORIZONTAL (1);
  472.                                     EXITIF (1);
  473.                                 ENDIF;
  474.                                 MOVE_HORIZONTAL (-1);
  475.                                 chcnt := chcnt - 1;
  476.                             ENDLOOP;
  477.                         ENDIF;
  478.                     ENDIF;
  479.                 ENDIF;
  480.             ENDIF;
  481.         ENDIF;
  482.  
  483.         ! Make sure everything is visible.
  484.  
  485.         vi$update (CURRENT_WINDOW);
  486.     ENDLOOP;
  487.  
  488.     ! Must get a new offset for the cursor now.
  489.  
  490.     vi$new_offset := 1;
  491.  
  492.     ! If we are not at the rightmost position that text was typed to, then
  493.     ! we must delete the garbage out to the right.
  494.  
  495.     IF max_mark <> MARK (NONE) THEN
  496.         current_mark := MARK (NONE);
  497.  
  498.         ! If we are in REPLACE, then the text out there should be replaced
  499.         ! with the stuff that was there originally.
  500.  
  501.         IF (NOT seen_eol) AND (replace <> 0) THEN
  502.             SET (OVERSTRIKE, CURRENT_BUFFER);
  503.             COPY_TEXT (SUBSTR (replace, CURRENT_OFFSET + 1,
  504.                                                 max_col - CURRENT_OFFSET));
  505.             POSITION (current_mark);
  506.         ELSE
  507.  
  508.             ! Otherwise we erase the stuff.
  509.  
  510.             POSITION (max_mark);
  511.             IF (MARK(NONE) <> BEGINNING_OF (CURRENT_BUFFER)) THEN
  512.                 MOVE_HORIZONTAL (-1);
  513.             ENDIF;
  514.             ERASE (CREATE_RANGE (MARK (NONE), current_mark, NONE));
  515.         ENDIF;
  516.     ENDIF;
  517.  
  518.     ! When INSERT is ended, the cursor moves back one position, providing
  519.     ! we are not at the beginning of the line.
  520.  
  521.     IF (MARK(NONE) <> BEGINNING_OF (CURRENT_BUFFER)) THEN
  522.         IF (chcnt <> 0) THEN
  523.             MOVE_HORIZONTAL (-1);
  524.         ENDIF;
  525.     ELSE
  526.         chcnt := 0;
  527.     ENDIF;
  528.  
  529.     ! Save the text that we typed for later repeat.
  530.  
  531.     ins_text := CREATE_RANGE (start_ins, MARK (NONE), NONE);
  532.  
  533.     ! Save last inserted text to buffer.
  534.  
  535.     ERASE (vi$last_insert);
  536.     pos := MARK (NONE);
  537.  
  538.     POSITION (vi$last_insert);
  539.     COPY_TEXT (ins_text);
  540.     SPLIT_LINE;
  541.     POSITION (BEGINNING_OF (vi$last_insert));
  542.  
  543.     POSITION (pos);
  544.  
  545.     SET (INSERT, CURRENT_BUFFER);
  546.  
  547.     ! If :set sm, then remove the MODE displayed.
  548.  
  549.     IF (vi$show_mode) THEN
  550.         MESSAGE ("");
  551.     ENDIF;
  552.  
  553.     ! Function value is approximately the number of characters typed.  This
  554.     ! is mainly for check for NONE verses SOME.
  555.  
  556.     RETURN (chcnt);
  557. ENDPROCEDURE;
  558.  
  559. !
  560. !   Create the initial left margin of auto indent.
  561. !
  562. PROCEDURE vi$do_auto_indent(forceit)
  563.     LOCAL
  564.         d_rng,
  565.         d_strt,
  566.         d_text,
  567.         pos,
  568.         istr;
  569.  
  570.     ON_ERROR
  571.         RETURN (0);
  572.     ENDON_ERROR;
  573.  
  574.     IF (vi$auto_indent = 0) THEN
  575.         RETURN;
  576.     ENDIF;
  577.  
  578.     IF (LENGTH (CURRENT_LINE) > 0) AND (forceit = 0) THEN
  579.           RETURN;
  580.     ENDIF;
  581.  
  582.     pos := MARK (NONE);
  583.     MOVE_VERTICAL (-1);
  584.     d_strt := MARK (NONE);
  585.     istr := vi$get_leading_blank;
  586.     d_text := (CURRENT_CHARACTER = "");
  587.  
  588.     IF (CURRENT_OFFSET > 0) THEN
  589.         MOVE_HORIZONTAL (-1);
  590.         d_rng := CREATE_RANGE (d_strt, MARK(NONE), NONE);
  591.     ELSE
  592.         d_rng := 0;
  593.     ENDIF;
  594.  
  595.     POSITION (pos);
  596.     POSITION (LINE_BEGIN);
  597.     COPY_TEXT (istr);
  598.     POSITION (pos);
  599.  
  600.     IF (d_text) AND (d_rng <> 0) THEN
  601.         ERASE (d_rng);
  602.     ENDIF;
  603.  
  604.     vi$update (CURRENT_WINDOW);
  605.     RETURN (LENGTH (istr));
  606. ENDPROCEDURE;
  607.  
  608. !
  609. !   Insert another tab while :set ai is active.
  610. !
  611. PROCEDURE vi$do_entab
  612.     vi$do_ai_tabbing (1);
  613. ENDPROCEDURE;
  614.  
  615. !
  616. !   Remove a tab while :set ai is active.
  617. !
  618. PROCEDURE vi$do_detab
  619.     vi$do_ai_tabbing (0);
  620. ENDPROCEDURE;
  621.  
  622. !
  623. !   Get the leading whitespace from the current line.  Used during :set ai
  624. !   to findout how to indent on the current line.
  625. !
  626. PROCEDURE vi$get_leading_blank
  627.     LOCAL
  628.         ln,
  629.         ch,
  630.         idx;
  631.  
  632.     ln := vi$current_line;
  633.  
  634.     rstr := "";
  635.     idx := 1;
  636.     LOOP
  637.         ch := SUBSTR (ln, idx, 1);
  638.         IF (ch = "") THEN
  639.             RETURN (rstr);
  640.         ENDIF;
  641.         EXITIF (INDEX (vi$_space_tab, ch) = 0);
  642.         rstr := rstr + ch;
  643.         idx := idx + 1;
  644.     ENDLOOP;
  645.  
  646.     RETURN (rstr);
  647. ENDPROCEDURE;
  648.  
  649. !
  650. !   Check the current line, and see if it is completely whitespace to
  651. !   determine how to alter its indention.
  652. !
  653. PROCEDURE vi$check_leading_blank
  654.     LOCAL
  655.         ln,
  656.         ch,
  657.         idx;
  658.  
  659.     ln := vi$current_line;
  660.  
  661.     idx := 1;
  662.     LOOP
  663.         ch := SUBSTR (ln, idx, 1);
  664.         IF (ch = "") THEN
  665.             RETURN (1);
  666.         ENDIF;
  667.         EXITIF (INDEX (vi$_space_tab, ch) = 0);
  668.         idx := idx + 1;
  669.     ENDLOOP;
  670.  
  671.     RETURN (0);
  672. ENDPROCEDURE;
  673.  
  674. !
  675. !   Do :set ai entabbing or detabbing.
  676. !
  677. PROCEDURE vi$do_ai_tabbing (mode)
  678.     LOCAL
  679.         needed,
  680.         copy_line,
  681.         exitnow,
  682.         cur_tabs,
  683.         tab_len;
  684.  
  685.     IF NOT vi$check_leading_blank THEN
  686.         vi$beep;
  687.         RETURN;
  688.     ENDIF;
  689.  
  690.     cur_tabs := GET_INFO (CURRENT_BUFFER, "TAB_STOPS");
  691.  
  692.     IF (GET_INFO (cur_tabs, "TYPE") = STRING) THEN
  693.         vi$info ("Can't do auto indent in buffer with uneven tabstops.");
  694.         RETURN;
  695.     ELSE
  696.         tab_len := cur_tabs;
  697.     ENDIF;
  698.  
  699.     exitnow := 0;
  700.  
  701.     copy_line := vi$current_line;
  702.  
  703.     IF (copy_line <> "") OR (mode = 1) THEN
  704.  
  705.         ! Copy line is truncated to have no leading spaces.
  706.  
  707.         needed := vi$vis_indent (copy_line, tab_len);
  708.  
  709.         IF mode = 1 THEN
  710.             needed := needed + vi$shift_width;
  711.         ELSE
  712.             needed := needed - vi$shift_width;
  713.         ENDIF;
  714.  
  715.         IF (needed < 0) THEN
  716.             needed := 0;
  717.         ENDIF;
  718.  
  719.         ERASE_LINE;
  720.         COPY_TEXT (vi$get_tabs (needed, tab_len)+copy_line);
  721.  
  722.         MOVE_HORIZONTAL (1);
  723.         IF (MARK (NONE) <> END_OF(CURRENT_BUFFER)) THEN
  724.             MOVE_HORIZONTAL (-1);
  725.             SPLIT_LINE;
  726.         ENDIF;
  727.         MOVE_HORIZONTAL (-1);
  728.         vi$update (CURRENT_WINDOW);
  729.     ELSE
  730.         vi$beep;
  731.     ENDIF;
  732.  
  733. ENDPROCEDURE;
  734.  
  735. !
  736. !   Check to see if 'cabbr' is a known abbreviation, and substitute the
  737. !   proper text if it is.
  738. !
  739. PROCEDURE vi$abbr (abbrs, rchar, cabbr, abbrlen)
  740.     LOCAL
  741.         strg;
  742.  
  743.     strg := "";
  744.  
  745.     IF (abbrlen > 0) THEN
  746.         EDIT (cabbr, UPPER);
  747.         IF (INDEX (abbrs, "VI$ABBR_"+cabbr+" ") <> 0) THEN
  748.             vi$global_var := 0;
  749.             EXECUTE (COMPILE ("vi$global_var := vi$abbr_"+cabbr+";"));
  750.             IF (vi$global_var <> 0) THEN
  751.                 ERASE_CHARACTER (-abbrlen);
  752.                 strg := vi$global_var;
  753.                 COPY_TEXT (strg);
  754.             ENDIF;
  755.         ENDIF;
  756.         cabbr := "";
  757.         abbrlen := 0;
  758.     ENDIF;
  759.     IF (rchar <> 0) THEN
  760.         COPY_TEXT (rchar);
  761.     ENDIF;
  762.     RETURN (LENGTH (strg) + (rchar <> 0));
  763. ENDPROCEDURE;
  764.  
  765. !
  766. !   Return a string describing the KEY_NAME passed.  For control characters,
  767. !   it is "^?" where the '?' is A-Z.  Otherwise, the value returned by the
  768. !   ASCII() builtin is used.
  769. !
  770. PROCEDURE vi$ascii_name (key_n)
  771.     LOCAL
  772.         key;
  773.  
  774.     key := key_n;
  775.     IF (GET_INFO (key, "TYPE") = KEYWORD) THEN
  776.         key := INT (key);
  777.     ENDIF;
  778.     key := (key - INT(CTRL_A_KEY)) / (INT(CTRL_B_KEY) - INT(CTRL_A_KEY));
  779.     IF (key > 31) OR (key < 0) THEN
  780.         key := ASCII (key_n);
  781.     ELSE
  782.         key := "^" + ASCII(key+65);
  783.     ENDIF;
  784.  
  785.     RETURN (key);
  786. ENDPROCEDURE;
  787.  
  788. !
  789. !   Perform some mapping of keys to different ASCII values.
  790. !
  791. PROCEDURE vi$ascii (key_n)
  792.     IF key_n = F12 THEN
  793.         RETURN (ASCII (8));
  794.     ENDIF;
  795.     IF key_n = F11 THEN
  796.         RETURN (ASCII (27));
  797.     ENDIF;
  798.     IF key_n = PF1 THEN
  799.         RETURN (ASCII (27));
  800.     ENDIF;
  801.     IF key_n = RET_KEY THEN
  802.         RETURN (ASCII (13));
  803.     ENDIF;
  804.     IF key_n = TAB_KEY THEN
  805.         RETURN (ASCII (9));
  806.     ENDIF;
  807.     RETURN (ASCII (key_n));
  808. ENDPROCEDURE;
  809.  
  810. !
  811. !   Move up by screens
  812. !
  813. PROCEDURE vi$prev_screen
  814.     ON_ERROR
  815.     ENDON_ERROR;
  816.  
  817.     MOVE_VERTICAL (-vi$cur_active_count *
  818.                         GET_INFO (CURRENT_WINDOW, "VISIBLE_LENGTH"));
  819.  
  820.     vi$beep_position (vi$first_no_space(0), 0, 1);
  821. ENDPROCEDURE;
  822.  
  823. !
  824. !   Move down by screens
  825. !
  826. PROCEDURE vi$next_screen
  827.     ON_ERROR
  828.     ENDON_ERROR;
  829.  
  830.     MOVE_VERTICAL (vi$cur_active_count *
  831.                         (GET_INFO (CURRENT_WINDOW, "VISIBLE_LENGTH") + 2));
  832.  
  833.     vi$beep_position (vi$first_no_space(0), 0, 1);
  834. ENDPROCEDURE;
  835.  
  836. !
  837. ! Scroll forward one screen
  838. !
  839. PROCEDURE vi$screen_forward
  840.  
  841.     vi$scroll_screen (1);
  842.  
  843. ENDPROCEDURE;
  844.  
  845. !
  846. ! Scroll back one screen
  847. !
  848. PROCEDURE vi$screen_backward
  849.  
  850.     vi$scroll_screen (-1);
  851.  
  852. ENDPROCEDURE;
  853.  
  854. !
  855. !   Scroll the screen up or down depending on the sign of "how_many_screens"
  856. !   The magnitude actually has effect as well, but is never greater than 1
  857. !   in this use.
  858. !
  859. PROCEDURE vi$scroll_screen (how_many_screens)
  860.  
  861.     LOCAL
  862.         scroll_window,          ! Window to be scrolled
  863.         this_window,            ! Current window
  864.         this_column,            ! Current column in scroll_window
  865.         this_row,               ! Current row in scroll_window
  866.         old_scroll_top,         ! Original value of scroll_top
  867.         old_scroll_bottom,      ! Original value of scroll_bottom
  868.         old_scroll_amount;      ! Original value of scroll_amount
  869.  
  870.     ! Trap and ignore messages about move beyond buffer boundaries -
  871.     ! just move to top or bottom line of buffer
  872.  
  873.     ON_ERROR
  874.     ENDON_ERROR;
  875.  
  876.     this_window := CURRENT_WINDOW;
  877.  
  878.     scroll_window := this_window;
  879.  
  880.     IF vi$active_count <> 0 THEN
  881.         vi$how_much_scroll := vi$cur_active_count;
  882.     ENDIF;
  883.  
  884.     this_row := GET_INFO (scroll_window, "CURRENT_ROW");
  885.  
  886.     IF this_row = 0 THEN
  887.         this_row := GET_INFO (scroll_window, "VISIBLE_TOP");
  888.     ENDIF;
  889.  
  890.     this_column := GET_INFO (scroll_window, "CURRENT_COLUMN");
  891.     POSITION (LINE_BEGIN);
  892.  
  893.     old_scroll_top := GET_INFO (scroll_window, "SCROLL_TOP");
  894.     old_scroll_bottom := GET_INFO (scroll_window, "SCROLL_BOTTOM");
  895.     old_scroll_amount := GET_INFO (scroll_window, "SCROLL_AMOUNT");
  896.  
  897.     SET (SCROLLING, scroll_window, ON,
  898.                     this_row - GET_INFO (scroll_window, "VISIBLE_TOP"),
  899.                     GET_INFO (scroll_window, "VISIBLE_BOTTOM") - this_row, 0);
  900.  
  901.     MOVE_VERTICAL (how_many_screens * vi$how_much_scroll);
  902.     vi$update (scroll_window);
  903.  
  904.     IF this_window <> CURRENT_WINDOW THEN
  905.         POSITION (this_window);
  906.     ENDIF;
  907.  
  908.     SET (SCROLLING, scroll_window, ON, old_scroll_top, old_scroll_bottom,
  909.                                                             old_scroll_amount);
  910. ENDPROCEDURE;
  911.  
  912. !
  913. !   Move forward logical words
  914. !
  915. PROCEDURE vi$_word_forward
  916.     vi$beep_position (vi$word_move (1), 0, 1);
  917. ENDPROCEDURE;
  918.  
  919. !
  920. !   Move backward logical words
  921. !
  922. PROCEDURE vi$_word_back
  923.     vi$beep_position (vi$word_move(-1), 0, 1);
  924. ENDPROCEDURE;
  925.  
  926. !
  927. !   Move by logical word taking into account the repeat count
  928. !
  929. PROCEDURE vi$word_move(dir)
  930.     LOCAL
  931.         old_pos,
  932.         pos;
  933.  
  934.     old_pos := MARK (NONE);
  935.  
  936.     IF vi$active_count <= 0 THEN
  937.         vi$active_count := 1;
  938.     ENDIF;
  939.  
  940.     LOOP
  941.         pos := vi$move_logical_word (dir);
  942.         EXITIF pos = 0;
  943.         POSITION (pos);
  944.         vi$active_count := vi$active_count - 1;
  945.         EXITIF vi$active_count = 0;
  946.     ENDLOOP;
  947.  
  948.     vi$yank_mode := VI$IN_LINE_MODE;
  949.     RETURN (vi$retpos (old_pos));
  950. ENDPROCEDURE;
  951.  
  952. !
  953. !   Move to end of logical word
  954. !
  955. PROCEDURE vi$_word_end
  956.     vi$beep_position (vi$word_end, 0, 1);
  957. ENDPROCEDURE;
  958.  
  959. !
  960. !   Move to end of physical word
  961. !
  962. PROCEDURE vi$_full_word_end
  963.     vi$beep_position (vi$full_word_end, 0, 1);
  964. ENDPROCEDURE;
  965.  
  966. !
  967. !   Move to the end of the current word.
  968. !
  969. PROCEDURE vi$word_end
  970.     LOCAL
  971.         old_pos,
  972.         pos;
  973.  
  974.     old_pos := MARK (NONE);
  975.  
  976.     IF vi$active_count <= 0 THEN
  977.         vi$active_count := 1;
  978.     ENDIF;
  979.  
  980.     LOOP
  981.         pos := vi$move_logical_end;
  982.         EXITIF pos = 0;
  983.         POSITION (pos);
  984.         vi$active_count := vi$active_count - 1;
  985.         EXITIF vi$active_count = 0;
  986.     ENDLOOP;
  987.  
  988.     vi$yank_mode := VI$IN_LINE_MODE;
  989.     RETURN (vi$retpos (old_pos));
  990. ENDPROCEDURE;
  991.  
  992. !
  993. !   Move to the end of a blank (eol is also considered blank) terminated word.
  994. !
  995. PROCEDURE vi$full_word_end
  996.  
  997.     LOCAL
  998.         old_pos,
  999.         pos;
  1000.  
  1001.     old_pos := MARK (NONE);
  1002.  
  1003.     IF vi$active_count <= 0 THEN
  1004.         vi$active_count := 1;
  1005.     ENDIF;
  1006.  
  1007.     LOOP
  1008.         pos := vi$move_full_end;
  1009.         EXITIF pos = 0;
  1010.         POSITION (pos);
  1011.         vi$active_count := vi$active_count - 1;
  1012.         EXITIF vi$active_count = 0;
  1013.     ENDLOOP;
  1014.  
  1015.     vi$yank_mode := VI$IN_LINE_MODE;
  1016.     RETURN (vi$retpos (old_pos));
  1017. ENDPROCEDURE;
  1018.  
  1019. !
  1020. !   Move forward by ONE white-space delimited word
  1021. !
  1022. PROCEDURE vi$_full_word_forward
  1023.     vi$beep_position (vi$full_word_move (1), 0, 1);
  1024. ENDPROCEDURE;
  1025.  
  1026. !
  1027. !
  1028. !   Move backward by ONE white-space delimited word
  1029. !
  1030. PROCEDURE vi$_full_word_back
  1031.     vi$beep_position (vi$full_word_move (-1), 0, 1);
  1032. ENDPROCEDURE;
  1033.  
  1034. !
  1035. !   Move by physical word taking the repeat count into account
  1036. !
  1037. PROCEDURE vi$full_word_move (dir)
  1038.  
  1039.     LOCAL
  1040.         old_pos,
  1041.         pos;
  1042.  
  1043.     old_pos := MARK (NONE);
  1044.  
  1045.     IF vi$active_count <= 0 THEN
  1046.         vi$active_count := 1;
  1047.     ENDIF;
  1048.  
  1049.     LOOP
  1050.         pos := vi$move_full_word (dir);
  1051.         EXITIF pos = 0;
  1052.         POSITION (pos);
  1053.         vi$active_count := vi$active_count - 1;
  1054.         EXITIF vi$active_count = 0;
  1055.     ENDLOOP;
  1056.  
  1057.     vi$yank_mode := VI$IN_LINE_MODE;
  1058.     RETURN (vi$retpos (old_pos));
  1059. ENDPROCEDURE;
  1060.  
  1061. !
  1062. !   Move the cursor by BLANK separated words.  DIRECTION is either
  1063. !   +1, or -1 to indicate the direction (forward, or backword respectfully)
  1064. !   to move
  1065. !
  1066. PROCEDURE vi$move_full_word (direction)
  1067.  
  1068.     LOCAL
  1069.         typ,
  1070.         pos;
  1071.  
  1072.     pos := MARK (NONE);
  1073.  
  1074.     IF (direction = -1) THEN
  1075.         LOOP
  1076.             EXITIF (MARK (NONE) = BEGINNING_OF (CURRENT_BUFFER));
  1077.             MOVE_HORIZONTAL (-1);
  1078.             typ := vi$get_type (CURRENT_CHARACTER);
  1079.             EXITIF (typ <> VI$SPACE_TYPE) AND (typ <> VI$EOL_TYPE);
  1080.         ENDLOOP;
  1081.     ENDIF;
  1082.  
  1083.     LOOP
  1084.         EXITIF ((MARK (NONE) = BEGINNING_OF (CURRENT_BUFFER)) AND
  1085.                 (direction = -1));
  1086.         EXITIF ((MARK (NONE) = END_OF (CURRENT_BUFFER)) AND
  1087.                 (direction = 1));
  1088.         EXITIF (CURRENT_CHARACTER = "");
  1089.         EXITIF vi$get_type (CURRENT_CHARACTER) = VI$SPACE_TYPE;
  1090.         MOVE_HORIZONTAL (direction);
  1091.     ENDLOOP;
  1092.  
  1093.     ! A hack to make change work like it is supposed to with "cw".
  1094.  
  1095.     IF (vi$command_type = VI$CHANGE_TYPE) AND (direction = 1) THEN
  1096.         vi$new_endpos := MARK (NONE);
  1097.     ENDIF;
  1098.  
  1099.     IF (direction = 1) THEN
  1100.         LOOP
  1101.             EXITIF (MARK (NONE) = END_OF (CURRENT_BUFFER));
  1102.             EXITIF (CURRENT_CHARACTER = "") AND
  1103.                                         (vi$command_type <> VI$OTHER_TYPE);
  1104.             MOVE_HORIZONTAL (1);
  1105.             EXITIF vi$get_type (CURRENT_CHARACTER) <> VI$SPACE_TYPE;
  1106.         ENDLOOP;
  1107.     ELSE
  1108.         IF (MARK (NONE) <> BEGINNING_OF (CURRENT_BUFFER)) THEN
  1109.             MOVE_HORIZONTAL (1);
  1110.         ENDIF;
  1111.     ENDIF;
  1112.  
  1113.     RETURN (vi$retpos(pos));
  1114. ENDPROCEDURE;
  1115.  
  1116. !
  1117. !   Move the cursor by logical words.  Note that words in this case are
  1118. !   delimited by a change from one type of character to another.  The
  1119. !   predefined types
  1120. !
  1121. !       VI$ALPHA_TYPE, VI$PUNCT_TYPE, and VI$SPACE_TYPE
  1122. !
  1123. !   are used to detect transitions from one word to the next;
  1124. !
  1125. PROCEDURE vi$move_logical_word (direction)
  1126.  
  1127.     LOCAL
  1128.         this_type,
  1129.         this_char,
  1130.         typec,
  1131.         pos;
  1132.  
  1133.     pos := MARK (NONE);
  1134.  
  1135.     !   If direction is back, then skip SPACE characters until no space
  1136.     !   is found.
  1137.  
  1138.     IF (direction = -1) THEN
  1139.         LOOP
  1140.             EXITIF (MARK (NONE) = BEGINNING_OF (CURRENT_BUFFER));
  1141.             MOVE_HORIZONTAL (-1);
  1142.             typec := vi$get_type (CURRENT_CHARACTER);
  1143.             EXITIF (typec <> VI$SPACE_TYPE) AND (typec <> VI$EOL_TYPE);
  1144.         ENDLOOP;
  1145.     ENDIF;
  1146.  
  1147.     IF (MARK (NONE) <> END_OF (CURRENT_BUFFER)) THEN
  1148.         this_char := CURRENT_CHARACTER;
  1149.         this_type := vi$get_type (this_char);
  1150.     ENDIF;
  1151.  
  1152.     LOOP
  1153.         EXITIF ((MARK (NONE) = BEGINNING_OF (CURRENT_BUFFER)) AND
  1154.                 (direction = -1));
  1155.  
  1156.         EXITIF ((MARK (NONE) = END_OF (CURRENT_BUFFER)) AND
  1157.                 (direction = 1));
  1158.  
  1159.         MOVE_HORIZONTAL (direction);
  1160.         EXITIF (vi$get_type (CURRENT_CHARACTER) <> this_type);
  1161.     ENDLOOP;
  1162.  
  1163.     ! A hack to make change work like it is supposed to with "cw".
  1164.  
  1165.     IF (vi$command_type = VI$CHANGE_TYPE) AND (direction = 1) THEN
  1166.         vi$new_endpos := MARK (NONE);
  1167.     ENDIF;
  1168.  
  1169.     IF (direction = 1) THEN
  1170.         LOOP
  1171.             EXITIF (MARK (NONE) = BEGINNING_OF (CURRENT_BUFFER)) AND
  1172.                     (direction = -1);
  1173.             EXITIF (MARK (NONE) = END_OF (CURRENT_BUFFER)) AND
  1174.                     (direction = 1);
  1175.             typec := vi$get_type(CURRENT_CHARACTER);
  1176.             EXITIF (typec  < VI$SPACE_TYPE);
  1177.             EXITIF (vi$command_type <> VI$OTHER_TYPE) AND
  1178.                                                 (typec <> VI$SPACE_TYPE);
  1179.             MOVE_HORIZONTAL (1);
  1180.             EXITIF (MARK (NONE) = END_OF (CURRENT_BUFFER));
  1181.         ENDLOOP;
  1182.     ELSE
  1183.         IF (MARK (NONE) <> BEGINNING_OF (CURRENT_BUFFER)) THEN
  1184.             MOVE_HORIZONTAL (1);
  1185.         ENDIF;
  1186.     ENDIF;
  1187.  
  1188.     RETURN (vi$retpos (pos));
  1189. ENDPROCEDURE;
  1190.  
  1191. !
  1192. !   Move the cursor by BLANK separated words.  DIRECTION is either
  1193. !   +1, or -1 to indicate the direction (forward, or backword respectfully)
  1194. !   to move
  1195. !
  1196. PROCEDURE vi$move_full_end
  1197.  
  1198.     LOCAL
  1199.         ctype,
  1200.         pos;
  1201.  
  1202.     pos := MARK (NONE);
  1203.  
  1204.     IF (pos = END_OF (CURRENT_BUFFER)) THEN
  1205.         RETURN (0);
  1206.     ENDIF;
  1207.  
  1208.     LOOP
  1209.         MOVE_HORIZONTAL (1);
  1210.         EXITIF (MARK (NONE) = END_OF (CURRENT_BUFFER));
  1211.         ctype := vi$get_type (CURRENT_CHARACTER);
  1212.         EXITIF (ctype <> VI$SPACE_TYPE) AND (ctype <> VI$EOL_TYPE);
  1213.     ENDLOOP;
  1214.  
  1215.     LOOP
  1216.         EXITIF (MARK (NONE) = END_OF (CURRENT_BUFFER));
  1217.         ctype := vi$get_type (CURRENT_CHARACTER);
  1218.         EXITIF (ctype = VI$EOL_TYPE) OR (ctype = VI$SPACE_TYPE);
  1219.         MOVE_HORIZONTAL (1);
  1220.     ENDLOOP;
  1221.  
  1222.     MOVE_HORIZONTAL (-1);
  1223.     RETURN (vi$retpos(pos));
  1224. ENDPROCEDURE;
  1225.  
  1226. !
  1227. !   Move the cursor by logical words.  Note that words in this case are
  1228. !   delimited by a change from one type of character to another.  The
  1229. !   predefined types
  1230. !
  1231. !       VI$ALPHA_TYPE, VI$PUNCT_TYPE, and VI$SPACE_TYPE
  1232. !
  1233. !   are used to detect transitions from one word to the next;
  1234. !
  1235. PROCEDURE vi$move_logical_end
  1236.  
  1237.     LOCAL
  1238.         ctype,
  1239.         this_type,
  1240.         this_char,
  1241.         pos;
  1242.  
  1243.     pos := MARK (NONE);
  1244.  
  1245.     IF (pos = END_OF (CURRENT_BUFFER)) THEN
  1246.         RETURN (0);
  1247.     ENDIF;
  1248.  
  1249.     LOOP
  1250.         MOVE_HORIZONTAL (1);
  1251.         EXITIF (MARK (NONE) = END_OF (CURRENT_BUFFER));
  1252.         ctype := vi$get_type (CURRENT_CHARACTER);
  1253.         EXITIF (ctype <> VI$SPACE_TYPE) AND (ctype <> VI$EOL_TYPE);
  1254.     ENDLOOP;
  1255.  
  1256.     this_char := CURRENT_CHARACTER;
  1257.     this_type := vi$get_type (this_char);
  1258.  
  1259.     LOOP
  1260.         EXITIF (MARK (NONE) = END_OF (CURRENT_BUFFER));
  1261.         EXITIF (CURRENT_CHARACTER) = "";
  1262.         EXITIF (vi$get_type (CURRENT_CHARACTER) <> this_type);
  1263.         MOVE_HORIZONTAL (1);
  1264.     ENDLOOP;
  1265.  
  1266.     MOVE_HORIZONTAL (-1);
  1267.     RETURN (vi$retpos (pos));
  1268. ENDPROCEDURE;
  1269.  
  1270. !
  1271. !   Return the logical type of the character passed.  This is typically used
  1272. !   by the move_by_word routines to determine when a word ends.
  1273. !
  1274. PROCEDURE vi$get_type (this_char)
  1275.  
  1276.     LOCAL
  1277.         this_type;
  1278.  
  1279.     IF (this_char = "") THEN
  1280.         RETURN (VI$EOL_TYPE);
  1281.     ENDIF;
  1282.  
  1283.     this_type := VI$SPACE_TYPE;
  1284.  
  1285.     IF (INDEX (vi$_alpha_chars, this_char) <> 0) THEN
  1286.         this_type := VI$ALPHA_TYPE;
  1287.     ELSE
  1288.         IF (INDEX (vi$_punct_chars, this_char) <> 0) THEN
  1289.             this_type := VI$PUNCT_TYPE;
  1290.         ENDIF;
  1291.     ENDIF;
  1292.  
  1293.     RETURN (this_type);
  1294. ENDPROCEDURE;
  1295.  
  1296. !
  1297. !   This procedure determines what line the cursor is currently positioned
  1298. !   on. and then prints that information, along with other items of interest
  1299. !   in the message window.
  1300. !
  1301. PROCEDURE vi$what_line
  1302.  
  1303.     LOCAL
  1304.         bmode,
  1305.         percent,
  1306.         mod,
  1307.         outfile,
  1308.         lines,
  1309.         nowr,
  1310.         pos,
  1311.         cnt;
  1312.  
  1313.     ON_ERROR;
  1314.         lines := GET_INFO (CURRENT_BUFFER, "RECORD_COUNT");
  1315.         IF (cnt) > lines THEN
  1316.             cnt := lines;
  1317.         ENDIF;
  1318.  
  1319.         IF lines = 0 THEN
  1320.             percent := 0;
  1321.         ELSE
  1322.             percent := (cnt*100)/lines;
  1323.         ENDIF;
  1324.  
  1325.         vi$info (FAO ("!ASLine !UL of !UL, !UL%, !AS!AS!AS",
  1326.                             nowr, cnt, lines, percent, bmode, mod, outfile));
  1327.  
  1328.         SET (TIMER, OFF);
  1329.         RETURN;
  1330.     ENDON_ERROR;
  1331.  
  1332.     IF (vi$getbufmode (CURRENT_BUFFER)) THEN
  1333.         bmode := "[readonly] ";
  1334.     ELSE
  1335.         bmode := "";
  1336.     ENDIF;
  1337.  
  1338.     nowr := " ";
  1339.     IF (GET_INFO (CURRENT_BUFFER, "NO_WRITE")) AND (bmode = "") THEN
  1340.         nowr := "*";
  1341.     ENDIF;
  1342.  
  1343.     mod := "";
  1344.     IF GET_INFO (CURRENT_BUFFER, "MODIFIED") THEN
  1345.         mod := "[modified] ";
  1346.     ENDIF;
  1347.  
  1348.     pos := MARK(NONE);
  1349.     POSITION (LINE_BEGIN);
  1350.  
  1351.     cnt := 0;
  1352.     lines := 0;
  1353.     outfile := GET_INFO (CURRENT_BUFFER, "OUTPUT_FILE");
  1354.     IF (outfile = 0) THEN
  1355.         outfile := "Not Edited";
  1356.     ELSE
  1357.         outfile := """"+outfile+"""";
  1358.     ENDIF;
  1359.  
  1360.     cnt := vi$cur_line_no;
  1361.  
  1362.     POSITION (pos);
  1363.  
  1364.     lines := GET_INFO (CURRENT_BUFFER, "RECORD_COUNT");
  1365.     IF (cnt) > lines THEN
  1366.         cnt := lines;
  1367.     ENDIF;
  1368.  
  1369.     IF lines = 0 THEN
  1370.         percent := 0;
  1371.     ELSE
  1372.         percent := (cnt*100)/lines;
  1373.     ENDIF;
  1374.  
  1375.     vi$info (FAO ("!ASLine !UL of !UL, !UL%, !AS!AS!AS",
  1376.                             nowr, cnt, lines, percent, bmode, mod, outfile));
  1377.     SET (TIMER, OFF);
  1378. ENDPROCEDURE;
  1379.  
  1380. !
  1381. PROCEDURE vi$file_info
  1382.  
  1383.     LOCAL
  1384.         bmode,
  1385.         outfile;
  1386.  
  1387.     IF (vi$getbufmode (CURRENT_BUFFER)) THEN
  1388.         bmode := "[readonly] ";
  1389.     ELSE
  1390.         bmode := "";
  1391.     ENDIF;
  1392.  
  1393.     outfile := GET_INFO (CURRENT_BUFFER, "OUTPUT_FILE");
  1394.     IF (outfile = 0) THEN
  1395.         outfile := "Not Edited";
  1396.     ELSE
  1397.         outfile := """"+outfile+"""";
  1398.     ENDIF;
  1399.  
  1400.     vi$info (FAO ("!AS!AS !UL lines", outfile, bmode,
  1401.             GET_INFO (CURRENT_BUFFER, "RECORD_COUNT")));
  1402. ENDPROCEDURE;
  1403.  
  1404. !
  1405. !   This function moves to "pos" if it is non-zero.  If "pos" is zero, then
  1406. !   any current macro is aborted, and the current position is not changed.
  1407. !   "save_pos" is a boolean value that indicates whether or not the current
  1408. !   location is remembered so that it can be returned to later with the
  1409. !   "'" (go to marker) command.
  1410. !
  1411. PROCEDURE vi$beep_position (pos, save_pos, dobeep)
  1412.     IF (pos <> 0) THEN
  1413.         IF save_pos THEN
  1414.             vi$old_place := MARK (NONE);
  1415.         ENDIF;
  1416.         POSITION (pos);
  1417.     ELSE
  1418.         IF dobeep THEN
  1419.             vi$beep;
  1420.         ENDIF;
  1421.         RETURN (vi$abort (0));
  1422.     ENDIF;
  1423.     RETURN (pos);
  1424. ENDPROCEDURE;
  1425.  
  1426. !
  1427. !   This function implements the command mode function of joining the
  1428. !   current line with the one below it.
  1429. !
  1430. !   The undo operation consists of deleting the line created by joining
  1431. !   the two lines, and then inserting the original contents of the two
  1432. !   joined lines.
  1433. !
  1434. PROCEDURE vi$_join_lines
  1435.  
  1436.     LOCAL
  1437.         start,
  1438.         spos,
  1439.         epos,
  1440.         pos,
  1441.         plen,
  1442.         len;
  1443.  
  1444.     ON_ERROR
  1445.         !  Throw away moved beyond end of buffer messages.
  1446.         RETURN;
  1447.     ENDON_ERROR;
  1448.  
  1449.     spos := MARK (NONE);
  1450.     POSITION (LINE_BEGIN);
  1451.     pos := MARK (NONE);
  1452.     IF (MARK (NONE) <> END_OF (CURRENT_BUFFER)) THEN
  1453.         MOVE_VERTICAL (1);
  1454.         IF (MARK (NONE) <> END_OF (CURRENT_BUFFER)) THEN
  1455.             MOVE_VERTICAL (1);
  1456.             MOVE_HORIZONTAL (-1);
  1457.             epos := MARK (NONE);
  1458.             POSITION (spos);
  1459.             vi$save_for_undo (CREATE_RANGE (pos, epos, NONE),
  1460.                                                             VI$LINE_MODE, 1);
  1461.             POSITION (pos);
  1462.         ELSE
  1463.             RETURN;
  1464.         ENDIF;
  1465.     ELSE
  1466.         RETURN;
  1467.     ENDIF;
  1468.  
  1469.     POSITION (LINE_END);
  1470.  
  1471.     LOOP
  1472.         EXITIF (CURRENT_OFFSET = 0);
  1473.         MOVE_HORIZONTAL (-1);
  1474.         EXITIF INDEX (vi$_space_tab, CURRENT_CHARACTER) = 0;
  1475.         ERASE_CHARACTER (1);
  1476.     ENDLOOP;
  1477. a    plen := LENGTH (vi$current_line);
  1478.     vi$_next_line;
  1479.  
  1480.     IF (CURRENT_OFFSET > 0) AND (plen > 0) THEN
  1481.         ERASE_CHARACTER (-CURRENT_OFFSET);
  1482.     ENDIF;
  1483.  
  1484.     len := LENGTH (vi$current_line);
  1485.     APPEND_LINE;
  1486.  
  1487.     IF (len > 0) AND (plen > 0) THEN
  1488.         COPY_TEXT (" ");
  1489.         MOVE_HORIZONTAL (-1);
  1490.     ELSE
  1491.         vi$check_rmarg;
  1492.     ENDIF;
  1493.  
  1494.     pos := MARK (NONE);
  1495.  
  1496.     POSITION (LINE_BEGIN);
  1497.     vi$undo_start := MARK (NONE);
  1498.     POSITION (LINE_END);
  1499.     vi$undo_end := MARK (NONE);
  1500.  
  1501.     POSITION (pos);
  1502. ENDPROCEDURE;
  1503.  
  1504. !
  1505. !   This function filters the selected region through the command
  1506. !   given.
  1507. !
  1508. PROCEDURE vi$region_filter
  1509.  
  1510.     LOCAL
  1511.         era_range,
  1512.         prog,
  1513.         nchar,
  1514. $$EOD$$
  1515.