1 /* 2 * Copyright (C) 1984-2026 Mark Nudelman 3 * 4 * You may distribute under the terms of either the GNU General Public 5 * License or the Less License, as specified in the README file. 6 * 7 * For more information, see the README file. 8 */ 9 10 11 /* 12 * Routines to decode user commands. 13 * 14 * This is all table driven. 15 * A command table is a sequence of command descriptors. 16 * Each command descriptor is a sequence of bytes with the following format: 17 * <c1><c2>...<cN><0><action> 18 * The characters c1,c2,...,cN are the command string; that is, 19 * the characters which the user must type. 20 * It is terminated by a null <0> byte. 21 * The byte after the null byte is the action code associated 22 * with the command string. 23 * If an action byte is OR-ed with A_EXTRA, this indicates 24 * that the option byte is followed by an extra string. 25 * 26 * There may be many command tables. 27 * The first (default) table is built-in. 28 * Other tables are read in from "lesskey" files. 29 * All the tables are linked together and are searched in order. 30 */ 31 32 #include "less.h" 33 #include "cmd.h" 34 #include "lesskey.h" 35 36 extern int erase_char, erase2_char, kill_char; 37 extern int mousecap; 38 extern int sc_height; 39 extern char *no_config; 40 41 static constant lbool allow_drag = TRUE; 42 43 #if USERFILE 44 /* "content" is lesskey source, never binary. */ 45 static void add_content_table(int (*call_lesskey)(constant char *, lbool), constant char *envname, lbool sysvar); 46 static int add_hometable(int (*call_lesskey)(constant char *, lbool), constant char *envname, constant char *def_filename, lbool sysvar); 47 #endif /* USERFILE */ 48 49 #define SK(k) \ 50 SK_SPECIAL_KEY, (k), 6, 1, 1, 1 51 /* 52 * Command table is ordered roughly according to expected 53 * frequency of use, so the common commands are near the beginning. 54 */ 55 56 static unsigned char cmdtable[] = 57 { 58 '\r',0, A_F_LINE, 59 '\n',0, A_F_LINE, 60 'e',0, A_F_LINE, 61 'j',0, A_F_LINE, 62 SK(SK_DOWN_ARROW),0, A_F_LINE, 63 CONTROL('E'),0, A_F_LINE, 64 CONTROL('N'),0, A_F_LINE, 65 'k',0, A_B_LINE, 66 'y',0, A_B_LINE, 67 CONTROL('Y'),0, A_B_LINE, 68 SK(SK_CONTROL_K),0, A_B_LINE, 69 CONTROL('P'),0, A_B_LINE, 70 SK(SK_UP_ARROW),0, A_B_LINE, 71 'J',0, A_FF_LINE, 72 'K',0, A_BF_LINE, 73 'Y',0, A_BF_LINE, 74 'd',0, A_F_SCROLL, 75 CONTROL('D'),0, A_F_SCROLL, 76 'u',0, A_B_SCROLL, 77 CONTROL('U'),0, A_B_SCROLL, 78 ESC,'[','M',0, A_X11MOUSE_IN, 79 ESC,'[','<',0, A_X116MOUSE_IN, 80 ' ',0, A_F_SCREEN, 81 'f',0, A_F_SCREEN, 82 CONTROL('F'),0, A_F_SCREEN, 83 CONTROL('V'),0, A_F_SCREEN, 84 SK(SK_PAGE_DOWN),0, A_F_SCREEN, 85 'b',0, A_B_SCREEN, 86 CONTROL('B'),0, A_B_SCREEN, 87 ESC,'v',0, A_B_SCREEN, 88 SK(SK_PAGE_UP),0, A_B_SCREEN, 89 'z',0, A_F_WINDOW, 90 'w',0, A_B_WINDOW, 91 ESC,' ',0, A_FF_SCREEN, 92 ESC,'b',0, A_BF_SCREEN, 93 ESC,'j',0, A_F_NEWLINE, 94 ESC,'k',0, A_B_NEWLINE, 95 'F',0, A_F_FOREVER, 96 ESC,'f',0, A_F_FOREVER_BELL, 97 ESC,'F',0, A_F_UNTIL_HILITE, 98 'R',0, A_FREPAINT, 99 'r',0, A_REPAINT, 100 CONTROL('R'),0, A_REPAINT, 101 CONTROL('L'),0, A_REPAINT, 102 ESC,'u',0, A_UNDO_SEARCH, 103 ESC,'U',0, A_CLR_SEARCH, 104 'g',0, A_GOLINE, 105 SK(SK_HOME),0, A_GOLINE, 106 SK(SK_SHIFT_HOME),0, A_GOLINE|A_EXTRA, ESC,'{',0, 107 SK(SK_CTL_HOME),0, A_GOLINE|A_EXTRA, ESC,'{',0, 108 SK(SK_END),0, A_GOEND, 109 SK(SK_SHIFT_END),0, A_GOEND|A_EXTRA, ESC,'}',0, 110 SK(SK_CTL_END),0, A_GOEND|A_EXTRA, ESC,'}',0, 111 '<',0, A_GOLINE, 112 ESC,'<',0, A_GOLINE, 113 'p',0, A_PERCENT, 114 '%',0, A_PERCENT, 115 ESC,'(',0, A_LSHIFT, 116 ESC,')',0, A_RSHIFT, 117 ESC,'{',0, A_LLSHIFT, 118 ESC,'}',0, A_RRSHIFT, 119 SK(SK_RIGHT_ARROW),0, A_RSHIFT, 120 SK(SK_LEFT_ARROW),0, A_LSHIFT, 121 SK(SK_CTL_RIGHT_ARROW),0, A_RRSHIFT, 122 SK(SK_CTL_LEFT_ARROW),0, A_LLSHIFT, 123 SK(SK_SHIFT_RIGHT_ARROW),0, A_RRSHIFT, 124 SK(SK_SHIFT_LEFT_ARROW),0, A_LLSHIFT, 125 '{',0, A_F_BRACKET|A_EXTRA, '{','}',0, 126 '}',0, A_B_BRACKET|A_EXTRA, '{','}',0, 127 '(',0, A_F_BRACKET|A_EXTRA, '(',')',0, 128 ')',0, A_B_BRACKET|A_EXTRA, '(',')',0, 129 '[',0, A_F_BRACKET|A_EXTRA, '[',']',0, 130 ']',0, A_B_BRACKET|A_EXTRA, '[',']',0, 131 ESC,CONTROL('F'),0, A_F_BRACKET, 132 ESC,CONTROL('B'),0, A_B_BRACKET, 133 'G',0, A_GOEND, 134 ESC,'G',0, A_GOEND_BUF, 135 ESC,'>',0, A_GOEND, 136 '>',0, A_GOEND, 137 'P',0, A_GOPOS, 138 139 '0',0, A_DIGIT, 140 '1',0, A_DIGIT, 141 '2',0, A_DIGIT, 142 '3',0, A_DIGIT, 143 '4',0, A_DIGIT, 144 '5',0, A_DIGIT, 145 '6',0, A_DIGIT, 146 '7',0, A_DIGIT, 147 '8',0, A_DIGIT, 148 '9',0, A_DIGIT, 149 '.',0, A_DIGIT, 150 151 '=',0, A_STAT, 152 CONTROL('G'),0, A_STAT, 153 ':','f',0, A_STAT, 154 '/',0, A_F_SEARCH, 155 '?',0, A_B_SEARCH, 156 ESC,'/',0, A_F_SEARCH|A_EXTRA, '*',0, 157 ESC,'?',0, A_B_SEARCH|A_EXTRA, '*',0, 158 'n',0, A_AGAIN_SEARCH, 159 ESC,'n',0, A_T_AGAIN_SEARCH, 160 'N',0, A_REVERSE_SEARCH, 161 ESC,'N',0, A_T_REVERSE_SEARCH, 162 '&',0, A_FILTER, 163 'm',0, A_SETMARK, 164 'M',0, A_SETMARKBOT, 165 ESC,'m',0, A_CLRMARK, 166 '\'',0, A_GOMARK, 167 CONTROL('X'),CONTROL('X'),0, A_GOMARK, 168 'E',0, A_EXAMINE, 169 ':','e',0, A_EXAMINE, 170 CONTROL('X'),CONTROL('V'),0, A_EXAMINE, 171 ':','n',0, A_NEXT_FILE, 172 ':','p',0, A_PREV_FILE, 173 CONTROL('O'),CONTROL('N'),0, A_OSC8_F_SEARCH, 174 CONTROL('O'),'n',0, A_OSC8_F_SEARCH, 175 CONTROL('O'),CONTROL('P'),0, A_OSC8_B_SEARCH, 176 CONTROL('O'),'p',0, A_OSC8_B_SEARCH, 177 CONTROL('O'),CONTROL('O'),0, A_OSC8_OPEN, 178 CONTROL('O'),'o',0, A_OSC8_OPEN, 179 CONTROL('O'),CONTROL('L'),0, A_OSC8_JUMP, 180 CONTROL('O'),'l',0, A_OSC8_JUMP, 181 't',0, A_NEXT_TAG, 182 'T',0, A_PREV_TAG, 183 ':','x',0, A_INDEX_FILE, 184 ':','d',0, A_REMOVE_FILE, 185 '-',0, A_OPT_TOGGLE, 186 ':','t',0, A_OPT_TOGGLE|A_EXTRA, 't',0, 187 's',0, A_OPT_TOGGLE|A_EXTRA, 'o',0, 188 '_',0, A_DISP_OPTION, 189 '|',0, A_PIPE, 190 'v',0, A_VISUAL, 191 '!',0, A_SHELL, 192 '#',0, A_PSHELL, 193 '+',0, A_FIRSTCMD, 194 195 SK(SK_PAD_U),0, A_B_LINE, 196 SK(SK_PAD_D),0, A_F_LINE, 197 SK(SK_PAD_R),0, A_RSHIFT, 198 SK(SK_PAD_L),0, A_LSHIFT, 199 SK(SK_PAD_UR),0, A_B_SCREEN, 200 SK(SK_PAD_UL),0, A_GOLINE, 201 SK(SK_PAD_DR),0, A_F_SCREEN, 202 SK(SK_PAD_DL),0, A_GOEND, 203 SK(SK_PAD_STAR),0, A_NOACTION|A_EXTRA, '*',0, 204 SK(SK_PAD_SLASH),0, A_NOACTION|A_EXTRA, '/',0, 205 SK(SK_PAD_DASH),0, A_NOACTION|A_EXTRA, '-',0, 206 SK(SK_PAD_PLUS),0, A_NOACTION|A_EXTRA, '+',0, 207 SK(SK_PAD_DOT),0, A_NOACTION|A_EXTRA, '.',0, 208 SK(SK_PAD_COMMA),0, A_NOACTION, 209 SK(SK_PAD_ZERO),0, A_NOACTION|A_EXTRA, '0',0, 210 SK(SK_PAD_CENTER),0, A_NOACTION, 211 212 ESC,'[','2','0','0','~',0, A_START_PASTE, 213 ESC,'[','2','0','1','~',0, A_END_PASTE, 214 215 'H',0, A_HELP, 216 'h',0, A_HELP, 217 SK(SK_F1),0, A_HELP, 218 'V',0, A_VERSION, 219 'q',0, A_QUIT, 220 'Q',0, A_QUIT, 221 ':','q',0, A_QUIT, 222 ':','Q',0, A_QUIT, 223 'Z','Z',0, A_QUIT 224 }; 225 226 static unsigned char edittable[] = 227 { 228 '\t',0, EC_F_COMPLETE, /* TAB */ 229 '\17',0, EC_B_COMPLETE, /* BACKTAB */ 230 SK(SK_BACKTAB),0, EC_B_COMPLETE, /* BACKTAB */ 231 ESC,'\t',0, EC_B_COMPLETE, /* ESC TAB */ 232 CONTROL('L'),0, EC_EXPAND, /* CTRL-L */ 233 CONTROL('V'),0, EC_LITERAL, /* BACKSLASH */ 234 CONTROL('A'),0, EC_LITERAL, /* BACKSLASH */ 235 ESC,'l',0, EC_RIGHT, /* ESC l */ 236 SK(SK_RIGHT_ARROW),0, EC_RIGHT, /* RIGHTARROW */ 237 ESC,'h',0, EC_LEFT, /* ESC h */ 238 SK(SK_LEFT_ARROW),0, EC_LEFT, /* LEFTARROW */ 239 ESC,'b',0, EC_W_LEFT, /* ESC b */ 240 ESC,SK(SK_LEFT_ARROW),0, EC_W_LEFT, /* ESC LEFTARROW */ 241 SK(SK_CTL_LEFT_ARROW),0, EC_W_LEFT, /* CTRL-LEFTARROW */ 242 ESC,'w',0, EC_W_RIGHT, /* ESC w */ 243 ESC,SK(SK_RIGHT_ARROW),0, EC_W_RIGHT, /* ESC RIGHTARROW */ 244 SK(SK_CTL_RIGHT_ARROW),0, EC_W_RIGHT, /* CTRL-RIGHTARROW */ 245 ESC,'i',0, EC_INSERT, /* ESC i */ 246 SK(SK_INSERT),0, EC_INSERT, /* INSERT */ 247 ESC,'x',0, EC_DELETE, /* ESC x */ 248 SK(SK_DELETE),0, EC_DELETE, /* DELETE */ 249 ESC,'X',0, EC_W_DELETE, /* ESC X */ 250 ESC,SK(SK_DELETE),0, EC_W_DELETE, /* ESC DELETE */ 251 SK(SK_CTL_DELETE),0, EC_W_DELETE, /* CTRL-DELETE */ 252 SK(SK_CTL_BACKSPACE),0, EC_W_BACKSPACE, /* CTRL-BACKSPACE */ 253 ESC,SK(SK_BACKSPACE),0, EC_W_BACKSPACE, /* ESC BACKSPACE */ 254 ESC,'0',0, EC_HOME, /* ESC 0 */ 255 SK(SK_HOME),0, EC_HOME, /* HOME */ 256 SK(SK_SHIFT_HOME),0, EC_HOME, /* SHIFT-HOME */ 257 SK(SK_CTL_HOME),0, EC_HOME, /* CTRL-HOME */ 258 ESC,'$',0, EC_END, /* ESC $ */ 259 SK(SK_END),0, EC_END, /* END */ 260 SK(SK_SHIFT_END),0, EC_END, /* SHIFT-END */ 261 SK(SK_CTL_END),0, EC_END, /* CTRL-END */ 262 ESC,'k',0, EC_UP, /* ESC k */ 263 SK(SK_UP_ARROW),0, EC_UP, /* UPARROW */ 264 ESC,'j',0, EC_DOWN, /* ESC j */ 265 SK(SK_DOWN_ARROW),0, EC_DOWN, /* DOWNARROW */ 266 CONTROL('G'),0, EC_ABORT, /* CTRL-G */ 267 SK(SK_PAD_U),0, EC_UP, 268 SK(SK_PAD_D),0, EC_DOWN, 269 SK(SK_PAD_R),0, EC_RIGHT, 270 SK(SK_PAD_L),0, EC_LEFT, 271 SK(SK_PAD_UR),0, A_NOACTION, 272 SK(SK_PAD_UL),0, EC_HOME, 273 SK(SK_PAD_DR),0, A_NOACTION, 274 SK(SK_PAD_DL),0, EC_END, 275 SK(SK_PAD_STAR),0, A_NOACTION|A_EXTRA, '*',0, 276 SK(SK_PAD_SLASH),0, A_NOACTION|A_EXTRA, '/',0, 277 SK(SK_PAD_DASH),0, A_NOACTION|A_EXTRA, '-',0, 278 SK(SK_PAD_PLUS),0, A_NOACTION|A_EXTRA, '+',0, 279 SK(SK_PAD_DOT),0, A_NOACTION|A_EXTRA, '.',0, 280 SK(SK_PAD_COMMA),0, A_NOACTION|A_EXTRA, ',',0, 281 SK(SK_PAD_ZERO),0, A_NOACTION|A_EXTRA, '0',0, 282 SK(SK_PAD_CENTER),0, A_NOACTION, 283 ESC,'[','M',0, EC_X11MOUSE, /* X11 mouse report */ 284 ESC,'[','<',0, EC_X116MOUSE, /* X11 1006 mouse report */ 285 ESC,'[','2','0','0','~',0, A_START_PASTE, /* open paste bracket */ 286 ESC,'[','2','0','1','~',0, A_END_PASTE, /* close paste bracket */ 287 }; 288 289 static unsigned char dflt_vartable[] = 290 { 291 'L','E','S','S','_','O','S','C','8','_','m','a','n', 0, EV_OK|A_EXTRA, 292 /* echo '%o' | sed -e "s,^man\:\\([^(]*\\)( *\\([^)]*\\)\.*,-man '\\2' '\\1'," -e"t X" -e"s,\.*,-echo Invalid man link," -e"\: X" */ 293 'e','c','h','o',' ','\'','%','o','\'',' ','|',' ','s','e','d',' ','-','e',' ','"','s',',','^','m','a','n','\\',':','\\','\\','(','[','^','(',']','*','\\','\\',')','(',' ','*','\\','\\','(','[','^',')',']','*','\\','\\',')','\\','.','*',',','-','m','a','n',' ','\'','\\','\\','2','\'',' ','\'','\\','\\','1','\'',',','"',' ','-','e','"','t',' ','X','"',' ','-','e','"','s',',','\\','.','*',',','-','e','c','h','o',' ','I','n','v','a','l','i','d',' ','m','a','n',' ','l','i','n','k',',','"',' ','-','e','"','\\',':',' ','X','"', 294 0, 295 296 'L','E','S','S','_','O','S','C','8','_','f','i','l','e', 0, EV_OK|A_EXTRA, 297 /* eval `echo '%o' | sed -e "s,^file://\\([^/]*\\)\\(.*\\),_H=\\1;_P=\\2;_E=0," -e"t X" -e"s,.*,_E=1," -e": X"`; if [ "$_E" = 1 ]; then echo -echo Invalid file link; elif [ -z "$_H" -o "$_H" = localhost -o "$_H" = $HOSTNAME ]; then echo ":e $_P"; else echo -echo Cannot open remote file on "$_H"; fi */ 298 'e','v','a','l',' ','`','e','c','h','o',' ','\'','%','o','\'',' ','|',' ','s','e','d',' ','-','e',' ','"','s',',','^','f','i','l','e','\\',':','/','/','\\','\\','(','[','^','/',']','*','\\','\\',')','\\','\\','(','\\','.','*','\\','\\',')',',','_','H','=','\\','\\','1',';','_','P','=','\\','\\','2',';','_','E','=','0',',','"',' ','-','e','"','t',' ','X','"',' ','-','e','"','s',',','\\','.','*',',','_','E','=','1',',','"',' ','-','e','"','\\',':',' ','X','"','`',';',' ','i','f',' ','[',' ','"','$','_','E','"',' ','=',' ','1',' ',']',';',' ','t','h','e','n',' ','e','c','h','o',' ','-','e','c','h','o',' ','I','n','v','a','l','i','d',' ','f','i','l','e',' ','l','i','n','k',';',' ','e','l','i','f',' ','[',' ','-','z',' ','"','$','_','H','"',' ','-','o',' ','"','$','_','H','"',' ','=',' ','l','o','c','a','l','h','o','s','t',' ','-','o',' ','"','$','_','H','"',' ','=',' ','$','H','O','S','T','N','A','M','E',' ',']',';',' ','t','h','e','n',' ','e','c','h','o',' ','"','\\',':','e',' ','$','_','P','"',';',' ','e','l','s','e',' ','e','c','h','o',' ','-','e','c','h','o',' ','C','a','n','n','o','t',' ','o','p','e','n',' ','r','e','m','o','t','e',' ','f','i','l','e',' ','o','n',' ','"','$','_','H','"',';',' ','f','i', 299 0, 300 }; 301 302 /* 303 * Structure to support a list of command tables. 304 */ 305 struct tablelist 306 { 307 struct tablelist *t_next; 308 unsigned char *t_start; 309 unsigned char *t_end; 310 }; 311 312 /* 313 * List of command tables and list of line-edit tables. 314 */ 315 static struct tablelist *list_fcmd_tables = NULL; 316 static struct tablelist *list_ecmd_tables = NULL; 317 static struct tablelist *list_var_tables = NULL; 318 static struct tablelist *list_sysvar_tables = NULL; 319 320 321 /* 322 * Expand special key abbreviations in a command table. 323 */ 324 static void expand_special_keys(unsigned char *table, size_t len) 325 { 326 unsigned char *fm; 327 unsigned char *to; 328 int a; 329 constant char *repl; 330 size_t klen; 331 332 for (fm = table; fm < table + len; ) 333 { 334 /* 335 * Rewrite each command in the table with any 336 * special key abbreviations expanded. 337 */ 338 for (to = fm; *fm != '\0'; ) 339 { 340 if (*fm != SK_SPECIAL_KEY) 341 { 342 *to++ = *fm++; 343 continue; 344 } 345 /* 346 * After SK_SPECIAL_KEY, next byte is the type 347 * of special key (one of the SK_* constants), 348 * and the byte after that is the number of bytes, 349 * N, reserved by the abbreviation (including the 350 * SK_SPECIAL_KEY and key type bytes). 351 * Replace all N bytes with the actual bytes 352 * output by the special key on this terminal. 353 */ 354 repl = special_key_str(fm[1]); 355 klen = fm[2] & 0377; 356 fm += klen; 357 if (repl == NULL || strlen(repl) > klen) 358 repl = "\377"; 359 while (*repl != '\0') 360 *to++ = (unsigned char) *repl++; /*{{type-issue}}*/ 361 } 362 *to++ = '\0'; 363 /* 364 * Fill any unused bytes between end of command and 365 * the action byte with A_SKIP. 366 */ 367 while (to <= fm) 368 *to++ = A_SKIP; 369 fm++; 370 a = *fm++ & 0377; 371 if (a & A_EXTRA) 372 { 373 while (*fm++ != '\0') 374 continue; 375 } 376 } 377 } 378 379 /* 380 * Expand special key abbreviations in a list of command tables. 381 */ 382 static void expand_cmd_table(struct tablelist *tlist) 383 { 384 struct tablelist *t; 385 for (t = tlist; t != NULL; t = t->t_next) 386 { 387 expand_special_keys(t->t_start, ptr_diff(t->t_end, t->t_start)); 388 } 389 } 390 391 /* 392 * Expand special key abbreviations in all command tables. 393 */ 394 public void expand_cmd_tables(void) 395 { 396 expand_cmd_table(list_fcmd_tables); 397 expand_cmd_table(list_ecmd_tables); 398 expand_cmd_table(list_var_tables); 399 expand_cmd_table(list_sysvar_tables); 400 } 401 402 /* 403 * Initialize the command lists. 404 */ 405 public void init_cmds(void) 406 { 407 /* 408 * Add the default command tables. 409 */ 410 add_fcmd_table(cmdtable, sizeof(cmdtable)); 411 add_ecmd_table(edittable, sizeof(edittable)); 412 add_sysvar_table(dflt_vartable, sizeof(dflt_vartable)); 413 #if USERFILE 414 #ifdef BINDIR /* For backwards compatibility */ 415 /* Try to add tables in the OLD system lesskey file. */ 416 add_hometable(lesskey, NULL, BINDIR "/.sysless", TRUE); 417 #endif 418 /* 419 * Try to load lesskey source file or binary file. 420 * If the source file succeeds, don't load binary file. 421 * The binary file is likely to have been generated from 422 * a (possibly out of date) copy of the src file, 423 * so loading it is at best redundant. 424 */ 425 /* 426 * Try to add tables in system lesskey src file. 427 */ 428 #if HAVE_LESSKEYSRC 429 if (add_hometable(lesskey_src, "LESSKEYIN_SYSTEM", LESSKEYINFILE_SYS, TRUE) != 0) 430 #endif 431 { 432 /* 433 * Try to add the tables in the system lesskey binary file. 434 */ 435 add_hometable(lesskey, "LESSKEY_SYSTEM", LESSKEYFILE_SYS, TRUE); 436 } 437 /* 438 * Try to add tables in the lesskey src file "$HOME/.lesskey". 439 */ 440 #if HAVE_LESSKEYSRC 441 if (add_hometable(lesskey_src, "LESSKEYIN", DEF_LESSKEYINFILE, FALSE) != 0) 442 #endif 443 { 444 /* 445 * Try to add the tables in the standard lesskey binary file "$HOME/.less". 446 */ 447 add_hometable(lesskey, "LESSKEY", LESSKEYFILE, FALSE); 448 } 449 450 add_content_table(lesskey_content, "LESSKEY_CONTENT_SYSTEM", TRUE); 451 add_content_table(lesskey_content, "LESSKEY_CONTENT", FALSE); 452 #endif /* USERFILE */ 453 } 454 455 /* 456 * Add a command table. 457 */ 458 static int add_cmd_table(struct tablelist **tlist, unsigned char *buf, size_t len) 459 { 460 struct tablelist *t; 461 462 if (len == 0) 463 return (0); 464 /* 465 * Allocate a tablelist structure, initialize it, 466 * and link it into the list of tables. 467 */ 468 if ((t = (struct tablelist *) 469 calloc(1, sizeof(struct tablelist))) == NULL) 470 { 471 return (-1); 472 } 473 t->t_start = buf; 474 t->t_end = buf + len; 475 t->t_next = NULL; 476 if (*tlist == NULL) 477 *tlist = t; 478 else 479 { 480 struct tablelist *e; 481 for (e = *tlist; e->t_next != NULL; e = e->t_next) 482 continue; 483 e->t_next = t; 484 } 485 return (0); 486 } 487 488 /* 489 * Remove the last command table in a list. 490 */ 491 static void pop_cmd_table(struct tablelist **tlist) 492 { 493 struct tablelist *t; 494 if (*tlist == NULL) 495 return; 496 if ((*tlist)->t_next == NULL) 497 { 498 t = *tlist; 499 *tlist = NULL; 500 } else 501 { 502 struct tablelist *e; 503 for (e = *tlist; e->t_next->t_next != NULL; e = e->t_next) 504 continue; 505 t = e->t_next; 506 e->t_next = NULL; 507 } 508 free(t); 509 } 510 511 /* 512 * Add a command table. 513 */ 514 public void add_fcmd_table(unsigned char *buf, size_t len) 515 { 516 if (add_cmd_table(&list_fcmd_tables, buf, len) < 0) 517 error("Warning: some commands disabled", NULL_PARG); 518 } 519 520 /* 521 * Add an editing command table. 522 */ 523 public void add_ecmd_table(unsigned char *buf, size_t len) 524 { 525 if (add_cmd_table(&list_ecmd_tables, buf, len) < 0) 526 error("Warning: some edit commands disabled", NULL_PARG); 527 } 528 529 /* 530 * Add an environment variable table. 531 */ 532 static void add_var_table(struct tablelist **tlist, mutable unsigned char *buf, size_t len) 533 { 534 struct xbuffer xbuf; 535 536 xbuf_init(&xbuf); 537 expand_evars((mutable char*)buf, len, &xbuf); /*{{unsigned-issue}}*/ 538 /* {{ We leak the table in buf. expand_evars scribbled in it so it's useless anyway. }} */ 539 if (add_cmd_table(tlist, xbuf.data, xbuf.end) < 0) 540 error("Warning: environment variables from lesskey file unavailable", NULL_PARG); 541 } 542 543 public void add_uvar_table(unsigned char *buf, size_t len) 544 { 545 add_var_table(&list_var_tables, buf, len); 546 } 547 548 public void add_sysvar_table(unsigned char *buf, size_t len) 549 { 550 add_var_table(&list_sysvar_tables, buf, len); 551 } 552 553 /* 554 * Return action for a mouse wheel down event. 555 */ 556 static int mouse_wheel_down(void) 557 { 558 return ((mousecap == OPT_ONPLUS) ? A_B_MOUSE : A_F_MOUSE); 559 } 560 561 /* 562 * Return action for a mouse wheel up event. 563 */ 564 static int mouse_wheel_up(void) 565 { 566 return ((mousecap == OPT_ONPLUS) ? A_F_MOUSE : A_B_MOUSE); 567 } 568 569 /* 570 * Return action for the left mouse button trigger. 571 */ 572 static int mouse_button_left(int x, int y, lbool down, lbool drag) 573 { 574 static int last_drag_y = -1; 575 static int last_click_y = -1; 576 577 if (down && !drag) 578 { 579 last_drag_y = last_click_y = y; 580 } 581 if (allow_drag && drag && last_drag_y >= 0) 582 { 583 /* Drag text up/down */ 584 if (y > last_drag_y) 585 { 586 cmd_exec(); 587 backward(y - last_drag_y, FALSE, FALSE, FALSE); 588 last_drag_y = y; 589 } else if (y < last_drag_y) 590 { 591 cmd_exec(); 592 forward(last_drag_y - y, FALSE, FALSE, FALSE); 593 last_drag_y = y; 594 } 595 } else if (!down) 596 { 597 #if OSC8_LINK 598 if (secure_allow(SF_OSC8_OPEN)) 599 { 600 if (osc8_click(y, x)) 601 return (A_NOACTION); 602 } 603 #else 604 (void) x; 605 #endif /* OSC8_LINK */ 606 if (y < sc_height-1 && y == last_click_y) 607 { 608 setmark('#', y); 609 screen_trashed(); 610 } 611 } 612 return (A_NOACTION); 613 } 614 615 /* 616 * Return action for the right mouse button trigger. 617 */ 618 static int mouse_button_right(int x, int y, lbool down, lbool drag) 619 { 620 (void) x; (void) drag; 621 /* 622 * {{ unlike mouse_button_left, we could return an action, 623 * but keep it near mouse_button_left for readability. }} 624 */ 625 if (!down && y < sc_height-1) 626 { 627 gomark('#'); 628 screen_trashed(); 629 } 630 return (A_NOACTION); 631 } 632 633 /* 634 * Read a decimal integer. Return the integer and set *pterm to the terminating char. 635 */ 636 static int getcc_int(char *pterm) 637 { 638 int num = 0; 639 int digits = 0; 640 for (;;) 641 { 642 char ch = getcc(); 643 if (ch < '0' || ch > '9') 644 { 645 if (pterm != NULL) *pterm = ch; 646 if (digits == 0) 647 return (-1); 648 return (num); 649 } 650 if (ckd_mul(&num, num, 10) || ckd_add(&num, num, ch - '0')) 651 return -1; 652 ++digits; 653 } 654 } 655 656 static int x11mouse_button(int btn, int x, int y, lbool down, lbool drag) 657 { 658 switch (btn) { 659 case X11MOUSE_BUTTON1: 660 return mouse_button_left(x, y, down, drag); 661 /* is BUTTON2 the rightmost with 2-buttons mouse? */ 662 case X11MOUSE_BUTTON2: 663 case X11MOUSE_BUTTON3: 664 return mouse_button_right(x, y, down, drag); 665 } 666 return (A_NOACTION); 667 } 668 669 /* 670 * Read suffix of mouse input and return the action to take. 671 * The prefix ("\e[M") has already been read. 672 */ 673 static int x11mouse_action(lbool skip) 674 { 675 static int prev_b = X11MOUSE_BUTTON_REL; 676 int x, y; 677 int b = getcc() - X11MOUSE_OFFSET; 678 lbool drag = ((b & X11MOUSE_DRAG) != 0); 679 b &= ~X11MOUSE_DRAG; 680 x = getcc() - X11MOUSE_OFFSET-1; 681 y = getcc() - X11MOUSE_OFFSET-1; 682 if (skip) 683 return (A_NOACTION); 684 switch (b) { 685 case X11MOUSE_WHEEL_DOWN: 686 return mouse_wheel_down(); 687 case X11MOUSE_WHEEL_UP: 688 return mouse_wheel_up(); 689 case X11MOUSE_BUTTON1: 690 case X11MOUSE_BUTTON2: 691 case X11MOUSE_BUTTON3: 692 prev_b = b; 693 return x11mouse_button(b, x, y, TRUE, drag); 694 case X11MOUSE_BUTTON_REL: /* button up */ 695 return x11mouse_button(prev_b, x, y, FALSE, drag); 696 } 697 return (A_NOACTION); 698 } 699 700 /* 701 * Read suffix of mouse input and return the action to take. 702 * The prefix ("\e[<") has already been read. 703 */ 704 static int x116mouse_action(lbool skip) 705 { 706 char ch; 707 int x, y; 708 int b = getcc_int(&ch); 709 lbool drag = ((b & X11MOUSE_DRAG) != 0); 710 b &= ~X11MOUSE_DRAG; 711 if (b < 0 || ch != ';') return (A_NOACTION); 712 x = getcc_int(&ch) - 1; 713 if (x < 0 || ch != ';') return (A_NOACTION); 714 y = getcc_int(&ch) - 1; 715 if (y < 0) return (A_NOACTION); 716 if (skip) 717 return (A_NOACTION); 718 switch (b) { 719 case X11MOUSE_WHEEL_DOWN: 720 return mouse_wheel_down(); 721 case X11MOUSE_WHEEL_UP: 722 return mouse_wheel_up(); 723 case X11MOUSE_BUTTON1: 724 case X11MOUSE_BUTTON2: 725 case X11MOUSE_BUTTON3: { 726 lbool down = (ch == 'M'); 727 lbool up = (ch == 'm'); 728 if (up || down) 729 return x11mouse_button(b, x, y, down, drag); 730 break; } 731 } 732 return (A_NOACTION); 733 } 734 735 /* 736 * Return the largest N such that the first N chars of goal 737 * are equal to the last N chars of str. 738 */ 739 static size_t cmd_match(constant char *goal, constant char *str) 740 { 741 size_t slen = strlen(str); 742 size_t len; 743 for (len = slen; len > 0; len--) 744 if (strncmp(str + slen - len, goal, len) == 0) 745 break; 746 return len; 747 } 748 749 /* 750 * Return pointer to next command table entry. 751 * Also return the action and the extra string from the entry. 752 */ 753 static constant unsigned char * cmd_next_entry(constant unsigned char *entry, mutable int *action, mutable constant unsigned char **extra, mutable size_t *cmdlen) 754 { 755 int a; 756 constant unsigned char *oentry = entry; 757 while (*entry != '\0') /* skip cmd */ 758 ++entry; 759 if (cmdlen != NULL) 760 *cmdlen = ptr_diff(entry, oentry); 761 do 762 a = *++entry; /* get action */ 763 while (a == A_SKIP); 764 ++entry; /* skip action */ 765 if (extra != NULL) 766 *extra = (a & A_EXTRA) ? entry : NULL; 767 if (a & A_EXTRA) 768 { 769 while (*entry++ != '\0') /* skip extra string */ 770 continue; 771 a &= ~A_EXTRA; 772 } 773 if (action != NULL) 774 *action = a; 775 return entry; 776 } 777 778 /* 779 * Search a single command table for the command string in cmd. 780 */ 781 static int cmd_search(constant char *cmd, constant unsigned char *table, constant unsigned char *endtable, constant unsigned char **extra, size_t *mlen) 782 { 783 int action = A_INVALID; 784 size_t match_len = 0; 785 if (extra != NULL) 786 *extra = NULL; 787 while (table < endtable) 788 { 789 int taction; 790 constant unsigned char *textra; 791 size_t cmdlen; 792 size_t match = cmd_match((constant char *) table, cmd); 793 table = cmd_next_entry(table, &taction, &textra, &cmdlen); 794 if (taction == A_END_LIST) 795 return (-action); 796 if (match >= match_len) 797 { 798 if (match == cmdlen) /* (last chars of) cmd matches this table entry */ 799 { 800 action = taction; 801 if (extra != NULL) 802 *extra = textra; 803 } else if (match > 0 && action == A_INVALID) /* cmd is a prefix of this table entry */ 804 { 805 action = A_PREFIX; 806 } 807 match_len = match; 808 } 809 } 810 if (mlen != NULL) 811 *mlen = match_len; 812 return (action); 813 } 814 815 /* 816 * Decode a command character and return the associated action. 817 * The "extra" string, if any, is returned in sp. 818 */ 819 static int cmd_decode(struct tablelist *tlist, constant char *cmd, constant char **sp) 820 { 821 struct tablelist *t; 822 int action = A_INVALID; 823 size_t match_len = 0; 824 825 /* 826 * Search for the cmd thru all the command tables. 827 * If we find it more than once, take the last one. 828 */ 829 *sp = NULL; 830 for (t = tlist; t != NULL; t = t->t_next) 831 { 832 constant unsigned char *tsp; 833 size_t mlen = match_len; 834 int taction = cmd_search(cmd, t->t_start, t->t_end, &tsp, &mlen); 835 if (mlen >= match_len) 836 { 837 match_len = mlen; 838 if (taction != A_INVALID) 839 { 840 *sp = (constant char *) tsp; 841 if (taction < 0) 842 { 843 action = -taction; 844 break; 845 } 846 action = taction; 847 } 848 } 849 } 850 if (action == A_X11MOUSE_IN) 851 action = x11mouse_action(FALSE); 852 else if (action == A_X116MOUSE_IN) 853 action = x116mouse_action(FALSE); 854 return (action); 855 } 856 857 /* 858 * Decode a command from the cmdtables list. 859 */ 860 public int fcmd_decode(constant char *cmd, constant char **sp) 861 { 862 return (cmd_decode(list_fcmd_tables, cmd, sp)); 863 } 864 865 /* 866 * Decode a command from the edittables list. 867 */ 868 public int ecmd_decode(constant char *cmd, constant char **sp) 869 { 870 return (cmd_decode(list_ecmd_tables, cmd, sp)); 871 } 872 873 /* 874 * Parse a comma-separated list. 875 * Call func repeatedly, passing each item in the list. 876 * Stop and return FALSE if func ever returns FALSE, 877 * otherwise parse the entire list and return TRUE. 878 */ 879 public lbool parse_csl(lbool (*func)(constant char *word, size_t wlen, void *arg), constant char *str, void *arg) 880 { 881 for (;;) 882 { 883 constant char *estr; 884 while (*str == ' ' || *str == ',') ++str; /* skip leading spaces/commas */ 885 if (*str == '\0') break; 886 estr = strchr(str, ','); 887 if (estr == NULL) estr = str + strlen(str); 888 while (estr > str && estr[-1] == ' ') --estr; /* trim trailing spaces */ 889 if (!(*func)(str, ptr_diff(estr, str), arg)) 890 return FALSE; 891 str = estr; 892 } 893 return TRUE; 894 } 895 896 /* 897 * Should we ignore the setting of an environment variable? 898 */ 899 static lbool word_no_match(constant char *word, size_t wlen, void *arg) 900 { 901 constant char *var = (constant char *) arg; 902 return !(wlen == strlen(var) && strncmp(var, word, wlen) == 0); 903 } 904 static lbool ignore_env(constant char *var) 905 { 906 if (isnullenv(no_config)) 907 return FALSE; /* no_config is not set; don't ignore anything */ 908 /* no_config is set; ignore any var that does not appear in no_config */ 909 return parse_csl(word_no_match, no_config, (void*) var); 910 } 911 912 /* 913 * Get the value of an environment variable. 914 * Looks first in the lesskey file, then in the real environment. 915 */ 916 public constant char * lgetenv(constant char *var) 917 { 918 int a; 919 constant char *s; 920 921 if (ignore_env(var)) 922 return (NULL); 923 a = cmd_decode(list_var_tables, var, &s); 924 if (a == EV_OK) 925 return (s); 926 s = getenv(var); 927 if (s != NULL && *s != '\0') 928 return (s); 929 a = cmd_decode(list_sysvar_tables, var, &s); 930 if (a == EV_OK) 931 return (s); 932 return (NULL); 933 } 934 935 /* 936 * Like lgetenv, but also uses a buffer partially filled with an env table. 937 */ 938 public constant char * lgetenv_ext(constant char *var, unsigned char *env_buf, size_t env_buf_len) 939 { 940 constant char *r; 941 size_t e; 942 size_t env_end = 0; 943 944 for (e = 0;;) 945 { 946 for (; e < env_buf_len; e++) 947 if (env_buf[e] == '\0') 948 break; 949 if (e >= env_buf_len) break; 950 if (env_buf[++e] & A_EXTRA) 951 { 952 for (e = e+1; e < env_buf_len; e++) 953 if (env_buf[e] == '\0') 954 break; 955 } 956 e++; 957 if (e >= env_buf_len) break; 958 env_end = e; 959 } 960 /* Temporarily add env_buf to var_tables, do the lookup, then remove it. */ 961 add_uvar_table(env_buf, env_end); 962 r = lgetenv(var); 963 pop_cmd_table(&list_var_tables); 964 return r; 965 } 966 967 /* 968 * Is a string null or empty? 969 */ 970 public lbool isnullenv(constant char *s) 971 { 972 return (s == NULL || *s == '\0'); 973 } 974 975 #if USERFILE 976 /* 977 * Get an "integer" from a lesskey file. 978 * Integers are stored in a funny format: 979 * two bytes, low order first, in radix KRADIX. 980 */ 981 static size_t gint(unsigned char **sp) 982 { 983 size_t n; 984 985 n = *(*sp)++; 986 n += *(*sp)++ * KRADIX; 987 return (n); 988 } 989 990 /* 991 * Process an old (pre-v241) lesskey file. 992 */ 993 static int old_lesskey(unsigned char *buf, size_t len) 994 { 995 /* 996 * Old-style lesskey file. 997 * The file must end with either 998 * ...,cmd,0,action 999 * or ...,cmd,0,action|A_EXTRA,string,0 1000 * So the last byte or the second to last byte must be zero. 1001 */ 1002 if (buf[len-1] != '\0' && buf[len-2] != '\0') 1003 return (-1); 1004 add_fcmd_table(buf, len); 1005 return (0); 1006 } 1007 1008 /* 1009 * Process a new (post-v241) lesskey file. 1010 */ 1011 static int new_lesskey(unsigned char *buf, size_t len, lbool sysvar) 1012 { 1013 unsigned char *p; 1014 unsigned char *end; 1015 int c; 1016 size_t n; 1017 1018 /* 1019 * New-style lesskey file. 1020 * Extract the pieces. 1021 */ 1022 if (buf[len-3] != C0_END_LESSKEY_MAGIC || 1023 buf[len-2] != C1_END_LESSKEY_MAGIC || 1024 buf[len-1] != C2_END_LESSKEY_MAGIC) 1025 return (-1); 1026 p = buf + 4; 1027 end = buf + len; 1028 for (;;) 1029 { 1030 c = *p++; 1031 switch (c) 1032 { 1033 case CMD_SECTION: 1034 n = gint(&p); 1035 if (p+n >= end) 1036 return (-1); 1037 add_fcmd_table(p, n); 1038 p += n; 1039 break; 1040 case EDIT_SECTION: 1041 n = gint(&p); 1042 if (p+n >= end) 1043 return (-1); 1044 add_ecmd_table(p, n); 1045 p += n; 1046 break; 1047 case VAR_SECTION: 1048 n = gint(&p); 1049 if (p+n >= end) 1050 return (-1); 1051 if (sysvar) 1052 add_sysvar_table(p, n); 1053 else 1054 add_uvar_table(p, n); 1055 p += n; 1056 break; 1057 case END_SECTION: 1058 return (0); 1059 default: 1060 /* 1061 * Unrecognized section type. 1062 */ 1063 return (-1); 1064 } 1065 } 1066 } 1067 1068 /* 1069 * Set up a user command table, based on a "lesskey" file. 1070 */ 1071 public int lesskey(constant char *filename, lbool sysvar) 1072 { 1073 unsigned char *buf; 1074 POSITION len; 1075 ssize_t n; 1076 int f; 1077 1078 if (!secure_allow(SF_LESSKEY) || !isnullenv(no_config)) 1079 return (1); 1080 /* 1081 * Try to open the lesskey file. 1082 */ 1083 f = open(filename, OPEN_READ); 1084 if (f < 0) 1085 return (1); 1086 1087 /* 1088 * Read the file into a buffer. 1089 * We first figure out the size of the file and allocate space for it. 1090 * {{ Minimal error checking is done here. 1091 * A garbage .less file will produce strange results. 1092 * To avoid a large amount of error checking code here, we 1093 * rely on the lesskey program to generate a good .less file. }} 1094 */ 1095 len = filesize(f); 1096 if (len == NULL_POSITION || len < 3) 1097 { 1098 /* 1099 * Bad file (valid file must have at least 3 chars). 1100 */ 1101 close(f); 1102 return (-1); 1103 } 1104 if ((buf = (unsigned char *) calloc((size_t)len, sizeof(char))) == NULL) 1105 { 1106 close(f); 1107 return (-1); 1108 } 1109 if (less_lseek(f, (less_off_t)0, SEEK_SET) == BAD_LSEEK) 1110 { 1111 free(buf); 1112 close(f); 1113 return (-1); 1114 } 1115 n = read(f, buf, (size_t) len); 1116 close(f); 1117 if (n != len) 1118 { 1119 free(buf); 1120 return (-1); 1121 } 1122 1123 /* 1124 * Figure out if this is an old-style (before version 241) 1125 * or new-style lesskey file format. 1126 */ 1127 if (len < 4 || 1128 buf[0] != C0_LESSKEY_MAGIC || buf[1] != C1_LESSKEY_MAGIC || 1129 buf[2] != C2_LESSKEY_MAGIC || buf[3] != C3_LESSKEY_MAGIC) 1130 return (old_lesskey(buf, (size_t) len)); 1131 return (new_lesskey(buf, (size_t) len, sysvar)); 1132 } 1133 1134 #if HAVE_LESSKEYSRC 1135 static int lesskey_text(constant char *filename, lbool sysvar, lbool content) 1136 { 1137 int r; 1138 static struct lesskey_tables tables; 1139 1140 if (!secure_allow(SF_LESSKEY) || !isnullenv(no_config)) 1141 return (1); 1142 r = content ? parse_lesskey_content(filename, &tables) : parse_lesskey(filename, &tables); 1143 if (r != 0) 1144 return (r); 1145 add_fcmd_table(tables.cmdtable.buf.data, tables.cmdtable.buf.end); 1146 add_ecmd_table(tables.edittable.buf.data, tables.edittable.buf.end); 1147 if (sysvar) 1148 add_sysvar_table(tables.vartable.buf.data, tables.vartable.buf.end); 1149 else 1150 add_uvar_table(tables.vartable.buf.data, tables.vartable.buf.end); 1151 return (0); 1152 } 1153 1154 public int lesskey_src(constant char *filename, lbool sysvar) 1155 { 1156 return lesskey_text(filename, sysvar, FALSE); 1157 } 1158 1159 public int lesskey_content(constant char *content, lbool sysvar) 1160 { 1161 return lesskey_text(content, sysvar, TRUE); 1162 } 1163 1164 void lesskey_parse_error(char *s) 1165 { 1166 PARG parg; 1167 parg.p_string = s; 1168 error("%s", &parg); 1169 } 1170 #endif /* HAVE_LESSKEYSRC */ 1171 1172 /* 1173 * Add a lesskey file. 1174 */ 1175 static int add_hometable(int (*call_lesskey)(constant char *, lbool), constant char *envname, constant char *def_filename, lbool sysvar) 1176 { 1177 char *filename = NULL; 1178 constant char *efilename; 1179 int r; 1180 1181 #if LESSTEST 1182 if (is_lesstest()) /* Don't use lesskey files in lesstest */ 1183 return -1; 1184 #endif 1185 if (envname != NULL && (efilename = lgetenv(envname)) != NULL) 1186 filename = save(efilename); 1187 else if (sysvar) /* def_filename is full path */ 1188 filename = save(def_filename); 1189 else /* def_filename is just basename */ 1190 { 1191 /* Remove first char (normally a dot) unless stored in $HOME. */ 1192 constant char *xdg = lgetenv("XDG_CONFIG_HOME"); 1193 if (!isnullenv(xdg)) 1194 filename = dirfile(xdg, &def_filename[1], 1); 1195 if (filename == NULL) 1196 { 1197 constant char *home = lgetenv("HOME"); 1198 if (!isnullenv(home)) 1199 { 1200 char *cfg_dir = dirfile(home, ".config", 0); 1201 filename = dirfile(cfg_dir, &def_filename[1], 1); 1202 free(cfg_dir); 1203 } 1204 } 1205 if (filename == NULL) 1206 filename = homefile(def_filename); 1207 } 1208 if (filename == NULL) 1209 return -1; 1210 r = (*call_lesskey)(filename, sysvar); 1211 free(filename); 1212 return (r); 1213 } 1214 1215 /* 1216 * Add the content of a lesskey source file. 1217 */ 1218 static void add_content_table(int (*call_lesskey)(constant char *, lbool), constant char *envname, lbool sysvar) 1219 { 1220 constant char *content; 1221 1222 (void) call_lesskey; /* not used */ 1223 content = lgetenv(envname); 1224 if (isnullenv(content)) 1225 return; 1226 lesskey_content(content, sysvar); 1227 } 1228 #endif /* USERFILE */ 1229 1230 /* 1231 * See if a char is a special line-editing command. 1232 */ 1233 public int editchar(char c, int flags) 1234 { 1235 int action; 1236 int nch; 1237 constant char *s; 1238 char usercmd[MAX_CMDLEN+1]; 1239 1240 /* 1241 * An editing character could actually be a sequence of characters; 1242 * for example, an escape sequence sent by pressing the uparrow key. 1243 * To match the editing string, we use the command decoder 1244 * but give it the edit-commands command table 1245 * This table is constructed to match the user's keyboard. 1246 */ 1247 if (c == erase_char || c == erase2_char) 1248 return (EC_BACKSPACE); 1249 if (c == kill_char) 1250 { 1251 #if MSDOS_COMPILER==WIN32C 1252 if (!win32_kbhit()) 1253 #endif 1254 return (EC_LINEKILL); 1255 } 1256 1257 /* 1258 * Collect characters in a buffer. 1259 * Start with the one we have, and get more if we need them. 1260 */ 1261 nch = 0; 1262 do { 1263 if (nch > 0) 1264 c = getcc(); 1265 usercmd[nch] = c; 1266 usercmd[nch+1] = '\0'; 1267 nch++; 1268 action = ecmd_decode(usercmd, &s); 1269 } while (action == A_PREFIX && nch < MAX_CMDLEN); 1270 1271 if (action == EC_X11MOUSE) 1272 return (x11mouse_action(TRUE)); 1273 if (action == EC_X116MOUSE) 1274 return (x116mouse_action(TRUE)); 1275 1276 if (flags & ECF_NORIGHTLEFT) 1277 { 1278 switch (action) 1279 { 1280 case EC_RIGHT: 1281 case EC_LEFT: 1282 action = A_INVALID; 1283 break; 1284 } 1285 } 1286 #if CMD_HISTORY 1287 if (flags & ECF_NOHISTORY) 1288 { 1289 /* 1290 * The caller says there is no history list. 1291 * Reject any history-manipulation action. 1292 */ 1293 switch (action) 1294 { 1295 case EC_UP: 1296 case EC_DOWN: 1297 action = A_INVALID; 1298 break; 1299 } 1300 } 1301 #endif 1302 if (flags & ECF_NOCOMPLETE) 1303 { 1304 /* 1305 * The caller says we don't want any filename completion cmds. 1306 * Reject them. 1307 */ 1308 switch (action) 1309 { 1310 case EC_F_COMPLETE: 1311 case EC_B_COMPLETE: 1312 case EC_EXPAND: 1313 action = A_INVALID; 1314 break; 1315 } 1316 } 1317 if ((flags & ECF_PEEK) || action == A_INVALID) 1318 { 1319 /* 1320 * We're just peeking, or we didn't understand the command. 1321 * Unget all the characters we read in the loop above. 1322 * This does NOT include the original character that was 1323 * passed in as a parameter. 1324 */ 1325 while (nch > 1) 1326 { 1327 ungetcc(usercmd[--nch]); 1328 } 1329 } else 1330 { 1331 if (s != NULL) 1332 ungetsc(s); 1333 } 1334 return action; 1335 } 1336 1337