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