+run_ctrlsk = proc (e: env, c: char) signals (errmsg(string), stop(env, string))
+
+ help_msg1 = "@,?w: move to end of # previous word\n" ||
+ "A,?x: move backward # pages\n" ||
+ "B,?r: move forward # pages\n" ||
+ "C,?v: move right # words\n" ||
+ "D,?t: move left # word\n" ||
+ "H: enter alternate keypad mode\n" ||
+ "J: pattern search # times for default\n" ||
+ "L,?q: delete backward to end of # previous word\n" ||
+ "M,?s: delete forward to start of # next word\n" ||
+ "N,?y: move to start of # next word\n"
+
+ norm_msg = "P: delete forward # words\n" ||
+ "Q: delete backward # words\n"
+
+ swap_msg = "P: delete backward # words\n" ||
+ "Q: delete forward # words\n"
+
+ help_msg2 = "R: forward string search # times for default\n" ||
+ "S,?p: backward string search # times for default\n" ||
+ "T: set case mode for searches\n" ||
+ "U: scroll window up # lines\n" ||
+ "V: scroll window down # lines\n" ||
+ "W: reposition window at cursor\n" ||
+ "?n: exit to EXEC\n" ||
+ "?u: exit alternate keypad mode\n" ||
+ "?M: garbage collect address space\n" ||
+ "&c: scroll window to button position\n" ||
+ "&C: set cursor to button position\n" ||
+ "&t: (#<1: normal, #=1: underline, #>1: invert) between cursor and mouse\n" ||
+ "&T: set mark to button position\n" ||
+ "&w: move to button window cursor\n" ||
+ "&W: move to button window\n" ||
+ "&Y: insert cut buffer |#| times (#<0: before cursor)\n" ||
+ "&z: store saved text in cut buffer\n" ||
+ "&Z: store deleted text in cut buffer\n" ||
+ "esc: redefine escape sequence"
+
+ prompt1 = "Escape: "
+ prompt2 = "Escape: ?"
+ prompt3 = "Escape: ["
+ prompt4 = "Escape: O"
+
+ own any_defs: bool := false
+ own redefs: string
+ own qredefs: string
+ own swapped: bool := false
+ if c = ctrlat
+ then prompt = "Set red & blue function keys"
+ legal = "RS"
+ help = "R: reset to normal mode\n" ||
+ "S: swap meanings"
+ i: int, opt: char := get_option (e, prompt, legal, help, false)
+ swapped := opt = 'S'
+ return
+ end resignal errmsg
+ b: buf := e.buffer
+ arg: int := e.this_arg
+ line, pos: int := buf$get_cursor(b)
+ dotop: bool := false
+ prompt: string := prompt1
+ qmark: bool := false
+ xmark: bool := false
+ while true do
+ if ~dotop
+ then dotop := ~_pending_wait() end
+ if dotop
+ then top_display(e, prompt)
+ env$display(e)
+ end
+ c := _getc()
+ if xmark cand c >= 'a' cand c <= 'z'
+ then qmark := true end
+ if dotop
+ then top_display(e, string$append(prompt, c)) end
+ if any_defs cand ((~qmark cand string$indexc(c, redefs) > 0) cor
+ (qmark cand string$indexc(c, qredefs) > 0))
+ then s: string
+ if qmark
+ then s := "esc-?"
+ else s := "esc-"
+ end
+ s := env$fetch_str(e, string$append(s, c), "")
+ env$forget_char(e)
+ for i: int in int$from_to_by(arg, 1, -1) do
+ _push_macro_chars(s)
+ end
+ return
+ end
+ if (~qmark cand (c = 'A' cor c = 'B')) cor
+ (qmark cand (c = 'x' cor c = 'r'))
+ then % move up/down arg pages
+ if c = 'A' cor c = 'x' then arg := - arg end
+ env$move_window(e, arg)
+ elseif (~qmark cand (c = 'C' cor c = 'D')) cor
+ (qmark cand (c = 'v' cor c = 't'))
+ then % move right/left # words
+ if c = 'D' cor c = 't' then arg := - arg end
+ line, pos := scan_word(b, line, pos, arg,
+ false, e.word_chars)
+ buf$set_cursor(b, line, pos)
+ elseif (~qmark cand (c = '@' cor c = 'N')) cor
+ (qmark cand (c = 'w' cor c = 'y'))
+ then % move right/left # start/end words
+ if c = '@' cor c = 'w' then arg := - arg end
+ line, pos := scan_word(b, line, pos, arg,
+ true, e.word_chars)
+ buf$set_cursor(b, line, pos)
+ elseif ~qmark cand c = 'H'
+ then % enter alternate keypad mode
+ if _set_keypad_mode(true)
+ then top_display(e, "Entering Alternate Keypad Mode.")
+ env$store_num(e, "keypad", 1)
+ end
+ elseif ~qmark cand c = 'J'
+ then % pattern search # times
+ obj: string := env$fetch_str(e, "psearch", "")
+ case: bool := env$fetch_num(e, "ignore_case", 0) = 0
+ confirm: bool := env$fetch_num(e, "pconfirm", 0) ~= 0
+ immed: bool := false
+ if arg < 0
+ then immed := true
+ arg := -arg
+ end
+ for arg in int$from_to_by(arg, 1, -1) do
+ if ~pattern$search(e, obj, case, immed, confirm)
+ then signal errmsg("Not found!") end
+ end
+ elseif (~qmark cand (c = 'L' cor c = 'M')) cor
+ (qmark cand (c = 'q' cor c = 's'))
+ then % delete left/right # end/start words
+ if c = 'L' cor c = 'q' then arg := -arg end
+ line, pos := scan_word(b, line, pos, arg,
+ true, e.word_chars)
+ env$delete1(e, line, pos)
+ elseif ~qmark cand (c = 'P' cor c = 'Q')
+ then % delete left/right # words
+ if (c = 'P' cand swapped) cor (c = 'Q' cand ~swapped)
+ then arg := -arg end
+ line, pos := scan_word(b, line, pos, arg,
+ false, e.word_chars)
+ env$delete1(e, line, pos)
+ elseif (~qmark cand (c = 'R' cor c = 'S')) cor (qmark cand c = 'p')
+ then % search left/right # times
+ if c ~= 'R' then arg := - arg end
+ if ~string_search(e.buffer,
+ env$fetch_str(e, "search", ""),
+ arg,
+ env$fetch_num(e, "ignore_case", 0) = 0)
+ then signal errmsg("Not found!") end
+ elseif ~qmark cand c = 'T'
+ then % set case mode
+ arg := 0
+ if mconfirm(e, "Ignore upper/lower case in searches", true)
+ then arg := 1 end
+ env$store_num(e, "ignore_case", arg)
+ elseif ~qmark cand (c = 'U' cor c = 'V')
+ then % scroll up/down
+ if c = 'U' then arg := -arg end
+ pos := env$choose_window(e) + arg
+ if line < pos
+ then if arg = -1 cor arg = 1 then _bell() end
+ pos := line
+ elseif line >= pos + e.size
+ then if arg = -1 cor arg = 1 then _bell() end
+ pos := line - e.size + 1
+ end
+ e.window_top := pos
+ elseif ~qmark cand c = 'W'
+ then % reposition at cursor
+ env$new_window(e)
+ elseif qmark cand (c = 'M' cor c = 'n')
+ then % enter EXEC or GC
+ e.this_arg := 1
+ if c = 'M' then e.this_arg := 4 end
+ run_ctrlat(e, ctrlat)
+ elseif qmark cand c = 'u'
+ then % exit alternate keypad mode
+ if _set_keypad_mode(false)
+ then top_display(e, "Leaving Alternate Keypad Mode.")
+ env$store_num(e, "keypad", 0)
+ end
+ elseif c = '?'
+ then % read next char or print help
+ if ~qmark cand ~xmark
+ then qmark := true
+ prompt := prompt2
+ continue
+ end
+ help: string := norm_msg
+ if swapped then help := swap_msg end
+ type_string(e, help_msg1 || help || help_msg2,
+ "---- Help for escape sequences")
+ dotop := true
+ xmark := false
+ qmark := false
+ prompt := prompt1
+ continue
+ elseif c = '[' cand ~xmark cand ~qmark
+ then xmark := true
+ prompt := prompt3
+ continue
+ elseif c = 'O' cand ~xmark cand ~qmark
+ then xmark := true
+ prompt := prompt4
+ continue
+ elseif c = esc cand ~qmark cand ~xmark
+ then % redefine escape sequence
+ prompt := "Define escape: "
+ while true do
+ if ~_in_macro()
+ then top_display(e, prompt)
+ env$display(e)
+ end
+ c := _getc()
+ prompt := string$append(prompt, c)
+ if qmark cor xmark
+ then break
+ elseif c = '?'
+ then qmark := true
+ elseif c = '[' cor c = 'O'
+ then xmark := true
+ else break end
+ end
+ if ~_in_macro() then top_display(e, prompt) end
+ if env$is_argenv(e)
+ then signal errmsg("Can't redefine now!") end
+ if xmark cand c >= 'a' cand c <= 'z'
+ then qmark := true end
+ if (~qmark cand c = esc) cor (qmark cand c = '?')
+ then signal errmsg("Can't redefine this sequence!") end
+ if ~any_defs
+ then redefs := ""
+ qredefs := ""
+ end
+ name, defs: string
+ if qmark
+ then name := string$append("esc-?", c)
+ defs := qredefs
+ else name := string$append("esc-", c)
+ defs := redefs
+ end
+ s: string := get_string_arg(e, "Define as",
+ env$fetch_str(e, name, ""))
+ env$store_str(e, name, s)
+ arg := string$indexc(c, defs)
+ if string$empty(s)
+ then if arg > 0
+ then defs := string$substr(defs, 1, arg - 1) ||
+ string$rest(defs, arg + 1)
+ any_defs := ~string$empty(redefs) cor
+ ~string$empty(qredefs)
+ end
+ elseif arg = 0
+ then defs := string$append(defs, c)
+ any_defs := true
+ end
+ if qmark
+ then qredefs := defs
+ else redefs := defs
+ end
+ elseif (c = '\303' cor c = '\324') cand ~qmark cand ~xmark
+ then hpos, vpos: int := input$bpos()
+ lines, chars: int := _get_screen_size()
+ if vpos <= e.top_line cor vpos > e.last_line cor
+ hpos < 0 cor hpos >= chars
+ then signal errmsg("Position lies outside window") end
+ vpos := vpos - e.top_line + e.window_top - 1
+ s: string := b[vpos]
+ i: int := int$min(hpos + 2, string$size(s) + 1)
+ while _calc_hpos(s, i) > hpos do
+ i := i - 1
+ end
+ if c = '\303'
+ then buf$set_cursor(b, vpos, i)
+ else buf$set_mark(b, vpos, i)
+ end
+ elseif (c = '\327' cor c = '\343' cor c = '\367') cand
+ ~qmark cand ~xmark
+ then hpos, vpos: int := input$bpos()
+ lines, chars: int := _get_screen_size()
+ for ee: env in winset$displayed() do
+ if vpos <= ee.top_line cor vpos > ee.last_line cor
+ hpos < 0 cor hpos >= chars cor
+ (env$is_argenv(e) cand e ~= ee)
+ then continue end
+ if c = '\343'
+ then if e ~= ee
+ then break end
+ e.window_top := line + e.top_line + 1 - vpos
+ return
+ end
+ if c = '\327'
+ then env$choose_cursor(ee) end
+ signal stop(ee, "")
+ end
+ signal errmsg("Position lies outside window")
+ elseif c = '\331' cand ~qmark cand ~xmark
+ then s: string := x_fetch_cut(0)
+ if string$empty(s) then return end
+ for i: int in int$from_to_by(int$abs(arg), 1, -1) do
+ if ~env$insert1(e, s) then
+ signal errmsg("Can't insert text here!")
+ end
+ end
+ if arg > 0 then buf$set_cursor(b, line, pos) end
+ elseif (c = '\332' cor c = '\372') cand ~qmark cand ~xmark
+ then cut: buf
+ if c = '\332'
+ then cut := e.killed
+ else cut := e.saved
+ end
+ buf$set_cursor(cut, 1, 1)
+ x_store_cut(0, buf$b2s(cut, buf$size(cut), max_int))
+ buf$set_cursor(cut, 1, 1)
+ elseif c = '\364' cand ~qmark cand ~xmark
+ then input$set_highlight(arg > 0)
+ screen$set_highlight(arg > 0, arg > 1)
+ elseif ((c >= '\301' cand c <= '\330') cor
+ (c >= '\341' cand c <= '\370')) cand
+ ~qmark cand ~xmark
+ then return
+ elseif c = ctrlg
+ then % quit
+ signal errmsg(quit_msg)
+ else signal errmsg("Illegal escape char: '" || c2s(c) || "'")
+ end resignal errmsg
+ return
+ end
+ end run_ctrlsk