home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume4 / vms-vi-2 / part09 < prev    next >
Encoding:
Internet Message Format  |  1989-02-03  |  38.6 KB

  1. Path: xanth!mcnc!gatech!bloom-beacon!bu-cs!mirror!necntc!ncoast!allbery
  2. From: gregg@a.cs.okstate.edu (Gregg Wonderly)
  3. Newsgroups: comp.sources.misc
  4. Subject: v04i100: TPUVI for VMS part 9 of 17
  5. Message-ID: <8809212103.AA08775@uunet.UU.NET>
  6. Date: 27 Sep 88 01:54:42 GMT
  7. Sender: allbery@ncoast.UUCP
  8. Reply-To: gregg@a.cs.okstate.edu (Gregg Wonderly)
  9. Lines: 1504
  10. Approved: allbery@ncoast.UUCP
  11.  
  12. Posting-number: Volume 4, Issue 100
  13. Submitted-by: "Gregg Wonderly" <gregg@a.cs.okstate.edu>
  14. Archive-name: vms-vi-2/Part09
  15.  
  16. $ WRITE SYS$OUTPUT "Creating ""VI.5"""
  17. $ CREATE VI.5
  18. $ DECK/DOLLARS=$$EOD$$
  19.         next_key := vi$read_a_key;
  20.         EXITIF INDEX (vi$_numeric_chars, ASCII (next_key)) = 0;
  21.         vi$active_count := vi$active_count * 10 +
  22.                                             INT (ASCII (KEY_NAME (next_key)));
  23.     ENDLOOP;
  24.  
  25.     IF (next_key = F11) OR ((next_key <> RET_KEY) AND
  26.                     (next_key <> KEY_NAME ('.')) AND
  27.                     (next_key <> KEY_NAME ('+')) AND
  28.                     (next_key <> KEY_NAME ('-'))) THEN
  29.         vi$active_count := 0;
  30.         RETURN;
  31.     ENDIF;
  32.  
  33.     IF (vi$active_count > 0) AND (next_key <> KEY_NAME ('.')) THEN
  34.         vi$old_place := MARK (NONE);
  35.         pos := vi$to_line (vi$active_count);
  36.     ELSE
  37.         pos := MARK (NONE);
  38.     ENDIF;
  39.  
  40.     cur_window := CURRENT_WINDOW;
  41.     scroll_top    := GET_INFO (cur_window, "SCROLL_TOP");
  42.     scroll_bottom := GET_INFO (cur_window, "SCROLL_BOTTOM");
  43.     scroll_amount := GET_INFO (cur_window, "SCROLL_AMOUNT");
  44.  
  45.     done := 0;
  46.  
  47.     IF next_key = KEY_NAME ('-') THEN
  48.         scrl_value := (GET_INFO (cur_window, "VISIBLE_LENGTH") / 2);
  49.  
  50.         SET (SCROLLING, cur_window, ON, scrl_value, scrl_value, scrl_value);
  51.  
  52.         POSITION (pos);
  53.         vi$update (cur_window);
  54.         done := 1;
  55.     ELSE
  56.         IF next_key = KEY_NAME ('+') THEN
  57.             scrl_value := GET_INFO (cur_window, "VISIBLE_LENGTH");
  58.             SET (SCROLLING, cur_window, ON, scrl_value, scrl_value, scrl_value);
  59.             POSITION (pos);
  60.             vi$update (cur_window);
  61.  
  62.             done := 1;
  63.         ELSE
  64.             IF next_key = RET_KEY THEN
  65.                 vi$do_set_window (vi$cur_active_count);
  66.                 scrl_value := GET_INFO (cur_window, "VISIBLE_LENGTH");
  67.                 SET (SCROLLING, cur_window, ON, 0, scrl_value, scrl_value);
  68.                 POSITION (pos);
  69.                 vi$update (cur_window);
  70.  
  71.                 done := 1;
  72.             ELSE
  73.                 IF next_key = KEY_NAME ('.') THEN
  74.                     vi$pos_in_middle (MARK (NONE));
  75.                     done := 0;
  76.                 ENDIF;
  77.             ENDIF;
  78.         ENDIF;
  79.     ENDIF;
  80.  
  81.     IF (done) THEN
  82.         SET (SCROLLING, cur_window, ON, scroll_top, scroll_bottom,
  83.                                                                 scroll_amount);
  84.     ENDIF;
  85. ENDPROCEDURE;
  86.  
  87. !
  88. !   Perform the 'r' command
  89. !
  90. PROCEDURE vi$_replace_char
  91.  
  92.     LOCAL
  93.         act_cnt,
  94.         key,
  95.         pos;
  96.  
  97.     ON_ERROR;
  98.         POSITION (pos);
  99.         RETURN;
  100.     ENDON_ERROR;
  101.  
  102.     pos := MARK (NONE);
  103.     act_cnt := vi$cur_active_count;
  104.     IF (vi$show_mode) THEN
  105.         vi$mess_select (BOLD);
  106.         MESSAGE (FAO ("!7*  REPLACE"));
  107.         vi$mess_select (REVERSE);
  108.     ENDIF;
  109.     key := vi$read_a_key;
  110.  
  111.     IF (key = F11) THEN
  112.         IF (vi$show_mode) THEN
  113.             MESSAGE ("");
  114.         ENDIF;
  115.         RETURN;
  116.     ENDIF;
  117.  
  118.     IF (key = TAB_KEY) THEN
  119.          key := ASCII (9);
  120.     ELSE
  121.         IF (key = RET_KEY) THEN
  122.              key := ASCII (13);
  123.         ELSE
  124.             IF (key = DEL_KEY) THEN
  125.                  key := ASCII (8);
  126.             ELSE
  127.                 key := ASCII (key);
  128.             ENDIF;
  129.         ENDIF;
  130.     ENDIF;
  131.  
  132.     IF ((CURRENT_OFFSET + act_cnt) <= LENGTH (vi$current_line)) THEN
  133.         IF (key = ASCII (13)) THEN
  134.             MOVE_HORIZONTAL (act_cnt);
  135.         ELSE
  136.             MOVE_HORIZONTAL (act_cnt - 1);
  137.         ENDIF;
  138.         vi$save_for_undo (CREATE_RANGE (pos, MARK(NONE), NONE),
  139.                                                         VI$IN_LINE_MODE, 1);
  140.         IF (key = ASCII (13)) THEN
  141.             MOVE_HORIZONTAL (-act_cnt);
  142.         ELSE
  143.             MOVE_HORIZONTAL (-(act_cnt-1));
  144.         ENDIF;
  145.         IF (MARK (NONE) <> BEGINNING_OF (CURRENT_BUFFER)) THEN
  146.             MOVE_HORIZONTAL (-1);
  147.             vi$undo_start := MARK (NONE);
  148.             MOVE_HORIZONTAL (1);
  149.         ELSE
  150.             vi$undo_start := 0;
  151.         ENDIF;
  152.  
  153.         SET (OVERSTRIKE, CURRENT_BUFFER);
  154.         LOOP
  155.             IF (key = ASCII (13)) THEN
  156.                 SPLIT_LINE;
  157.                 ERASE_CHARACTER (1);
  158.             ELSE
  159.                 COPY_TEXT (key);
  160.             ENDIF;
  161.             act_cnt := act_cnt - 1;
  162.             EXITIF act_cnt = 0;
  163.         ENDLOOP;
  164.  
  165.         IF (key = ASCII (13)) THEN
  166.             MOVE_HORIZONTAL (1);
  167.         ENDIF;
  168.  
  169.         MOVE_HORIZONTAL (-1);
  170.         vi$undo_end := MARK (NONE);
  171.  
  172.         SET (INSERT, CURRENT_BUFFER);
  173.         IF (vi$undo_start = 0) THEN
  174.             vi$undo_start := BEGINNING_OF (CURRENT_BUFFER);
  175.         ELSE
  176.             pos := MARK (NONE);
  177.             POSITION (vi$undo_start);
  178.             MOVE_HORIZONTAL (1);
  179.             vi$undo_start := MARK (NONE);
  180.             POSITION (pos);
  181.         ENDIF;
  182.     ELSE
  183.         POSITION (pos);
  184.     ENDIF;
  185.  
  186.     IF (vi$show_mode) THEN
  187.         MESSAGE ("");
  188.     ENDIF;
  189.     RETURN;
  190. ENDPROCEDURE
  191.  
  192. !
  193. !   Perform the 'R' command
  194. !
  195. PROCEDURE vi$_replace_str
  196.  
  197.     LOCAL
  198.         replace,
  199.         max_mark,
  200.         start_pos,
  201.         spos,
  202.         pos,
  203.         max_col;
  204.  
  205.     pos := MARK (NONE);
  206.     max_col := CURRENT_OFFSET;
  207.     start_pos := max_col;
  208.     POSITION (LINE_END);
  209.     max_mark := MARK(NONE);
  210.     vi$undo_end := MARK (NONE);
  211.     POSITION (pos);
  212.     vi$update (CURRENT_WINDOW);
  213.     replace := CURRENT_LINE;
  214.     spos := vi$get_undo_start;
  215.     vi$save_for_undo (CREATE_RANGE (pos, max_mark, NONE), VI$IN_LINE_MODE, 1);
  216.  
  217.     vi$line_edit (max_col, start_pos, max_mark, replace);
  218.     IF (CURRENT_CHARACTER = "") THEN
  219.         MOVE_HORIZONTAL (1);
  220.         pos := MARK (NONE);
  221.         MOVE_HORIZONTAL (-1);
  222.     ELSE
  223.         pos := MARK (NONE);
  224.     ENDIF;
  225.     vi$undo_start := vi$set_undo_start (spos);
  226.     POSITION (pos);
  227. ENDPROCEDURE;
  228.  
  229. !
  230. !   As in REAL vi, this procedure does not recognize a repeat count.
  231. !   A simple loop would make it possible to use the repeat count contained
  232. !   in "vi$active_count".  A macro is used so that all of the crap for undo
  233. !   need not be placed here.
  234. !
  235. PROCEDURE vi$_change_case
  236.     LOCAL
  237.         pos;
  238.  
  239.     vi$active_count := 0;
  240.     pos := INDEX (vi$_lower_chars, CURRENT_CHARACTER);
  241.     IF pos <> 0 THEN
  242.         vi$do_macro ("r"+SUBSTR (vi$_upper_chars, pos, 1)+"l", 0);
  243.     ELSE
  244.         pos := INDEX (vi$_upper_chars, CURRENT_CHARACTER);
  245.         IF pos <> 0 THEN
  246.             vi$do_macro ("r"+SUBSTR (vi$_lower_chars, pos, 1)+"l", 0);
  247.         ELSE
  248.             vi$kill_undo;
  249.             vi$undo_end := 0;
  250.             MOVE_HORIZONTAL (1);
  251.         ENDIF;
  252.     ENDIF;
  253.  
  254. ENDPROCEDURE;
  255.  
  256. !
  257. !
  258. !
  259. PROCEDURE vi$init_action (olen)
  260.     LOCAL
  261.         nchar;
  262.  
  263.     olen := GET_INFO (CURRENT_BUFFER, "RECORD_COUNT");
  264.  
  265.     IF (vi$select_pos = 0) THEN
  266.         nchar := vi$read_a_key;
  267.         IF (INDEX ("123456789", ASCII(nchar)) <> 0) THEN
  268.             vi$active_count := INDEX (vi$_numeric_chars, ASCII(nchar)) - 1;
  269.             LOOP
  270.                 nchar := vi$read_a_key;
  271.                 EXITIF (INDEX (vi$_numeric_chars, ASCII(nchar)) = 0);
  272.                 vi$active_count := vi$active_count *
  273.                         10 + (INDEX (vi$_numeric_chars, ASCII (nchar)) - 1);
  274.             ENDLOOP;
  275.         ENDIF;
  276.     ELSE
  277.         nchar := KEY_NAME (".");
  278.     ENDIF;
  279.     RETURN (nchar);
  280. ENDPROCEDURE;
  281.  
  282. !
  283. !
  284. !
  285. PROCEDURE vi$get_prog (nchar)
  286.     IF (vi$select_pos = 0) THEN
  287.         RETURN (LOOKUP_KEY (KEY_NAME (nchar), COMMENT, vi$move_keys));
  288.     ELSE
  289.         RETURN ("vi$get_select_pos");
  290.     ENDIF;
  291. ENDPROCEDURE;
  292.  
  293. !
  294. !
  295. !
  296. PROCEDURE vi$do_movement (prog, mtype)
  297.  
  298.     vi$endpos := 0;
  299.     vi$new_endpos := 0;
  300.     vi$command_type := mtype;
  301.  
  302.     EXECUTE (COMPILE ("vi$endpos := " + prog));
  303.     IF vi$new_endpos <> 0 THEN
  304.         vi$endpos := vi$new_endpos;
  305.     ENDIF;
  306. ENDPROCEDURE;
  307.  
  308. !
  309. !   Perform the operations associated with the 'c' command.
  310. !
  311. PROCEDURE vi$_change
  312.  
  313.     LOCAL
  314.         max_mark,
  315.         max_col,
  316.         start_col,
  317.         start_offset,
  318.         end_offset,
  319.         start_line,
  320.         end_line,
  321.         cha_range,
  322.         pos,
  323.         olen,
  324.         prog,
  325.         do_back,
  326.         nchar;
  327.  
  328.     ON_ERROR;
  329.         vi$info ("Error occured during change, at line: "+STR(ERROR_LINE));
  330.         POSITION (vi$start_pos);
  331.         RETURN;
  332.     ENDON_ERROR;
  333.  
  334.     vi$new_offset := 1;
  335.     nchar := vi$init_action (olen);
  336.  
  337.     IF (nchar = KEY_NAME ('c')) THEN
  338.         vi$_big_s;
  339.         RETURN;
  340.     ENDIF;
  341.  
  342.     ! If the movement will be backwards, then the region must not include
  343.     ! the current character.
  344.  
  345.     do_back := vi$get_direction (nchar);
  346.  
  347.     IF do_back THEN
  348.         vi$move_horizontal (-1);
  349.         vi$start_pos := MARK (NONE);
  350.         vi$move_horizontal (1);
  351.     ELSE
  352.         vi$start_pos := MARK (NONE);
  353.     ENDIF;
  354.  
  355.     prog := vi$get_prog (nchar);
  356.  
  357.     IF prog <> "" THEN
  358.         vi$do_movement (prog, VI$CHANGE_TYPE);
  359.  
  360.         POSITION (vi$start_pos);
  361.         start_offset := CURRENT_OFFSET;
  362.         POSITION (LINE_BEGIN);
  363.         start_line := MARK (NONE);
  364.         POSITION (vi$start_pos);
  365.  
  366.         IF (vi$endpos <> 0) THEN
  367.             POSITION (vi$endpos);
  368.             POSITION (LINE_BEGIN);
  369.             end_line := MARK (NONE);
  370.             POSITION (vi$endpos);
  371.  
  372.             IF (MARK (NONE) <> BEGINNING_OF (CURRENT_BUFFER)) AND
  373.                         (NOT do_back) AND
  374.                         (INDEX (vi$weird_moves, ASCII (nchar)) = 0) THEN
  375.                 vi$move_horizontal (-1);
  376.             ENDIF;
  377.             end_offset := CURRENT_OFFSET + 1;
  378.  
  379.             cha_range := CREATE_RANGE (vi$start_pos, MARK (NONE), NONE);
  380.  
  381.             IF (start_line <> end_line) THEN
  382.                 IF (cha_range <> 0) THEN
  383.                     POSITION (vi$start_pos);
  384.  
  385.                     vi$undo_start := vi$get_undo_start;
  386.                     vi$save_for_undo (cha_range, vi$yank_mode, 0);
  387.  
  388.                     vi$type2buf (STR(vi$yank_mode), vi$temp_buf);
  389.                     vi$cur_text := vi$cp2buf (cha_range, vi$temp_buf);
  390.  
  391.                     ERASE (cha_range);
  392.  
  393.                     IF (vi$while_not_esc = 0) THEN
  394.                         vi$undo_end := 0;
  395.                     ELSE
  396.                         vi$undo_end := MARK (NONE);
  397.                         vi$undo_start := vi$set_undo_start (vi$undo_start);
  398.                         POSITION (vi$undo_end);
  399.                         IF (CURRENT_CHARACTER = "") THEN
  400.                             MOVE_HORIZONTAL (1);
  401.                         ENDIF;
  402.                     ENDIF;
  403.                 ELSE
  404.                     vi$info ("Internal error while changing!");
  405.                 ENDIF;
  406.             ELSE
  407.                 IF (cha_range <> 0) THEN
  408.                     IF (start_offset < end_offset) THEN
  409.                         max_col := end_offset;
  410.                         MOVE_HORIZONTAL (1);
  411.                         max_mark := MARK (NONE);
  412.                         MOVE_HORIZONTAL (-1);
  413.                         start_col := start_offset;
  414.                     ELSE
  415.                         POSITION (vi$start_pos);
  416.                         MOVE_HORIZONTAL (1);
  417.                         max_col := CURRENT_OFFSET;
  418.                         max_mark := MARK (NONE);
  419.                         POSITION (vi$start_pos);
  420.                         start_col := end_offset - 1;
  421.                     ENDIF;
  422.  
  423.                     cha_range := SUBSTR (vi$current_line, start_col + 1,
  424.                                                     max_col - start_col);
  425.  
  426.                     vi$type2buf (STR (vi$yank_mode), vi$temp_buf);
  427.                     vi$cur_text := vi$cp2buf (cha_range, vi$temp_buf);
  428.  
  429.                     vi$save_for_undo (cha_range, vi$yank_mode, 0);
  430.  
  431.                     SET (OVERSTRIKE, CURRENT_BUFFER);
  432.                     COPY_TEXT ("$");
  433.                     SET (INSERT, CURRENT_BUFFER);
  434.  
  435.                     IF (start_offset < end_offset) THEN
  436.                         POSITION (vi$start_pos);
  437.                     ELSE
  438.                         POSITION (vi$endpos);
  439.                     ENDIF;
  440.  
  441.                     vi$update (CURRENT_WINDOW);
  442.  
  443.                     vi$undo_start := vi$get_undo_start;
  444.  
  445.                     if (vi$line_edit (max_col, start_col, max_mark, 0) = 0) THEN
  446.                         vi$undo_end := 0;
  447.                     ELSE
  448.                         vi$undo_end := MARK (NONE);
  449.                         IF (CURRENT_CHARACTER = "") THEN
  450.                             MOVE_HORIZONTAL (1);
  451.                         ENDIF;
  452.                     ENDIF;
  453.  
  454.                     pos := MARK (NONE);
  455.  
  456.                     vi$undo_start := vi$set_undo_start (vi$undo_start);
  457.                     POSITION (pos);
  458.                 ELSE
  459.                     vi$info ("Internal error while changing!");
  460.                 ENDIF;
  461.             ENDIF;
  462.         ELSE
  463.             vi$abort (0);
  464.         ENDIF;
  465.     ELSE
  466.         vi$abort (0);
  467.     ENDIF;
  468.  
  469.     vi$check_length (olen);
  470. ENDPROCEDURE;
  471.  
  472. !
  473. !   Decide which direction the movement will be based on whether or not
  474. !   the last movement was a t, T, f, F, or other backward movement.
  475. !
  476. PROCEDURE vi$get_direction (nchar)
  477.     LOCAL
  478.         do_back;
  479.  
  480.     do_back := 0;
  481.  
  482.     IF ((ASCII (nchar) = ",") AND ((vi$last_s_func = "vi$find_char") OR
  483.                                         (vi$last_s_func = "vi$to_char"))) OR
  484.         ((ASCII (nchar) = ";") AND ((vi$last_s_func = "vi$back_find_char") OR
  485.                                     (vi$last_s_func = "vi$back_to_char"))) THEN
  486.         do_back := 1;
  487.     ENDIF;
  488.  
  489.     IF (INDEX (vi$back_moves + vi$weird2_moves, ASCII(nchar)) <> 0) THEN
  490.         do_back := 1;
  491.     ENDIF;
  492.  
  493.     IF (ASCII (nchar) = 'G') AND (vi$cur_line_no > vi$active_count) AND
  494.                                                 (vi$active_count > 0) THEN
  495.         do_back := 1;
  496.     ENDIF;
  497.  
  498.     RETURN (do_back);
  499. ENDPROCEDURE;
  500.  
  501. !
  502. !   Given the fact that a select range is active, modify vi$start_pos
  503. !   to be the start of that range, and return the end of the select
  504. !   range.
  505. !
  506. PROCEDURE vi$get_select_pos
  507.     LOCAL
  508.         pos,
  509.         rng;
  510.  
  511.     rng := SELECT_RANGE;
  512.     IF (rng <> 0) THEN
  513.         pos := MARK (NONE);
  514.         vi$select_pos := 0;
  515.         vi$start_pos := BEGINNING_OF (rng);
  516.         POSITION (END_OF (rng));
  517.         MOVE_HORIZONTAL (1);
  518.         MESSAGE ("");
  519.         RETURN (vi$retpos (pos));
  520.     ELSE
  521.         vi$select_pos := 0;
  522.         vi$info ("No region selected!");
  523.     ENDIF;
  524.     RETURN (0);
  525. ENDPROCEDURE;
  526.  
  527. !
  528. !   Perform the operations associated with the 'S' command.
  529. !
  530. PROCEDURE vi$_big_s
  531.     LOCAL
  532.         max_mark,
  533.         start_pos,
  534.         max_col,
  535.         rng,
  536.         start,
  537.         tend,
  538.         pos;
  539.  
  540.     POSITION (LINE_BEGIN);
  541.  
  542.     IF (MARK (NONE) <> BEGINNING_OF (CURRENT_BUFFER)) THEN
  543.         MOVE_HORIZONTAL (-1);
  544.         vi$undo_start := MARK (NONE);
  545.         MOVE_HORIZONTAL (1);
  546.     ELSE
  547.         vi$undo_start := 0;
  548.     ENDIF;
  549.  
  550.     IF (MARK (NONE) = BEGINNING_OF (CURRENT_BUFFER)) THEN
  551.         vi$undo_end := 0;
  552.     ENDIF;
  553.  
  554.     start := MARK (NONE);
  555.     MOVE_VERTICAL (vi$cur_active_count - 1);
  556.     IF (LENGTH (vi$current_line) > 0) THEN
  557.         POSITION (LINE_END);
  558.         MOVE_HORIZONTAL (-1);
  559.     ENDIF;
  560.  
  561.     tend := MARK (NONE);
  562.     rng := CREATE_RANGE (start, tend, NONE);
  563.     POSITION (start);
  564.     vi$save_for_undo (rng, VI$IN_LINE_MODE, 1);
  565.  
  566.     ERASE (rng);
  567.  
  568.     max_col := CURRENT_OFFSET;
  569.     start_pos := max_col;
  570.     max_mark := MARK(NONE);
  571.  
  572.     vi$update (CURRENT_WINDOW);
  573.  
  574.     IF (vi$line_edit (max_col, start_pos, max_mark, 0) <> 0) THEN
  575.         vi$undo_end := MARK (NONE);
  576.         IF (CURRENT_CHARACTER = "") THEN
  577.             MOVE_HORIZONTAL (1);
  578.         ENDIF;
  579.     ELSE
  580.         vi$undo_end := 0;
  581.     ENDIF;
  582.     pos := MARK (NONE);
  583.     vi$undo_start := vi$set_undo_start (vi$undo_start);
  584.     POSITION (pos);
  585. ENDPROCEDURE;
  586.  
  587. !
  588. !   This function performs the operations associated with the '"' command
  589. !   that allows one of the 26 named buffers, or one of the 10 delete
  590. !   buffers to be the target of a 'd', 'D', 'x', 'X', 'y', 'Y', 'p' or 'P'
  591. !   command.
  592. !
  593. PROCEDURE vi$select_buffer
  594.     LOCAL
  595.         numeric,
  596.         asc_action,
  597.         action,
  598.         prog,
  599.         buf_name,
  600.         nchar;
  601.  
  602.     ON_ERROR;
  603.         RETURN;
  604.     ENDON_ERROR;
  605.  
  606.     nchar := ASCII (vi$read_a_key);
  607.     action := vi$read_a_key;
  608.     asc_action := ASCII (action);
  609.     numeric := (INDEX (vi$_numeric_chars, asc_action) <> 0);
  610.  
  611.     IF numeric THEN
  612.         vi$active_count := INDEX (vi$_numeric_chars, asc_action) - 1;
  613.         LOOP
  614.             action := vi$read_a_key;
  615.             asc_action := ASCII (action);
  616.             EXITIF (INDEX (vi$_numeric_chars, asc_action) = 0);
  617.             vi$active_count := (vi$active_count * 10) +
  618.                                     (INDEX (vi$_numeric_chars, asc_action) - 1);
  619.         ENDLOOP;
  620.     ENDIF;
  621.  
  622.     IF  (asc_action <> 'P') AND (asc_action <> 'p') AND (asc_action <> 'd') AND
  623.         (asc_action <> 'D') AND (asc_action <> 'y') AND (asc_action <> 'Y') AND
  624.         (asc_action <> 'x') AND (asc_action <> 'X') AND (NOT numeric) THEN
  625.  
  626.         vi$info ("Unrecognized buffer action, ignoring: '"+asc_action+"'");
  627.  
  628.         RETURN;
  629.     ENDIF;
  630.  
  631.     IF (INDEX ("123456789", nchar) <> 0) THEN
  632.  
  633.         IF  (asc_action <> 'P') AND (asc_action <> 'p') THEN
  634.             RETURN;
  635.         ENDIF;
  636.  
  637.         ! Selected a deletion buffer.
  638.  
  639.         buf_name := "vi$del_buf_"+nchar;
  640.  
  641.     ELSE
  642.         IF (INDEX (vi$_letter_chars, nchar) <> 0) THEN
  643.  
  644.             ! Selected a named buffer.
  645.  
  646.             IF (INDEX (vi$_upper_chars, nchar) <> 0) THEN
  647.                 nchar := SUBSTR (vi$_lower_chars,
  648.                             INDEX (vi$_upper_chars, nchar), 1);
  649.                 vi$append_it := 1;
  650.             ENDIF;
  651.             buf_name := "vi$ins_buf_"+nchar;
  652.  
  653.             ! Only create a buffer if we are going to put something into it.
  654.  
  655.             IF  (asc_action <> 'P') AND (asc_action <> 'p') THEN
  656.                 EXECUTE (COMPILE ('vi$get_ins_buf(' +
  657.                                             buf_name + ', "'+buf_name+'");'));
  658.             ELSE
  659.                 vi$global_var := 0;
  660.                 EXECUTE (COMPILE ("vi$global_var:="+buf_name));
  661.                 IF (vi$global_var = 0) THEN
  662.                     vi$info ("There is nothing in that buffer!");
  663.                     RETURN;
  664.                 ENDIF;
  665.             ENDIF;
  666.         ELSE
  667.             vi$info ("Invalid buffer!");
  668.             RETURN;
  669.         ENDIF;
  670.     ENDIF;
  671.  
  672.     ! We now have a buffer, and the next command key, so envoke the
  673.     ! proper code.
  674.  
  675.     vi$do_buf_act (asc_action, 'P', "vi$put_here (VI$HERE, "+buf_name+");");
  676.     vi$do_buf_act  (asc_action, 'p', "vi$put_after ("+buf_name+");");
  677.     vi$do_buf_act  (asc_action, 'd', "vi$_delete (0, "+buf_name+");");
  678.     vi$do_buf_act  (asc_action, 'D',
  679.                                 "vi$_delete (KEY_NAME('$'), "+buf_name+");");
  680.     vi$do_buf_act  (asc_action, 'x', "vi$_delete ('l', "+buf_name+");");
  681.     vi$do_buf_act  (asc_action, 'X', "vi$_delete ('h', "+buf_name+");");
  682.     vi$do_buf_act  (asc_action, 'y', "vi$_yank (0, "+buf_name+");");
  683.     vi$do_buf_act  (asc_action, 'Y', "vi$_yank ('y', "+buf_name+");");
  684.     vi$do_buf_act  (asc_action, 'Y', "vi$_yank (KEY_NAME('y'), "+buf_name+");");
  685. ENDPROCEDURE;
  686.  
  687. !
  688. !   Perform action based on key typed and passed data
  689. !
  690. PROCEDURE vi$do_buf_act (act_type, look_for, what_to_do)
  691.  
  692.     IF (act_type = look_for) THEN
  693.         EXECUTE (COMPILE (what_to_do));
  694.     ENDIF;
  695. ENDPROCEDURE;
  696.  
  697. !
  698. !   Create a buffer named 'bname' providing that there is not already a
  699. !   buffer by that name.
  700. !
  701. PROCEDURE vi$get_ins_buf (buf, bname)
  702.  
  703.     IF (buf = 0) THEN
  704.         buf := vi$init_buffer (bname, "");
  705.     ENDIF;
  706.  
  707.     IF buf = 0 THEN
  708.         vi$info ("Error creating named buffer!");
  709.     ENDIF;
  710. ENDPROCEDURE;
  711.  
  712. !
  713. !   Perform the delete command tied to the 'd' key.
  714. !
  715. PROCEDURE vi$_delete (opchar, dest_buf)
  716.  
  717.     LOCAL
  718.         olen,
  719.         old_offset,
  720.         new_offset,
  721.         era_range,
  722.         opos,
  723.         prog,
  724.         do_back,
  725.         nchar;
  726.  
  727.     ON_ERROR;
  728.         vi$info ("Error occured during delete, at line: "+STR(ERROR_LINE));
  729.         POSITION (vi$start_pos);
  730.         RETURN;
  731.     ENDON_ERROR;
  732.  
  733.     vi$new_offset := 1;
  734.     nchar := opchar;
  735.  
  736.     opos := MARK (NONE);
  737.     IF (nchar = 0) THEN
  738.         nchar := vi$init_action (olen);
  739.     ELSE
  740.         olen := GET_INFO (CURRENT_BUFFER, "RECORD_COUNT");
  741.     ENDIF;
  742.  
  743.     ! If the movement will be backwards, then the region must not include
  744.     ! the current character.
  745.  
  746.     old_offset := -1;
  747.     new_offset := -1;
  748.  
  749.     do_back := vi$get_direction (nchar);
  750.  
  751.     IF do_back THEN
  752.         old_offset := CURRENT_OFFSET;
  753.         vi$move_horizontal (-1);
  754.         new_offset := CURRENT_OFFSET;
  755.     ENDIF;
  756.  
  757.     vi$start_pos := MARK (NONE);
  758.  
  759.     ! For "dh" or "X" (a macro of "dh"), we must let vi$left do the movement.
  760.  
  761.     IF (INDEX (vi$weird2_moves, ASCII(nchar)) <> 0) AND
  762.                                                 (old_offset <> new_offset) THEN
  763.         MOVE_HORIZONTAL (1);
  764.     ENDIF;
  765.  
  766.     prog := vi$get_prog (nchar);
  767.  
  768.     IF prog <> "" THEN
  769.         vi$do_movement (prog, VI$DELETE_TYPE);
  770.  
  771.         IF (vi$endpos <> 0) THEN
  772.             IF (do_back) AND (vi$yank_mode = VI$LINE_MODE) THEN
  773.                 POSITION (vi$start_pos);
  774.                 vi$move_vertical (1);
  775.                 IF (LENGTH(vi$current_line) > 0) THEN
  776.                     MOVE_HORIZONTAL (-1);
  777.                 ENDIF;
  778.                 vi$start_pos := MARK (NONE);
  779.             ENDIF;
  780.  
  781.             POSITION (vi$endpos);
  782.  
  783.             IF (MARK (NONE) <> BEGINNING_OF (CURRENT_BUFFER)) AND
  784.                         (NOT do_back) AND
  785.                         (INDEX (vi$weird_moves, ASCII (nchar)) = 0) THEN
  786.                 MOVE_HORIZONTAL (-1);
  787.             ENDIF;
  788.  
  789.             era_range := CREATE_RANGE (vi$start_pos, MARK (NONE), NONE);
  790.  
  791.             IF (era_range <> 0) THEN
  792.                 IF (GET_INFO (dest_buf, "TYPE") = INTEGER) THEN
  793.                     vi$cur_text := vi$put2del_buf (vi$yank_mode, era_range);
  794.                 ELSE
  795.                     vi$type2buf (STR (vi$yank_mode), dest_buf);
  796.                     vi$cur_text := vi$cp2buf (era_range, dest_buf);
  797.                 ENDIF;
  798.  
  799.                 vi$undo_end := 0;
  800.                 POSITION (BEGINNING_OF (era_range));
  801.                 vi$save_for_undo (era_range, vi$yank_mode, 1);
  802.                 vi$undo_start := vi$start_pos;
  803.                 ERASE (era_range);
  804.             ELSE
  805.                 vi$info ("Internal error while deleting!");
  806.             ENDIF;
  807.  
  808.             POSITION (vi$start_pos);
  809.         ELSE
  810.             vi$abort (0);
  811.             POSITION (opos);
  812.         ENDIF;
  813.     ELSE
  814.         POSITION (opos);
  815.         vi$abort (0);
  816.     ENDIF;
  817.  
  818.     vi$check_length (olen);
  819. ENDPROCEDURE;
  820.  
  821. !
  822. !   This procedure checks a change in the size of the buffer, and reports
  823. !   the change if it is greater than the number set with ":set report"
  824. !
  825. PROCEDURE vi$check_length (olen)
  826.     LOCAL
  827.         nlen;
  828.  
  829.     nlen := GET_INFO (CURRENT_BUFFER, "RECORD_COUNT");
  830.  
  831.     IF (nlen - vi$report) >= olen THEN
  832.         vi$info (STR (nlen - olen) + " more lines!");
  833.     ELSE
  834.         IF (nlen + vi$report <= olen) THEN
  835.             vi$info (STR (olen - nlen) + " fewer lines!");
  836.         ENDIF;
  837.     ENDIF;
  838. ENDPROCEDURE;
  839.  
  840. !
  841. !   Perform the yank command tied to the 'y' key.
  842. !
  843. PROCEDURE vi$_yank (opchar, dest_buf)
  844.  
  845.     LOCAL
  846.         old_offset,
  847.         new_offset,
  848.         pos,
  849.         oline,
  850.         nline,
  851.         yank_range,
  852.         prog,
  853.         do_back,
  854.         nchar;
  855.  
  856.     ON_ERROR;
  857.         vi$info ("Error occured during yank, at line: "+STR(ERROR_LINE));
  858.         POSITION (vi$start_pos);
  859.         RETURN;
  860.     ENDON_ERROR;
  861.  
  862.     nchar := opchar;
  863.     pos := MARK (NONE);
  864.  
  865.     IF nchar = 0 THEN
  866.         nchar := vi$init_action (oline);
  867.     ENDIF;
  868.  
  869.     old_offset := -1;
  870.     new_offset := -1;
  871.  
  872.     ! If the movement will be backwards, then the region must not include
  873.     ! the current character.
  874.  
  875.     do_back := vi$get_direction (nchar);
  876.  
  877.     IF do_back THEN
  878.         old_offset := CURRENT_OFFSET;
  879.         vi$move_horizontal (-1);
  880.         new_offset := CURRENT_OFFSET;
  881.     ENDIF;
  882.  
  883.     vi$start_pos := MARK (NONE);
  884.  
  885.     ! For "yl" and similar moves, we must let vi$left to the movement.
  886.  
  887.     IF (INDEX (vi$weird2_moves, ASCII(nchar)) <> 0) AND
  888.                                                 (old_offset <> new_offset) THEN
  889.         MOVE_HORIZONTAL (1);
  890.     ENDIF;
  891.  
  892.     prog := vi$get_prog (nchar);
  893.  
  894.     IF prog <> "" THEN
  895.         vi$do_movement (prog, VI$YANK_TYPE);
  896.  
  897.         oline := vi$cur_line_no;
  898.         IF (vi$endpos <> 0) THEN
  899.             POSITION (vi$endpos);
  900.             nline := vi$abs (vi$cur_line_no - oline);
  901.             IF (nline >= vi$report) THEN
  902.                 vi$info (STR (nline) + " lines yanked");
  903.             ENDIF;
  904.             IF (MARK (NONE) <> BEGINNING_OF (CURRENT_BUFFER)) AND
  905.                         (NOT do_back) AND
  906.                         (INDEX (vi$weird_moves, ASCII (nchar)) = 0) THEN
  907.                 MOVE_HORIZONTAL (-1);
  908.             ENDIF;
  909.  
  910.             yank_range := CREATE_RANGE (vi$start_pos, MARK (NONE), NONE);
  911.  
  912.             IF (yank_range <> 0) THEN
  913.                 IF (GET_INFO (dest_buf, "TYPE") = INTEGER) THEN
  914.                     vi$cur_text := vi$put2yank_buf (yank_range, vi$temp_buf);
  915.                 ELSE
  916.                     vi$cur_text := vi$put2yank_buf (yank_range, dest_buf);
  917.                 ENDIF;
  918.             ELSE
  919.                 vi$info ("Internal error while yanking!");
  920.             ENDIF;
  921.         ELSE
  922.             vi$abort (0);
  923.         ENDIF;
  924.  
  925.         POSITION (pos);
  926.     ELSE
  927.         vi$abort (0);
  928.     ENDIF;
  929.  
  930. ENDPROCEDURE;
  931.  
  932. !
  933. !   Return the absolute value of the value passed.
  934. !
  935. PROCEDURE vi$abs (val)
  936.     IF val < 0 THEN
  937.         RETURN (-val);
  938.     ENDIF;
  939.     RETURN (val);
  940. ENDPROCEDURE;
  941.  
  942. !
  943. !   Given a range of a buffer, or a string, place it into the "kill-ring"
  944. !   sliding the text back one slot that is already there.
  945. !
  946. PROCEDURE vi$put2del_buf (mode, string_parm)
  947.  
  948.     LOCAL
  949.         local_str,
  950.         pos;
  951.  
  952.     pos := MARK (NONE);
  953.  
  954.     IF (mode = VI$LINE_MODE) THEN
  955.  
  956.         ! Slide each range back one slot, throwing away the last.
  957.  
  958.         vi$mv2buf (vi$del_buf_8, vi$del_buf_9);
  959.         vi$mv2buf (vi$del_buf_7, vi$del_buf_8);
  960.         vi$mv2buf (vi$del_buf_6, vi$del_buf_7);
  961.         vi$mv2buf (vi$del_buf_5, vi$del_buf_6);
  962.         vi$mv2buf (vi$del_buf_4, vi$del_buf_5);
  963.         vi$mv2buf (vi$del_buf_3, vi$del_buf_4);
  964.         vi$mv2buf (vi$del_buf_2, vi$del_buf_3);
  965.         vi$mv2buf (vi$del_buf_1, vi$del_buf_2);
  966.  
  967.         ! Place the new text at the front.
  968.  
  969.         vi$type2buf (STR(mode), vi$del_buf_1);
  970.         vi$cp2buf (string_parm, vi$del_buf_1);
  971.     ENDIF;
  972.  
  973.     ! Save the text so that a normal 'p' or 'P' command also works.
  974.  
  975.     vi$type2buf (STR(mode), vi$temp_buf);
  976.     vi$cp2buf (string_parm, vi$temp_buf);
  977.  
  978.     POSITION (pos);
  979.     RETURN (vi$temp_buf);
  980. ENDPROCEDURE;
  981.  
  982. !
  983. !   Copy the text specified by source into the delete buffer given by
  984. !   dest.  If dest is zero, the it will be set to the value of a newly
  985. !   created buffer.
  986. !
  987. PROCEDURE vi$cp2buf (source, dest)
  988.     LOCAL
  989.         pos;
  990.  
  991.     pos := MARK (NONE);
  992.  
  993.     IF (source <> 0) THEN
  994.         IF (dest = 0) THEN
  995.             dest := vi$init_buffer ("TEMP_BUF_"+str(vi$temp_buf_num), "");
  996.             vi$temp_buf_num := vi$temp_buf_num + 1;
  997.         ENDIF;
  998.  
  999.         POSITION (dest);
  1000.         COPY_TEXT (source);
  1001.     ENDIF;
  1002.  
  1003.     POSITION (pos);
  1004.     RETURN (dest);
  1005. ENDPROCEDURE;
  1006.  
  1007. !
  1008. !   vi$mv2buf is like vi$cp2buf except that vi$mv2buf erases the buffer before
  1009. !   performing the copy.
  1010. !
  1011. PROCEDURE vi$mv2buf (source, dest)
  1012.     LOCAL
  1013.         pos;
  1014.  
  1015.     pos := MARK (NONE);
  1016.  
  1017.     IF (source <> 0) THEN
  1018.         IF (dest = 0) THEN
  1019.             dest := vi$init_buffer ("TEMP_BUF_"+str(vi$temp_buf_num), "");
  1020.             vi$temp_buf_num := vi$temp_buf_num + 1;
  1021.         ELSE
  1022.             ERASE (dest);
  1023.         ENDIF;
  1024.  
  1025.         POSITION (dest);
  1026.         COPY_TEXT (source);
  1027.     ENDIF;
  1028.  
  1029.     POSITION (pos);
  1030. ENDPROCEDURE;
  1031.  
  1032. !
  1033. !   Given the string representation of either VI$LINE_MODE or VI$IN_LINE_MODE,
  1034. !   place that text into the buffer given by dest.
  1035. !
  1036. PROCEDURE vi$type2buf (source, dest)
  1037.     LOCAL
  1038.         pos;
  1039.  
  1040.     pos := MARK (NONE);
  1041.  
  1042.     IF (source <> 0) THEN
  1043.         IF (dest = 0) THEN
  1044.             dest := vi$init_buffer ("TEMP_BUF_"+str(vi$temp_buf_num), "");
  1045.             vi$temp_buf_num := vi$temp_buf_num + 1;
  1046.         ELSE
  1047.             ERASE (dest);
  1048.         ENDIF;
  1049.  
  1050.         POSITION (BEGINNING_OF (dest));
  1051.         COPY_TEXT (source);
  1052.         SPLIT_LINE;
  1053.     ENDIF;
  1054.  
  1055.     POSITION (pos);
  1056. ENDPROCEDURE;
  1057.  
  1058. !
  1059. !   Save a piece of yanked text including the mode that it was yanked.
  1060. !
  1061. PROCEDURE vi$put2yank_buf (string_parm, dest_buf)
  1062.  
  1063.     LOCAL
  1064.         pos;
  1065.  
  1066.     pos := MARK (NONE);
  1067.  
  1068.     ! Set type of text in buffer.
  1069.  
  1070.     IF (vi$append_it = 0) THEN
  1071.         vi$type2buf (STR (vi$yank_mode), dest_buf);
  1072.     ELSE
  1073.  
  1074.         ! If empty buffer then put in type.
  1075.  
  1076.         IF (GET_INFO (dest_buf, "RECORD_COUNT") < 2) THEN
  1077.             vi$type2buf (STR (vi$yank_mode), dest_buf);
  1078.         ENDIF;
  1079.         vi$append_it := 0;
  1080.     ENDIF;
  1081.     vi$cp2buf (string_parm, dest_buf);
  1082.     POSITION (pos);
  1083.  
  1084.     RETURN (dest_buf);
  1085. ENDPROCEDURE;
  1086.  
  1087. !
  1088. !   This is a debugging procedure used to view the contents of a buffer.
  1089. !   It displays the buffer indicated by 'buf', and sets the status line
  1090. !   of the window displayed to contain the text given by 'stat_line'.
  1091. !
  1092. PROCEDURE vi$show_buf (buf, stat_line)
  1093.     LOCAL
  1094.         this_key,
  1095.         pos,
  1096.         new_win;
  1097.  
  1098.     IF (GET_INFO (buf, "TYPE") <> BUFFER) THEN
  1099.         vi$info ("show_buf called with non_buffer, message: "+stat_line);
  1100.         RETURN;
  1101.     ENDIF;
  1102.  
  1103.     pos := MARK (NONE);
  1104.     new_win := CREATE_WINDOW (1, 23, ON);
  1105.     MAP (new_win, buf);
  1106.     POSITION (buf);
  1107.     SET (STATUS_LINE, new_win, REVERSE, stat_line +
  1108.                 ", BUFFER NAME: '"+GET_INFO (buf, "NAME")+"'");
  1109.     vi$pos_in_middle (MARK (NONE));
  1110.     UPDATE (new_win);
  1111.     LOOP
  1112.         vi$info ("Press RETURN to continue editing...");
  1113.         this_key := READ_KEY;
  1114.         EXITIF (this_key = RET_KEY);
  1115.  
  1116.         IF (this_key = CTRL_D_KEY) OR
  1117.            (this_key = CTRL_U_KEY) OR
  1118.            (this_key = CTRL_F_KEY) OR
  1119.            (this_key = CTRL_B_KEY) OR
  1120.            (this_key = KEY_NAME ('h')) OR
  1121.            (this_key = KEY_NAME ('j')) OR
  1122.            (this_key = KEY_NAME ('k')) OR
  1123.            (this_key = KEY_NAME ('l')) THEN
  1124.  
  1125.             EXECUTE (LOOKUP_KEY (this_key, PROGRAM, vi$cmd_keys));
  1126.             UPDATE (new_win);
  1127.         ENDIF;
  1128.     ENDLOOP;
  1129.  
  1130.     UNMAP (new_win);
  1131.     DELETE (new_win);
  1132.     POSITION (pos);
  1133.     UPDATE (CURRENT_WINDOW);
  1134. ENDPROCEDURE;
  1135.  
  1136. !
  1137. !   This procedure moves the cursor down the number of lines indicated by
  1138. !   vi$active count.  The parameter passed is used by delete and yank
  1139. !   operations to differentiate them from normal cursor movement.
  1140. !
  1141. PROCEDURE vi$downline (adj)
  1142.  
  1143.     LOCAL
  1144.         pos,
  1145.         tabstops,
  1146.         cur_off,
  1147.         offset;
  1148.  
  1149.     !  Ignore error messages
  1150.  
  1151.     ON_ERROR
  1152.         vi$active_count := 0;
  1153.         POSITION (pos);
  1154.         RETURN (0);
  1155.     ENDON_ERROR;
  1156.  
  1157.     pos := MARK (NONE);
  1158.  
  1159.     POSITION (LINE_BEGIN);
  1160.     vi$start_pos := MARK (NONE);
  1161.  
  1162.     POSITION (pos);
  1163.  
  1164.     tabstops := GET_INFO (CURRENT_BUFFER, "TAB_STOPS");
  1165.  
  1166.     IF (GET_INFO (tabstops, "TYPE") <> STRING) THEN
  1167.         offset := CURRENT_OFFSET;
  1168.         cur_off := GET_INFO (SCREEN, "CURRENT_COLUMN") - 1;
  1169.         MOVE_VERTICAL (vi$cur_active_count + adj);
  1170.         POSITION (LINE_BEGIN);
  1171.         IF (vi$new_offset = 1) THEN
  1172.             vi$max_offset := cur_off;
  1173.             vi$new_offset := 0;
  1174.         ELSE
  1175.             IF (cur_off < vi$max_offset) THEN
  1176.                 cur_off := vi$max_offset;
  1177.             ENDIF;
  1178.         ENDIF;
  1179.  
  1180.         !  Save the beginning of the line as the new beginning.
  1181.  
  1182.         vi$new_endpos := MARK (NONE);
  1183.         IF (vi$new_endpos = END_OF (CURRENT_BUFFER)) THEN
  1184.             POSITION (pos);
  1185.             RETURN (0);
  1186.         ENDIF;
  1187.         vi$to_offset (vi$current_line, cur_off, tabstops);
  1188.     ELSE
  1189.         MOVE_VERTICAL (vi$cur_active_count + adj);
  1190.     ENDIF;
  1191.  
  1192.     vi$yank_mode := VI$LINE_MODE;
  1193.     RETURN (vi$retpos (pos));
  1194. ENDPROCEDURE;
  1195.  
  1196. !
  1197. ! Move left one location.  Do not wrap at edge of the screen.
  1198. !
  1199. PROCEDURE vi$left
  1200.  
  1201.     LOCAL
  1202.         pos;
  1203.  
  1204.     !  Ignore error messages
  1205.  
  1206.     ON_ERROR
  1207.         vi$active_count := 0;
  1208.         POSITION (pos);
  1209.         RETURN (0);
  1210.     ENDON_ERROR;
  1211.  
  1212.     pos := MARK (NONE);
  1213.  
  1214.     vi$new_offset := 1;
  1215.     IF (CURRENT_OFFSET < vi$active_count) OR (CURRENT_OFFSET = 0) THEN
  1216.         vi$active_count := 0;
  1217.         RETURN (0);
  1218.     ENDIF;
  1219.  
  1220.     MOVE_HORIZONTAL (-vi$cur_active_count);
  1221.     vi$yank_mode := VI$IN_LINE_MODE;
  1222.     RETURN (vi$retpos (pos));
  1223. ENDPROCEDURE;
  1224.  
  1225. !
  1226. ! Move right one location.  Stop at the end of the line, but, do not
  1227. ! wrap at edge of the screen.
  1228. !
  1229. PROCEDURE vi$right
  1230.  
  1231.     LOCAL
  1232.         pos,
  1233.         line,
  1234.         offset;
  1235.  
  1236.     !  Ignore error messages
  1237.  
  1238.     ON_ERROR
  1239.         vi$active_count := 0;
  1240.         POSITION (pos);
  1241.         RETURN (0);
  1242.     ENDON_ERROR
  1243.  
  1244.     pos := MARK (NONE);
  1245.  
  1246.     line := CURRENT_LINE;
  1247.     offset := CURRENT_OFFSET;
  1248.  
  1249.     ! This makes it possible to use the "s" command at the end of the line.
  1250.  
  1251.     IF (vi$command_type <> VI$OTHER_TYPE) THEN
  1252.         offset := offset - 1;
  1253.         IF (LENGTH (CURRENT_LINE) = 0) THEN
  1254.             COPY_TEXT (" ");
  1255.             MOVE_HORIZONTAL (-1);
  1256.             vi$start_pos := MARK (NONE);
  1257.         ENDIF;
  1258.     ENDIF;
  1259.  
  1260.     IF (vi$active_count < (LENGTH (line) - offset -
  1261.                                     (vi$command_type = VI$OTHER_TYPE))) THEN
  1262.         MOVE_HORIZONTAL (vi$cur_active_count);
  1263.     ELSE
  1264.         vi$active_count := 0;
  1265.         RETURN (0);
  1266.     ENDIF;
  1267.  
  1268.     vi$new_offset := 1;
  1269.  
  1270.     vi$yank_mode := VI$IN_LINE_MODE;
  1271.     RETURN (vi$retpos (pos));
  1272. ENDPROCEDURE;
  1273.  
  1274. !
  1275. ! Move up one row, staying in the same column.  Scroll if necessary.
  1276. !
  1277. PROCEDURE vi$upline
  1278.  
  1279.     LOCAL
  1280.         pos,
  1281.         tabstops,
  1282.         offset,
  1283.         cur_off;
  1284.  
  1285.     !  Ignore error messages
  1286.  
  1287.     ON_ERROR
  1288.         vi$active_count := 0;
  1289.         POSITION (pos);
  1290.         RETURN (0);
  1291.     ENDON_ERROR;
  1292.  
  1293.     pos := MARK (NONE);
  1294.  
  1295.     tabstops := GET_INFO (CURRENT_BUFFER, "TAB_STOPS");
  1296.  
  1297.     POSITION (LINE_END);
  1298.     vi$new_endpos := MARK(NONE);
  1299.  
  1300.     POSITION (pos);
  1301.  
  1302.     ! We must understand it (i.e. it must be an integer) inorder to process
  1303.     ! the tabs properly.
  1304.  
  1305.     IF (GET_INFO (tabstops, "TYPE") <> STRING) THEN
  1306.         offset := CURRENT_OFFSET;
  1307.  
  1308.         cur_off := GET_INFO (SCREEN, "CURRENT_COLUMN") - 1;
  1309.         MOVE_VERTICAL(-vi$cur_active_count);
  1310.         POSITION (LINE_BEGIN);
  1311.  
  1312.         IF vi$new_offset = 1 THEN
  1313.             vi$max_offset := cur_off;
  1314.             vi$new_offset := 0;
  1315.         ENDIF;
  1316.  
  1317.         IF (cur_off < vi$max_offset) THEN
  1318.             cur_off := vi$max_offset;
  1319.         ENDIF;
  1320.  
  1321.         !  Save the beginning of the line as the new beginning.
  1322.  
  1323.         vi$start_pos := MARK (NONE);
  1324.         vi$to_offset (CURRENT_LINE, cur_off, tabstops);
  1325.     ELSE
  1326.         MOVE_VERTICAL (-vi$cur_active_count);
  1327.     ENDIF;
  1328.     vi$yank_mode := VI$LINE_MODE;
  1329.     RETURN (vi$retpos (pos));
  1330. ENDPROCEDURE;
  1331.  
  1332. !
  1333. !   Move the cursor to the offset given by 'offset' counting tabs as expanded
  1334. !   spaces.
  1335. !
  1336. PROCEDURE vi$to_offset (line, offset, tabstops)
  1337.     LOCAL
  1338.         cur_ch,
  1339.         col,
  1340.         diff,
  1341.         len,
  1342.         tab,
  1343.         idx;
  1344.  
  1345.     idx := 1;
  1346.     col := 0;
  1347.     len := LENGTH (line);
  1348.     tab := ASCII (9);
  1349.  
  1350.     LOOP
  1351.         EXITIF (len < idx) OR (col >= offset);
  1352.         IF (SUBSTR (line, idx, 1) = tab) THEN
  1353.             diff := (((col+tabstops)/tabstops)*tabstops)-col;
  1354.         ELSE
  1355.             diff := 1;
  1356.         ENDIF;
  1357.         col := col + diff;
  1358.         idx := idx + 1;
  1359.     ENDLOOP;
  1360.  
  1361.     !  Move N characters to the right.
  1362.  
  1363.     MOVE_HORIZONTAL (idx - 1);
  1364. ENDPROCEDURE;
  1365.  
  1366. !
  1367. !   Search for a text string.  This procedure is activated by typing
  1368. !   either a '/' or a '?'.
  1369. !
  1370. PROCEDURE vi$search (direction)
  1371.     LOCAL
  1372.         where,
  1373.         i,
  1374.         pos,
  1375.         ch,
  1376.         sstr,
  1377.         cnt,
  1378.         add_spec,
  1379.         prompt;
  1380.  
  1381.     pos := MARK (NONE);
  1382.  
  1383.     IF (direction > 0) THEN
  1384.         prompt := "/";
  1385.     ELSE
  1386.         prompt := "?";
  1387.     ENDIF;
  1388.  
  1389.     IF (vi$read_a_line (prompt, sstr) = 0) THEN
  1390.         RETURN (0);
  1391.     ENDIF;
  1392.  
  1393.     i := 1;
  1394.     LOOP
  1395.         EXITIF (i > LENGTH (sstr));
  1396.         ch := SUBSTR (sstr, i, 1);
  1397.         IF (ch = "\") THEN
  1398.             i := i + 1;
  1399.         ELSE
  1400.             EXITIF (ch = prompt);
  1401.         ENDIF;
  1402.         i := i + 1;
  1403.     ENDLOOP;
  1404.  
  1405.     ! If the search string is followed by the delimiter, then allow an
  1406.     ! additional line offset specification.
  1407.  
  1408.     add_spec := 0;
  1409.     IF (ch = prompt) THEN
  1410.         add_spec := SUBSTR (sstr, i+1, 255);
  1411.         sstr := SUBSTR (sstr, 1, i-1);
  1412.     ENDIF;
  1413.  
  1414.     IF (direction > 0) THEN
  1415.         SET (FORWARD, CURRENT_BUFFER);
  1416.         vi$last_search_dir := 1;
  1417.         vi$move_horizontal (1);
  1418.     ELSE
  1419.         SET (REVERSE, CURRENT_BUFFER);
  1420.         vi$last_search_dir := -1;
  1421.     ENDIF;
  1422.  
  1423.     IF sstr <> "" THEN
  1424.         vi$search_string := sstr;
  1425.     ELSE
  1426.         IF vi$search_string = 0 THEN
  1427.             vi$info ("No previous string to search for!");
  1428.             POSITION (pos);
  1429.             RETURN (0);
  1430.         ENDIF;
  1431.     ENDIF;
  1432.  
  1433.     ! Search for the nth occurance.
  1434.  
  1435.     cnt := vi$cur_active_count;
  1436.     LOOP
  1437.         where := vi$find_str (vi$search_string, 0, 0);
  1438.         EXITIF (where = 0);
  1439.         POSITION (BEGINNING_OF (where));
  1440.         IF (CURRENT_DIRECTION = FORWARD) THEN
  1441.             MOVE_HORIZONTAL (1);
  1442.         ELSE
  1443.             MOVE_HORIZONTAL (-1);
  1444.         ENDIF;
  1445.         cnt := cnt - 1;
  1446.         EXITIF cnt = 0;
  1447.     ENDLOOP;
  1448.  
  1449.     ! Check to see that we found one.
  1450.  
  1451.     IF (where = 0) THEN
  1452.         vi$info ("String not found");
  1453.     ELSE
  1454.  
  1455.         ! Check for a relative line number after the search string.
  1456.  
  1457.         IF add_spec <> 0 THEN
  1458.             POSITION (where);
  1459.             IF add_spec = "-" THEN
  1460.                 add_spec := "-1";
  1461.             ELSE
  1462.                 IF (SUBSTR (add_spec, 1, 1) = "+") THEN
  1463.                     IF (add_spec = "+") THEN
  1464.                         add_spec := "1";
  1465.                     ENDIF;
  1466.                 ELSE
  1467.                     add_spec := SUBSTR (add_spec, 2, 255);
  1468.                 ENDIF;
  1469.             ENDIF;
  1470.  
  1471.             i := INT (add_spec);
  1472.             MOVE_VERTICAL (i);
  1473.             vi$_bol (0);
  1474.             where := MARK (NONE);
  1475.         ELSE
  1476.             POSITION (BEGINNING_OF (where));
  1477.             bpos := MARK (NONE);
  1478.             POSITION (END_OF (where));
  1479.             vi$find_rng := CREATE_RANGE (bpos, MARK(NONE), BOLD);
  1480.         ENDIF;
  1481.     ENDIF;
  1482.  
  1483.     POSITION (pos);
  1484.  
  1485.     ! On success then return the position we moved to.
  1486.  
  1487.     RETURN (where);
  1488. ENDPROCEDURE;
  1489.  
  1490. !
  1491. !   Search for the next occurence of the previously searched for string.
  1492. !   The procedure is actived by typing an 'n' or 'N' keystroke.
  1493. !
  1494. PROCEDURE vi$search_next (direction)
  1495.     LOCAL
  1496.         prompt,
  1497.         where,
  1498.         pos,
  1499.         cnt,
  1500.         sstr;
  1501.  
  1502.     pos := MARK (NONE);
  1503.  
  1504.     IF vi$search_string = 0 THEN
  1505.         vi$info ("No previous string to search for!");
  1506.         POSITION (pos);
  1507.         RETURN (0);
  1508.     ENDIF;
  1509.  
  1510.     IF (direction > 0) THEN
  1511.         prompt := "/" + vi$search_string;
  1512.         SET (FORWARD, CURRENT_BUFFER);
  1513.         IF (MARK (NONE) <> END_OF (CURRENT_BUFFER)) THEN
  1514.             MOVE_HORIZONTAL (1);
  1515. $$EOD$$
  1516.