home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / p2demo21.exe / PEL / VI.PEL < prev    next >
Text File  |  1995-04-10  |  151KB  |  5,785 lines

  1. # $Header:   P:\source\wmacros\vi.pev   1.74   10 Apr 1995 17:08:00   NOBLE  $
  2. ## $Tabs:4 7$
  3.  
  4. ##############################################################################
  5. #
  6. #       Compuware Corporation
  7. #         31440 Northwestern Highway
  8. #           Farmington Hills, Michigan 48334-2564
  9. #
  10. #   This source code listing contains information that is
  11. #   proprietary to Compuware Corporation and may not be copied
  12. #   duplicated, translated, transmitted, stored, retrieved
  13. #   or in any manner or by any method conveyed or disclosed
  14. #   to a third party or parties without express written
  15. #   permission from Compuware Corporation.
  16. #
  17. #
  18. ##############################################################################
  19.  
  20. #### $Workfile:   vi.pel  $:  vi support keymaps and macros
  21.  
  22. #### VI emulation mode release notes:
  23. #
  24. #  EXTENSIONS:
  25. #
  26. #  This editor is capable of editing multiple files at once.  Thus it's 
  27. #  unnecessary to write the current file before switching to a new one, 
  28. #  nor is it necessary to process multiple files in strict sequential 
  29. #  order.  ":n" switches to next buffer without writing file; 
  30. #  and ":e file" loads a new file or 
  31. #  switches to an existing buffer by that name -- all without requiring 
  32. #  files to be written at each step.  The unnamed put buffer's contents 
  33. #  are not lost when buffer changes, thereby allowing cuts and pastes 
  34. #  between buffers without writing to intermediate files.
  35. #
  36. #  This implementation allows empty files and files with incomplete last
  37. #  lines to be edited.  This is in contrast with VI, which prohibits
  38. #  incomplete last lines and insists that there exist at least a single
  39. #  newline character in the file.
  40. #
  41. #  This implementation provides multiple windows.  Each window may view 
  42. #  different parts of the same file or different files.
  43. #
  44. #  The mouse can be used for all window operations, and for making text 
  45. #  selections.  Vi operators apply to the selected text, if any, in lieu 
  46. #  of a vi motion.  (Make a selection, then type "dd" to delete it.)  The
  47. #  mouse allows column selections in addition to character- and line-
  48. #  oriented ones, however, line commands such as dd, yy, << and >> will
  49. #  treat all selections as line selections.
  50. #
  51. #  vi-style undo has been augmented by PREDITOR's unlimited undo and redo.
  52. #  <Alt-U> undoes and <Alt-Y> redoes.  Also, vi's "U" command has been
  53. #  extended to toggle between the fully edited and undone versions of 
  54. #  the current line, like "u" does for a single edit.
  55. #
  56. #  This implementation always portrays a 100% accurate representation 
  57. #  of the text in the buffer.  Standard vi employed "@" and "$" symbols
  58. #  to indicate where text had been or would be deleted while minimizing 
  59. #  screen updates.  This kludge is unnecessary and unsupported in this 
  60. #  implementation.
  61. #
  62. #  Similarly, this editor is capable of scrolling horizontally, so long
  63. #  lines are not wrapped.  The redraw commands are not needed, so
  64. #  <Ctrl-L> and <Ctrl-R> scroll left and right, respectively.
  65. #
  66. #  Search commands have additional history associated with them.
  67. #  Previous search patterns entered are accessable via vertical arrow
  68. #  keys.  The default is the most recently entered search pattern, same
  69. #  as normal vi.  One subtle difference is that the previous pattern is
  70. #  visible on the command line.  Patterns histories may be accepted by 
  71. #  pressing enter, or rejected by pressing escape.  They also may be
  72. #  edited with the DEL, backspace, and horizontal arrow keys.  Newly
  73. #  entered text overwrites the pattern unless an editing character has
  74. #  been pressed first.  Thus "/<Enter>" and "/newpattern<Enter>" work 
  75. #  exactly as before, although additional functionality is available as
  76. #  a superset of previous key sequences.
  77. #
  78. #  DEL and ^U are used to cancel insert input on the current line.  ^C 
  79. #  terminates an insert. Ctrl-Break resets the editor.
  80. #
  81. #  ^V is only needed to quote keys that have a special meaning in insert 
  82. #  mode (  ^J  ^M  ^[  ^@  ^C  ^D  ^H  ^W  ^U  ^V  ).  Other control 
  83. #  keys will self-insert, though they still can be quoted with ^V.
  84. #
  85. #  The various forms of ^D are not required in insert mode.  
  86. #  Backspace unindents, and tab indents.
  87. #
  88. #  The Ctrl-@ command in insert mode works for any previous insertion,
  89. #  and is not limited to 128 chars.  If typed as NOT the first character,
  90. #  it inserts the previous insertion without terminating insert mode.
  91. #
  92. #  Most of the native mode function- and Alt-keys are implemented for 
  93. #  convenience.  Thus <Alt-W><Alt-Q> works like :wq
  94. #
  95. #  Other special features, such as error-fix, word-processing, and "Code 
  96. #  Processing" support are available in VI mode via "<F10>command", and
  97. #  other native key bindings.
  98. #
  99. #  Code Processing template inserting is bound to the <F2> key during
  100. #  insert mode.
  101. #
  102. #  A new command is added: "g" goes to top of buffer if no address is 
  103. #  specified, otherwise goes to the specified line, like "G".
  104. #
  105. #
  106. #  Known limitations of this emulation:
  107. #
  108. #  Should allow cascaded searches: "/abc/;/def/"
  109. #
  110. #  Positioning past EOL and EOF are sometimes off by one:
  111. #
  112. #     "A^["   "Gj"
  113. #
  114. #  Sentence motions should stop at paragraph and section breaks.
  115. #
  116. #  The ":l" command and "l" suffix to ":g" and ":s" should work
  117. #  differently from "p"
  118. #
  119. #
  120. #  TODO LIST (to be implemented next):
  121. #
  122. #  should use VI's regex syntax instead of our own
  123. #
  124. #  ":s" and ":g" commands should accept multi-line input
  125. #
  126. #  ":G" and ":V" commands ( :g or :v with prompt ).
  127. #
  128. #  should prohibit motion past EOL of last line
  129. #
  130. #  should prohibit having 0 lines in buffer
  131. #
  132. #
  133. #  OMISSIONS (likely never to be implemented):
  134. #
  135. #  Several vi commands take a count to adjust the window size.  Presently
  136. #  all such counts are ignored, and, given the more robust windowing
  137. #  capabilities, they are unlikely ever to be implemented. In some 
  138. #  cases, like searches, the counts may be changed to repeat counts.
  139. #  Commands I know about (purportedly all "large motion" commands)...
  140. #
  141. #     "z<num>." (change window size to <num>)
  142. #
  143. #     ^D  ^U  ^F  ^B  /  ?  [[  ]]  `  and '  
  144. #
  145. #     maybe:  ^E  ^Y   n   N
  146. #
  147. #  # -- macro character -- substitute for FN keys on keyboards w/out
  148. #
  149. #  @ macro commands
  150. #
  151. #  :n filename... command (use :e instead) -- can manually add or delete
  152. #     from buffer list, but can't replace it.  Not meaningful,
  153. #     since we are a multi-buffer editor.          
  154. #
  155. #  nO to open n lines -- no way to "open" lines. text is always 100%
  156. #     accurate re the buffer being edited
  157. #
  158. #  lisp mode commands: "(", ")", "{", "}", "="
  159. #
  160. ####
  161.  
  162. ### local variables
  163.  
  164. ## vi keymaps:
  165.  
  166. local vi_command_keymap = -1
  167. local vi_insert_keymap = -1
  168. local default_vi_command_keymap   # copy of command mode keymap used when vi.pel is recompiled
  169. local default_vi_insert_keymap    # similarily for insert mode keymap
  170.  
  171. ## vi constants:
  172.  
  173. local DEFAULT_BID = "1"    # default buffer id if not explicitly spec'd
  174. local LINE_BUFFER = 0x80000   # user buffer flag for line mode buffers
  175. local AND_DELETE = 1    # optional arg to vi_yank()
  176. local DEFAULT_ONE = 1      # optional arg to ex_ and select_range()
  177. #local DONT_DELETE = 0     # optional arg to vi_yank()
  178. local SEARCH_HIST  = "SEARCH" # history index for "/" and "?" commands
  179. local NEWLINE_BEFORE = 1   # insert flag -- put newline before or
  180. local NEWLINE_AFTER = 2    #  -- after  current line
  181. local ESC = 0x011B      # escape key
  182. local MOVE = 1       # move-mode argument to ex_copy
  183. local COPY = 0       # copy-mode argument to ex_copy
  184. local VI_EXPAND_TEMPLATE_KEY   = "<F2>"
  185. local VI_NEXT_FIELD_KEY        = "<Ctrl-Enter>"
  186.  
  187. #local vi_exact_mark = "`" # syntax for exact mark motion
  188. local vi_line_mark = "'"   # syntax for line mark motion
  189.    # block of reserved marks (ugh!!)
  190. local vi_major_mark = 909  # place from which last major motion moved
  191. local vi_base_mark = 910   # named marks --  first is "''" / ""
  192.  
  193. local DONT_MARK = 2     # op_type_2b() arg to suppress mark_context()
  194. local MAJOR_MARK = 1    # op_type_2b() arg mark major motion
  195. #local MAGIC = 1         # 1 if :e! file should delete current
  196.             # buffer before starting new file; 0 otherwise
  197. local READWRITE = 2     # read/write mode for fopen
  198. local END_FILE = 2      # end of file mode for fseek
  199. local BOB = 908         # beginning of buffer bookmark            
  200. local B1 = 907       # marks one end of text to be appended to a file
  201. local B2 = 906       # marks the other end
  202.  
  203.  
  204. local in_ex_mode = 1    # vi mode flag, 1 = command mode, 0 = insert mode
  205.  
  206. ## vi emulation internal states:
  207.  
  208. local pending_operator     # name of pending operator, if any
  209.  
  210. local number_register = 0  # number register used to repeat commands
  211. local option_disable[]     # array of disable flags for :set options
  212. local option_value[]    # value associated with :set option
  213. local recursion_count[]    # recursion protector for :map command
  214. local insert_macro_table[] # table of inert mode macros for :map 
  215. local command_macro_table[]   # table of command mode macros for :map
  216. local covered_command_macro_body[]  # record of overwritten macros
  217. local covered_insert_macro_body[]
  218. local covered_insert_macro[]
  219. local covered_command_macro[]
  220. local templates[]       # abbreviations for :abb mode
  221. local original_com      # original version of command string
  222. local save_command      # pattern part of last :s command for replay
  223. local prev_old          # previous match pattern for :s command
  224. local prev_new          # previous substitution string for :s command
  225. local last_buffer       # previous buffer for :e #
  226.  
  227.  
  228. local vi_read_name         # file name in last read command
  229. local in_glob              # executing a :g or :v command
  230. local browser_bid = 0      # buffer containing ":" command's "printed" output
  231. local browser_keymap = -1  # keymap for browser process
  232. local confirmer_bid = 0    # buffer for confirmer process
  233. local confirmer_keymap = -1   # keymap for confirmer process
  234.  
  235. local buf_id               # selected get/put buffer
  236. local buf_id_map           # maps buf_id names to buffer handles
  237.  
  238. local vi_search_dir        # previous search direction for search_again
  239. local vi_search_flags      # vi search flags
  240.  
  241. local vi_find_com          # last find command, if any
  242. local vi_find_char         # last find character, if any
  243.  
  244.    # address ranges for ":" commands:
  245.  
  246. local ex_addr1             # first address if any
  247. local ex_addr2             # second address if any
  248. local ex_addresses = 0     # count of addresses supplied
  249. local ex_com               # command string being parsed
  250.  
  251. local line_search          # signal from ex_address() to vi_search_key()
  252.  
  253.    # state vars for vi-style undo and "."/again:
  254.  
  255. local undo_baseline        # undo indicies to undo previous command
  256. local redo_baseline        # undo indicies to undo previous command
  257. local undo_line_baseline   # undo indicies to undo all changes to...
  258. local undo_line            # ...the current line (remembered here)
  259.  
  260. local again_string         # playback string to re-execute last command
  261. local tmp_again            # interim version of again_string
  262. local playing_again = 0    # flag for insert, et al.
  263.  
  264. local prev_number_register = 1   # previous repeat count
  265. local prev_buf_id          # previous buf id
  266. local prev_mark_id         # previous mark id
  267. local vi_r_ch              # previous "r" command replacement string
  268.  
  269. local insert_buffer        # buffer at start of insert mode
  270. local insert_window        # window at start of insert mode
  271. ## vi mode user-visible external states, flags and modes
  272.  
  273. local clipboard_only = 1   # Do not use vi numbered delete registers
  274.                            #  much improved performance for cuts/pastes
  275. local last_yank_type = 0   # Used when pasting from system clipboard. Set
  276.                            #  to LINE_BUFFER when yanking lines.
  277. local bs_over_ai = 1       # allow bs over ai in insert mode
  278.                            #  (extension to standard vi)
  279.  
  280. local mouse_handlers[]     # prexisting mouse handlers overriden by insert mode
  281. ### vi emulation
  282. #
  283.  
  284. ## vi -- enter vi mode
  285. #
  286. function vi()
  287. {
  288.    if ( emulation_mode == "vi" )
  289.       return;
  290.  
  291.    emulation_mode = "vi"
  292.    execute_event_handler( EVENT.EMULATION_CHANGED );
  293.  
  294.    # Misc. items...
  295.  
  296.    visible_end_buffer    = default_visible_end_buffer    = ""
  297.    visible_virtual_lines = default_visible_virtual_lines = "~"
  298.  
  299.    # turn on prompts on statusbar
  300.    status_bar_flags = or(status_bar_flags, STB_PROMPTS)
  301.    
  302.    window_page_overlap = 2
  303.  
  304.    search_flags = vi_search_flags = SEARCH_REGEX          \
  305.                                   + SEARCH_MAXIMAL_MATCH  \
  306.                                   + SEARCH_WRAPS          \
  307.                                   + SEARCH_ADVANCE        \
  308.                                   + SEARCH_FORWARD        \
  309.                                   + SEARCH_CASE
  310.    vi_check_flags()
  311.  
  312.    if (!editor_running)
  313.       attach_event_handler( EVENT.EDITOR_RUNNING, "vi_setup")
  314.    else
  315.       vi_setup()
  316. }
  317.  
  318. function vi_setup()
  319. {
  320. #   message("begin vi_setup")
  321.    delete_event( EVENT.EDITOR_RUNNING, "vi_setup")
  322.    attach_event_handler( EVENT.EMULATION_CHANGED, "vi_restore" )
  323.    
  324.    attach_event_handler( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
  325.    attach_event_handler( EVENT.NEW_CURNT_WINDOW, "vi_new_current_win")
  326.  
  327.    attach_event_handler( EVENT.ERROR, "vi_reset" )
  328.  
  329.    # initialize all the ex :set options
  330.    ex_init_state()      
  331.  
  332.    assign_key( VI_EXPAND_TEMPLATE_KEY, "beep" )
  333.    assign_key( VI_NEXT_FIELD_KEY, "beep" ) 
  334.    set_expand_template_key( VI_EXPAND_TEMPLATE_KEY )
  335.    set_next_field_key( VI_NEXT_FIELD_KEY ) 
  336.    
  337.    create_vi_command_keymaps()
  338.    vi_menu()
  339.    create_vi_insert_keymaps()
  340.    vi_command_mode(1)    # vi starts in command mode
  341. #   message("vi_setup complete")
  342. }
  343.  
  344.  
  345. # change flags so that they are correct for vi
  346. #
  347. local function vi_check_flags()
  348. {
  349. #   local mod_ro = and(buffer_flags, BUFFER_READ_ONLY+BUFFER_MODIFIED)
  350. #
  351. #   buffer_flags = default_buffer_flags = or( default_buffer_flags,      
  352. #                                                BUFFER_REAL_SPACE_ONLY + \
  353. #                                                BUFFER_WHOLE_LINES )
  354. #   buffer_flags = default_buffer_flags = or( default_buffer_flags,      
  355. #                                                BUFFER_REAL_SPACE_ONLY)
  356. #   # restore those bits
  357. #   #
  358. #   buffer_flags = or(buffer_flags, mod_ro)
  359. }
  360.  
  361. function vi_process_end()
  362. {
  363.    # check to see if in insert mode, if so end process
  364.    #
  365.    #if (current_keymap == vi_insert_keymap )
  366.    if (!in_ex_mode || (current_keymap != vi_command_keymap) )
  367.    {
  368.       # since we use keymap as a check, change keymap so that we don't come in here
  369.       # again
  370.       #current_keymap = vi_command_keymap
  371.       # Note: this call to vi_command_mode() may be redundant but we'll do it
  372.       # here in case process_end() fails.
  373.       vi_command_mode(1)   # 1 - force the command keymap if it's not current
  374.    }
  375.  
  376.    process_end() # does nothing if not in process_begin
  377. }
  378.  
  379.  
  380. # handler invoked when changing out of vi mode
  381. function vi_restore()
  382. {
  383.    # clear buffer flags set during vi mode initialization
  384. #   buffer_flags = buffer_flags       \
  385. #                = and( buffer_flags, \
  386. #                       not(BUFFER_REAL_SPACE_ONLY + BUFFER_WHOLE_LINES ))
  387.  
  388.    # reset misc. display attributes
  389.    visible_virtual_lines = default_visible_virtual_lines = ""
  390.  
  391.    # remove this event handler
  392.    delete_event( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
  393.    delete_event( EVENT.NEW_CURNT_WINDOW, "vi_new_current_win")
  394.    delete_event( EVENT.EMULATION_CHANGED, "vi_restore" )
  395.    delete_event( EVENT.ERROR, "vi_reset" )
  396.    restore_id()
  397. }
  398.  
  399.  
  400. ## entering vi command mode
  401. #
  402. #  This gets called at the conclusion of insert mode, after all errors, 
  403. #  after all "operators", and after each "ex" command.
  404. #
  405. local function vi_command_mode(force_keymap)
  406. {
  407.    # reset to normal state
  408.  
  409.    if (!in_ex_mode)
  410.    {
  411.       in_ex_mode = 1
  412. #      if (!handler_active(EVENT.LMOUSE_DRAG, function_id("lmouse_drag")))
  413. #         attach_event_handler(EVENT.LMOUSE_DRAG,      function_id("lmouse_drag"))
  414. #      if (!handler_active(EVENT.MOUSE_LEFT_DOWN,  function_id("mouse_left_down")))
  415. #         attach_event_handler(EVENT.MOUSE_LEFT_DOWN,  function_id("mouse_left_down"))
  416. #      if (!handler_active(EVENT.MOUSE_LEFT_UP,    function_id("mouse_left_up")))
  417. #         attach_event_handler(EVENT.MOUSE_LEFT_UP,    function_id("mouse_left_up"))
  418.       message( "[ Command mode ]" )
  419.    }
  420.  
  421.    if (force_keymap && current_keymap != vi_command_keymap)
  422.    {
  423.       if (!vi_keymap_valid(vi_command_keymap))
  424.       {
  425.          create_vi_command_keymaps()
  426.       }
  427.       current_keymap = vi_command_keymap
  428.    }
  429.  
  430.    # calling use_number() overwrites prev num
  431.    number_register = 0                 
  432.  
  433.    buf_id = DEFAULT_BID
  434.    in_glob = 0
  435.    op_flush()
  436. }
  437.  
  438. ## indicate a common error
  439.  
  440. local function vi_error( s )
  441. {
  442.    vi_warning()
  443.    pending_operator = ""   # cancel any pending operator
  444.  
  445.    
  446.    #vi_command_mode() 
  447.    
  448.    delete_event( EVENT.ERROR, "vi_reset" )
  449.    vi_reset()# reset to command mode
  450.    attach_event_handler( EVENT.ERROR, "vi_reset" )
  451.  
  452.    error( s )     # cause longjump to editor top-level
  453.                   #     ( "never" returns )
  454. }
  455.  
  456. ## errors e.g. within insert mode must return
  457.  
  458. local function vi_warning( s )
  459. {
  460.    if ( s ) 
  461.       warning( s )
  462.    else if ( ! option_disable[ "errorbells" ] ) 
  463.       beep()
  464. #   if ( ! option_disable[ "errorbells" ] && pause_on_error ) 
  465. #      beep()
  466. }
  467.  
  468. function vi_redo_index()
  469. {
  470.    return redo_index()
  471. }
  472.  
  473. # kludge to force accurate undo_index within macros
  474.  
  475. function vi_undo_index()
  476. {
  477. #   record( 1 )
  478. #   record( 0 )
  479.  
  480.    return undo_index()
  481. }
  482.  
  483. function establish_baseline()
  484. {
  485.    undo_baseline[ current_buffer ] = vi_undo_index()
  486.    redo_baseline[ current_buffer ] = vi_redo_index()
  487.  
  488.    if ( undo_line[ current_buffer ] != current_line )
  489.       {
  490.       undo_line[ current_buffer ] = current_line
  491.       undo_line_baseline[ current_buffer ] = vi_undo_index()
  492.       }
  493. }
  494.  
  495. function vi_undo_redo( baseline )
  496. {
  497.    local new, old
  498.  
  499.    if ( current_buffer in baseline )
  500.       {
  501.       new = baseline[ current_buffer ]
  502.       baseline[ current_buffer ] = old = vi_undo_index()
  503.  
  504.       if ( new < old )
  505.          undo( new )
  506.       else
  507.          redo( redo_baseline[ current_buffer ] )
  508.  
  509.       } 
  510.    else 
  511.       vi_error()
  512. }
  513.  
  514. function vi_undo()
  515. {
  516.    vi_undo_redo( undo_baseline )
  517. }
  518.  
  519. function vi_undo_line()
  520. {
  521.    if ( undo_line[ current_buffer ] == current_line )
  522.       vi_undo_redo( undo_line_baseline )
  523. }
  524.  
  525. function vi_again()
  526. {
  527.    local i, ch
  528.  
  529.    if ( !again_string )
  530.       vi_error()
  531.  
  532.    if ( prev_buf_id ~ /[1-8]/ )
  533.       prev_buf_id = chr( ord( prev_buf_id ) + 1 )
  534.    buf_id = prev_buf_id
  535.  
  536.    if ( !number_register )
  537.       number_register = prev_number_register 
  538.  
  539.    playing_again = 1
  540.    playback( again_string )
  541.    playing_again = 0
  542. }
  543.  
  544.  
  545. # setup for a single-char command, like "Y" or "~".
  546. #
  547. #  Generate an error if there is a pending operator.
  548. #  Record the appropriate information for undo and again.
  549. #  Special case "single-char" commands like "[[" and "]]" must
  550. #     manually insert an extra record_op()
  551.  
  552. local function op_type_1()
  553. {
  554.    operator_disallowed()
  555.    prev_number_register = number_register 
  556.    record_op()
  557.    establish_baseline()
  558.    op_wrap()
  559. }
  560.  
  561. # setup for the first of a two-char operator command, like "dW" or "yy":
  562. #
  563. #  Record the appropriate information for undo and again.
  564.  
  565. local function op_type_2a()
  566. {
  567.    prev_number_register = number_register 
  568.    record_op()
  569.    establish_baseline()
  570. }
  571.  
  572. # setup for a motion that might be paired with a two-char operator.
  573. #  like "!" or ">w" (may or may not be paired with an operator):
  574. #
  575. #  Record the appropriate information for undo and again.
  576.  
  577. local function op_type_2b( context_type )
  578. {
  579.    if ( pending_operator )
  580.       prev_number_register = number_register 
  581.  
  582.    if ( tmp_again )
  583.       record_op()
  584.  
  585.    mark_context( context_type )
  586. }
  587.  
  588. # wrap-up for the successful completion of any command
  589.  
  590. local function op_wrap()
  591. {
  592.    if ( tmp_again )
  593.       {
  594.       prev_buf_id = buf_id
  595.       again_string = tmp_again
  596.       tmp_again = ""
  597.       }
  598. }
  599.  
  600. # cancel command history accumulated thus far
  601.  
  602. local function op_flush()
  603. {
  604.       tmp_again = ""
  605. }
  606.  
  607. # record a key into the again string
  608.  
  609. local function record_op( key )
  610. {
  611.    if ( !argcount() )
  612.       key = current_key
  613.  
  614.    if ( key == 0 )
  615.       key = key_to_int("<Ctrl-@>")
  616.  
  617.    tmp_again = tmp_again chr( key ) chr( shiftr( key, 8 ))
  618. }
  619.  
  620.  
  621. ## a numeric count may preceed most commands.
  622. #
  623. #  leading zeros are illegal ("0" is a command, executed here).
  624. #  no count means 1.
  625. #  the count is reset after every command, whether the count is used or not.
  626. #  a copy of the previous count is maintained for vi_again
  627.  
  628. function vi_digit()
  629. {
  630.    local key = and( current_key, 0xF )
  631.  
  632.    if ( number_register )
  633.       number_register = number_register * 10 + key
  634.    else
  635.       if ( key )
  636.          number_register = key
  637.       else
  638.          vi_goto_bol()
  639.  
  640. #  message("number_register = %d", number_register);
  641. }
  642.  
  643. function vi_prompt( a, b, c, d ) 
  644. {
  645.    local r
  646.  
  647.    attach_event_handler( EVENT.INVALID_PCHAR, "vi_prompt_err" )
  648.  
  649.    r = prompt_history( a, b, c, d )
  650.  
  651.    delete_event( EVENT.INVALID_PCHAR, "vi_prompt_err" )
  652.  
  653.    return r
  654. }
  655.  
  656. # allow <Backspace> to cancel prompts:
  657.  
  658. function vi_prompt_err() 
  659. {
  660.    if ( prompt_response == "" && current_key == key_to_int("<Backspace>") )
  661.       ungetkey( ESC )
  662. }
  663.  
  664.  
  665. local function use_number()
  666. {
  667.    local prev = number_register
  668.  
  669.    if ( prev == 0 )
  670.       prev = 1
  671.  
  672.    number_register = 0
  673.    return prev
  674. }
  675.  
  676. local function disallow_number()
  677. {
  678.    if ( number_register != 0 )
  679.       message( "Count ignored with this command." )
  680.  
  681.    number_register = 0
  682. }
  683.  
  684. local function vi_getc()
  685. {
  686.    local ch
  687.  
  688.    if (!playing_again && (ch = int_to_ascii(getkey())) && (ch != ASCII_ESC) )
  689.       return chr( ch )
  690.    else
  691.       vi_error()
  692. }
  693.  
  694.  
  695. ### vi operators
  696.  
  697. # process operator command
  698. #
  699. #  if first one and no selection then remember it pending next motion
  700. #  if first one and existing selection, execute operator
  701. #  if second one matches previous one, execute operator in line mode
  702. #  else error
  703.  
  704. function vi_operator()
  705. {
  706.    local ch = chr( int_to_ascii( current_key ))  # get operator
  707.    local selection
  708.  
  709.    op_type_2a()
  710.  
  711.    if ( pending_operator )
  712.    {
  713.       if ( ch == pending_operator )
  714.       {
  715.          # always operate on full lines
  716.          select_lines( use_number() )
  717.          xeq_op()    # operate on lines
  718.       } 
  719.       else 
  720.          vi_error()     # operator mismatch
  721.    } 
  722.    else
  723.    {
  724.       pending_operator = ch      # remember for later
  725.       if ( selection_type() )
  726.       {
  727.          # Convert possible mouse selection into a regular selection
  728.          selection = get_selection_info() 
  729.          UnhighlightMouseSel()
  730.          cua_remove_selection(1)
  731.          restore_selection_info(selection)
  732.    #      xeq_op()                   # !! can't do again # now we can!
  733.       } 
  734.    }
  735. }  
  736.  
  737. local function operator_disallowed(){
  738.    tmp_again = ""       # avoids problem w/vi_space, etc
  739.                #    followed by "."
  740.    if( pending_operator )
  741.       vi_error()     # never returns
  742. }
  743.  
  744. # manually insert an operator
  745. #
  746. #  usage: "pend_operator( op ); motion()"
  747. #
  748. local function pend_operator( op )
  749. {    
  750.    operator_disallowed()
  751.    pending_operator = op
  752. }
  753.  
  754. # process pending operator, if any
  755. #
  756. #  mode specifies the mode of the operation: line, normal, or inclusive
  757. #  if a region has already been selected, mode is ignored
  758.  
  759. local function xeq_op( mode )
  760. {
  761.    local rt
  762.    local line
  763.  
  764.    if ( pending_operator )
  765.       {
  766.  
  767.       if ( !argcount() )
  768.          mode = NORMAL_SELECTION
  769.    
  770.       op_wrap()
  771.    
  772.       if ( ( rt = selection_type()) )
  773.          mode = rt
  774.       else 
  775.          {
  776.          drop_anchor( mode )
  777.          goto_mark( vi_base_mark )
  778.          }
  779.  
  780.       if ( pending_operator == "y" )
  781.          {
  782.          vi_yank()
  783.          #raise_anchor()
  784.          } 
  785.       else if ( pending_operator == "d" )
  786.          {
  787.          vi_yank( AND_DELETE )
  788.  
  789.          if( mode == LINE_SELECTION )
  790.             skip_whitespace()
  791.  
  792.          } 
  793.       else if( pending_operator == "c" )
  794.          {
  795.          vi_yank( AND_DELETE )
  796.          vi_insert( (mode == LINE_SELECTION) ? NEWLINE_BEFORE : 0 )
  797.          } 
  798.       else if ( pending_operator == "<" )
  799.          {
  800. #         outdent_tabs()
  801.          outdent_lines()
  802.          raise_anchor()
  803.          } 
  804.       else if ( pending_operator == ">" )
  805.          {
  806. #         indent_tabs()
  807.          indent_lines()
  808.          raise_anchor()
  809.          } 
  810.       else if ( pending_operator == "!" )
  811.          {
  812.          vi_filter()
  813.          raise_anchor()
  814.          } 
  815.       else 
  816.          vi_error( "bad VI operator: " pending_operator )
  817.  
  818.       pending_operator = ""
  819.    
  820.       } 
  821.    else 
  822.       op_flush()
  823.  
  824.    vi_command_mode()
  825. }
  826.  
  827.  
  828.  
  829. ### yank/put buffers & associated stuff
  830.  
  831.  
  832. # select n lines, starting with the current
  833.  
  834. local function select_lines( n )
  835. {
  836.    local target = current_line + n - 1
  837.    local selection
  838.  
  839.           
  840.    if (selection_type() != LINE_SELECTION)
  841.    {
  842.       if (selection_type())
  843.       {
  844.          # convert current selection into a line selection
  845.          selection = get_selection_info() 
  846.          remove_selection()
  847.          n = (selection.endline - selection.startline) + 1
  848.          prev_number_register = n   # set so vi_again will work correctly
  849.          goto_line(selection.startline) # move to 1st column of line
  850.          skip_whitespace()
  851.       }    
  852.       
  853.       save_position()
  854.       if ( n > 1 )
  855.       {
  856.          target = current_line + n - 1
  857.          current_line = target
  858.    
  859.          if ( current_line != target )
  860.          {
  861.             # there were not enough lines in the file
  862.             restore_position(1)
  863.             vi_error() # never returns
  864.          }
  865.       }
  866.       
  867.       # select from the end to the beginning, current position is then
  868.       # at the beginning of the selection
  869.       begin_selection( LINE_SELECTION )
  870.       restore_position(1)
  871.    }
  872.  
  873. }  
  874.  
  875. # select n characters, n>0 => select towards EOL; n<0 => select towards BOL
  876. #  error if selection bypasses BOL or EOL
  877. #  n=0 => error
  878. #  do nothing if there is already a selection
  879. local function select_chars( n )
  880. {
  881.    local selection
  882.    
  883.    if (!selection_type())
  884.    {
  885.       if ( n > 0 && current_line_length - current_line_offset >= n )
  886.       {
  887.          drop_anchor( NORMAL_SELECTION )
  888.          next_char( n )
  889.       } 
  890.       else if ( (n < 0) && (current_line_offset >= -n) )
  891.       {
  892.          drop_anchor( NORMAL_SELECTION )
  893.          prev_char( -n )
  894.       } 
  895.       else 
  896.          vi_error()
  897.    }
  898.    else
  899.    {
  900.       selection = get_selection_info()
  901.       if (selection.startline != selection.endline)
  902.          vi_error()
  903.       else
  904.       {
  905.          n = marked_region_size()
  906.          prev_number_register = n   # set so vi_again will work correctly
  907.       }
  908.    }
  909.    
  910.    return n
  911. }
  912.  
  913. local function move_down( n )
  914. {
  915.    local target = current_line + n
  916.  
  917.    #create_mark( TEMP_MARK )
  918.    save_position()
  919.    current_line = target
  920.  
  921.    if ( current_line != target )
  922.       {
  923.       #goto_mark( TEMP_MARK )
  924.       restore_position(1)
  925.       vi_error()  
  926.       }
  927.    else   
  928.       restore_position(0)
  929. }
  930.  
  931. # prompt for buffer id
  932.  
  933. function vi_bufid_key()
  934. {
  935.    local ch = vi_getc()
  936.  
  937.    # buffer id "@" is also used internally
  938.    if ( ch ~ /[a-zA-Z1-9]/ )  
  939.       buf_id = ch
  940.    else
  941.       vi_error()
  942. }
  943.  
  944. # returns a buffer id of a system buffer associated with the current buf_id
  945. #  if clear is true, the buffer returned is empty.
  946. #
  947. local function yank_bid( put, del )
  948. {
  949.    local bid = tolower( buf_id )
  950.    local buf, i, b2, b1
  951.  
  952.    if ( bid in buf_id_map )      # we're reusing an existing buffer:
  953.       {   
  954.       if ( bid == DEFAULT_BID && del )
  955.          {
  956.          # save 9 most recent deletes:
  957.  
  958.          if ( "9" in buf_id_map )
  959.             delete_buffer( buf_id_map[ "9" ])
  960.  
  961.          for( i = 9; i > 1; i-- )
  962.             {
  963.             b2 = "" i
  964.             b1 = "" i - 1
  965.             if ( b1 in buf_id_map )
  966.                buf_id_map[ b2 ] = buf_id_map[ b1 ]
  967.             }
  968.          # and fall to the return to create the new one
  969.          } 
  970.       else 
  971.          {
  972.          if ( put || isupper( buf_id ))    # upper=> append        # !del || 
  973.  
  974.             return buf_id_map[ bid ]
  975.  
  976.          # we're reusing a bid and need it to be empty
  977.          # and we fall through to create an empty one
  978.          else
  979.  
  980.             delete_buffer( buf_id_map[ bid ])
  981.          }
  982.  
  983.       } 
  984.    else if ( put )
  985.  
  986.       vi_error( "Nothing has been put in buffer \"" buf_id )
  987.  
  988.    return ( buf_id_map[ bid ] = create_buffer( "vi_yank_" bid, "", BUFFER_SYSTEM ))
  989. }
  990.  
  991. # yank or delete the current selection into the current "put" buffer
  992. #
  993. #  this is the yank command, and also used by other commands.
  994.  
  995. function vi_yank( del )
  996. {
  997.    local prev = current_buffer
  998.    local line = ( selection_type() == LINE_SELECTION )
  999.    local end_line
  1000.    local selection = get_selection_info() 
  1001.    local real_space
  1002.    #local str = get_region_as_str()
  1003.    
  1004.    # if line > 0, then we have a line selection, so set line to be the
  1005.    # number of lines to be yanked
  1006.    if (line)
  1007.       line = selection.endline - selection.startline + 1
  1008.    
  1009.    # must set last_yank_type to be used by paste if using clipboard only
  1010.    last_yank_type = line ? LINE_BUFFER : 0
  1011.         
  1012.    #restore_selection_info(selection)
  1013.    if( del )
  1014.    {
  1015.       # Turn off REAL_SPACE_ONLY to prevent shorter lines that follow a deleted
  1016.       # line from being padded out to the current cursor column.
  1017.       real_space = and(buffer_flags, BUFFER_REAL_SPACE_ONLY)
  1018.       buffer_flags = set_flag_bits(buffer_flags, BUFFER_REAL_SPACE_ONLY, 0)
  1019.       delete_to_scrap()
  1020.       if (line)
  1021.       {
  1022.          goto_bol()
  1023.          skip_whitespace()
  1024.       }
  1025.       buffer_flags = set_flag_bits(buffer_flags, BUFFER_REAL_SPACE_ONLY, real_space)
  1026.    }
  1027.    else
  1028.       copy_to_scrap()
  1029.  
  1030.    raise_anchor()
  1031.  
  1032.    if (!clipboard_only)
  1033.    {
  1034.       delete_event( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
  1035.       current_buffer = yank_bid( 0, del )
  1036.    
  1037.       if( line )
  1038.          buffer_flags = or( buffer_flags, LINE_BUFFER )
  1039.       else
  1040.          buffer_flags = and( buffer_flags, not( LINE_BUFFER ))
  1041.    
  1042.       insert_scrap()
  1043.      #insert_string(str)
  1044.       current_buffer = prev 
  1045.    
  1046.       attach_event_handler( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
  1047.    }
  1048.    
  1049.    # The following code deletes the last line of the buffer by removing
  1050.    # the EOL character from the preceding line.
  1051.    if (line && buffer_offset >= buffer_size)
  1052.    {
  1053.       end_line = current_line
  1054.       prev_char()
  1055.       if (current_line < end_line && read_buffer() == "")
  1056.          delete_chars(1)
  1057.       else
  1058.          next_char()
  1059.    }
  1060.    
  1061.    message("%s%s%s%s to scrap",  (line > 1 ? line " ": ""), 
  1062.                               (line ? "Line" : "Block"), 
  1063.                               (line > 1 ? "s " : " "),
  1064.                               (del ? "deleted" : "copied") );
  1065.    return line
  1066. }
  1067.  
  1068. # put the current yank buffer's contents into the scrap buffer,
  1069. #  and return true if the buffer was a "LINE" buffer.
  1070. #
  1071. #  Most clients need to perform some fine positioning
  1072. #  depending on the line status, before actually inserting the text.
  1073. #  Hence the obscure nature of this function.
  1074. #
  1075. local function put_buffer_to_scrap()
  1076. {
  1077.    local prev = current_buffer
  1078.    local line_oriented
  1079.  
  1080.    delete_event( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
  1081.    current_buffer = yank_bid( 1, 0 )
  1082.  
  1083.    goto_buffer_top()
  1084.    drop_anchor()
  1085.    goto_buffer_bottom()
  1086.    copy_to_scrap()
  1087.    raise_anchor()
  1088.  
  1089.    line_oriented =  and( buffer_flags, LINE_BUFFER )
  1090.    current_buffer = prev
  1091.    attach_event_handler( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
  1092.  
  1093.    return line_oriented
  1094. }
  1095.  
  1096. function vi_Y()   # yank lines or selected block
  1097. {           
  1098.    local temp = selection_type();
  1099.    local n = use_number();
  1100.  
  1101.    op_type_1()
  1102.  
  1103.    if ( !temp ) 
  1104.       select_lines( n )  
  1105.  
  1106.    vi_yank()
  1107. }
  1108.  
  1109. function vi_p()   # put after cursor/line
  1110. {           
  1111.    local lnum, lines
  1112.  
  1113.    disallow_number()
  1114.    op_type_1()
  1115.    lines = clipboard_only ? last_yank_type : put_buffer_to_scrap()
  1116.  
  1117.    #if( (lines = put_buffer_to_scrap()) )
  1118.    if( lines )
  1119.       {
  1120.       down()
  1121.       goto_bol()
  1122.       lnum = current_line
  1123.       } 
  1124.    else 
  1125.       right()
  1126.  
  1127.    insert_scrap()
  1128.  
  1129.    if( lines )
  1130.       vi_goto_line( lnum )
  1131.    else
  1132.       prev_char()
  1133. }
  1134.  
  1135. function vi_P()   # put before cursor/line
  1136. {           
  1137.    local lnum, lines
  1138.  
  1139.    op_type_1()
  1140.  
  1141.    lines = clipboard_only ? last_yank_type : put_buffer_to_scrap()
  1142.    #if( (lines = put_buffer_to_scrap()) )
  1143.    if( lines )
  1144.       {
  1145.       goto_bol()
  1146.       lnum = current_line
  1147.       }
  1148.  
  1149.    insert_scrap()
  1150.  
  1151.    if( lines )
  1152.       vi_goto_line( lnum )
  1153.    else
  1154.       prev_char()
  1155. }
  1156.  
  1157.  
  1158. ### insert mode and auxiliary operations
  1159.  
  1160. local insert_text       # copy of text of previous insert
  1161. local insert_start      # start offset of insert
  1162. local insert_start_line # start line of insert
  1163. local insert_backstop   # backstop of this line; cancel deletes back to here
  1164. local insert_indent     # flag, true <=> backstop follows auto-indent text
  1165.  
  1166. # set insert_backstop and insert_indent
  1167. local function set_backstop( ai )
  1168. {
  1169.    insert_indent = ai     
  1170.    insert_backstop = buffer_offset
  1171. }
  1172.  
  1173. local function vi_reset_insert()
  1174. {
  1175.    set_backstop( 0 )
  1176.    insert_text = ""
  1177.    # remember starting position:
  1178.    insert_start = buffer_offset
  1179.    insert_start_line = current_line
  1180. }
  1181.  
  1182. function vi_insert_mouse_handler()
  1183. {
  1184.    if ( event_id == EVENT.MOUSE_LEFT_DOWN ||
  1185.         event_id == EVENT.MOUSE_RIGHT_DOWN )
  1186.    {
  1187.       mouse_left_down()
  1188.       vi_reset_insert()
  1189.    }                  
  1190. }
  1191.  
  1192. local function vi_override_events()
  1193. {
  1194.    local event
  1195.    local mouse_events[]
  1196.    local mouse_drag_events[]
  1197.    local exit_events[]
  1198.    local dummy_handler_id = function_id( "vi_insert_mouse_handler" )
  1199.  
  1200.    # store all mouse event handlers 
  1201.    mouse_events      = save_and_remove_events( EVENT.MOUSE_LEFT_DOWN, EVENT.MOUSE_MID_CLICK2 ) 
  1202.    mouse_drag_events = save_and_remove_events( EVENT.LMOUSE_DRAG, EVENT.RMOUSE_DRAG )
  1203.  
  1204.    mouse_handlers = merge_arrays( mouse_events, mouse_drag_events )
  1205.  
  1206.    # attach dummy handlers for all mouse events
  1207.    for ( event in mouse_handlers )
  1208.       attach_event_handler( event, dummy_handler_id )
  1209.  
  1210. }                                                         
  1211.  
  1212. local function vi_restore_events()
  1213. {
  1214.    local event
  1215.    local dummy_handler_id = function_id( "vi_insert_mouse_handler" )
  1216.  
  1217.    # delete dummy handlers for all mouse events
  1218.    for ( event in mouse_handlers )
  1219.       delete_event( event, dummy_handler_id )
  1220.  
  1221.    restore_events( mouse_handlers )
  1222.  
  1223.    delete mouse_handlers
  1224. }                                                   
  1225.  
  1226.  
  1227. # all common text insertion functionality
  1228. #
  1229. #  -- auto indents
  1230. #  -- repeated inserts
  1231. #  -- macro playback
  1232. #  -- intra-line editing
  1233.  
  1234. local function vi_insert( newline )
  1235. {
  1236.    local n, count, col, line, offset
  1237.    local new_cb, new_cw
  1238.    local insert_buffer_name
  1239.  
  1240.    in_ex_mode = 0
  1241.    count = use_number()
  1242.  
  1243.       # insert requested newlines
  1244.       #  _BEFORE => before current line (e.g. "O", "S")
  1245.       #  _AFTER => after current line (e.g. "o")
  1246.       #  else => no newline
  1247.  
  1248.       if ( newline == NEWLINE_BEFORE )
  1249.       {
  1250.          if ( current_line == 1 )
  1251.             { 
  1252.             goto_bol()
  1253.             insert_newline()
  1254.             up()
  1255.             } 
  1256.          else 
  1257.             {
  1258.             up()
  1259.             vi_insert_nl(newline)
  1260.             }
  1261.  
  1262. #         insert_text = "\n"
  1263.       } 
  1264.       else if ( newline == NEWLINE_AFTER )
  1265.       {
  1266.          vi_insert_nl()
  1267. #         insert_text = "\n"
  1268.       } 
  1269.          
  1270.    if( playing_again )
  1271.    {
  1272.       if ( and(buffer_flags, BUFFER_OVERTYPE_MODE) )
  1273.          delete_chars( length( insert_text ))
  1274.  
  1275.       #warning("insert_text is: |%s|", insert_text)
  1276.       reinsert_string( insert_text, newline)
  1277.    } 
  1278.    else 
  1279.    {
  1280.       # mark starting point for BS/CAN and remember starting position
  1281.       vi_reset_insert()
  1282.                       
  1283.       # interpret insert keymap:
  1284.  
  1285.       message( "[ Insert mode ]" )
  1286.       if (current_keymap != vi_insert_keymap)
  1287.       {
  1288.          if (!vi_keymap_valid(vi_insert_keymap))
  1289.          {
  1290.             if (!vi_keymap_valid(default_vi_insert_keymap))
  1291.                create_vi_insert_keymaps()
  1292.             else
  1293.                vi_insert_keymap = default_vi_insert_keymap
  1294.          }
  1295.          current_keymap = vi_insert_keymap
  1296.       }
  1297.       
  1298.       # save buffer and window we start in before process_begin
  1299.       #
  1300.       insert_buffer = current_buffer
  1301.       insert_buffer_name = buffer_filename
  1302.       insert_window = current_window
  1303.       
  1304.       if (playingback)
  1305.          return
  1306.          
  1307.       vi_override_events()
  1308.       
  1309.       process_begin()
  1310.       
  1311. #      warning("Leaving Insert Mode")
  1312.       vi_restore_events()
  1313.  
  1314.  
  1315.       if (current_buffer != insert_buffer)
  1316.       {
  1317.          vi_reset(1)
  1318.          return
  1319.       }
  1320.       
  1321.       # save the new buffer and window we may now be in
  1322.       #                        
  1323.       new_cb = current_buffer
  1324.       new_cw = current_window
  1325.       
  1326.       # change to the window then the buffer you were in before the process_begin
  1327.       # to finish executing the following code
  1328. #      if (window_valid(insert_window) && new_cw != insert_window)
  1329. #         current_window = insert_window
  1330.       if (insert_buffer && new_cb != insert_buffer)
  1331.          next_buffer(insert_buffer_name, 0, 1)
  1332. #         current_buffer = insert_buffer
  1333.       
  1334.       # no longer in insert mode--erase status message
  1335. #      message( "" )
  1336.  
  1337.       # copy inserted text to insert_text (!!string length limit)
  1338.  
  1339.       if (buffer_offset <= insert_start)
  1340.          insert_text = ""
  1341.       else if ( current_line == insert_start_line )
  1342.       {
  1343.          insert_text = insert_text  \
  1344.             read_buffer( insert_start - buffer_offset ) # read backwards
  1345.       } 
  1346.       else 
  1347.       {
  1348.          line = current_line - 1
  1349.          offset = buffer_offset
  1350.          goto_buffer_offset( insert_start )
  1351.          
  1352.          # if the only text before the first line is whitespace, then include
  1353.          # it in insert_text.  This will allow reinsert_string() to properly
  1354.          # handle auto_indents.
  1355.          if ( !(read_buffer(-current_line_offset) ~ /[^ \t]/) )
  1356.             goto_bol()
  1357.  
  1358.          insert_text = insert_text read_buffer()
  1359.          current_column = 0
  1360.  
  1361.          while( current_line++ < line )
  1362.             insert_text = insert_text "\n" read_buffer()
  1363.  
  1364.          goto_buffer_offset( offset )
  1365.          insert_text = insert_text "\n" read_buffer( -current_line_offset )
  1366.       }
  1367.    }
  1368.  
  1369.    # process repeat count, if any
  1370.  
  1371.    while( count-- > 1 )
  1372.       reinsert_string( insert_text, newline)
  1373.  
  1374.    # change to new window first, then the buffer we were in after process_begin
  1375.    #
  1376. #   if ( new_cw && (new_cw != current_window) ) 
  1377. #      current_window = new_cw
  1378.  
  1379.    if ( new_cb && (new_cb != current_buffer) ) 
  1380.       current_buffer = new_cb
  1381.    
  1382.    left()   # move back one character after insert to better emulate real vi
  1383.    vi_command_mode(1)
  1384. }
  1385.  
  1386.  
  1387. local function get_column_diff(str_a, str_b)
  1388. {
  1389.    local diff
  1390.    local end_offset
  1391.    
  1392.    save_position()
  1393.    goto_eol()
  1394.    save_position()
  1395.    insert_string("\n")
  1396.    insert_string(str_a)
  1397.    diff = current_column
  1398.    goto_bol()
  1399.    delete_to_eol()
  1400.    insert_string(str_b)
  1401.    diff -= current_column
  1402.    end_offset = buffer_offset
  1403.    restore_position(1)
  1404.    delete_chars(end_offset - buffer_offset - 1)
  1405.    restore_position(1)
  1406.    
  1407.    return diff
  1408. }
  1409.  
  1410. ##
  1411. # reinsert_string (text, newline)
  1412. # Reinsert previously inserted text, properly auto-indented. 'text' contains the
  1413. # text to insert which is usually the global 'insert_text' set by vi_insert().
  1414. # 'newline' will be non-zero if vi_insert() was called by the o or O commands.  If
  1415. # it is non-zero, then vi_insert() has already inserted the first carriage return.
  1416. #  
  1417. local function reinsert_string( text, newline)
  1418. {
  1419.    local i, line, s, n
  1420.    local prev_white
  1421.    local curr_white
  1422.    local diff
  1423.    local dist
  1424.  
  1425.    if (!auto_indent_mode)
  1426.       insert_string(text)
  1427.    else
  1428.    {
  1429.       n = split( text, line, "\n" )
  1430.    
  1431.       for ( i = 1; i<= n; i++ )
  1432.       {
  1433.          s = line[ i ]
  1434.    
  1435.          # if this is the first line of a muli-line insertion, and the original
  1436.          # insertion was on a new line (o or O commands), then strip off the
  1437.          # leading whitespace since we are already at the proper auto indent level.
  1438.          if ( i == 1 && i < n && newline)
  1439.             sub( /^[ \t]+/, "", s )
  1440.          else if (i > 1 && match(line[i-1], /^[ \t]+/))
  1441.          {
  1442.             # subsequent lines must remove extra whitesspace already accounted for
  1443.             # by auto indent
  1444.             prev_white = substr(line[i-1], RSTART, RLENGTH)
  1445.             if (!sub(prev_white, "", s))
  1446.             {
  1447.                # if however, this line is indented less than the previous one
  1448.                # figure out how far back to move it
  1449.                match(line[i], /^[ \t]+/)
  1450.                curr_white = substr(line[i], RSTART, RLENGTH)
  1451.                
  1452.                # get the actual column difference between the two lines
  1453.                diff = get_column_diff(prev_white, curr_white)
  1454.                
  1455.                # move back to the closest tab stop
  1456.                while (diff > 0)
  1457.                {
  1458.                   dist = distance_prev_tab
  1459.                   current_column -= dist
  1460.                   diff -= dist 
  1461.                }
  1462.                
  1463.                # remove the extra whitespace from the line
  1464.                delete_to_eol()
  1465.                
  1466.                # remove leading whitespace from string since we're at indent level
  1467.                sub(curr_white, "", s)
  1468.             }
  1469.          }
  1470.          
  1471.          insert_string( s )
  1472.    
  1473.          if ( i < n )
  1474.             vi_insert_cr()
  1475.       }  # end for(lines)
  1476.    }
  1477. }
  1478.  
  1479. ## following functions are bound to keys for insert mode actions
  1480.  
  1481. # cancel input thus far on the current line
  1482.  
  1483. function vi_insert_cancel()
  1484. {
  1485.    local n = buffer_offset - insert_backstop
  1486.    if( n > 0 )
  1487.       {
  1488.       prev_char( n )
  1489.       delete_chars( n )
  1490.       }
  1491. }
  1492.  
  1493. # backspace in insert mode
  1494. #
  1495. #  The standard VI BS function can only bs over user-generated chars, 
  1496. #  and in particular cannot bs over auto-indent text.  This 
  1497. #  implementation allows backspacing over a-i text.  To disallow it
  1498. #  (and more faithfully emulate VI) set the bs_over_ai variable to 0.
  1499. #
  1500.  
  1501. function vi_insert_bs()
  1502. {
  1503.    if ( insert_backstop < buffer_offset )
  1504.       backspace()
  1505.    else if (   bs_over_ai     &&  
  1506.                insert_indent  && 
  1507.                (current_line_offset < buffer_offset) )
  1508.       {
  1509.       backspace()
  1510.       set_backstop( insert_indent )
  1511.       } 
  1512.    else 
  1513.       vi_warning()
  1514. }
  1515.  
  1516. # Standard VI ^D/unindent and ^T/reindent functions: ?? incomplete
  1517.  
  1518. function vi_insert_unindent()
  1519. {
  1520.    local ch = read_buffer( -1 )
  1521.    local start_offset
  1522.  
  1523.    if ( ch == "^" || ch == "0" )
  1524.       {
  1525.       insert_backstop = buffer_offset - current_line_offset
  1526.       vi_insert_cancel()
  1527.  
  1528.       # ^D should save this new indent for subsequent lines
  1529.  
  1530.       } 
  1531.    else if ( (ch == "\t" || ch == " ") &&
  1532.             !(read_buffer(-current_line_offset) ~ /[^ \t]/) )
  1533.    {
  1534.       if (ch == "\t")
  1535.          backspace()
  1536.       else
  1537.       {
  1538.          start_offset = buffer_offset
  1539.          current_column = current_column - distance_prev_tab
  1540.          delete_chars(start_offset - buffer_offset)
  1541.       }
  1542.       insert_backstop = buffer_offset
  1543.       if (insert_start_line == current_line)
  1544.          vi_reset_insert()
  1545.    }
  1546.    else
  1547.       vi_warning()
  1548. }
  1549.  
  1550. function vi_insert_reindent()
  1551. {
  1552.    if ( !(read_buffer(-current_line_offset) ~ /[^ \t]/) )
  1553.       insert_string( "\t" )
  1554.    else
  1555.       vi_warning()
  1556. }
  1557.  
  1558.  
  1559. # "dB", limited by scope of current insert
  1560.  
  1561. function vi_insert_dB()
  1562. {
  1563.    local patt = "[^a-z0-9A-Z_ \t]+" word_patt
  1564.  
  1565.    local sflags = SEARCH_REGEX + SEARCH_MAXIMAL_MATCH    \
  1566.          + SEARCH_ADVANCE + SEARCH_BACKWARD     \
  1567.          + and( search_flags, SEARCH_CASE ) \
  1568.          + and( search_flags, SEARCH_WRAPS )
  1569.          #+ and( vi_search_flags, SEARCH_CASE ) \
  1570.          #+ and( vi_search_flags, SEARCH_WRAPS )
  1571.  
  1572.    if( buffer_offset <= insert_backstop )
  1573.       vi_warning()
  1574.    else 
  1575.       {
  1576.       drop_anchor( NORMAL_SELECTION )
  1577.  
  1578.       #if( search( WORD_patt, sflags ))
  1579.       if( search( patt, sflags ))
  1580.          {                         
  1581.          if ( buffer_offset < insert_backstop )
  1582.             goto_buffer_offset( insert_backstop )
  1583.  
  1584.          delete_chars()
  1585.          #warning("after delete_chars")
  1586.          }
  1587.          
  1588.       raise_anchor()
  1589.       }
  1590. }
  1591.  
  1592. function vi_reinsert()
  1593. {
  1594.    local flag = ( insert_backstop < buffer_offset )
  1595.  
  1596.    reinsert_string( insert_text )
  1597.  
  1598.    if( !flag )
  1599.       process_end()
  1600.  
  1601.    # a more accurate implementation would beep if !flag
  1602.    # and always process_end() the insert; this is an extension
  1603. }
  1604.  
  1605. function vi_insert_quoted()
  1606. {
  1607.    local key
  1608.    local ch
  1609.  
  1610.    message( "Type ascii key to be inserted:" )
  1611.  
  1612.    key = getkey()
  1613.    ch  = int_to_ascii(key)
  1614.  
  1615.    if ( ch || key == key_to_int("<Ctrl-@>") )
  1616.    {
  1617.       insert_key( ch )
  1618.       message( "" )
  1619.    } 
  1620.    else 
  1621.       # error() would screw-up insert mode
  1622.       warning( "Can't insert non-ascii keys." )
  1623. }
  1624.  
  1625. # normal newline text entry; implements auto-indenting if enabled.
  1626.  
  1627. function vi_insert_cr(newline)
  1628. {
  1629.    local str
  1630.    insert_string( "\n" )
  1631.  
  1632.    if ( auto_indent_mode )
  1633.       {
  1634.       # if the command was O, use indenting from line below, else use line above
  1635.       if (newline == NEWLINE_BEFORE)
  1636.          next_line()
  1637.       else
  1638.          prev_line()
  1639.  
  1640.       drop_anchor()
  1641.  
  1642.       goto_bol()
  1643. #      copy_to_scrap()
  1644.       str = get_region_as_str()
  1645.  
  1646.       raise_anchor()
  1647.  
  1648.       if (newline == NEWLINE_BEFORE)
  1649.          up()
  1650.       else
  1651.          down()
  1652.          
  1653.       insert_string(str)
  1654. #      insert_scrap()
  1655.       }
  1656.  
  1657.    set_backstop( auto_indent_mode )
  1658. }
  1659.  
  1660. # shift-enter -- goto EOL and enter
  1661.  
  1662. function vi_insert_nl(newline)
  1663. {
  1664.    goto_eol()
  1665.    vi_insert_cr(newline)
  1666. }
  1667.  
  1668.  
  1669. ### Template Editing
  1670. #
  1671. #
  1672. #  vi_expand_template() checks the buffer for an abbreviation,
  1673. #     and replaces the abbreviation with the expansion
  1674. #     if there is one
  1675. #
  1676. #     Function is activated by the space, tab, or <Enter> keys
  1677. #     in insertion mode.
  1678.  
  1679.  
  1680. function vi_expand_template() 
  1681. {
  1682.    local string, len, abbreviation, ok
  1683.  
  1684.    for ( abbreviation in templates ) 
  1685.       {
  1686.       len = length( abbreviation )
  1687.  
  1688.       string = read_buffer( - len -1 )
  1689.  
  1690.       if ( ( ok = string ~ "^[ \t]" ) )
  1691.          string = substr( string, 2 ) 
  1692.  
  1693.       if ( ok || current_column == len + 1 ) 
  1694.          {
  1695.          if ( abbreviation == string ) 
  1696.             {
  1697.             current_column -= len
  1698.             delete_chars( len )
  1699.             insert_string( templates[ abbreviation ] )
  1700.             }
  1701.          }
  1702.       }
  1703.  
  1704.    if ( and( current_key, 0x00ff) == 13 )
  1705.       vi_insert_cr()
  1706.    else
  1707.       insert_key()
  1708. }
  1709.  
  1710.  
  1711. ### insertion commands
  1712.  
  1713. function vi_i()
  1714. {
  1715.    op_type_1()
  1716.  
  1717.    vi_insert()
  1718. }
  1719.  
  1720. function vi_I()
  1721. {
  1722.    op_type_1()
  1723.  
  1724.    skip_whitespace()
  1725.    vi_insert()
  1726. }
  1727.  
  1728.  
  1729. function vi_a()
  1730. {
  1731.    op_type_1()
  1732.  
  1733.    if ( current_line_length > current_line_offset )
  1734.       right()
  1735.  
  1736.    vi_insert()
  1737. }
  1738.  
  1739. function vi_A()
  1740. {
  1741.    op_type_1()
  1742.  
  1743.    goto_eol()
  1744.    vi_insert()
  1745. }
  1746.  
  1747.  
  1748. function vi_o()
  1749. {
  1750.    op_type_1()
  1751.  
  1752.    vi_insert( NEWLINE_AFTER )
  1753. }
  1754.  
  1755. function vi_O()
  1756. {
  1757.    op_type_1()
  1758.  
  1759.    vi_insert( NEWLINE_BEFORE )
  1760. }
  1761.  
  1762. ### deletions
  1763. #
  1764. #  all deletions delete_to_scrap() even if single character
  1765.  
  1766.  
  1767. function vi_X()
  1768. {
  1769.    op_type_1()
  1770.  
  1771.    select_chars( -use_number() )
  1772.    vi_yank( AND_DELETE )
  1773. }
  1774.  
  1775. function vi_x()
  1776. {
  1777.    op_type_1()
  1778.  
  1779.    select_chars( use_number())
  1780.    vi_yank( AND_DELETE )
  1781.  
  1782.    if ( (current_line_length <= current_line_offset) && 
  1783.          current_line_offset )
  1784.       prev_char()
  1785. }
  1786.  
  1787. ## more complex edits -- combinations of delete/insert
  1788.  
  1789. function vi_J()
  1790. {
  1791.    local n = use_number()
  1792.    local ins
  1793.  
  1794.    op_type_1()
  1795.  
  1796.    if ( n > 1 )
  1797.       n -= 1
  1798.  
  1799.    while ( n-- > 0 )
  1800.       {
  1801.       goto_eol()
  1802.  
  1803.       while ( read_buffer( -1 ) ~ /[ \t]/ )
  1804.          prev_char()
  1805.  
  1806.       ins = ( read_buffer( -1 ) == "." ) ? "  " : " "
  1807.  
  1808.       drop_anchor()
  1809.       next_line()
  1810.       vi_yank( AND_DELETE )
  1811.  
  1812.       if ( read_buffer( 1 ) != "(" )
  1813.          insert_string( ins )
  1814.       }
  1815. }
  1816.  
  1817. function vi_D()
  1818. {
  1819.    op_type_1()
  1820.    pend_operator( "d" )
  1821.    vi_goto_eol()
  1822. }
  1823.  
  1824. function vi_C()
  1825. {
  1826.    vi_D()
  1827.    vi_insert()
  1828. }
  1829.  
  1830. function vi_R()
  1831. {
  1832.    op_type_1()
  1833.    buffer_flags = or( buffer_flags, BUFFER_OVERTYPE_MODE )
  1834.  
  1835.    vi_insert()
  1836.    buffer_flags = xor( buffer_flags, BUFFER_OVERTYPE_MODE )
  1837. }
  1838.  
  1839. function vi_r( )
  1840. {
  1841.    local n
  1842.  
  1843.    op_type_1()
  1844.  
  1845.    n = use_number()
  1846.  
  1847.    # limit scope to current line
  1848.    if ( current_line_length - current_line_offset < n )
  1849.       vi_error()
  1850.  
  1851.    if ( !playing_again )
  1852.       vi_r_ch = vi_getc()
  1853.    if ( vi_r_ch == "\r" || vi_r_ch == "\n" )
  1854.       vi_r_ch = "\n"
  1855.  
  1856.    # number returned will be different than n if there is already a selection
  1857.    n = select_chars( n )
  1858.    vi_yank( AND_DELETE )
  1859.  
  1860.    if ( vi_r_ch == "\n" )
  1861.       {
  1862.       while( n-- > 0 )
  1863.          vi_insert_cr()
  1864.       } 
  1865.    else 
  1866.       {
  1867.       while( n-- > 0 )
  1868.          insert_string( vi_r_ch )
  1869.  
  1870.       if ( current_column > 1 )
  1871.          prev_char()
  1872.       }
  1873. }
  1874.  
  1875. function vi_S()
  1876. {
  1877.    op_type_1()
  1878.  
  1879.    select_lines( use_number())
  1880.    vi_yank( AND_DELETE )
  1881.    vi_insert( NEWLINE_BEFORE )
  1882. }
  1883.  
  1884. function vi_s()
  1885. {
  1886.    op_type_1()
  1887.  
  1888. # This command is not needed, restore position is never called 
  1889. # on it.  I removed it because of a current bug in raise_anchor:
  1890. # it will restore the selection set by save_position, leaving
  1891. # the user an unwanted selection.  DWM
  1892. #   save_position();
  1893.  
  1894.    select_chars( use_number())
  1895.    vi_yank( AND_DELETE )
  1896.    vi_insert()
  1897. }
  1898.  
  1899. function vi_tilde()
  1900. {
  1901.    op_type_1()
  1902.  
  1903.    select_chars( use_number())
  1904.       
  1905.    reverse()
  1906.  
  1907.    raise_anchor()
  1908.    
  1909.    if ( (current_line_length <= current_line_offset) && 
  1910.          current_line_offset )
  1911.       prev_char()
  1912. }
  1913.  
  1914. ### vi motion commands
  1915.  
  1916. local function vi_goto_line( line )
  1917. {
  1918.    current_line = line
  1919.    skip_whitespace()
  1920. }
  1921.  
  1922. function vi_goto( n )
  1923. {
  1924.    op_type_2b( MAJOR_MARK )
  1925.  
  1926.    if ( number_register )
  1927.       n = use_number()     # need a range check
  1928.    else if( !n )
  1929.          n = buffer_last_line
  1930.  
  1931.    vi_goto_line( n )
  1932.  
  1933.    xeq_op( LINE_SELECTION )
  1934. }
  1935.  
  1936. local vert_column
  1937. local vert_motion
  1938.  
  1939. # vi_arrow()
  1940. # Arrow key handler.
  1941. # which: 1 = up, 2 = down, 3 = left, 4 = right
  1942. function vi_arrow(which)
  1943. {
  1944.    local n = 1
  1945.    local remaining = current_line_length - (current_line_offset + 1)
  1946.    local down_target
  1947.  
  1948.    if (in_ex_mode)
  1949.    {
  1950.       n = use_number()
  1951.  
  1952.       if (which < 1 || which > 4)
  1953.          which = 1
  1954.          
  1955.       if ( which == 1 && current_line <= n )
  1956.          n = current_line
  1957.       else if (which == 2 && (down_target = current_line + n) > buffer_last_line)
  1958.          n = buffer_last_line - current_line
  1959.       else if (which == 3 && current_line_offset < n )
  1960.          n = current_line_offset
  1961.       else if (which == 4 &&  remaining <= n )
  1962.          n = remaining + (pending_operator == "" ? 0 : 1)
  1963.  
  1964.       if (n < 1)
  1965.          vi_error()
  1966.          
  1967.       op_type_2b()
  1968.    }
  1969.    
  1970.    vi_move(which, n)
  1971.  
  1972.    if (in_ex_mode)
  1973.       xeq_op(which < 3 ? LINE_SELECTION : 0)
  1974.    else
  1975.       vi_reset_insert()
  1976. }
  1977.  
  1978. # vi_insert_arrow()
  1979. # Arrow key handler when in insert mode.
  1980. # which: 1 = up, 2 = down, 3 = left, 4 = right,
  1981. #        5 = home, 6 = end, 7 = page up, 8 = page down
  1982. function vi_insert_arrow(which)
  1983. {
  1984.    if (which < 5)
  1985.       vi_move(which, 1)
  1986.    else if (which == 5)
  1987.       home_key()
  1988.    else if (which == 6)
  1989.       end_key()
  1990.    else if (which == 7)
  1991.       page_up()
  1992.    else if (which == 8)
  1993.       page_down()
  1994.  
  1995.    vi_reset_insert()
  1996. }
  1997.  
  1998.  
  1999. local function vi_move(dir, n)
  2000. {
  2001.    if (dir < 3) # up or down
  2002.       if ( prev_command != vert_motion )
  2003.          vert_column = current_column
  2004.       else
  2005.          current_column = vert_column
  2006.  
  2007.    if (dir == 1)
  2008.    {
  2009.       vert_motion = current_command
  2010.       up( n )
  2011.       vi_adjust_column()
  2012.    }
  2013.    else if (dir == 2)
  2014.    {
  2015.       vert_motion = current_command
  2016.       move_down( n )
  2017.       vi_adjust_column()
  2018.    }
  2019.    else if (dir == 3)
  2020.    {
  2021.       vert_motion = 0
  2022.       prev_char( n )
  2023.    }
  2024.    else if (dir == 4)
  2025.    {
  2026.       vert_motion = 0
  2027.       next_char( n )
  2028.    }
  2029.  
  2030. function vi_space()
  2031. {
  2032.    if ( selection_type() )
  2033.       {
  2034.       operator_disallowed()
  2035.  
  2036.       if ( and(keyboard_flags, KB_ALT) )
  2037.          outdent_columns()
  2038.       else
  2039.          indent_columns()  
  2040.  
  2041.       } 
  2042.    else 
  2043.       vi_arrow(4) # right
  2044. }
  2045.  
  2046. function vi_bksp()
  2047. {
  2048.    if ( selection_type() )
  2049.       {
  2050.       operator_disallowed()
  2051.       outdent_columns()    
  2052.       } 
  2053.    else 
  2054.       vi_arrow(3) # left
  2055. }
  2056.  
  2057. function vi_tab()
  2058. {
  2059.    if ( selection_type() )
  2060.       {
  2061.       operator_disallowed()
  2062.       if ( and(keyboard_flags, KB_ALT) )
  2063.          outdent_lines()
  2064.          #outdent_tabs()
  2065.       else
  2066.          indent_lines()
  2067.          #indent_tabs()
  2068.       } 
  2069.    else 
  2070.       vi_error()
  2071. }
  2072.  
  2073. function vi_back_tab()
  2074. {
  2075.    if ( selection_type() )
  2076.       {
  2077.       operator_disallowed()
  2078.       outdent_lines()
  2079.       #outdent_tabs()
  2080.       } 
  2081.    else 
  2082.       vi_error()
  2083. }
  2084.  
  2085. function vi_goto_column()
  2086. {
  2087.    local n = use_number()
  2088.  
  2089.    if ( current_line_width < n )
  2090.       vi_error()
  2091.  
  2092.    op_type_2b()
  2093.    current_column = n
  2094.    xeq_op()
  2095. }
  2096.  
  2097. function vi_H()
  2098. {
  2099.    local n = use_number() - 1
  2100.  
  2101.    op_type_2b( MAJOR_MARK )
  2102.  
  2103.    goto_window_top()
  2104.  
  2105.    if ( n >= window_text_height )
  2106.       n = window_text_height - 1
  2107.  
  2108.    down( n )
  2109.    skip_whitespace()
  2110.  
  2111.    xeq_op( LINE_SELECTION )
  2112. }
  2113.  
  2114. function vi_M()
  2115. {
  2116.    disallow_number()
  2117.    op_type_2b( MAJOR_MARK )
  2118.  
  2119.    goto_window_middle()
  2120.    skip_whitespace()
  2121.  
  2122.    xeq_op( LINE_SELECTION )
  2123. }
  2124.  
  2125. function vi_L()
  2126. {
  2127.    local n = use_number() - 1
  2128.  
  2129.    op_type_2b( MAJOR_MARK )
  2130.  
  2131.    goto_window_bottom()
  2132.  
  2133.    if ( n >= window_text_height )
  2134.       n = window_text_height - 1
  2135.  
  2136.    up( n )
  2137.    skip_whitespace()
  2138.  
  2139.    xeq_op( LINE_SELECTION )
  2140. }
  2141.  
  2142. function vi_goto_bol()
  2143. {
  2144.    disallow_number()
  2145.  
  2146.    op_type_2b()
  2147.    goto_bol()
  2148.    xeq_op()
  2149. }
  2150.  
  2151. function vi_goto_eol()
  2152. {
  2153.    local n = use_number()
  2154.  
  2155.    op_type_2b()
  2156.  
  2157.    if ( n > 1 )
  2158.       down( n - 1 )
  2159.  
  2160.    current_column = current_line_width # goto_eol()
  2161.    xeq_op( current_line_width ? INCLUSIVE_SELECTION : NORMAL_SELECTION )
  2162. }
  2163.  
  2164. function vi_next_line()
  2165. {
  2166.    local n = use_number()
  2167.  
  2168.    op_type_2b()
  2169.    next_line( n )
  2170.    xeq_op( LINE_SELECTION )
  2171. }
  2172.  
  2173. function vi_prev_line()
  2174. {
  2175.    local n = use_number()
  2176.  
  2177.    op_type_2b()
  2178.    prev_line( n )
  2179.    xeq_op( LINE_SELECTION )
  2180. }
  2181.  
  2182. function vi_skip_whitespace()
  2183. {
  2184.    local n = use_number()
  2185.  
  2186.    op_type_2b()
  2187.    skip_whitespace()
  2188.    xeq_op()
  2189. }
  2190.  
  2191. function vi_goto_matching()
  2192. {
  2193.    local n = use_number()
  2194.  
  2195.    op_type_2b( MAJOR_MARK )
  2196.    if ( read_buffer( 1 ) !~ /[(){}[\]]/ )
  2197.       if ( match(read_buffer(), "[(){}[\\]]") )
  2198.          next_char( RSTART - 1 )
  2199.       else
  2200.          vi_error()
  2201.  
  2202.    goto_matching()
  2203.    xeq_op( INCLUSIVE_SELECTION )
  2204. }
  2205.  
  2206. local function vi_adjust_column()
  2207. {
  2208.  
  2209. #   if (  and( buffer_flags, BUFFER_POSITION_IS_VIRTUAL ) || 
  2210. #         ( current_line_offset == current_line_length )     )
  2211.    if ( current_line_offset >= current_line_length )
  2212.       {
  2213.       goto_eol()
  2214.  
  2215.       if ( current_line_offset )
  2216.          prev_char()
  2217.       }
  2218.  
  2219.    origPos = buffer_offset;
  2220. }
  2221.  
  2222.  
  2223.  
  2224. # Word, sentence, paragraph, and section motions.
  2225. # Different commands call vi_motion with one of the patterns below.
  2226. #
  2227. # Different dialects of vi implement subtle variations for certain combos:
  2228. #
  2229. #  Commands.....     SPE   MKS   Sun   AIX
  2230. #
  2231. #  !w >W etc.     yes   yes   error error
  2232. #
  2233. #  w W b B over \n      skips skips stops error
  2234. #           \n \n at \n
  2235. #
  2236. #
  2237. #  add "|^[ \t]*$" to word_, WORD_, end_, and END_patt to make these
  2238. #  motions treat newlines as words, ala Sun vi.
  2239.  
  2240.  
  2241. # the following search pattern would be better to use, but cursor placement
  2242. # seems to mess things up.
  2243. local word_patt = "[a-z0-9A-Z_]+"
  2244. local WORD_patt = "[^ \t]+"
  2245. #local  end_patt = "[a-z0-9A-Z_]*[a-z0-9A-Z_]\\c"#|[^a-z0-9A-Z_ \t]*\\c[^a-z0-9A-Z_ \t]"
  2246. # The following 2 patterns are used by vi_e() to define a word.  See the comment
  2247. # for the vi_e() function.
  2248. local  end_patt = "[a-z0-9A-Z_]*\\c[a-z0-9A-Z_]"
  2249. local  end_patt2 = "[^a-z0-9A-Z_ \t]*\\c[^a-z0-9A-Z_ \t]"
  2250. local END_patt = "[^ \t]*\\c[^ \t]"
  2251. #local END_patt = "[a-z0-9A-Z_]([ \t]+|$)"
  2252. #local sent_patt   = "(\\.|!|\\?)[)\"'\\]]*(\t| |$|^)"
  2253. #local para_patt   = "^$|^\\.PP$|^\\.NH|^\\.IP|^\\.LP|^\\.PP|^\\.QP|^\\.LI"
  2254. #local sect_patt   = "^\\.NH|^\\.SH|^\\.H([ \t]|$)|^\\.HU|^\\{|^function|^local[ \t]+function|^global[ \t]+function|^\f"
  2255.  
  2256.  
  2257. local function vi_motion( patt, direction )
  2258. {
  2259.    if ( direction )
  2260.       return vi_forward_motion( patt )
  2261.    else
  2262.       return vi_backward_motion( patt )
  2263. }
  2264.  
  2265. local origPos 
  2266.  
  2267. # this function is never used
  2268. #local function vi_save_pos()
  2269. #{
  2270. #   origPos = buffer_offset;
  2271. #}
  2272.  
  2273. local function vi_forward_motion( patt )
  2274. {
  2275.    local sflags = SEARCH_REGEX + SEARCH_MAXIMAL_MATCH + SEARCH_FORWARD + SEARCH_HIGHLIGHT
  2276.    local count
  2277.    origPos = buffer_offset;
  2278.  
  2279.    search_count = 0
  2280.    count = use_number()
  2281.    op_type_2b( MAJOR_MARK )
  2282.  
  2283.    save_position()
  2284.  
  2285.    do
  2286.       {
  2287.       if ( !search( patt, sflags ) )
  2288.          {
  2289.          restore_position( 1 )
  2290.          vi_error()
  2291.          break
  2292.          }
  2293.  
  2294.       if ( buffer_offset == origPos )
  2295.          {
  2296.          # didn't move, so advance past current pattern
  2297.          if ( !search_string_length )
  2298.             break
  2299.  
  2300.          goto_buffer_offset( origPos + search_string_length )
  2301.          count++
  2302.          } 
  2303.       else 
  2304.          origPos = buffer_offset
  2305.  
  2306.       # WGN - Removed following to better emulate vi
  2307.       #if ( patt == END_patt )
  2308.          #right();
  2309.  
  2310.       } 
  2311.    while ( --count > 0 )
  2312.  
  2313.    restore_position( 0 )
  2314. }
  2315.  
  2316. local function vi_backward_motion( patt )
  2317. {
  2318.    local sflags = SEARCH_REGEX + 
  2319.                   SEARCH_MAXIMAL_MATCH + 
  2320.                   SEARCH_FORWARD + 
  2321.                   SEARCH_ONCE_PER_LINE
  2322.    local count
  2323.    local found
  2324.    local line
  2325.    local origPos = buffer_offset
  2326.  
  2327.    count = use_number()
  2328.    search_count = 0
  2329.  
  2330.    op_type_2b( MAJOR_MARK )
  2331.  
  2332.    save_position()
  2333.  
  2334.    do 
  2335.       {
  2336.       # advance forward to the match immediately preceeding the
  2337.       # cursor position
  2338.       #
  2339.  
  2340.       found = FALSE
  2341.       save_position()
  2342.  
  2343.       for ( line = current_line; line; line-- )
  2344.          {
  2345.          goto_pos( line, 1 )
  2346.  
  2347.          while ( search( patt, sflags ) && (buffer_offset < origPos ) )
  2348.             {
  2349.             found = TRUE
  2350.             restore_position( 0 )
  2351.             save_position()
  2352.             goto_buffer_offset( buffer_offset + search_string_length )
  2353.             }
  2354.  
  2355.          if ( found )
  2356.             {
  2357.             restore_position( 1 )
  2358.             origPos = buffer_offset
  2359.             count--
  2360.             break
  2361.             }
  2362.          }
  2363.       } while ( count > 0 && line )
  2364.  
  2365.    restore_position( 0 )
  2366.  
  2367.    if ( !found )
  2368.       {
  2369.       restore_position( 1 )
  2370.       vi_error()
  2371.       }
  2372. }
  2373.  
  2374.  
  2375. function vi_w( patt )
  2376. {
  2377.    local toLeft = 0;
  2378.  
  2379.    if ( !argcount() )
  2380. #      patt = word_patt
  2381.       # WGN - Added 0-9 to following pattern to work like real vi
  2382. #      patt = "[^a-zA-Z_ \t]+|" word_patt
  2383.       patt = "[^a-z0-9A-Z_ \t]+|" word_patt
  2384.  
  2385.    if ( pending_operator ~ /[ycd]/ )
  2386.       patt = patt "|$"
  2387.  
  2388.    if ( pending_operator == "c" )
  2389.       vi_cw( patt )
  2390.    else 
  2391.       {
  2392.       vi_motion( patt, SEARCH_FORWARD )
  2393.  
  2394.       xeq_op( NORMAL_SELECTION )
  2395.       }
  2396. }
  2397.  
  2398. function vi_cw( patt )
  2399. {
  2400.    local ch = read_buffer( 1 )
  2401.  
  2402.    vi_motion( patt, SEARCH_FORWARD )
  2403.  
  2404.    if ( ch !~ /[ \t]/ )
  2405.       {
  2406.       search( "[^ \t]\\c", 
  2407.                SEARCH_REGEX + 
  2408.                SEARCH_ADVANCE + 
  2409.                and( search_flags, SEARCH_CASE ) + 
  2410.                and( search_flags, SEARCH_WRAPS) )
  2411.                #and( vi_search_flags, SEARCH_CASE ) + 
  2412.                #and( vi_search_flags, SEARCH_WRAPS) )
  2413.       }
  2414.  
  2415.       xeq_op( NORMAL_SELECTION )
  2416.  
  2417. }
  2418.  
  2419. function vi_W()
  2420. {
  2421.    vi_w( WORD_patt )
  2422. }
  2423.             
  2424. ## vi_e()
  2425. ##
  2426. # Move to the end of the current word, or the end of the next word if already
  2427. # at the end of a word.  For vi_e(), a word is either a string of letters,
  2428. # numbers and underscores, or a string of punctuation characters.  Which
  2429. # definition of a word to use depends on the value of the next non-whitespace
  2430. # character.
  2431. function vi_e()
  2432. {
  2433.    local char
  2434.    local patt = end_patt
  2435.    
  2436.    save_position()
  2437.    
  2438.    next_char()
  2439.    char = read_buffer(1)
  2440.    while (char && isspace(char))
  2441.    {
  2442.       next_char()
  2443.       char = read_buffer(1)
  2444.    }
  2445.    
  2446.    if (char && (char ~ end_patt2) )
  2447.       patt = end_patt2
  2448.       
  2449.    restore_position(1)  # go back to where we started
  2450.    vi_motion( patt, SEARCH_FORWARD )
  2451.    xeq_op( INCLUSIVE_SELECTION )
  2452. }
  2453.  
  2454. ## vi_E()
  2455. ##
  2456. # Move to the end of the current word, or the end of the next word if already
  2457. # at the end of a word.  For vi_E(), a word is either a string of 
  2458. # non-whitespace characters.
  2459. function vi_E()
  2460. {
  2461.    vi_motion( END_patt, SEARCH_FORWARD )
  2462.    xeq_op( INCLUSIVE_SELECTION )
  2463. }
  2464.  
  2465. function vi_B()
  2466. {
  2467.    vi_motion( WORD_patt, SEARCH_BACKWARD )
  2468.    xeq_op( NORMAL_SELECTION )
  2469. }
  2470.  
  2471. function vi_b()
  2472. {
  2473.    local toLeft;
  2474.    local patt = "[^a-z0-9A-Z_ \t]+|" word_patt
  2475.    
  2476.    vi_motion( patt, SEARCH_BACKWARD )
  2477.  
  2478.    xeq_op( NORMAL_SELECTION )
  2479. }
  2480.  
  2481. function vi_prev_sent()
  2482. {
  2483.    op_type_2b( MAJOR_MARK )
  2484.  
  2485.    if (!prev_sentence(use_number()))
  2486.       goto_buffer_top()
  2487.    else
  2488.       xeq_op( NORMAL_SELECTION )
  2489. }
  2490.  
  2491. function vi_next_sent()
  2492. {
  2493.    op_type_2b( MAJOR_MARK )
  2494.  
  2495.    if (!next_sentence(use_number()))
  2496.       goto_buffer_bottom()
  2497.    else
  2498.       xeq_op( NORMAL_SELECTION )
  2499. }
  2500.  
  2501. function vi_prev_para()
  2502. {
  2503.    op_type_2b( MAJOR_MARK )
  2504.  
  2505.    if (!prev_paragraph(use_number()))
  2506.       goto_buffer_top()
  2507.    else
  2508.    {
  2509.       if ( pending_operator )
  2510.          current_line--
  2511.    
  2512.       xeq_op( LINE_SELECTION )
  2513.    }
  2514. }
  2515.  
  2516. function vi_next_para()
  2517. {
  2518.    op_type_2b( MAJOR_MARK )
  2519.  
  2520.    if (!next_paragraph(use_number()))
  2521.       goto_buffer_bottom()
  2522.    else
  2523.    {
  2524.       if ( pending_operator )
  2525.          current_line--
  2526.    
  2527.       xeq_op( LINE_SELECTION )
  2528.    }
  2529. }
  2530.  
  2531. function vi_prev_sect()
  2532. {
  2533.    local ch = "["
  2534.    
  2535.    op_type_2b( MAJOR_MARK )
  2536.  
  2537.    if (!playing_again)
  2538.       ch = vi_getc()
  2539.  
  2540.    if (ch != "[")
  2541.       vi_error()
  2542.    else
  2543.    {
  2544.       if (!prev_section(use_number()))
  2545.          goto_buffer_top()
  2546.  
  2547.       if ( pending_operator && current_line > 1)
  2548.          current_line--
  2549.    
  2550.       # must update view to use window_first
  2551.       update_current_view()
  2552.       if (current_line > 1 && current_line == window_first)
  2553.       {
  2554.          save_position()
  2555.          message("scrolling window")
  2556.          scroll_up_1()
  2557.          restore_position(1)
  2558.       }
  2559.       xeq_op( LINE_SELECTION )
  2560.    }
  2561. }
  2562.  
  2563. function vi_next_sect()
  2564. {
  2565.    local ch = "]"
  2566.    local result = 0
  2567.    local patt = section_pattern
  2568.    
  2569.    op_type_2b( MAJOR_MARK )
  2570.  
  2571.    if (!playing_again)
  2572.       ch = vi_getc()
  2573.  
  2574.    if (ch != "]")
  2575.       vi_error()
  2576.    else
  2577.    {
  2578.       if (pending_operator)
  2579.          patt = section_end_pattern "|" patt
  2580.       
  2581.       if (!next_section(use_number(), patt))
  2582.       {
  2583.          goto_buffer_bottom()
  2584.          goto_bol()
  2585.       }
  2586.       else
  2587.          if (pending_operator && (read_buffer(1) ~ section_pattern) )
  2588.             current_line--
  2589.  
  2590.       xeq_op( LINE_SELECTION )
  2591.    }
  2592. }
  2593.  
  2594.  
  2595.  
  2596.  
  2597. ## vi "find" commands
  2598. #  fx skip forward to character "x"
  2599. #  Fx skip backwards to character "x"
  2600. #  tx skip forwards to just before character "x"
  2601. #  Tx backwards just before "x"
  2602. #  ;  repeat last find command
  2603. #  ,  execute last find command in reverse direction
  2604.  
  2605. function vi_find_repeat()
  2606. {
  2607.    vi_find_it( vi_find_com )
  2608. }
  2609.  
  2610. function vi_find_reverse()
  2611. {
  2612.    local com = toreverse( vi_find_com )
  2613.    vi_find_it( com )
  2614. }
  2615.  
  2616. function vi_find()
  2617. {
  2618.    vi_find_com = chr( current_key )
  2619.    vi_find_it( vi_find_com, 1 )
  2620. }
  2621.  
  2622. local function vi_find_it( com, needChar )
  2623. {
  2624.    local i, n
  2625.  
  2626.    op_type_2b( MAJOR_MARK )
  2627.  
  2628.    if ( needChar && !playing_again) 
  2629.       vi_find_char = vi_getc()
  2630.  
  2631.    if ( !vi_find_char )
  2632.       vi_error()
  2633.  
  2634.    n = use_number()
  2635.  
  2636.    while ( n-- > 0 )
  2637.       {
  2638.       # forwards
  2639.       if ( islower( com ) )
  2640.          {    
  2641.          i = index( substr( read_buffer(), 2), vi_find_char )
  2642.          if ( i )
  2643.             next_char( i - ( com == "t" ))
  2644.          } 
  2645.       # backwards
  2646.       else 
  2647.          {       
  2648.          i = bindex( read_buffer( - current_column ), vi_find_char )
  2649.          if ( i )
  2650.             prev_char( i - ( com == "T" ))
  2651.          }
  2652.       }
  2653.  
  2654.    if ( !i )
  2655.       vi_error()
  2656.  
  2657.    xeq_op( islower( com ) ? INCLUSIVE_SELECTION : NORMAL_SELECTION )
  2658. }
  2659.  
  2660. # backwards index
  2661. #  return the offset left from the end of the string of the rightmost 
  2662. #  instance of ch, if any, in s
  2663.  
  2664. local function bindex( s, ch )
  2665. {
  2666.    local n = rindex( s, ch )
  2667.  
  2668.    if ( n )
  2669.       n = length( s ) - n + 1
  2670.  
  2671.    return n
  2672. }
  2673.  
  2674. function vi_scroll_window()
  2675. {
  2676.    local key
  2677.    local c
  2678.    disallow_number()
  2679.    if ( !playing_again )
  2680.       key = getkey()
  2681.  
  2682.    if ( key == KEYCODE_KEYPAD_ENTER || key == KEYCODE_ENTER)
  2683.       scroll_window_top()
  2684.    else 
  2685.    {
  2686.       c = chr(int_to_ascii(key)) 
  2687.       if (c == ".")
  2688.          scroll_window_middle()
  2689.       else if (c == "-")
  2690.          scroll_window_bottom()
  2691.       else
  2692.          vi_error()  
  2693.    }
  2694. }
  2695.  
  2696.  
  2697. ## marks & mark motion
  2698.  
  2699. # translate a mark ascii name to a bookmark id
  2700.  
  2701. local function vi_mark_id( ch )
  2702. {
  2703.    if ( !argcount())
  2704.       if (!playing_again)
  2705.          ch = vi_getc()
  2706.       else
  2707.          ch = prev_mark_id
  2708.  
  2709.    prev_mark_id = ch 
  2710.  
  2711.    if ( ch ~ /[`']/ )
  2712.       return vi_base_mark
  2713.  
  2714.    if ( ch ~ /[a-zA-Z]/ )
  2715.       return vi_base_mark + ord( toupper( ch )) - ord( "@" )
  2716.  
  2717.    vi_error()
  2718. }
  2719.  
  2720.  
  2721. # record current position into "`" mark
  2722. #  called by all motion commands to permit goto previous location,
  2723. #  and to mark the range of vi operators
  2724. #
  2725. #  context_type:  0 or Null =>   mark motion starting point
  2726. #        1 =>     mark non-relative motion starting point
  2727. #        2 =>     don't mark (for vi_place_mark)
  2728.  
  2729. local function mark_context( context_type )
  2730. {
  2731.    if ( context_type == DONT_MARK )
  2732.       return
  2733.  
  2734.    if ( context_type == MAJOR_MARK )
  2735.       create_mark( vi_major_mark ) 
  2736.  
  2737.    create_mark( vi_base_mark ) #, current_line, current_column )
  2738. }
  2739.  
  2740.  
  2741. # m command -- places a mark here
  2742.  
  2743. function vi_place_mark()
  2744. {
  2745.    operator_disallowed()
  2746.    disallow_number()
  2747.    record_op()
  2748.    create_mark( vi_mark_id(), current_line, current_column )
  2749. }
  2750.  
  2751. function vi_goto_mark()
  2752. {
  2753.    local lines = chr( int_to_ascii(current_key) ) == vi_line_mark
  2754.    local mark
  2755.  
  2756.    disallow_number()
  2757.    record_op()
  2758.    op_type_2b( DONT_MARK )
  2759.    mark = vi_mark_id()
  2760.  
  2761.    if ( mark == vi_base_mark )
  2762.       swap_marks( vi_major_mark, 0 )
  2763.    else 
  2764.       {
  2765.       if ( !mark_defined( mark ))
  2766.          vi_error()
  2767.  
  2768.       mark_context( MAJOR_MARK )
  2769.       goto_mark( mark )
  2770.       }
  2771.  
  2772.    if ( lines )
  2773.       skip_whitespace()
  2774.  
  2775.    xeq_op( lines ? LINE_SELECTION : NORMAL_SELECTION )
  2776. }
  2777.  
  2778. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  2779.  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  2780. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  2781.  
  2782. ### ex mode
  2783. #
  2784. #  ex_key:     prompt the user; call x_mode if something entered
  2785. #
  2786. #  ex_mode: handle non-globable prefix and suffix
  2787. #
  2788. #  ex_mode1:   called recursively by ex_glob()
  2789. #
  2790. #  Bug: this version ignores superflurous stuff after a valid command
  2791.  
  2792. function ex_key()
  2793. {
  2794.    local com
  2795.  
  2796.    operator_disallowed()
  2797.    establish_baseline()
  2798.  
  2799.    com = vi_prompt( "EX_MODE", ":" )
  2800.    original_com = com
  2801.  
  2802.    if ( com )
  2803.       ex_mode( com )
  2804. }
  2805.  
  2806. local function ex_mode( command )
  2807. {
  2808.    ex_com = command 
  2809.    ex_address_range()
  2810.  
  2811.    ex_mode1()
  2812.  
  2813.    if ( browser_bid )
  2814.       browse_printed()
  2815.  
  2816.    vi_command_mode()
  2817. }
  2818.  
  2819. local function ex_mode1( command )
  2820. {
  2821.    local times, n, com, variant, macro_name, key_function, macro_body
  2822.    local tmp_str = ""
  2823.    local covered_macro, covered_name, covered_body
  2824.    local abbreviation
  2825.    local buf_char
  2826.    local count, mark
  2827.    local NI = " -- not implemented"
  2828.  
  2829.  
  2830.    if( command )
  2831.       ex_com = command 
  2832.  
  2833.    com = eat_command() # eat_command operates on ex_com (via eat_match())
  2834.  
  2835.    #count = eat_count()
  2836. #   message("com = %s, ex_addresses = %d, ex_addr1 = %d, ex_addr2 = %d, command = %s",
  2837. #            com, ex_addresses, ex_addr1, ex_addr2, command)
  2838. #   message("original_com = %s, command = %s, ex_com = %s, com = %s",
  2839. #            original_com, command, ex_com, com)
  2840.    #return
  2841.             
  2842.    if( com == "" && ex_addresses == 1)
  2843.       vi_goto_line( ex_addr1 )   
  2844.  
  2845.    else if ( com == "" && ex_addresses > 1 )
  2846.       {
  2847.       select_range( DEFAULT_ONE )
  2848.       copy_to_scrap()
  2849.       scrap_to_browser()
  2850.       raise_anchor()
  2851.       }
  2852.    else if( com == "" )
  2853.       {
  2854.       vi_error( "unrecognized command" )
  2855.       }
  2856.    else if( index( "append", com ) == 1 )
  2857.       {
  2858.       eat_space()
  2859.       next_line()
  2860.       prev_char()
  2861.       insert_string( ex_com )
  2862.       insert_newline()
  2863.       }
  2864.    else if( index( "abbreviate", com ) == 1 )
  2865.       {
  2866.       if( in_glob )
  2867.          vi_error( "can't glob:" com ex_com )
  2868.       else if( ex_addresses )
  2869.          vi_error( "addresses are illegal for :" com ex_com )
  2870.       else
  2871.          {
  2872.          abbreviation = eat_word()
  2873.          if( !abbreviation)
  2874.             vi_error( "null abbreviation not allowed" )
  2875.          else
  2876.             {
  2877.             eat_space()
  2878.             templates[ abbreviation ] = ex_com
  2879.             }
  2880.          }
  2881.       }
  2882.    else if( index( "args", com ) == 1 )
  2883.       vi_error( "args" NI )
  2884.    
  2885.    else if( index( "change", com ) == 1 )
  2886.       {
  2887.       count = eat_count()
  2888.       if( ex_addresses == 1 && count )
  2889.          {
  2890.          ex_addresses = 2
  2891.          ex_addr2 = ex_addr1 + count - 1
  2892.          }
  2893.  
  2894.       select_range( DEFAULT_ONE )
  2895.       vi_yank( AND_DELETE )
  2896.       variant = eat_bang()
  2897.       if( variant )
  2898.          auto_indent_mode = option_disable[ "autoindent" ]
  2899.       vi_O()
  2900.       if( variant )
  2901.          auto_indent_mode = ! option_disable[ "autoindent" ]
  2902.  
  2903.       }
  2904.    else if( index( "cd", com ) == 1 )
  2905.       vi_error( "cd" NI )
  2906.  
  2907.    else if( index( "copy", com ) == 1 )
  2908.       ex_copy( COPY )
  2909.  
  2910.    else if( index( "cr", com ) == 1 )
  2911.       vi_error( "cr" NI )
  2912.  
  2913.    else if( com == "X" )
  2914.       vi_error( "X" NI )
  2915.  
  2916.    else if( index( "delete", com ) == 1 )
  2917.       {
  2918.       eat_space()
  2919.       buf_char = eat_match( "[a-zA-Z]" )
  2920.       count = eat_count()
  2921.       if( ex_addresses == 1 && count )
  2922.          {
  2923.          ex_addresses = 2
  2924.          ex_addr2 = ex_addr1 + count - 1
  2925.          }
  2926.       select_range( DEFAULT_ONE )
  2927.       if( buf_char ) buf_id = buf_char
  2928.          vi_yank( AND_DELETE )
  2929.       }
  2930.  
  2931.    else if( index( "ex", com ) == 1 || index( "edit", com ) == 1 )
  2932.       {
  2933.       if( in_glob )
  2934.          vi_error( "can't glob:" com ex_com )
  2935.       else if( ex_addresses )
  2936.          vi_error( "addresses are illegal for :" com ex_com )
  2937.       else
  2938.          ex_edit()
  2939.       }
  2940.  
  2941.    else if( index( "file", com ) == 1 )
  2942.       {
  2943.       if( in_glob )
  2944.          vi_error( "can't glob:" com ex_com )
  2945.       else if( ex_addresses )
  2946.          vi_error( "addresses are illegal for :" com ex_com )
  2947.       else
  2948.          {
  2949.          ex_file_subst()
  2950.          if(( com = eat_arg()))
  2951.             {
  2952.             buffer_filename = com
  2953.             buffer_name = path_fname(buffer_filename) \
  2954.                          path_ext(buffer_filename)
  2955.             buffer_flags = and( buffer_flags, not( BUFFER_MODIFIED ))
  2956.             display_redraw()
  2957.             }
  2958.          }
  2959.       ex_show_file()
  2960.       }
  2961.  
  2962.    else if( index( "global", com ) == 1 || com == "v" )
  2963.       {
  2964.       if( com != "v" && eat_bang() )
  2965.          com = "v"
  2966.       ex_glob( com )
  2967.       }
  2968.  
  2969.    else if( index( "insert", com ) == 1 )
  2970.       {
  2971.       eat_space()
  2972.       prev_char()
  2973.       insert_string( ex_com )
  2974.       insert_newline()
  2975.       }
  2976.  
  2977.    else if( index( "join", com ) == 1 )
  2978.       {
  2979.       count = eat_count()
  2980.       if (ex_addresses)
  2981.          vi_goto_line( ex_addr1 )   
  2982.       if (ex_addresses == 2)
  2983.          number_register = ex_addr2 - ex_addr1
  2984.       else
  2985.          number_register = count
  2986.       if (number_register < 1)
  2987.          number_register = 1;
  2988.       vi_J()
  2989. #      vi_error( "join" NI )
  2990.       }
  2991.  
  2992.    else if( index( "list", com ) == 1 || index( "print", com ) == 1 )
  2993.       {
  2994.       count = eat_count()
  2995.       if( ex_addresses == 1 && count )
  2996.          {
  2997.          ex_addresses = 2
  2998.          ex_addr2 = ex_addr1 + count - 1
  2999.          }
  3000.       select_range( DEFAULT_ONE )
  3001.       copy_to_scrap()
  3002.       scrap_to_browser()
  3003.       raise_anchor()
  3004.       }
  3005.  
  3006.    else if( index( "move", com ) == 1 )
  3007.       ex_copy( MOVE )
  3008.  
  3009.    else if( index( "mark", com ) == 1 || com == "k")
  3010.       {
  3011.       eat_space()
  3012.       mark = eat_letter()
  3013.       create_mark( vi_mark_id( mark ))
  3014.       }
  3015.  
  3016.    else if( match( com, "^k[a-z]" ))
  3017.       {
  3018.       mark = substr( com, 2 )
  3019.       create_mark( vi_mark_id( mark ))
  3020.       }
  3021.  
  3022.    else if( index( "map", com ) == 1 )
  3023.       {
  3024.       if( in_glob )
  3025.          vi_error( "can't glob:" com ex_com)
  3026.       else if( ex_addresses )
  3027.          vi_error( "addresses are illegal for :" com ex_com )
  3028.       else
  3029.          {
  3030.          variant = eat_bang()
  3031.          macro_name = eat_word()
  3032.          if( !macro_name )
  3033.             {
  3034.             if( !variant )
  3035.                {
  3036.                for( macro_name in command_macro_table)
  3037.                   pbuf( macro_name " " command_macro_table[ macro_name ] )
  3038.                }
  3039.             else 
  3040.                for( macro_name in insert_macro_table )
  3041.                   pbuf( macro_name " " insert_macro_table[ macro_name ] )
  3042.             }
  3043.          else
  3044.             {
  3045.             macro_body = eat_word()
  3046.             if ( !variant )
  3047.                {
  3048.                covered_name = covered_binding( macro_name, variant)
  3049.                covered_command_macro[ macro_name ] =covered_name
  3050.                covered_command_macro_body[ macro_name ] = keymap_binding( covered_name, current_keymap)
  3051.                delete command_macro_table[ covered_command_macro[ macro_name ]]
  3052.                command_macro_table[ macro_name ] = macro_body
  3053.                assign_key( macro_name, "expand_macro " macro_name ) 
  3054.                }
  3055.             else
  3056.                {
  3057.                current_keymap = vi_insert_keymap 
  3058.                covered_name = covered_binding( macro_name, variant )
  3059.                covered_insert_macro[ macro_name ] = covered_name
  3060.                covered_insert_macro_body[ macro_name ] = keymap_binding( covered_name, current_keymap )
  3061.                delete insert_macro_table[ covered_insert_macro[ macro_name ]]
  3062.                insert_macro_table[ macro_name ] = macro_body
  3063.                assign_key( macro_name, "expand_macro " macro_name ) 
  3064.                current_keymap = vi_command_keymap 
  3065.                }
  3066.             }
  3067.          }
  3068.       }
  3069.    else if( index( "next", com ) == 1 )
  3070.       {
  3071.       variant = eat_bang()
  3072.        if( in_glob )
  3073.          vi_error( "can't glob:" com ex_com )
  3074.       else if( ex_addresses )
  3075.          vi_error( "addresses are illegal for :" com ex_com )
  3076.       else
  3077.          {
  3078.          ex_file_subst()
  3079.          if( !variant )
  3080.             ex_autowrite()
  3081.          next_buffer()
  3082.          ex_show_file()
  3083.          }
  3084.       }
  3085.  
  3086.    else if( index( "number", com ) == 1 || com == "#" )
  3087.       vi_error( "number" NI )
  3088.  
  3089.    else if( index( "open", com ) == 1 )
  3090.       vi_error( "open" NI )
  3091.  
  3092.    else if( index( "put", com ) == 1 )
  3093.    {
  3094.       if( in_glob )
  3095.          vi_error( "can't glob:" com ex_com )
  3096.       else if (eat_count())
  3097.          vi_error( "count invalid for: put" )
  3098.       else
  3099.       {
  3100.       if (ex_addresses == 1)
  3101.          vi_goto_line( ex_addr1 )   
  3102.       else if (ex_addresses == 2)
  3103.          vi_goto_line( ex_addr2 )   
  3104.       vi_p()
  3105.       }   
  3106.    }
  3107.  
  3108.    else if( index( "preserve", com ) == 1 )
  3109.    {
  3110.       # can't make :p = prev_buffer, since it is defined as 'print' above (see list)
  3111.       vi_error( "preserve" NI )
  3112.    }
  3113.    
  3114.    else if( index( "quit", com ) == 1 )
  3115.       {
  3116.       if( in_glob )
  3117.          vi_error( "can't glob:" com ex_com )
  3118.       else if( ex_addresses )
  3119.          vi_error( "addresses are illegal for :" com ex_com )
  3120.       else
  3121.          ex_quit()
  3122.       }
  3123.  
  3124.    else if( index( "read", com ) == 1 )
  3125.       {
  3126.       eat_space()
  3127.       if( eat_bang() )
  3128.          ex_shell_read()
  3129.       else
  3130.          ex_read()
  3131.       }
  3132.  
  3133.    else if( index( "recover", com ) == 1 )
  3134.       vi_error( "recover" NI )
  3135.  
  3136.    else if( index( "rewind", com ) == 1 )
  3137.       # need to account for autowrite when this code is implemented
  3138.       # see :next code for method
  3139.       vi_error( "rewind" NI )
  3140.  
  3141.    else if ( index( "substitute", com ) == 1 )
  3142.       {
  3143.       if( ex_com ) 
  3144.          save_command = ex_com
  3145.       ex_sub()
  3146.       }
  3147.  
  3148.    else if ( index( "set", com ) == 1 )
  3149.       ex_set()
  3150.  
  3151.    else if( index( "shell", com ) == 1 )
  3152.       {
  3153.       if( in_glob )
  3154.          vi_error( "can't glob:" com ex_com )
  3155.       else if( ex_addresses )
  3156.          vi_error( "addresses are illegal for :" com ex_com )
  3157.       else
  3158.          system()
  3159.       }
  3160.  
  3161.    else if( index( "source", com ) == 1 )
  3162.       vi_error( "source" NI )
  3163.  
  3164.    else if( index( "t", com ) == 1 )
  3165.       ex_copy( COPY )
  3166.  
  3167.    else if( index( "tag", com ) == 1 )
  3168.       {
  3169.       variant = eat_bang()
  3170.       n = eat_word()
  3171.       if( !variant )
  3172.          ex_autowrite()
  3173.       vi_tags( n )
  3174.       }
  3175.  
  3176.    else if( index( "undo", com ) == 1 )
  3177.       {
  3178. #      vi_error( "undo" NI )
  3179.       undo()
  3180.       }
  3181.  
  3182.    else if( index( "unabbreviate", com ) == 1 )
  3183.       {
  3184.       if( in_glob )
  3185.          vi_error( "can't glob:" com ex_com )
  3186.       else if( ex_addresses )
  3187.          vi_error( "addresses are illegal for :" com ex_com )
  3188.       else
  3189.          {
  3190.          abbreviation = eat_word()
  3191.          if( !abbreviation )
  3192.             vi_error( "null abbreviation not allowed" )
  3193.          else
  3194.             delete templates[ abbreviation ]
  3195.          }
  3196.       }
  3197.  
  3198.    else if( index( "unmap", com ) == 1 )
  3199.       {
  3200.       if( in_glob )
  3201.          vi_error( "can't glob:" com ex_com)
  3202.       else if( ex_addresses )
  3203.          vi_error( "addresses are illegal for :" com ex_com )
  3204.       else
  3205.          {
  3206.          variant = eat_bang()
  3207.          macro_name = eat_word()
  3208.          if( !macro_name )
  3209.             vi_error( "need macro name to unmap" )
  3210.          else
  3211.             {
  3212.             if( !variant )
  3213.                {
  3214.                delete command_macro_table[ macro_name ]
  3215.                covered_macro = covered_command_macro[ macro_name ]
  3216.                if( covered_macro )
  3217.                   {
  3218.                   covered_body = covered_command_macro_body[ macro_name ]
  3219.                   if( covered_body ~ /expand_macro/ )
  3220.                      command_macro_table[ covered_macro ] = covered_body
  3221.                   delete covered_command_macro_body[ macro_name ]
  3222.                   delete covered_command_macro[ macro_name ]
  3223.                   assign_key( covered_macro, covered_body )
  3224.                   }
  3225.                }
  3226.             else
  3227.                {
  3228.                delete insert_macro_table[ macro_name ]
  3229.                covered_macro = covered_insert_macro[ macro_name ]
  3230.                if( covered_macro )
  3231.                   {
  3232.                   covered_body = covered_insert_macro_body[ macro_name ]
  3233.                   if( covered_body ~ /expand_macro/ )
  3234.                      insert_macro_table[ covered_macro ] = covered_body
  3235.                   delete covered_insert_macro_body[ macro_name ]
  3236.                   delete covered_insert_macro[ macro_name ]
  3237.                   push_keymap( vi_insert_keymap )
  3238.                   assign_key( covered_macro, covered_body )
  3239.                   pop_keymap()
  3240.                   }
  3241.                }
  3242.             }
  3243.          }
  3244.  
  3245.       }
  3246.  
  3247.    else if( index( "version", com ) == 1 )
  3248.       print_version()
  3249.  
  3250. #  else if( index( "visual", com ) == 1 )
  3251. #
  3252.    else if( index( "w", com ) == 1 && match( ex_com, "^ !" ))
  3253.       {
  3254.       eat_match( " !" )
  3255.       ex_shell_write()
  3256.       }
  3257.  
  3258.    else if( index( "write", com ) == 1 )
  3259.       ex_write()
  3260.  
  3261.    else if( index( "xit", com ) == 1 || com == "wq" )
  3262.       {
  3263.       if( in_glob )
  3264.          vi_error( "can't glob:" com ex_com )
  3265.       else if( ex_addresses )
  3266.          vi_error( "addresses are illegal for :" com ex_com )
  3267.       else
  3268.          ex_write_and_quit()
  3269.       }
  3270.  
  3271.    else if( index( "yank", com ) == 1 )
  3272.       {
  3273.       eat_space()
  3274.       buf_char = eat_match( "[a-zA-Z]" )
  3275.       count = eat_count()
  3276.       if( ex_addresses == 1 && count )
  3277.          {
  3278.          ex_addresses = 2
  3279.          ex_addr2 = ex_addr1 + count - 1
  3280.          }
  3281.       #message("buf_char = %s", buf_char);
  3282.       select_range( DEFAULT_ONE )
  3283.       if( buf_char )
  3284.          buf_id = buf_char
  3285.       vi_yank()
  3286.       }
  3287.  
  3288.    else if( com == "z" )
  3289.    {
  3290.       if( in_glob )
  3291.          vi_error( "can't glob:" com ex_com )
  3292.       else if (eat_count())
  3293.          vi_error( "count invalid for: z" )
  3294.       else
  3295.       {
  3296.       if (ex_addresses == 1)
  3297.          vi_goto_line( ex_addr1 )   
  3298.       else if (ex_addresses == 2)
  3299.          vi_goto_line( ex_addr2 )   
  3300.          
  3301.       if (ex_com == ".")
  3302.          scroll_window_middle()
  3303.       else if (ex_com == "-")
  3304.          scroll_window_bottom()
  3305.       else if (ex_com == "")
  3306.          scroll_window_top()
  3307.       else
  3308.          vi_error( "Extra characters at end of z command")
  3309.       }
  3310.    }
  3311.  
  3312.    else if( index( com, "<" ) == 1 || index( com, ">" ) == 1 )
  3313.    {
  3314.       count = eat_count()
  3315.       if (count && ex_addresses == 2)
  3316.          vi_goto_line(ex_addr2)
  3317.       else
  3318.          vi_goto_line(ex_addr1)
  3319.       
  3320.       if (!count)
  3321.          count = ex_addr2 - ex_addr1 + 1
  3322.          
  3323.       message("count = %d", count)
  3324.       times = length(com)
  3325.       while (times-- > 0)
  3326.          if ( index( com, "<") )
  3327.             outdent_lines(count) 
  3328.          else
  3329.             indent_lines(count)  
  3330.    }
  3331.    else if( com == "!" )
  3332.       {
  3333.       if( in_glob )
  3334.          vi_error( "can't glob:" com ex_com )
  3335.       else if( ex_addresses )
  3336.          vi_error( "addresses are illegal for :" com ex_com )
  3337.       else
  3338.          {
  3339.          ex_file_subst()
  3340.          if( !option_disable[ "warn" ] && buffers_modified )
  3341.             vi_error( "warning: buffers have been modified" )
  3342.          ex_autowrite()
  3343.          if( eat_bang())
  3344.             com = current_history_item( "SYSTEM" )
  3345. #            com = current_history_item( "!" )
  3346.          else
  3347. #            com = add_prompt_history( "!", eat_arg())
  3348.             com = eat_arg()
  3349.          system_key( com )
  3350.          }
  3351.       }
  3352.  
  3353.    else if( com == "=" )
  3354.       {
  3355.       n = ( ex_addresses > 1 ) ? ex_addr2 : ex_addr1
  3356.       if( in_glob )
  3357.          pbuf( n )
  3358.       else
  3359.          message( n )
  3360.       }
  3361.  
  3362.    else if( com == "&" )
  3363.       {
  3364.       if( save_command )
  3365.          {
  3366.          ex_com = save_command
  3367.          ex_sub()
  3368.          }
  3369.       else 
  3370.          vi_error( "no previous :s command to repeat" )
  3371.       }
  3372.  
  3373.    else
  3374.       vi_error( "unrecognized command" )
  3375. }
  3376.  
  3377. local function ex_show_state( option )
  3378. {
  3379.    #was message
  3380.    message( option_disable[ option ] option (option_value[ option ]? "=" option_value[ option ] : ""))
  3381. }
  3382.  
  3383.  
  3384. local function ex_set_state( option, option_switch, value )
  3385. {
  3386.    #message("ex_set_state");
  3387.    option_disable[ option ] = option_switch
  3388.    option_value[ option ] = value
  3389. }
  3390.  
  3391. function ex_show_match()
  3392. {
  3393.    local i, top_line, found_match, the_line, the_column
  3394.    local terminator = ""
  3395.  
  3396.    #message("ex_show_match");
  3397.    insert_key()
  3398.    if (chr(current_key) != "}")
  3399.       terminator = "{"
  3400.    the_line = current_line
  3401.    the_column = current_column
  3402.    left(1)
  3403.    top_line = current_line - distance_to_window_top()
  3404.    found_match = goto_matching(terminator)
  3405.    if( current_line >= top_line && found_match )
  3406.    {
  3407.       begin_selection(INCLUSIVE_SELECTION)
  3408.       end_selection()
  3409.       display_update()
  3410.       for(i=1; i<10000; i++) {}  # ugly delay
  3411.       remove_selection()
  3412.       goto_pos( the_line, the_column )
  3413.       display_update()
  3414.    }
  3415.    else 
  3416.       goto_pos( the_line, the_column )
  3417. }      
  3418.  
  3419.  
  3420. local function ex_copy( mode ) {
  3421.    local destination
  3422.  
  3423.    #message("ex_copy");
  3424.    select_range( DEFAULT_ONE )
  3425.    copy_to_scrap()
  3426.    raise_anchor()
  3427.    destination = ex_address()
  3428.    if( destination )
  3429.       destination++
  3430.    goto_line( destination )
  3431.    insert_scrap()
  3432.    if( mode )
  3433.       {
  3434.       select_range( DEFAULT_ONE )
  3435.       delete_to_scrap()
  3436.       }
  3437.  
  3438. }
  3439.  
  3440. # set the state of all the settable options
  3441. local function ex_init_state() 
  3442.    #message("ex_init_state");
  3443.  
  3444.    ex_set_state( "autoindent", "no", "" )
  3445.    ex_set_state( "autoprint", "", "" )
  3446.    ex_set_state( "autowrite", "no", "" )
  3447.    ex_set_state( "beautify", "no", "" )
  3448.    ex_set_state( "directory", "", "/tmp" )
  3449.    ex_set_state( "edcompatible", "no", "" )
  3450.    ex_set_state( "errorbells", "", "" )
  3451.    ex_set_state( "flash", "no", "" )
  3452.    ex_set_state( "hardtabs", "", "8" )
  3453.    ex_set_state( "ignorecase", "no", "" )
  3454.    ex_set_state( "lisp", "no", "" )
  3455.    ex_set_state( "list", "no", "" )
  3456.    ex_set_state( "magic", "", "" )
  3457.    ex_set_state( "mesg", "", "" )
  3458.    ex_set_state( "modelines", "no", "" )
  3459.    ex_set_state( "novice", "no", "" )
  3460.    ex_set_state( "number", "no", "" )
  3461.    ex_set_state( "optimize", "no", "" )
  3462.    ex_set_state( "paragraphs", "", "IPLPPPQPP LIbp" )
  3463.    ex_set_state( "prompt", "", "" )
  3464.    ex_set_state( "readonly", "no", "" )
  3465.    ex_set_state( "redraw", "", "" )
  3466.    ex_set_state( "remap", "", "" )
  3467.    ex_set_state( "report", "", "5" )
  3468.    ex_set_state( "scroll", "", "11" )
  3469.    ex_set_state( "sections", "", "NHSHH HU" )
  3470.    ex_set_state( "shell", "", "/bin/sh" )
  3471.    ex_set_state( "shiftwidth", "", "8h" )
  3472.    ex_set_state( "showmatch", "no", "" )
  3473.    ex_set_state( "showmode", "no", "" )
  3474.    ex_set_state( "slowopen", "no", "" )
  3475.    ex_set_state( "tabstop", "", "8" )
  3476.    ex_set_state( "taglength", "", "0" )
  3477.    ex_set_state( "tags", "", "tags" )
  3478. #   vi_tags_init( "tags" )
  3479.    vi_tags_init( tags_path )
  3480.    ex_set_state( "term", "", "vt100" )   
  3481.    ex_set_state( "terse", "no", "" )
  3482.    ex_set_state( "timeout", "", "" )
  3483.    ex_set_state( "ttytype", "", "vt100" )
  3484.    ex_set_state( "warn", "", "" )
  3485.    ex_set_state( "window", "", "8" )
  3486.    ex_set_state( "wrapscan", "", "" )
  3487.    ex_set_state( "wrapmargin", "", "0" )
  3488.    ex_set_state( "writeany", "no", "" )
  3489. }
  3490.  
  3491. local function ex_set() {
  3492.  
  3493.    local option, option_switch, query
  3494.    option_switch = eat_no()
  3495.    option = eat_option()
  3496.    query = eat_question_mark()
  3497.    #message("ex_set: option = %s; op_switch = %s; query = %s", option, option_switch, query);
  3498.  
  3499.    if( option == "all" || option == "")
  3500.       {
  3501.       if (auto_indent_mode)
  3502.          ex_set_state( "autoindent", "", "" )
  3503.       else
  3504.          ex_set_state( "autoindent", "no", "" )
  3505.       for ( option in option_disable )
  3506.          pbuf( option_disable[ option ] option (option_value[ option ]? "=" option_value[ option ] : "" ))
  3507.       }
  3508.  
  3509.    else if( option == "ai" || option == "autoindent" )
  3510.       {
  3511.       if( query )  
  3512.          ex_show_state( "autoindent" )
  3513.       else
  3514.          {
  3515.          default_auto_indent_mode = auto_indent_mode =  !option_switch 
  3516.          ex_set_state( "autoindent", option_switch, "" )
  3517.          }
  3518.       }
  3519.  
  3520.    else if( option == "ap" || option == "autoprint" )
  3521.       {
  3522.       if( query )  
  3523.          ex_show_state( "autoprint" )
  3524.       else
  3525.          ex_set_state( "autoprint", option_switch, "" )
  3526.       }
  3527.  
  3528.    else if( option == "aw" || option == "autowrite" )
  3529.       {
  3530.       if( query )  
  3531.          ex_show_state( "autowrite" )
  3532.       else
  3533.          ex_set_state( "autowrite", option_switch, "" )  
  3534.       }
  3535.  
  3536.    else if( option == "bf" || option == "beautify" )
  3537.       {
  3538.       if( query )  
  3539.          ex_show_state( "beautify" )
  3540.       else
  3541.          ex_set_state( "beautify", option_switch, "" )   
  3542.       }
  3543.  
  3544.    else if( option == "dir" || option == "directory" )
  3545.       {
  3546.       if( option_switch )
  3547.          vi_error( option " cannot be disabled" )
  3548.       else
  3549.          if( query )  
  3550.             ex_show_state( "directory" )
  3551.          else
  3552.             if( eat_equal())
  3553.                ex_set_state( "directory", "", ex_com )   
  3554.             else
  3555.                vi_error( "syntax" ) 
  3556.       }
  3557.  
  3558.    else if( option == "ed" || option == "edcompatible" )
  3559.       {
  3560.       if( query )  
  3561.          ex_show_state( "edcompatible" )
  3562.       else
  3563.          ex_set_state( "edcompatible", option_switch, "" ) 
  3564.       }
  3565.  
  3566.    else if( option == "eb" || option == "errorbells" )
  3567.       {
  3568.       if( query )  
  3569.          ex_show_state( "errorbells" )
  3570.       else
  3571.          ex_set_state( "errorbells", option_switch, "" )
  3572.       }
  3573.  
  3574.    else if( option == "fl" || option == "flash" )
  3575.       {
  3576.       if( query )  
  3577.          ex_show_state( "flash" )
  3578.       else
  3579.          ex_set_state( "flash", option_switch, "" )
  3580.       }
  3581.  
  3582.    else if( option == "ht" || option == "hardtabs" )
  3583.       {
  3584.       if( option_switch )
  3585.          vi_error( option " cannot be disabled" )
  3586.       else
  3587.          if( query )  
  3588.             ex_show_state( "hardtabs" )
  3589.          else
  3590.             if( eat_equal())
  3591.                ex_set_state( "hardtabs", "", ex_com )
  3592.       }
  3593.  
  3594.    else if( option == "ic" || option == "ignorecase" )
  3595.       {
  3596.       if( query )  
  3597.          ex_show_state( "ignorecase" )
  3598.       else
  3599.          {
  3600.          ex_set_state( "ignorecase", option_switch, "" )
  3601.          if( option_switch )
  3602.             search_flags = vi_search_flags = or( search_flags, SEARCH_CASE )
  3603.             #search_flags = vi_search_flags = or( vi_search_flags, SEARCH_CASE )
  3604.          else
  3605.             search_flags = vi_search_flags = and( search_flags, not(SEARCH_CASE) )
  3606.             #search_flags = vi_search_flags = and( vi_search_flags, not(SEARCH_CASE) )
  3607.          }
  3608.       }
  3609.  
  3610.    else if( option == "lisp" )
  3611.       {
  3612.       if( query )  
  3613.          ex_show_state( "lisp" )
  3614.       else
  3615.          ex_set_state( "lisp", option_switch, "" )
  3616.       }
  3617.  
  3618.    else if( option == "list" )
  3619.       {
  3620.       if( query )  
  3621.          ex_show_state( "list" )
  3622.       else
  3623.          ex_set_state( "list", option_switch, "" )
  3624.       }
  3625.  
  3626.    else if( option == "magic" )
  3627.       {
  3628.       if( query )  
  3629.          ex_show_state( "magic" )
  3630.       else
  3631.          ex_set_state( "magic", option_switch, "" )
  3632.       }
  3633.  
  3634.    else if( option == "mesg" )
  3635.       {
  3636.       if( query )  
  3637.          ex_show_state( "mesg" )
  3638.       else
  3639.          ex_set_state( "mesg", option_switch, "" )
  3640.       }
  3641.  
  3642.    else if( option == "modelines" )
  3643.       {
  3644.       if( query )  
  3645.          ex_show_state("modelines" )
  3646.       else
  3647.          ex_set_state( "modelines", option_switch, "" )
  3648.       }
  3649.  
  3650.    else if( option == "nu" || option == "number" )
  3651.       {
  3652.       if( query )  
  3653.          ex_show_state( "number" )
  3654.       else
  3655.          {
  3656.          if( option_switch )
  3657.             linenumber_format = ""
  3658.          else
  3659.             linenumber_format = "%5d"
  3660.          ex_set_state( "number", option_switch, "" )     
  3661.          }
  3662.       }
  3663.  
  3664.    else if( option == "opt" || option == "optimize" )
  3665.       {
  3666.       if( query )  
  3667.          ex_show_state( "optimize" )
  3668.       else
  3669.          ex_set_state( "optimize", option_switch, "" )
  3670.       }
  3671.  
  3672.    else if( option == "para" || option == "paragraphs" )
  3673.       {
  3674.       if( option_switch )
  3675.          vi_error( option " cannot be disabled." )
  3676.       else
  3677.          if( query )  
  3678.             ex_show_state( "paragraphs" )
  3679.          else
  3680.             if( eat_equal())
  3681.                ex_set_state( "paragraphs", "", ex_com )
  3682.             else
  3683.                vi_error( "syntax" ) 
  3684.       }
  3685.  
  3686.    else if( option == "prompt" )
  3687.       {
  3688.       if( query )  
  3689.          ex_show_state( "prompt" )
  3690.       else
  3691.          ex_set_state( "prompt", option_switch, "" )
  3692.       }
  3693.  
  3694.    else if( option == "ro" || option == "readonly" )
  3695.       {
  3696.       if( query )  
  3697.          ex_show_state( "readonly" )
  3698.       else
  3699.          ex_set_state( "readonly", option_switch, "" )
  3700.       }
  3701.  
  3702.    else if( option == "redraw" )
  3703.       {
  3704.       }
  3705.  
  3706.    else if( option == "remap" )
  3707.       {
  3708.       if( query )  
  3709.          ex_show_state( "remap" )
  3710.       else
  3711.          ex_set_state( "remap", option_switch, "" )
  3712.       }
  3713.  
  3714.    else if( option == "report" )
  3715.       {
  3716.       }
  3717.  
  3718.    else if( option == "scroll" )
  3719.       {
  3720.       }
  3721.  
  3722.    else if( option == "sh" || option == "shell" )
  3723.       {
  3724.       if( option_switch )
  3725.          vi_error( option " cannot be disabled." )
  3726.       else
  3727.          if( query )  
  3728.             ex_show_state( "shell" )
  3729.          else
  3730.             if( eat_equal() )
  3731.                ex_set_state( "shell", "", ex_com )
  3732.             else
  3733.                vi_error( "syntax" ) 
  3734.       }
  3735.  
  3736.    else if( option == "sw" || option == "shiftwidth" )
  3737.       {
  3738.       }
  3739.  
  3740.    else if( option == "sm" || option == "showmatch" )
  3741.       {
  3742.       if( query )  
  3743.          ex_show_state( "showmatch" )
  3744.       else
  3745.          ex_set_state( "showmatch", option_switch, "" )
  3746.  
  3747.       push_keymap( vi_insert_keymap )
  3748.       if( option_switch )
  3749.          {
  3750.          assign_key( ")", "insert_key" )
  3751.          assign_key( "]", "insert_key" )
  3752.          assign_key( "}", "insert_key" )
  3753.          }
  3754.       else
  3755.          {
  3756.          assign_key( ")", "ex_show_match" )
  3757.          assign_key( "]", "ex_show_match" )
  3758.          assign_key( "}", "ex_show_match" )
  3759.          }
  3760.       pop_keymap()
  3761.    }
  3762.  
  3763.    else if( option == "showmode" )
  3764.       {
  3765.       }
  3766.  
  3767.    else if( option == "slow" || option == "slowopen" )
  3768.       {
  3769.       }
  3770.  
  3771.    else if( option == "ts" || option == "tabstop" )
  3772.       {
  3773.       }
  3774.  
  3775.    else if( option == "tl" || option == "taglength" )
  3776.       {
  3777.       }
  3778.  
  3779.    else if( option == "tags" )
  3780.       {
  3781.       if( query )  
  3782.          ex_show_state( "tags" )
  3783.       else
  3784.          if( eat_equal())
  3785.             {
  3786.             ex_set_state( "tags", "", ex_com )
  3787.             vi_tags_init( ex_com )
  3788.             }
  3789.       }
  3790.  
  3791.    else if( option == "term" )
  3792.       {
  3793.       }
  3794.  
  3795.    else if( option == "terse" )
  3796.       {
  3797.       }
  3798.  
  3799.    else if( option == "timeout" )
  3800.       {
  3801.       }
  3802.  
  3803.    else if( option == "ttytype" )
  3804.       {
  3805.       }
  3806.  
  3807.    else if( option == "warn" )
  3808.       {
  3809.       if( query )  
  3810.          ex_show_state( "warn" )
  3811.       else
  3812.          ex_set_state( "warn", option_switch, "" )
  3813.       }
  3814.  
  3815.    else if( option == "window" )
  3816.       {
  3817.       }
  3818.  
  3819.    else if( option == "ws" || option == "wrapscan" )
  3820.       {
  3821.       if( query )  
  3822.          ex_show_state( "wrapscan" )
  3823.       else
  3824.          {
  3825.          ex_set_state( "wrapscan", option_switch, "" )
  3826.          if( option_switch )
  3827.             search_flags = vi_search_flags = and( search_flags, not( SEARCH_WRAPS ))
  3828.             #search_flags = vi_search_flags = and( vi_search_flags, not( SEARCH_WRAPS ))
  3829.          else
  3830.             search_flags = vi_search_flags = or( search_flags, SEARCH_WRAPS )
  3831.             #search_flags = vi_search_flags = or( vi_search_flags, SEARCH_WRAPS )
  3832.          }
  3833.       }
  3834.  
  3835.    else if( option == "wm" || option == "wrapmargin" )
  3836.       {
  3837.       }
  3838.  
  3839.    else if( option == "wa" || option == "writeany" )
  3840.       {
  3841.       }
  3842.  
  3843.    else 
  3844.       vi_error( "unknown option" )
  3845. }
  3846.  
  3847. # finds and substitutes file names for % and #
  3848. local function ex_file_subst()
  3849. {
  3850.    local remainder, subs = 0
  3851.    local start_buf = current_buffer
  3852.  
  3853.    #message("ex_file_subst");
  3854.    remainder = index(original_com, ex_com )
  3855.    subs = gsub( /%/, buffer_filename, ex_com )
  3856.    if (index(ex_com, /#/))
  3857.    {
  3858.       if (!last_buffer)
  3859.       {
  3860.          last_buffer = current_buffer
  3861.          prev_buffer("", 0, 1)
  3862.       }
  3863.       else
  3864.       {
  3865.          current_buffer = last_buffer
  3866.          last_buffer = start_buf
  3867.       }
  3868.       subs = subs + gsub( /#/, buffer_filename, ex_com )
  3869.    }
  3870.    
  3871.    if( subs ) 
  3872.       message( ":" substr(original_com, 1, remainder - 1 ) ex_com )
  3873.    
  3874.    current_buffer = start_buf
  3875. }
  3876.  
  3877. function vi_ZZ()
  3878. {
  3879.    local ch = "["
  3880.    
  3881.    if (!playing_again)
  3882.       ch = vi_getc()
  3883.  
  3884.    if (ch != "Z")
  3885.       vi_error()
  3886.    else
  3887.    {
  3888.       ex_addresses = 0
  3889.       ex_com = ""
  3890.       ex_write_and_quit()
  3891.    }
  3892. }
  3893.  
  3894. function ex_write_and_quit() {
  3895.    ex_write()
  3896.    ex_quit()
  3897. }
  3898.  
  3899. local function ex_quit( force ){
  3900.    force = force || eat_bang()
  3901.  
  3902.    if( !force && buffers_modified )
  3903.       warning( "File has been modified;\n\"q!\" quits and discards changes" )
  3904.    else
  3905.       done( force )
  3906. }
  3907.  
  3908. function vi_next_buffer(next_flag)
  3909. {
  3910.    if (argcount() < 1)
  3911.       next_flag = 1
  3912.       
  3913.    if (next_flag)
  3914.       next_buffer_mask()
  3915.    else
  3916.       prev_buffer_mask()
  3917.       
  3918.    ex_show_file()
  3919. }
  3920.  
  3921.  
  3922. function ex_subs_repeat() {
  3923.    #message("ex_subs_repeate");
  3924.    if( save_command )
  3925.       {
  3926.       ex_com = save_command
  3927.       ex_sub()
  3928.       }
  3929.    else 
  3930.       vi_error( "no previous :s command to repeat" )
  3931. }
  3932.  
  3933. local function append_buffer( filename ) 
  3934. {
  3935.    local ret_val = -1   # failure
  3936.    local fileID = fopen( filename, READWRITE )
  3937.  
  3938.    if ( fileID ) 
  3939.       {
  3940.       fseek( fileID, 0, END_FILE )
  3941.       fwrite( fileID, create_mark( BOB, 1, 1 ), buffer_size )
  3942.       fclose( fileID )
  3943.       ret_val = TRUE
  3944.       } 
  3945.    else 
  3946.       vi_error( "cannot open file " filename )
  3947.  
  3948.    return ret_val
  3949. }
  3950.  
  3951. local function append_marked_block( filename ) {
  3952.    local line_1, line_2, length_line_1, length_line_2, length
  3953.    local mark_1, mark_2
  3954.    local fileID = fopen( filename, READWRITE )
  3955.  
  3956.    #message("append marked block");
  3957.    if( fileID )
  3958.       {
  3959.       fseek( fileID, 0, END_FILE )
  3960.       line_1 = current_line
  3961.       length_line_1= current_line_length
  3962.       swap_selection_marks()
  3963.       line_2 = current_line
  3964.       length_line_2= current_line_length
  3965.       swap_selection_marks()
  3966.       mark_1 = create_mark( B1, line_1, 1 )
  3967.       mark_2 = create_mark( B2, line_2, 1 )
  3968.       length = distance_between_marks( mark_1, mark_2 )
  3969.       if( line_1 > line_2 )
  3970.          {
  3971.          length_line_2 = length_line_1
  3972.          mark_1 = mark_2
  3973.          }
  3974.       length = length + length_line_2
  3975.       fwrite( fileID, mark_1, length )       
  3976.       fclose( fileID )
  3977.       }
  3978.    else 
  3979.       vi_error( "cannot open file " filename )
  3980. }
  3981.  
  3982. local function ex_shell_read() 
  3983. {
  3984.    local input_redirection
  3985.  
  3986.    ex_file_subst()
  3987.    if ( ex_addresses > 1 )
  3988.       vi_error( "only 0 or 1 addresses allowed for :r!" )
  3989.  
  3990.    if ( ex_addresses )
  3991.       current_line = ex_addr1 + 1
  3992.    else
  3993.       current_line ++
  3994.  
  3995.    goto_bol()
  3996.    if ( ex_com ~ "\\<" )
  3997.       input_redirection = ""
  3998.    else
  3999.       input_redirection = "<temp_inp"
  4000.  
  4001.    system( ex_com input_redirection " >&temp_out" )
  4002.    read_file( "temp_out" )
  4003. }
  4004.  
  4005. local function ex_shell_write() 
  4006. {
  4007.    local output_redirection
  4008.  
  4009.    ex_file_subst()
  4010.    select_range( DEFAULT_ONE )
  4011.    write_marked_block( "temp_inp" )
  4012.  
  4013.    if ( ex_com ~ "\\>" ) 
  4014.       {
  4015.       output_redirection = ""
  4016.       sub( "\\>", " >\\&", ex_com )
  4017.       } 
  4018.    else 
  4019.       output_redirection = " >&temp_out"
  4020.  
  4021.    system( ex_com " <temp_inp" output_redirection )
  4022.  
  4023.    if ( output_redirection && filesize( "temp_out" ) > 0 )
  4024.       file_to_browser( "temp_out" )
  4025. }
  4026.  
  4027. function vi_saveas( fn )
  4028. {
  4029.    if ( !fn )
  4030.       fn = prompt( ":w ", "" )
  4031.  
  4032.    if (fn)
  4033.       ex_write(fn);
  4034. }
  4035.  
  4036. function vi_save()
  4037. {
  4038.    if ( is_scratch_file(current_buffer) )
  4039.       vi_saveas()
  4040.    else
  4041.       ex_write()
  4042. }
  4043.  
  4044. local function ex_write( name )
  4045. {
  4046.  
  4047.    local force = eat_bang()
  4048.    local append, name_supplied, file_exists, name_changed
  4049.    local success = -1
  4050.  
  4051.    #message("ex_write");
  4052.    ex_file_subst()
  4053.    append = eat_append()
  4054.  
  4055.    # check for filename passed in as parameter
  4056.    #
  4057.    if ( !name ) 
  4058.       {
  4059.       name = eat_arg()
  4060.       name_supplied = name
  4061.       }
  4062.  
  4063.    if ( !name ) 
  4064.       name = buffer_filename
  4065.  
  4066.    file_exists    = filesize( name ) >= 0
  4067.    name_changed   = (buffer_filename != buffer_original_filename)
  4068.  
  4069.    if ( file_exists && ! force && ( name_supplied || name_changed ) && ! append)
  4070.       vi_error( "attempt to overwrite existing file; use \"w!\" to overwrite" )  
  4071.  
  4072.    else if (   force                                        || 
  4073.                !file_exists                                 || 
  4074.                (!name_changed && buffer_is_modified())      || 
  4075.                (name_changed && !file_exists)               || 
  4076.                append ) 
  4077.       {
  4078.  
  4079.       if ( read_only() )
  4080.          vi_error( "file " name " is read-only" )
  4081.       else if ( !option_disable[ "readonly" ] && ! force )
  4082.          vi_error( "readonly option enabled; use \"w!\" to write" )
  4083.  
  4084.          else if ( selection_type() )
  4085.             {
  4086.             if ( append )
  4087.                append_marked_block( name )
  4088.             else
  4089.                write_marked_block( name )
  4090.             } 
  4091.          else if ( ex_addresses )
  4092.             {
  4093.             select_range()
  4094.             if ( append )
  4095.                append_marked_block( name )
  4096.             else
  4097.                write_marked_block( name )
  4098.  
  4099.             raise_anchor()
  4100.             } 
  4101.          else 
  4102.             {
  4103.             if ( append )
  4104.                success = append_buffer( name )
  4105.             else
  4106.             {
  4107.                if (buffer_filename == name)
  4108.                   backup_file( buffer_filename )
  4109.                success = write_buffer( name ) 
  4110.             }
  4111.  
  4112.             # reset BUFFER_MODIFIED only on a full write
  4113.             if ( success != -1 )
  4114.                buffer_flags = and( buffer_flags, not( BUFFER_MODIFIED ))
  4115.             }
  4116.  
  4117.       ex_show_file()
  4118.       }
  4119. }
  4120.  
  4121. local function ex_autowrite() 
  4122. {
  4123.    if ( !option_disable[ "autowrite" ] && buffer_is_modified()) 
  4124.       {
  4125.  
  4126.       if ( read_only() )
  4127.          vi_error( "file " buffer_filename " is read_only; unable to autowrite" )
  4128.       else if ( option_disable[ "readonly" ] )
  4129.          vi_error( "readonly option enabled; unable to autowrite" )
  4130.       else 
  4131.          {
  4132.          write_buffer()
  4133.          buffer_flags = and( buffer_flags, not( BUFFER_MODIFIED ))
  4134.          }
  4135.       }
  4136. }
  4137.  
  4138. # this catches <Ctl>^ keystrokes
  4139. function last_buffer_key() 
  4140.    ex_autowrite()
  4141.    prev_buffer()
  4142. }
  4143.  
  4144. # tests if current file is read-only
  4145. local function read_only() 
  4146. {  
  4147.    local is_ro = FALSE
  4148.    local mode  = filemode( buffer_filename )
  4149.  
  4150.    # check for non-error ret mode
  4151.    if ( mode >= 0 )
  4152.       is_ro = and( mode, _READ_ONLY)
  4153.  
  4154.    return is_ro
  4155. }
  4156.  
  4157. function vi_read( fn )
  4158. {
  4159.    if ( !fn )
  4160.       fn = prompt_history( "EDITFILE", ":r ", "" )
  4161.  
  4162.    if (fn)
  4163.       ex_read(fn);
  4164. }
  4165.  
  4166. local function ex_read( name )
  4167. {
  4168.    ex_file_subst()
  4169.  
  4170.    if ( !name )
  4171.       name = eat_arg()
  4172.  
  4173.    if ( !name ) 
  4174.       name = vi_read_name
  4175.  
  4176.    if ( !name ) 
  4177.       vi_error( "must supply file name to read" )
  4178.  
  4179.    vi_read_name = name
  4180.  
  4181.    if ( filesize( name ) < 0 )
  4182.       vi_error( name ": file does not exist" )
  4183.  
  4184.    if ( ex_addresses > 1 )
  4185.       vi_error( "only 0 or 1 addresses allowed for :r" )
  4186.  
  4187.    if ( ex_addresses )
  4188.       current_line = ex_addr1 + 1   # may be 0
  4189.    else
  4190.       current_line++
  4191.  
  4192.    goto_bol()
  4193.  
  4194.    read_file( name )
  4195.    ex_show_file()
  4196. }
  4197.  
  4198. function vi_open( fn )
  4199. {
  4200.    if ( !fn )
  4201.       fn = prompt_history( "EDITFILE", ":e ", "" )
  4202.  
  4203.    if (fn)
  4204.       ex_edit(fn);
  4205. }
  4206.  
  4207. local function ex_edit( fn )
  4208. {
  4209.    local force = eat_bang()
  4210.    local poundLine
  4211.    local name
  4212.  
  4213.    ex_file_subst()
  4214.  
  4215.    # read space or semicolon delimited argument
  4216.    eat_space()
  4217.  
  4218.    if ( fn )
  4219.       name = fn
  4220.    else
  4221.       name = eat_match( "[^; ]+" )
  4222.  
  4223.    if ( substr(name,1,1) == "+" )
  4224.       {
  4225.       # read line number to start at in specified file
  4226.       poundLine = substr( name, 2 ) + 0
  4227.       name = eat_arg()
  4228.       }
  4229.  
  4230.    if( !name && !force )
  4231.       vi_error( "file name required; use \"e!\" to restart current file" )
  4232.    else if ( name == buffer_name )
  4233.       vi_error( "file name same as current file; use \"e!\" to restart current file" )
  4234.    else 
  4235.       {
  4236.       if ( !name )
  4237.          {
  4238.          name = buffer_filename
  4239.  
  4240.          if ( !name )
  4241.             return
  4242.          }
  4243.  
  4244.       if ( force ) #&& MAGIC )
  4245.          {
  4246.          buffer_flags = and( buffer_flags, not( BUFFER_MODIFIED ) )
  4247.          delete_buffer()
  4248.          }
  4249.  
  4250. #      edit_file( name )
  4251.       create_buf_and_win(name)
  4252.  
  4253.       ex_show_file()
  4254.  
  4255.       if( poundLine )
  4256.          goto_line( poundLine )
  4257.       }
  4258. }
  4259.  
  4260. local function vi_filter()
  4261. {
  4262.    local com = prompt_history( "!", "!", "", 1 )
  4263.  
  4264.    if ( com )
  4265.       filter( com )
  4266. }
  4267.  
  4268. local function vi_tags_init( tags )
  4269. {
  4270.    sub( " ", ";", tags )
  4271.    tags_path = tags
  4272. }
  4273.  
  4274. local function vi_tags( n ){
  4275.    vi_tags_push()
  4276.    tags( n )   
  4277. }
  4278.  
  4279. function vi_tags_auto(){
  4280.    vi_tags_push()
  4281.    tags_auto()
  4282. }
  4283.  
  4284. local vi_tags_index = 0
  4285. local vi_tags_stack
  4286.  
  4287. function vi_tags_push(){
  4288.    vi_tags_stack[ vi_tags_index++ ] = buffer_filename " "   \
  4289.             buffer_offset        " "   \
  4290.             distance_to_window_top()  
  4291. }
  4292.  
  4293. function vi_tags_pop(){
  4294.    local loc
  4295.  
  4296.    if( vi_tags_index <= 0 )
  4297.       warning( "vi tags stack empty" )
  4298.  
  4299.    split( vi_tags_stack[ --vi_tags_index ], loc, " " )
  4300.    delete vi_tags_stack[ vi_tags_index ]
  4301.    edit_file( loc[ 1 ])
  4302.    goto_buffer_offset( loc[ 2 ])
  4303.  
  4304.    # restore window orientation, if possible
  4305.  
  4306.    scroll_window_top( loc[ 3 ])
  4307. }
  4308.  
  4309. function ex_show_file( annotation )
  4310. {
  4311.    local pct = 100
  4312.  
  4313.    # # faster, but less accurate:
  4314.    # if( buffer_size > 0 )
  4315.    #  pct = 100 * buffer_offset / buffer_size
  4316.    if( buffer_last_line > 0 )
  4317.       pct = 100 * current_line / buffer_last_line
  4318.  
  4319.    if( !annotation && and( buffer_flags, BUFFER_MODIFIED ))
  4320.       annotation = "[modified]"
  4321.  
  4322.    message( "\"%s\" %sline %d of %d -- %d%% --",   \
  4323.       buffer_filename,           \
  4324.       ( annotation ? annotation " " : "" ),     \
  4325.       current_line,              \
  4326.       buffer_last_line,          \
  4327.       pct )
  4328. }
  4329.  
  4330. local function ex_sub()
  4331. {
  4332.    local quote, old, new, gc, start, stop, count, n = 0, sflags
  4333.    local pflag, cflag, gflag
  4334.    local line_count
  4335.  
  4336.    #message("ex_sub");
  4337.    quote = eat_punctuation()
  4338.    if( !quote ) {
  4339.       if( prev_old && prev_new ) {
  4340.          old = prev_old
  4341.          new = prev_new
  4342.       } else 
  4343.          vi_error( "no previous substitute pattern to reuse" )
  4344.    } else {
  4345.       old = search_hist( eat_regex( quote ))
  4346.       new = eat_regex( quote )
  4347.       prev_old = old
  4348.       prev_new = new
  4349.    }
  4350.  
  4351.    gc = eat_match( "[gc]+" ) # ":s/foo" is the same as ":s/foo//"
  4352.  
  4353.    sflags = SEARCH_FORWARD          \
  4354.       + SEARCH_MAXIMAL_MATCH        \
  4355.       + SEARCH_BLOCK             \
  4356.       + SEARCH_REGEX          \
  4357.       + and( search_flags,       \
  4358.          or( SEARCH_CASE,    \
  4359.              SEARCH_WRAPS ))
  4360.       #+ and( vi_search_flags,       \
  4361.       #   or( SEARCH_CASE,    \
  4362.       #       SEARCH_WRAPS ))
  4363.  
  4364.    line_count = eat_count()
  4365.    if( ex_addresses == 1 && line_count )
  4366.       {
  4367.       ex_addresses = 2
  4368.       ex_addr2 = ex_addr1 + line_count 
  4369.       }
  4370.    else 
  4371.       ex_addr2 ++
  4372.  
  4373.    pflag = eat_match( "p" )
  4374.    cflag = gc ~ /c/
  4375.    gflag = gc ~ /g/
  4376.  
  4377.    if( match( "", old ) && RLENGTH == 0 )
  4378.       vi_error( "NULL search pattern length." );
  4379.  
  4380.    if( ! gflag && ! cflag ){
  4381.       search_count = 0x7FFFFFFF;
  4382.       if( ! gflag ){
  4383.          sflags = sflags + SEARCH_ONCE_PER_LINE
  4384.       }
  4385.       select_range( DEFAULT_ONE )
  4386.       n = replace( old, new, sflags )
  4387.       raise_anchor()
  4388.    } else {
  4389.       stop = ex_range( DEFAULT_ONE )
  4390.       start = current_line
  4391.       count = ( gflag ) ? 0x7FFFFFFF : 1
  4392.  
  4393.       while( start <= stop )
  4394.          {
  4395.          goto_pos( start++, 1 )
  4396.          drop_anchor( LINE_SELECTION )
  4397.          search_count = count
  4398.          if( cflag? ex_sub_confirm( old ) : 1 ) {
  4399.             n += replace( old, new, sflags )
  4400.             if( pflag ){
  4401.                copy_to_scrap()
  4402.                scrap_to_browser()
  4403.             }
  4404.          }
  4405.          raise_anchor()
  4406.       }
  4407.    }
  4408.  
  4409.    if( !in_glob )
  4410.       message( "%d replacements made", n )
  4411. }
  4412.  
  4413. local function ex_sub_confirm( old ) {
  4414.    local results
  4415.  
  4416.    #message("ex_sub_confirm");
  4417.    if( search( old, 23 + and( vi_search_flags,  SEARCH_CASE ))) {
  4418.       copy_to_scrap()
  4419.       scrap_to_confirmer()
  4420.       results = confirmer( current_column, 1, length( old ), 1 )
  4421.       return results
  4422.    }
  4423. }
  4424.  
  4425. local function ex_glob( gv ){
  4426.    local com, quote, regex, n = 0
  4427.    local sflags = or( or( SEARCH_FWD_REGEX_MAX, SEARCH_BLOCK ),   \
  4428.             or( SEARCH_ADVANCE,        \
  4429.                and( search_flags, SEARCH_CASE )))
  4430.                #and( vi_search_flags, SEARCH_CASE )))
  4431.  
  4432.    #message("ex_glob");
  4433.    if( in_glob )
  4434.       vi_error( "recursive :" gv " is not allowed" )
  4435.    in_glob = 1
  4436.    gv = ( gv == "v" )
  4437.  
  4438.    quote = eat_char()
  4439.    regex = search_hist( eat_regex( quote ))
  4440.  
  4441.    if( match( "", regex ) && RLENGTH == 0 )
  4442.       vi_error( "NULL search pattern length." );
  4443.  
  4444.    com = ex_com      # save remainder of glob command
  4445.    search_count = 1
  4446.  
  4447.    select_range()
  4448.    save_position()
  4449.  
  4450.    while( search( regex, sflags ) )
  4451.       {
  4452.  
  4453.       restore_position()
  4454.       save_position()
  4455.  
  4456.       ex_addr1 = ex_addr2 = current_line
  4457.       ex_addresses = 1
  4458.       ex_mode1( com )
  4459.  
  4460.       if(( n++ % 10 ) == 3 )
  4461.          display_update()
  4462.  
  4463.       if( com !~ /d/ )
  4464.          goto_eol()
  4465.       }
  4466.  
  4467.    restore_position( 1 )
  4468.  
  4469.    if( selection_type())
  4470.       raise_anchor()
  4471.    in_glob = 0
  4472.  
  4473.    message( "%d lines processed", n )
  4474. }
  4475.  
  4476. local function ex_range( defaultOne ){
  4477.    local start, stop
  4478.  
  4479.    #message("ex_range");
  4480.    if( selection_type())
  4481.       {
  4482.       stop = current_line
  4483.       swap_selection_marks()
  4484.       start = current_line
  4485.       raise_anchor()
  4486.       if( stop < start )
  4487.          {
  4488.          start = stop
  4489.          stop = current_line
  4490.          }
  4491.       }
  4492.    else if( ex_addresses )
  4493.       {
  4494.       start = ex_addr1
  4495.       stop = ex_addr2 - 1
  4496.       }
  4497.  
  4498.    else if( defaultOne )
  4499.       {
  4500.       start = stop = current_line
  4501.       }
  4502.  
  4503.    else
  4504.       {
  4505.       start = 1
  4506.       stop = buffer_last_line - 1
  4507.       }
  4508.  
  4509.    current_line = start
  4510.    return stop
  4511. }
  4512.  
  4513.  
  4514. local function browser_buffer(){
  4515.    #message("browser_buffer");
  4516.    if( !browser_bid )
  4517.       browser_bid = create_buffer( "", "", BUFFER_SYSTEM )
  4518.  
  4519.    return browser_bid
  4520. }
  4521.  
  4522. local function file_to_browser( filename ) {
  4523.    local cur_buf = current_buffer
  4524.  
  4525.    #message("file_to_browser");
  4526.    delete_event( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
  4527.    current_buffer = browser_buffer()
  4528.    read_file( filename )
  4529.    current_buffer = cur_buf
  4530.    attach_event_handler( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
  4531. }
  4532.  
  4533. local function scrap_to_browser(){
  4534.    local cur_buf = current_buffer
  4535.  
  4536.    #message("scrap_to_browser");
  4537.    delete_event( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
  4538.    current_buffer = browser_buffer()
  4539.    goto_buffer_bottom()
  4540.    insert_scrap()
  4541.  
  4542.    current_buffer = cur_buf
  4543.    attach_event_handler( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
  4544. }
  4545.  
  4546. local function scrap_to_confirmer() {
  4547.    local cur_buf = current_buffer
  4548.  
  4549.    #message("scrap_to_confirmer");
  4550.    delete_event( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
  4551.    current_buffer = confirmer_buffer()
  4552.    goto_buffer_bottom()
  4553.    insert_scrap()
  4554.  
  4555.    current_buffer = cur_buf
  4556.    attach_event_handler( EVENT.NEW_CURNT_BUFFER, "vi_new_current_win")
  4557. }
  4558.  
  4559. local function confirmer_buffer() {
  4560.    #message("confirmer_buffer")
  4561.    if( ! confirmer_bid )
  4562.       confirmer_bid = create_buffer( "", "", 1 )
  4563.  
  4564.    return confirmer_bid
  4565. }
  4566.  
  4567.  
  4568.  
  4569. ########################################################
  4570. #
  4571. #  confirmer(col, line, width, height )
  4572. #        displays lines placed by line_to confirmer in a window
  4573. #        returns 1 or 0 depending upon whether process was
  4574. #        terminated by a "y" key or not
  4575.  
  4576. local function confirmer( col, line, width, height )
  4577. {
  4578.    local cur_win = current_window
  4579.    local cur_buf = current_buffer
  4580.    local cur_map = current_keymap
  4581.    local i
  4582.    local results
  4583.    local old_wflags = window_flags
  4584.  
  4585.    #message("confirmer");
  4586.    # Must execute the next lines before changing keymaps since changing
  4587.    # buffers or windows sets the vi_command_keymap
  4588.    current_window = create_window()
  4589.    current_buffer = confirmer_buffer()
  4590.    window_flags = or(window_flags, WINDOW_SHOW_SYSTEM)
  4591.    attach_window_buffer( current_window, current_buffer )
  4592.    window_flags = old_wflags
  4593.    
  4594.    window_name = "Confirmer" 
  4595.    if( confirmer_keymap == -1 ){
  4596.       current_keymap = confirmer_keymap = create_keymap( empty_keymap )
  4597.  
  4598.       for( i = 0; i < 256; i ++ )
  4599.          assign_key( "#" i, "process_end" )
  4600.       assign_key( "y",  "process_end 1" )
  4601.       assign_key( "Y",  "process_end 1" )
  4602.    }
  4603.  
  4604.    current_keymap = confirmer_keymap 
  4605.  
  4606.    message( "Confirm with \"y\" key; reject with any other ascii key" )
  4607. #   highlight_window( window_text_x0 + col -2, window_text_y0 + line -2, width, height, 0x20 )
  4608.  
  4609.    results = process_begin()
  4610.  
  4611.    delete_buffer( current_buffer )
  4612.    delete_window( current_window )
  4613.    current_buffer = cur_buf
  4614.    current_window = cur_win
  4615.    current_keymap = cur_map 
  4616.  
  4617.    confirmer_bid = 0
  4618.  
  4619.    return results
  4620. }
  4621.  
  4622.  
  4623. local function browse_printed()
  4624. {
  4625.    local cur_buf = current_buffer
  4626.    local cur_map = current_keymap
  4627.    local win_name = window_name
  4628.    local old_wflags = window_flags
  4629.    
  4630.  
  4631.    # Changing the buffer must come before the keymap assignment because
  4632.    # changing buffers or windows causes the vi_command_keymap to be
  4633.    # assigned to current_keymap.
  4634.    current_buffer = browser_buffer()
  4635.    
  4636.    window_flags = or(window_flags, WINDOW_SHOW_SYSTEM)
  4637.    attach_window_buffer( current_window, current_buffer )
  4638.    window_flags = old_wflags
  4639.    goto_buffer_top()
  4640.  
  4641.    #message("browse_printed");
  4642.    if( browser_keymap == -1 )
  4643.    {
  4644.       current_keymap = browser_keymap = create_keymap( empty_keymap )
  4645.  
  4646.       assign_key( "<Esc>",    "vi_process_end" )
  4647.  
  4648.       # motion keys which call vi_error are not safe to use within
  4649.       #  browse_printed.  The following simulate vi
  4650.  
  4651.       assign_key( "<Ctrl-PageUp>",  "goto_buffer_top" )
  4652.       assign_key( "<Ctrl-PageDown>",   "goto_buffer_bottom" )
  4653.       assign_key( "<PageUp>",    "page_up" )
  4654.       assign_key( "<PageDown>",     "page_down" )
  4655.  
  4656.       assign_key( "<Ctrl-B>",    "page_up" )
  4657.       assign_key( "<Ctrl-F>",    "page_down" )
  4658.       assign_key( "<Ctrl-D>",    "scroll_down_half" )
  4659.       assign_key( "<Ctrl-U>",    "scroll_up_half" )
  4660.       assign_key( "<Ctrl-E>",    "scroll_down_1" )
  4661.       assign_key( "<Ctrl-Y>",    "scroll_up_1" )
  4662.  
  4663.       assign_key( "h",     "prev_char" )
  4664.       assign_key( "j",     "down" )
  4665.       assign_key( "k",     "up" )
  4666.       assign_key( "l",     "next_char" )
  4667.       assign_key( "<Left>",      "prev_char" )
  4668.       assign_key( "<Up>",        "up" )
  4669.       assign_key( "<Down>",      "down" )
  4670.       assign_key( "<Right>",     "next_char" )
  4671.  
  4672.       assign_key( "<Home>",      "goto_bol" )
  4673.       assign_key( "<End>",    "goto_eol" )
  4674.       assign_key( "G",     "goto_buffer_bottom" )
  4675.       assign_key( "g",     "goto_buffer_top" )
  4676.    }
  4677.    else
  4678.       current_keymap = browser_keymap 
  4679.  
  4680.    window_name  = "Command Output Listing"
  4681.    message( "Use <Esc> to exit, PageUp/PageDown keys to browse" )
  4682.    process_begin()
  4683.  
  4684.    message( "" )
  4685.    delete_buffer( current_buffer )
  4686.    window_name = win_name
  4687.    current_buffer = cur_buf
  4688.    current_keymap = cur_map 
  4689.  
  4690.    browser_bid = 0
  4691. }
  4692.  
  4693. local function pbuf( s ){
  4694.    local cur_buf = current_buffer
  4695.  
  4696.    current_buffer = browser_buffer()
  4697.    insert_string( s "\n" )
  4698.  
  4699.    current_buffer = cur_buf
  4700. }
  4701.  
  4702.  
  4703. # select a region based on starting and ending addresses
  4704.  
  4705. local function select_range( defaultOne ){
  4706.    #message("select_range");
  4707.    if( ex_addresses )
  4708.    {
  4709.       current_line = ex_addr1
  4710.       current_column = 1
  4711.       drop_anchor( LINE_SELECTION )
  4712.       if( ex_addresses > 1 )
  4713.          current_line = ex_addr2
  4714.    }
  4715.    else
  4716.    {
  4717.       if( defaultOne )
  4718.       {
  4719.          drop_anchor( LINE_SELECTION )
  4720.       }
  4721.       else
  4722.          current_line = current_column = 1
  4723.    }
  4724. }
  4725.  
  4726. # parse a range address expression; result is 0, 1 or 2 numbers
  4727. #
  4728. #  term:
  4729. #     .
  4730. #     $
  4731. #     'a
  4732. #     /.../
  4733. #     ?...?
  4734. #  addr: 
  4735. #     term
  4736. #     term + num
  4737. #     term - num
  4738. #     + num       # . + num
  4739. #     - num       # . - num
  4740. #     term +         # term + 1
  4741. #     term -         # term - 1
  4742. #     num
  4743. #  range:
  4744. #     /* empty */    # 1,$
  4745. #     addr
  4746. #     addr , addr
  4747. #     addr ; addr
  4748. #     ,        # 1,$
  4749. #     ;        # .,$
  4750. #
  4751. local function ex_address()
  4752. {
  4753.    local len1, len = length( ex_com )
  4754.    local num, patt, mark, op, ex_addr2 # made ex_addr2 local /Gj/
  4755.  
  4756.    #message("ex_address");
  4757.    # get stand-alone term, if any:
  4758.  
  4759.    if( eat_match( "/" )){
  4760.       vi_search( eat_regex( "/" ), SEARCH_FORWARD )
  4761.       ex_addr2 = current_line
  4762.  
  4763.    } else if( eat_match( "\\?" )){
  4764.       vi_search( eat_regex( "?" ), SEARCH_BACKWARD )
  4765.       ex_addr2 = current_line
  4766.  
  4767.    } else if( eat_match( "\\." )){
  4768.       ex_addr2 = current_line
  4769.  
  4770.    } else if( eat_match( "\\$" )){
  4771.       #ex_addr2 = buffer_last_line - 1
  4772.       ex_addr2 = buffer_last_line
  4773.  
  4774.    } else if(( patt = eat_match( "'." ))){
  4775.       mark = vi_mark_id( substr( patt, 2 ))
  4776.       if( !mark_defined( mark ))
  4777.          bad_addr()
  4778.       ex_addr2 = mark_line( mark )
  4779.  
  4780.       } else 
  4781.       ex_addr2 = current_line    # missing -- set default
  4782.  
  4783.    len1 = len - length( ex_com )
  4784.  
  4785.    # get operator, if any:
  4786.  
  4787.    op = eat_match( "[+\\-]" )
  4788.  
  4789.    line_search = !!op      # signal to vi_search_key()
  4790.  
  4791.    # get number, if any:
  4792.  
  4793.    if(( num = eat_match( "[0-9]+" ))){
  4794.       if( len1 && op == "" )
  4795.          bad_addr()
  4796.       num = 0 + num
  4797.       } else 
  4798.       num = 1
  4799.  
  4800.    # compute final value:
  4801.  
  4802.    if( op == "+" )
  4803.       ex_addr2 = ex_addr2 + num
  4804.    else if( op == "-" )
  4805.       ex_addr2 = ex_addr2 - num
  4806.    else if( !len1 )  # if no op and no stand-alone term
  4807.       ex_addr2 = num
  4808.  
  4809.    # update address count, if any:
  4810.  
  4811.    if( len != length( ex_com ))
  4812.       ex_addresses ++
  4813.  
  4814.    return ex_addr2
  4815. }
  4816.  
  4817. local function ex_address_range()
  4818. {
  4819.    local ch
  4820.  
  4821.    ex_addresses = 0
  4822.    mark_context()
  4823.    ex_addr1 = ex_address()
  4824.  
  4825.    #message("ex_address_range");
  4826.    if( ex_addresses )
  4827.       {
  4828.       if( ex_com ~ /^;/ )
  4829.          {
  4830.          vi_goto_line( ex_addr1 )   
  4831.          goto_bol()
  4832.          }
  4833.       if( eat_match( "[,;]" ))
  4834.          {
  4835.          ex_addr2 = ex_address()
  4836.          if( ex_addresses != 2 )
  4837.             bad_addr()
  4838.          }
  4839.       }
  4840.  
  4841.    else if( eat_match( "[,%]" ))
  4842.       {
  4843.       ex_addr1 = 1
  4844.       ex_addr2 = buffer_last_line - 1
  4845.       ex_addresses = 2
  4846.    }
  4847.  
  4848.    else if( eat_match( ";" ))
  4849.       {
  4850.       ex_addr1 = current_line
  4851.       ex_addr2 = buffer_last_line - 1
  4852.       ex_addresses = 2
  4853.       }
  4854.  
  4855.    else
  4856.       {
  4857.       ex_addr1 = ex_addr2 = current_line
  4858.       }
  4859.  
  4860.    if( ex_addresses > 1 && ex_addr1 > ex_addr2 )
  4861.       bad_addr()
  4862.  
  4863.    goto_mark( vi_base_mark )
  4864. }
  4865.  
  4866. local function bad_addr(){
  4867.    #message("bad_addr")
  4868.    goto_mark( vi_base_mark )     # restore initial position
  4869.    vi_error( "Ill-formed address" )
  4870. }
  4871.  
  4872. ## address and ex mode parser helpers
  4873. #
  4874. #  eat_... => if match then return the matched string and advance ex_com; 
  4875. #     else return null without advancing ex_com.
  4876.  
  4877. local function eat_match( patt )
  4878. {
  4879.    local r = ""
  4880.  
  4881.    if( match( ex_com, "^" patt )){
  4882.       r = substr( ex_com, 1, RLENGTH )
  4883.       ex_com = substr( ex_com, RLENGTH + 1 )
  4884.    }
  4885.    return r
  4886. }
  4887.  
  4888. local function eat_count() {
  4889.    local num
  4890.  
  4891.       eat_space()
  4892.    num = eat_match( "[0-9]+" )
  4893.    #message("eat_count = %d", num);
  4894.  
  4895.    if( num )
  4896.       return num + 0
  4897.       else
  4898.       return 0
  4899. }
  4900.  
  4901. local function eat_append() {
  4902.    local app
  4903.       eat_space()
  4904.    app = eat_match( "\\>\\>" )
  4905.    #message("eat_append = %s", app);
  4906.    return app;
  4907. }
  4908.  
  4909.  
  4910. local function eat_word() {
  4911.    local word;
  4912.  
  4913.    eat_space()
  4914.    word = eat_match( "[^" " \t]+" )
  4915.    return word
  4916. }
  4917.  
  4918. local function eat_command()
  4919. {
  4920.    local com =  eat_match( "[a-zA-Z]+|!|#|=|&|[<]+|[>]+" )
  4921.    return com;
  4922. }
  4923.  
  4924. local function eat_no() {
  4925.    local no;
  4926.    eat_space()
  4927.    no = eat_match( "no" )
  4928.    return no;
  4929. }
  4930.  
  4931. local function eat_option() {
  4932.    local op;
  4933.    eat_space()
  4934.    op = eat_match( "[a-z]+" )
  4935.    return op;
  4936. }
  4937.  
  4938. local function eat_space(){
  4939.    local space = eat_match( "[ \t]*" )
  4940.    return space;
  4941. }
  4942.  
  4943. local function eat_bang(){
  4944.    local bang = eat_match( "!" )
  4945.    return bang;
  4946. }
  4947.  
  4948. local function eat_equal() {
  4949.    local equal = eat_match( "=" )
  4950.    return equal
  4951. }
  4952.  
  4953. local function eat_question_mark() {
  4954.    local q = eat_match( "\\?" )
  4955.    return q;
  4956. }
  4957.  
  4958. local function eat_arg(){
  4959.    local arg;
  4960.    eat_space()
  4961.    arg = eat_match( "[^;]+" )
  4962.    return arg;
  4963. }
  4964.  
  4965. local function eat_char(){
  4966.    local ch = substr( ex_com, 1, 1 )
  4967.    ex_com = substr( ex_com, 2 )
  4968.    #message("eat_char = %s", ch);
  4969.    return ch
  4970. }
  4971.  
  4972. local function eat_punctuation()
  4973. {
  4974.    local p = eat_match( "[!@#%&-_={}\\:;\"\\'<>,/~\\`]" ) 
  4975.    return p;
  4976. }
  4977.  
  4978. local function eat_letter()
  4979. {
  4980.    local l = eat_match( "[a-z]" )
  4981.    return l;
  4982. }
  4983.  
  4984. # eat a regular expression, with <sep> as the separator.
  4985. #
  4986. #  syntax is   sep regex sep
  4987. #         or   sep regex EOL
  4988. #
  4989. #  sep is      "/"  or  "?"
  4990. #
  4991. #  Leading separator should already have been removed.
  4992. #  We don't use eat_match to avoid the slower anchored search.
  4993. #  This is a good place to implement nomagic and other regex
  4994. #     transformations.
  4995. #
  4996. local function eat_regex( sep )
  4997. {
  4998.    local patt, regex, len
  4999.  
  5000.    # closing seperator may be prefixed by an even number of backslashes;
  5001.    # an odd number of backslashes quote the seperator, requiring it to
  5002.    # be included in the regex.
  5003.  
  5004.    patt = "[^\\\\](\\\\\\\\)*"   
  5005.    if( sep ~ /[|\]^$*+(){}.?\\]/ )  # regex seperators must be quoted
  5006.       sep =  "\\" sep
  5007.  
  5008.    # split the input into a regex LHS and a remainder RHS, discarding
  5009.    # the closing seperator; two matches because first may not be longest
  5010.  
  5011.    if( match( ex_com, "^" sep ) || match( ex_com, patt sep ))
  5012.    {
  5013.       len = RSTART + RLENGTH - 1 # index of closing seperator
  5014.       regex = substr( ex_com, 1, len - 1 )
  5015.       ex_com = substr( ex_com, len + 1 )
  5016.    }
  5017.    else
  5018.    {
  5019.       regex = ex_com
  5020.       ex_com = ""
  5021.    }
  5022.  
  5023.    # Now remove quotes from quoted seperators.  By definition, they must 
  5024.    # have an odd number of preceeding backslashes, one of which must be
  5025.    # removed.  However, regex chars need to retain the quoting.
  5026.  
  5027.    if( length( sep ) == 1 )
  5028.       gsub( "\\" sep, sep, regex )
  5029.  
  5030.    return regex
  5031. }
  5032.  
  5033. #########################################################################
  5034. #
  5035. #       expand_macro replaces a set of keystrokes with
  5036. #        a string specified with the :map ex command.
  5037. #        Looks at setting of remap option to allow or
  5038. #        inhibit nested macros
  5039.  
  5040. function expand_macro( macro_name )
  5041. {
  5042.    local macro_body, the_key, success
  5043.    if( macro_name in recursion_count )
  5044.    {
  5045.       if( in_ex_mode )
  5046.          vi_error( "recursive map not allowed" )
  5047.       else
  5048.       {
  5049.          vi_warning( "recursive map not allowed" )
  5050.          flush_keyboard()
  5051.          process_end()
  5052.       }
  5053.    }
  5054.    else
  5055.    {
  5056.       recursion_count[ macro_name ] = ""
  5057.       if( option_disable[ "remap" ] )
  5058.       {
  5059.          if( in_ex_mode )
  5060.             push_keymap( default_vi_command_keymap )
  5061.          else
  5062.             push_keymap( default_vi_insert_keymap )
  5063.       }
  5064.  
  5065.       if( in_ex_mode)
  5066.       {
  5067.          macro_body = command_macro_table[ macro_name ]
  5068.          the_key = keymap_binding( macro_body )
  5069.          gsub( ".", "&\0", macro_body )
  5070.          the_key = keymap_binding( macro_body )
  5071.          success =playback( macro_body )
  5072.       }
  5073.       else
  5074.       {
  5075.          macro_body = insert_macro_table[ macro_name ]
  5076.          gsub( ".", "&\0", macro_body )
  5077.          playback( macro_body )
  5078.       }
  5079.  
  5080.       delete recursion_count[ macro_name ]
  5081.       if( option_disable[ "remap" ] )
  5082.          pop_keymap()
  5083.    }
  5084. }
  5085.  
  5086. ##########################################################################
  5087. #
  5088. #` covered_binding      finds any keybinding that will be covered
  5089. #           by the specified binding. Returns the
  5090. #           key string that will be covered.
  5091. #
  5092.  
  5093. local function covered_binding( macro_name, variant)
  5094. {
  5095.    local i, found = 1, name = "", prev_name, keymap, prev_prev_name
  5096.  
  5097.    # check keymap first for anything that might get covered
  5098.  
  5099.    if( variant )
  5100.       keymap = vi_insert_keymap
  5101.    else
  5102.       keymap = vi_command_keymap
  5103.  
  5104.    for( i = length( macro_name); found; i-- )
  5105.    {
  5106.       prev_name = name
  5107.       name = substr( macro_name, 1, i )
  5108.       found = keymap_binding( name, keymap )
  5109.    }
  5110.  
  5111.    if( prev_name ) # see if the one you are about to cover covers anything
  5112.    {
  5113.       if( variant )
  5114.       {
  5115.          prev_prev_name = covered_insert_macro[ prev_name ]
  5116.          return prev_prev_name? prev_prev_name: prev_name
  5117.       }
  5118.       else
  5119.       {
  5120.          prev_prev_name = covered_command_macro[ prev_name ]
  5121.          return prev_prev_name? prev_prev_name: prev_name
  5122.       }
  5123.    }
  5124.    else # if no luck in the keymap, check existing macros
  5125.    {
  5126.       if (variant)
  5127.       {
  5128.          for( prev_name in insert_macro_table) 
  5129.             if( index( macro_name, prev_name ) == 1 )
  5130.                return prev_name
  5131.       }
  5132.       else
  5133.       {
  5134.          for( prev_name in command_macro_table )
  5135.             if( index( macro_name, prev_name ) == 1 )
  5136.                return prev_name
  5137.       }
  5138.    }
  5139. }
  5140.  
  5141. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  5142.  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  5143. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  5144.  
  5145. ### regex searches
  5146. #
  5147. #  vi_search   searches forwards or backwards for 1 pattern
  5148. #        vi_error()s if the pattern is not found.
  5149. #  vi_search_key  prompts for input, and otherwise handles the 
  5150. #        "/" and "?" commands
  5151. #  vi_search_again   handles the "n", "N", and Alt-F5 and Alt-F6 commands.
  5152. #
  5153. #  search_hist()  recalls or remembers previous search pattern
  5154. #
  5155.  
  5156. function vi_search_key( key )
  5157. {
  5158.    local patt
  5159.  
  5160.    patt = vi_prompt( "SEARCH", key, "", 1 )
  5161.    if (patt == key)
  5162.    {
  5163.       delete_history_item("SEARCH", patt)
  5164.       vi_search_again(key)
  5165.    }
  5166.    else
  5167.    {
  5168.    disallow_number()
  5169.    op_type_2b( DONT_MARK )
  5170.  
  5171.    vi_search_on_pattern( key, patt )
  5172.    }
  5173. }
  5174.  
  5175. function vi_search_on_pattern( key, patt )
  5176. {
  5177.    local addr
  5178.  
  5179.    if( patt )
  5180.    {
  5181.       record_op( key_to_int("<Enter>") )
  5182.  
  5183.       vi_search_dir = ( key == "/" ? SEARCH_FORWARD : SEARCH_BACKWARD )
  5184.       mark_context( MAJOR_MARK ) # mark context for motion
  5185.  
  5186.       ex_com = key patt
  5187.       addr = ex_address()  # parse command, call vi_search() as needed
  5188.  
  5189.       # execute op, if any, in line or normal mode
  5190.  
  5191.       if( line_search )
  5192.       {
  5193.          vi_goto_line( addr )
  5194.          xeq_op( LINE_SELECTION )
  5195.       }
  5196.       else 
  5197.          xeq_op()
  5198.    }
  5199.    else 
  5200.       op_flush()
  5201. }
  5202.  
  5203. local function search_hist( patt )
  5204. {
  5205.    if( patt )
  5206.       add_prompt_history( SEARCH_HIST, patt )
  5207.    else
  5208.    {
  5209.       patt = current_history_item( SEARCH_HIST )
  5210.       if( !patt )
  5211.          vi_error( "Missing previous pattern" )
  5212.    }
  5213.  
  5214.    return patt
  5215. }
  5216.  
  5217.  
  5218. local function vi_search( pattern, dir )
  5219. {
  5220.    local search_op
  5221.    local orig_offset = buffer_offset
  5222.    local offset_diff
  5223.  
  5224.    # set vi_search_flags to not have a direction.  We must do it this
  5225.    # way since SEARCH_FORWARD = 4 and SEARCH_BACKWARD = 0
  5226.    vi_search_flags = or(SEARCH_ADVANCE, and(search_flags, not(SEARCH_FORWARD)))
  5227.    pattern = search_hist( pattern )
  5228.    if( !search( pattern, or( vi_search_flags, dir )))
  5229.       {
  5230.       message("Pattern not found: " pattern )
  5231.       vi_error() # never returns
  5232.       }     
  5233.    else
  5234.    {
  5235.       if( eat_match( "z-" ))
  5236.          scroll_window_bottom()
  5237.       else if( eat_match( "z." ))
  5238.          scroll_window_middle()
  5239.       else if( eat_match( "z" ))
  5240.          scroll_window_top()
  5241.       else if( eat_match( ";" ))
  5242.          if(( search_op = eat_match( "[/?]" )))
  5243.             vi_search_on_pattern( search_op, ex_com )
  5244.       else
  5245.          vi_error( "syntax" ) 
  5246.  
  5247.       offset_diff = (dir == SEARCH_FORWARD) ? buffer_offset - orig_offset : 
  5248.                                                 orig_offset - buffer_offset
  5249.       if (offset_diff < 0)
  5250.          notify("Search wrapped around")
  5251.       else
  5252.          message("")
  5253.    }
  5254. }
  5255.  
  5256. # search again is called by 4 different keys, with 4 different meanings:
  5257. #
  5258. #  n  search again, same direction as originally
  5259. #  N  search again, opposite direction as originally
  5260. #  Alt-F5   search again, backwards
  5261. #  Alt-F6   search again, forwards
  5262. #
  5263. function vi_search_again( key )
  5264. {
  5265.    local dir 
  5266.    local last_dir = vi_search_dir
  5267.  
  5268.    disallow_number()
  5269.    op_type_2b( MAJOR_MARK )
  5270.  
  5271.    #message("search again key is: %s", key)
  5272.    # figure out what direction we're going...
  5273.    if (key == "?")
  5274.       dir = SEARCH_BACKWARD
  5275.    else if (key == "/")
  5276.       dir = SEARCH_FORWARD
  5277.    else if (key == "N")  
  5278.       dir = (last_dir == SEARCH_FORWARD) ? SEARCH_BACKWARD : SEARCH_FORWARD
  5279.    else
  5280.       dir = last_dir
  5281.    
  5282.    
  5283.    #dir = (argcount() ? (( key == "?" ) ? SEARCH_BACKWARD : SEARCH_FORWARD ) \
  5284.    #                  : (( chr( int_to_ascii( current_key )) == "N" )        \
  5285.    #                    ? (( vi_search_dir == SEARCH_FORWARD )               \
  5286.    #                      ? SEARCH_BACKWARD : SEARCH_FORWARD )               \
  5287.    #                    : vi_search_dir ))
  5288.  
  5289.    message( "%s%s", ( dir == SEARCH_FORWARD ? "/" : "?" ), search_hist())
  5290.  
  5291.    # execute search operator:
  5292.  
  5293.    vi_search( "", dir )
  5294.    xeq_op()
  5295. }
  5296.  
  5297. function vi_new_current_win()
  5298. {
  5299.    # Do NOT call vi_reset() if bogus NEW_CURNT_WINDOW event (i.e. mouse click)
  5300.    # or if we are already in ex_mode
  5301.    if (!in_ex_mode && (current_window != insert_window || current_buffer != insert_buffer))
  5302.    {
  5303.       vi_process_end()
  5304.    }
  5305. }
  5306.  
  5307. function vi_reset(force_keymap)
  5308. {
  5309. #   message("In vi_reset, event_id is: %d", event_id)
  5310.       
  5311.    if (event_id == EVENT.ERROR)
  5312.       force_keymap = 1
  5313.       
  5314.    number_register=0
  5315.    pending_operator = ""
  5316.    raise_anchor()
  5317.    remove_selection()
  5318.    vi_command_mode(force_keymap)
  5319.    process_end()
  5320. }  
  5321.  
  5322. function vi_delete_buffer()
  5323. {
  5324.    # Delete the current buffer and make the NEXT buffer current
  5325.    # (0 instead of 1 makes the previous buffer current).
  5326.    delete_buffer_key("",1) 
  5327.    ex_show_file()
  5328. }
  5329.  
  5330.  
  5331. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  5332.  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  5333. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  5334.  
  5335. local function vi_keymap_valid(km)
  5336. {
  5337.    local result = 1
  5338.  
  5339.    if (km <= 0 || km > 16)
  5340.       result = 0
  5341.       
  5342. #  message("Keymap id is: %d", km)
  5343. #   if ( (km <= 0) || !push_keymap(km))
  5344. #      result = 0
  5345. #   else
  5346. #      pop_keymap()
  5347.   
  5348.    return result
  5349. }
  5350.  
  5351. ### create the VI keymaps if they have not already been
  5352. #
  5353. local function create_vi_command_keymaps()
  5354. {
  5355.    # Enable the keymap.  Subsequent mods to the keymap will persist 
  5356.    #  through the end of the session. 
  5357.    # Only one-time initialization should follow this point. 
  5358.  
  5359.    if( vi_keymap_valid(vi_command_keymap) )
  5360.    {
  5361.       current_keymap = vi_command_keymap 
  5362.    }
  5363.    else if (vi_keymap_valid(default_vi_command_keymap) )
  5364.    {
  5365.       vi_command_keymap = default_vi_command_keymap
  5366.       current_keymap = vi_command_keymap 
  5367.    }
  5368.    else
  5369.    {
  5370.       current_keymap = vi_command_keymap = create_keymap( empty_keymap )
  5371.  
  5372.       assign_vi_command_keys()
  5373.       
  5374.       optional_function("local_keys")
  5375.       default_vi_command_keymap = create_keymap( vi_command_keymap )
  5376.    } 
  5377. }
  5378.  
  5379. local function assign_vi_command_keys()
  5380. {
  5381.    assign_key( "0",     "vi_digit" )  
  5382.    assign_key( "1",     "vi_digit" )
  5383.    assign_key( "2",     "vi_digit" )
  5384.    assign_key( "3",     "vi_digit" )
  5385.    assign_key( "4",     "vi_digit" )
  5386.    assign_key( "5",     "vi_digit" )
  5387.    assign_key( "6",     "vi_digit" )
  5388.    assign_key( "7",     "vi_digit" )
  5389.    assign_key( "8",     "vi_digit" )
  5390.    assign_key( "9",     "vi_digit" )
  5391.  
  5392.    assign_key( "<Alt-1>",     "place_bookmark 1" )
  5393.    assign_key( "<Alt-2>",     "place_bookmark 2" )
  5394.    assign_key( "<Alt-3>",     "place_bookmark 3" )
  5395.    assign_key( "<Alt-4>",     "place_bookmark 4" )
  5396.    assign_key( "<Alt-5>",     "place_bookmark 5" )
  5397.    assign_key( "<Alt-6>",     "place_bookmark 6" )
  5398.    assign_key( "<Alt-7>",     "place_bookmark 7" )
  5399.    assign_key( "<Alt-8>",     "place_bookmark 8" )
  5400.    assign_key( "<Alt-9>",     "place_bookmark 9" )
  5401.    assign_key( "<Alt-0>",     "place_bookmark 10" )
  5402.  
  5403.    assign_key( "a",     "vi_a" )
  5404.    assign_key( "b",     "vi_b" )
  5405.    assign_key( "c",     "vi_operator" )
  5406.    assign_key( "d",     "vi_operator" )
  5407.    assign_key( "e",     "vi_e" )
  5408.    assign_key( "f",     "vi_find" )
  5409.    assign_key( "g",     "vi_goto 1" )
  5410. #   assign_key( "h",     "vi_left" )
  5411.    assign_key( "h",     "vi_arrow 3" )
  5412.    assign_key( "i",     "vi_i" )
  5413. #   assign_key( "j",     "vi_down" )
  5414.    assign_key( "j",     "vi_arrow 2" )
  5415. #   assign_key( "k",     "vi_up" )
  5416. #   assign_key( "l",     "vi_right" )
  5417.    assign_key( "k",     "vi_arrow 1" )
  5418.    assign_key( "l",     "vi_arrow 4" )
  5419.    assign_key( "m",     "vi_place_mark" )
  5420.    assign_key( "n",     "vi_search_again n" )
  5421.    assign_key( "o",     "vi_o" )
  5422.    assign_key( "p",     "vi_p" )
  5423.    assign_key( "r",     "vi_r" )
  5424.    assign_key( "s",     "vi_s" )
  5425.    assign_key( "t",     "vi_find" )
  5426.    assign_key( "u",     "vi_undo" )
  5427.    assign_key( "w",     "vi_w" )
  5428.    assign_key( "x",     "vi_x" )
  5429.    assign_key( "y",     "vi_operator" )
  5430.    assign_key( "z",     "vi_scroll_window" )
  5431.  
  5432.    assign_key( "A",     "vi_A" )
  5433.    assign_key( "B",     "vi_B" )
  5434.    assign_key( "C",     "vi_C" )
  5435.    assign_key( "D",     "vi_D" )
  5436.    assign_key( "E",     "vi_E" )
  5437.    assign_key( "F",     "vi_find" )
  5438.    assign_key( "G",     "vi_goto" )
  5439.    assign_key( "H",     "vi_H" )
  5440.    assign_key( "I",     "vi_I" )
  5441.    assign_key( "J",     "vi_J" )
  5442.    assign_key( "L",     "vi_L" )
  5443.    assign_key( "M",     "vi_M" )
  5444.    assign_key( "N",     "vi_search_again N" )
  5445.    assign_key( "O",     "vi_O" )
  5446.    assign_key( "P",     "vi_P" )
  5447.    assign_key( "R",     "vi_R" )
  5448.    assign_key( "S",     "vi_S" )
  5449.    assign_key( "T",     "vi_find" )
  5450.    assign_key( "U",     "vi_undo_line" )
  5451.    assign_key( "W",     "vi_W" )
  5452.    assign_key( "X",     "vi_X" )
  5453.    assign_key( "Y",     "vi_Y" )
  5454.    assign_key( "Z",     "vi_ZZ" )
  5455.  
  5456.    assign_key( "<Alt-A>",     "set_exclusive_mark" )
  5457.    assign_key( "<Alt-B>",     "buffer_list" )
  5458.    assign_key( "<Alt-C>",     "copy_to_scrap_key" )
  5459.    assign_key( "<Alt-D>",     "native_delete" )
  5460.    assign_key( "<Alt-E>",     "create_buf_and_win_key" )
  5461.    assign_key( "<Alt-F>",     "ex_show_file" )
  5462.    assign_key( "<Alt-G>",     "goto_line_or_mark" )
  5463.    assign_key( "<Alt-H>",     "display_help_item" )
  5464.    assign_key( "<Alt-I>",  function_id("toggle_buffer_flags", BUFFER_OVERTYPE_MODE))
  5465.    assign_key( "<Alt-J>",     "vi_J" )
  5466.    assign_key( "<Alt-K>",     "delete_to_eol" )
  5467.    assign_key( "<Alt-L>",     "set_line_mark" )
  5468.    assign_key( "<Alt-M>",     "mark_matching" )
  5469.    assign_key( "<Alt-N>",     "vi_next_buffer 1" )
  5470.    assign_key( "<Alt-->",     "vi_next_buffer 0" )
  5471.    assign_key( "<Alt-O>",     "change_output_name" )
  5472.    assign_key( "<Alt-P>",     "wrap_paragraph" )
  5473.    assign_key( "<Alt-Q>",     "done" )
  5474.    assign_key( "<Alt-R>",     "vi_read" )
  5475.    assign_key( "<Alt-S>",     "search_forward" )
  5476.    assign_key( "<Alt-T>",     "replace_forward" )
  5477.    assign_key( "<Alt-U>",     "undo" )
  5478.    assign_key( "<Alt-V>",     "print_version" )
  5479.    assign_key( "<Alt-W>",     "vi_save" )
  5480.    assign_key( "<Alt-X>",     "done" )
  5481.    assign_key( "<Alt-Y>",     "redo" )
  5482.    assign_key( "<Alt-Z>",     "system" )
  5483.  
  5484.    assign_key( "<Ctrl-A>",    "optional_function display_ascii_table" )
  5485.    assign_key( "<Ctrl-B>",    "page_up" )    # not a motion
  5486.    assign_key( "<Ctrl-C>",    "vi_process_end" )
  5487.    assign_key( "<Ctrl-D>",    "scroll_down_half" ) # not a motion
  5488.    assign_key( "<Ctrl-E>",    "scroll_down_1" ) # not a motion
  5489.    assign_key( "<Ctrl-F>",    "page_down" )     # not a motion
  5490.    assign_key( "<Ctrl-G>",    "ex_show_file" )
  5491.    assign_key( "<Ctrl-H>",    "vi_bksp" ) # Ctrl-H or Backspace
  5492.    assign_key( "<Ctrl-I>",    "vi_tab" )  # Ctrl-I or Tab
  5493.    assign_key( "<Ctrl-L>",    "scroll_left_1" )
  5494.    assign_key( "<Ctrl-N>",    "vi_arrow 2" ) # down
  5495.    assign_key( "<Ctrl-P>",    "vi_arrow 1" ) # up
  5496.    assign_key( "<Ctrl-R>",    "scroll_right_1" ) # inexact but appropriate substitution
  5497.    assign_key( "<Ctrl-T>",    "vi_tags_pop" )
  5498.    assign_key( "<Ctrl-U>",    "scroll_up_half" )   # not a motion
  5499.    assign_key( "<Ctrl-Y>",    "scroll_up_1" )      # not a motion
  5500.    assign_key( "<Ctrl-Z>",    "system" )
  5501.    assign_key( "<Ctrl-->",    "vi_delete_buffer" )
  5502.  
  5503.    assign_key( "<Esc>",             "vi_reset 1" )
  5504.    assign_key( "<PageUp>"       ,   "page_up" )    # not a motion
  5505.    assign_key( "<PageDown>"     ,   "page_down" )     # not a motion
  5506.    assign_key( "<Home>"         ,   "vi_goto_bol" )
  5507.    assign_key( "<End>"          ,   "vi_goto_eol" )
  5508.    assign_key( "<Up>"           ,   "vi_arrow 1" )
  5509.    assign_key( "<Down>"         ,   "vi_arrow 2" )
  5510.    assign_key( "<Left>"         ,   "vi_arrow 3" )
  5511.    assign_key( "<Right>"        ,   "vi_arrow 4" )
  5512.    assign_key( "<BackTab>"      ,   "vi_back_tab" )  #in PM use BackTab, else use shift-tab
  5513.    assign_key( "<Tab>"          ,   "vi_tab" )  # Ctrl-I or Tab
  5514.    assign_key( "<Enter>"        ,   "vi_next_line" )
  5515.    assign_key( "<Space>"        ,   "vi_space" )
  5516.    assign_key( "<Shift-Space>"  ,   "vi_space" )
  5517.    assign_key( "<Backspace>"    ,   "vi_bksp" ) # Ctrl-H or Backspace
  5518.    assign_key( "<Insert>"       ,   "vi_P" )
  5519.    #assign_key( "<Delete>"       ,   "vi_yank 1" )
  5520.    assign_key( "<Delete>"       ,   "delete_chars" )
  5521.  
  5522.    assign_key( "<Ctrl-PageUp>"  ,   "goto_buffer_top" )  # extension!
  5523.    assign_key( "<Ctrl-PageDown>",   "goto_buffer_bottom" )  # extension!
  5524.    assign_key( "<Ctrl-Left>"    ,   "vi_B" )
  5525.    assign_key( "<Ctrl-Right>"   ,   "vi_W" )
  5526.    assign_key( "<Ctrl-Home>"    ,   "goto_window_top" )
  5527.    assign_key( "<Ctrl-End>"     ,   "goto_window_bottom" )
  5528.  
  5529.    assign_key( "<Alt-Tab>"      ,   "vi_back_tab" )
  5530.  
  5531.    assign_key( "^",     "vi_skip_whitespace" )
  5532.    assign_key( "$",     "vi_goto_eol" )
  5533.    assign_key( "|",     "vi_goto_column" )
  5534.    assign_key( "+",     "vi_next_line" )
  5535.    assign_key( "-",     "vi_prev_line" )
  5536.    assign_key( ":",     "ex_key" )
  5537.    assign_key( "?",     "vi_search_key ?" )
  5538.    assign_key( "/",     "vi_search_key /" )
  5539.    assign_key( "%",     "vi_goto_matching" )
  5540.    assign_key( "(",     "vi_prev_sent" )
  5541.    assign_key( ")",     "vi_next_sent" )
  5542.    assign_key( "{",     "vi_prev_para" )
  5543.    assign_key( "}",     "vi_next_para" )
  5544.    assign_key( "[",     "vi_prev_sect" )
  5545.    assign_key( "]",     "vi_next_sect" )
  5546.    assign_key( ";",     "vi_find_repeat" )
  5547.    assign_key( ",",     "vi_find_reverse" )
  5548.    assign_key( "'",     "vi_goto_mark" )
  5549.    assign_key( "`",     "vi_goto_mark" )
  5550.    assign_key( ">",     "vi_operator" )
  5551.    assign_key( "!",     "vi_operator" )
  5552.    assign_key( ".",     "vi_again" )
  5553.    assign_key( "~",     "vi_tilde" )
  5554.    assign_key( "&",     "ex_subs_repeat" )
  5555.    assign_key( "\"",    "vi_bufid_key" )
  5556.    assign_key( "<",     "vi_operator" )
  5557.  
  5558. #  assign_key( "<Num-4>",   "left" )
  5559. #  assign_key( "<Num-6>",   "right" )
  5560.    assign_key( "<Num-->",  "vi_prev_line" )
  5561.    assign_key( "<Num-+>",  "vi_next_line" )
  5562.  
  5563.  
  5564.    assign_key( "<Ctrl-^>", "last_buffer_key" )
  5565.    assign_key( "<Ctrl-]>", "vi_tags_auto" )
  5566.  
  5567.    assign_key( "<F3>",     "vi_next_buffer 0" )
  5568.    assign_key( "<F4>",     "vi_next_buffer 1" )
  5569.    assign_key( "<F5>",     "vi_search_key ?" )
  5570.    assign_key( "<F6>",     "vi_search_key /" )
  5571. #   assign_key( "<F7>",     "record_key" )
  5572. #   assign_key( "<F8>",     "playback" )
  5573.    assign_key( "<F7>",     "record_macro" )
  5574.    assign_key( "<F8>",     "playback_macro" )
  5575.    assign_key( "<F9>",     "system_key" )
  5576.    assign_key( "<F10>",    "invoke_function" )
  5577.  
  5578. #  assign_key( "<Alt-F1>",    "split_window_horizontal" )
  5579. #  assign_key( "<Alt-F2>",    "split_window_vertical" )
  5580. #  assign_key( "<Alt-F3>",    "make_window" )
  5581. #  assign_key( "<Alt-F4>",    "delete_tiled_window" )
  5582.    assign_key( "<Alt-F4>",    "done" )
  5583.    assign_key( "<Alt-F5>",    "vi_search_again ?" )
  5584.    assign_key( "<Alt-F6>",    "vi_search_again /" )
  5585.    assign_key( "<Alt-F7>",    "learn_key" )
  5586. #  assign_key( "<Alt-F8>",    )
  5587. #  assign_key( "<Alt-F9>",    )
  5588.    assign_key( "<Alt-F10>",   "compile_buffer" )
  5589.  
  5590.    #  electric-c template expansion keys
  5591.    assign_key( VI_EXPAND_TEMPLATE_KEY, "beep" )
  5592.    assign_key( VI_NEXT_FIELD_KEY, "beep" ) 
  5593.    
  5594. #  assign_key( "<Shift-F1>",  "smaller_window" )
  5595. #  assign_key( "<Shift-F2>",  "larger_window" )
  5596. #  assign_key( "<Shift-F3>",  "organize_windows" )
  5597. #  assign_key( "<Shift-F4>",  "organize_buffers" )
  5598. #  assign_key( "<Shift-F5>",  )
  5599. #  assign_key( "<Shift-F6>",  )
  5600. #  assign_key( "<Shift-F7>",  "display_errors" )
  5601. #  assign_key( "<Shift-F8>",  "goto_next_error" )
  5602. #  assign_key( "<Shift-F9>",  )
  5603. #  assign_key( "<Shift-F10>", )
  5604.  
  5605.    assign_key( "<Ctrl-F1>",          "display_help_item" )
  5606. #  assign_key( "<Ctrl-F2>",   )
  5607. #  assign_key( "<Ctrl-F3>",   )
  5608. #  assign_key( "<Ctrl-F4>",   )
  5609. #  assign_key( "<Ctrl-F5>",   )
  5610. #  assign_key( "<Ctrl-F6>",   )
  5611. #  assign_key( "<Ctrl-F7>",   )
  5612. #  assign_key( "<Ctrl-F8>",   )
  5613. #  assign_key( "<Ctrl-F9>",   )
  5614. #  assign_key( "<Ctrl-F10>",  )
  5615. }
  5616.  
  5617.   
  5618. local function create_vi_insert_keymaps()
  5619. {
  5620.    # Enable the keymap.  Subsequent mods to the keymap will persist 
  5621.    #  through the end of the session. 
  5622.    # Only one-time initialization should follow this point. 
  5623.  
  5624.    if( vi_insert_keymap > 0 )
  5625.    {
  5626.       current_keymap = vi_insert_keymap 
  5627.    }
  5628.    else if (default_vi_insert_keymap > 0)
  5629.    {
  5630.       current_keymap = vi_insert_keymap = default_vi_insert_keymap
  5631.    }
  5632.    else
  5633.    {
  5634.       current_keymap = vi_insert_keymap = create_keymap( ascii_keymap )
  5635.    
  5636.       # changed the following from process_end to vi_process_end to add
  5637.       # some fail-safe processing.
  5638.       assign_key( "<Esc>",    "vi_process_end" )
  5639.       assign_key( "<Ctrl-]>", "vi_process_end" )      # Esc or Ctrl-]
  5640.       assign_key( "<Ctrl-C>", "vi_process_end" )
  5641.       assign_key( "<Ctrl-@>", "vi_reinsert" )
  5642.       assign_key( "<Ctrl-Enter>",   "vi_insert_nl" )  # Ctrl-J or Ctrl-Enter
  5643.       assign_key( "<Ctrl-J>", "vi_insert_nl" )  # Ctrl-J or Ctrl-Enter
  5644.       assign_key( "<Enter>",  "vi_insert_cr" )  # Ctrl-M or Enter
  5645.    
  5646.       assign_key( "<Backspace>", "vi_insert_bs" )  
  5647.       assign_key( "<Shift-Backspace>", "vi_insert_bs" )  
  5648.       assign_key( "<Ctrl-H>", "vi_insert_bs" )
  5649.    #  assign_key( "#",     "vi_insert_bs" )  # obsolete
  5650.       assign_key( "<Ctrl-W>", "vi_insert_dB" )
  5651.       assign_key( "<Ctrl-U>", "vi_insert_cancel" )
  5652.       assign_key( "<Delete>", "vi_insert_cancel" )
  5653.    
  5654.       assign_key( "<Up>"           ,   "vi_insert_arrow 1" )
  5655.       assign_key( "<Down>"         ,   "vi_insert_arrow 2" )
  5656.       assign_key( "<Left>"         ,   "vi_insert_arrow 3" )
  5657.       assign_key( "<Right>"        ,   "vi_insert_arrow 4" )
  5658.       assign_key( "<Num-Up>"       ,   "vi_insert_arrow 1" )
  5659.       assign_key( "<Num-Down>"     ,   "vi_insert_arrow 2" )
  5660.       assign_key( "<Num-Left>"     ,   "vi_insert_arrow 3" )
  5661.       assign_key( "<Num-Right>"    ,   "vi_insert_arrow 4" )
  5662.       assign_key( "<Home>"         ,   "vi_insert_arrow 5" )
  5663.       assign_key( "<End>"          ,   "vi_insert_arrow 6" )
  5664.       assign_key( "<PageUp>"       ,   "vi_insert_arrow 7" )
  5665.       assign_key( "<PageDown>"     ,   "vi_insert_arrow 8" )
  5666.                                             
  5667.    #  assign_key( "@",     "vi_insert_cancel" ) # obsolete
  5668.       assign_key( "<Ctrl-V>", "vi_insert_quoted" )
  5669.       assign_key( "<Ctrl-D>", "vi_insert_unindent" )
  5670.       assign_key( "<Ctrl-T>", "vi_insert_reindent" )
  5671.       assign_key( "<Space>", "insert_key 32" )
  5672.       assign_key( "<Shift-Space>", "insert_key 32" )
  5673.       
  5674.    #  electric-c template expansion keys
  5675.       assign_key( VI_EXPAND_TEMPLATE_KEY,     "expand_template_key" )
  5676.       assign_key( VI_NEXT_FIELD_KEY,   "next_field_key" )  # Ctrl-J or Ctrl-Enter
  5677.    #  assign_key( "<Space>", "vi_expand_template" )
  5678.    #  assign_key( "<Tab>",      "vi_expand_template" )
  5679.    #this was Enter
  5680.    #  assign_key( "<Ctrl-X>",   "vi_expand_template" )
  5681.    
  5682.       default_vi_insert_keymap = create_keymap( vi_insert_keymap )
  5683.    }
  5684. }
  5685.  
  5686. # This function will restore the menu and toolbar functions after VI
  5687. # has messed them up
  5688. function restore_id()
  5689. {
  5690.    local tbhand = toolbar_info(0);
  5691.  
  5692.    local undo   = "undo"
  5693.    local redo   = "redo"
  5694.    local cut    = "delete_to_scrap"
  5695.    local copy   = "copy_to_scrap_key"
  5696.    local paste  = "insert_scrap"
  5697.    
  5698.    menu_functions[IDM_UNDO]  = undo
  5699.    menu_functions[IDM_REDO]  = redo
  5700.    menu_functions[IDM_CUT]   = cut
  5701.    menu_functions[IDM_COPY]  = copy
  5702.    menu_functions[IDM_PASTE] = paste
  5703.    
  5704.    menu_functions[POP_CUT]   = cut
  5705.    menu_functions[POP_COPY]  = copy 
  5706.    menu_functions[POP_PASTE] = paste
  5707.  
  5708.    if ( tbhand )
  5709.    {
  5710.       modify_toolbaritem( tbhand,  IDM_UNDO, TI_FUNCTIONID, function_id(undo))
  5711.       modify_toolbaritem( tbhand,  IDM_REDO, TI_FUNCTIONID, function_id(redo))
  5712.       modify_toolbaritem( tbhand,  IDM_CUT, TI_FUNCTIONID, function_id(cut))
  5713.       modify_toolbaritem( tbhand,  IDM_COPY, TI_FUNCTIONID, function_id(copy))
  5714.       modify_toolbaritem( tbhand,  IDM_PASTE, TI_FUNCTIONID, function_id(paste))
  5715.    }
  5716. }
  5717.  
  5718. # this function is used to customize the menu so that the keys you have
  5719. # assigned to do the same thing as the menuitem will show on the menu
  5720. # It should be global and start with the string inside emulation_mode
  5721. function vi_menu() 
  5722. {
  5723.    local tbhand = toolbar_info(0);
  5724.    local menu = menu_info(0, MI_MENUHANDLE);
  5725.  
  5726.    local undo   = "vi_undo"
  5727.    local redo   = "vi_undo"
  5728.    local cut    = "vi_yank 1"
  5729.    local copy   = "vi_Y"
  5730.    local paste  = "vi_P"
  5731.  
  5732.    menu_functions[IDM_UNDO]  = undo
  5733.    menu_functions[IDM_REDO]  = redo
  5734.    menu_functions[IDM_CUT]   = cut
  5735.    menu_functions[IDM_COPY]  = copy 
  5736.    menu_functions[IDM_PASTE] = paste
  5737.    
  5738.    menu_functions[POP_CUT]   = cut
  5739.    menu_functions[POP_COPY]  = copy
  5740.    menu_functions[POP_PASTE] = paste
  5741.  
  5742.    modify_toolbaritem( tbhand,  IDM_UNDO, TI_FUNCTIONID,  function_id(undo))
  5743.    modify_toolbaritem( tbhand,  IDM_REDO, TI_FUNCTIONID,  function_id(redo))
  5744.    modify_toolbaritem( tbhand,  IDM_CUT, TI_FUNCTIONID,   function_id(cut))
  5745.    modify_toolbaritem( tbhand,  IDM_COPY, TI_FUNCTIONID,  function_id(copy))
  5746.    modify_toolbaritem( tbhand,  IDM_PASTE, TI_FUNCTIONID, function_id(paste))
  5747.  
  5748.    if ( menu )
  5749.    {
  5750.       modify_menuitem( menu, IDM_ADDFILE,     MI_KEYSTEXT, ":e filename")
  5751.       modify_menuitem( menu, IDM_INSERTFILE,  MI_KEYSTEXT, ":r")
  5752.       modify_menuitem( menu, IDM_SAVE,        MI_KEYSTEXT, ":w")
  5753.       modify_menuitem( menu, IDM_SAVEAS,      MI_KEYSTEXT, ":w filename")
  5754.       modify_menuitem( menu, IDM_SAVEALL,     MI_KEYSTEXT, "")
  5755.       modify_menuitem( menu, IDM_PRINT,       MI_KEYSTEXT, "")
  5756.       modify_menuitem( menu, IDM_EXIT,        MI_KEYSTEXT, ":q")
  5757.       
  5758.       modify_menuitem( menu, IDM_UNDO,        MI_KEYSTEXT, "u")
  5759.       modify_menuitem( menu, IDM_REDO,        MI_KEYSTEXT, "u")
  5760.       modify_menuitem( menu, IDM_CUT,         MI_KEYSTEXT, "Delete")
  5761.       modify_menuitem( menu, IDM_COPY,        MI_KEYSTEXT, "Y")
  5762.       modify_menuitem( menu, IDM_PASTE,       MI_KEYSTEXT, "P")
  5763.       modify_menuitem( menu, IDM_DELETE,      MI_KEYSTEXT, "d")
  5764.       
  5765.       modify_menuitem( menu, IDM_FIND,        MI_KEYSTEXT, "/")
  5766.       modify_menuitem( menu, IDM_FINDAGAIN,   MI_KEYSTEXT, "n")
  5767.       modify_menuitem( menu, IDM_CHANGE,      MI_KEYSTEXT, "")
  5768.       modify_menuitem( menu, IDM_GOTOLINE,    MI_KEYSTEXT, "(number) G")
  5769.       
  5770.       modify_menuitem( menu, IDM_COMMAND,     MI_KEYSTEXT, "F10")
  5771.       
  5772.       modify_menuitem( menu, IDM_NEXT,        MI_KEYSTEXT, "F3")
  5773.       modify_menuitem( menu, IDM_PREV,        MI_KEYSTEXT, "F4")
  5774.       modify_menuitem( menu, IDM_CLOSE,       MI_KEYSTEXT, "")
  5775.       modify_menuitem( menu, IDM_BUFFERLIST,  MI_KEYSTEXT, "")
  5776.       
  5777.       modify_menuitem( menu, IDM_INDEX,       MI_KEYSTEXT, "F1")
  5778.       modify_menuitem( menu, IDM_KEYWORDHELP, MI_KEYSTEXT, "Ctrl-F1" )
  5779.    }
  5780. }
  5781.