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 emouse; 38 extern int mouse_reverse; 39 extern int hshift; 40 extern int sc_height; 41 extern char *no_config; 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 /* 290 * Default environment variables. 291 * Note: "\201" is (A_EXTRA|EV_OK). 292 */ 293 static char dflt_vartable[] = 294 { 295 "LESS_OSC8_OPEN_ANY\0\201" 296 #ifdef LIBEXECDIR 297 "-" LIBEXECDIR "/less-osc8-open" 298 #else 299 "-less-osc8-open" 300 #endif 301 "\0" 302 }; 303 304 /* 305 * Structure to support a list of command tables. 306 */ 307 struct tablelist 308 { 309 struct tablelist *t_next; 310 unsigned char *t_start; 311 unsigned char *t_end; 312 }; 313 314 /* 315 * List of command tables and list of line-edit tables. 316 */ 317 static struct tablelist *list_fcmd_tables = NULL; 318 static struct tablelist *list_ecmd_tables = NULL; 319 static struct tablelist *list_var_tables = NULL; 320 static struct tablelist *list_sysvar_tables = NULL; 321 322 #if USERFILE 323 static struct tablelist * find_stop_table(struct tablelist *t); 324 #endif 325 326 /* 327 * Expand special key abbreviations in a command table. 328 */ 329 static void expand_special_keys(unsigned char *table, size_t len) 330 { 331 unsigned char *fm; 332 unsigned char *to; 333 int a; 334 constant char *repl; 335 size_t klen; 336 337 for (fm = table; fm < table + len; ) 338 { 339 /* 340 * Rewrite each command in the table with any 341 * special key abbreviations expanded. 342 */ 343 for (to = fm; fm < table + len && *fm != '\0'; ) 344 { 345 if (*fm != SK_SPECIAL_KEY) 346 { 347 *to++ = *fm++; 348 continue; 349 } 350 /* 351 * After SK_SPECIAL_KEY, next byte is the type 352 * of special key (one of the SK_* constants), 353 * and the byte after that is the number of bytes, 354 * N, reserved by the abbreviation (including the 355 * SK_SPECIAL_KEY and key type bytes). 356 * Replace all N bytes with the actual bytes 357 * output by the special key on this terminal. 358 */ 359 repl = special_key_str(fm[1]); 360 klen = fm[2]; 361 fm += klen; 362 if (repl == NULL || strlen(repl) > klen) 363 repl = "\377"; 364 while (*repl != '\0') 365 *to++ = (unsigned char) *repl++; /*{{type-issue}}*/ 366 } 367 *to++ = '\0'; 368 /* 369 * Fill any unused bytes between end of command and 370 * the action byte with A_SKIP. 371 */ 372 while (to <= fm) 373 *to++ = A_SKIP; 374 fm++; 375 a = *fm++; 376 if (a & A_EXTRA) 377 { 378 while (fm < table + len && *fm++ != '\0') 379 continue; 380 } 381 } 382 } 383 384 /* 385 * Expand special key abbreviations in a list of command tables. 386 */ 387 static void expand_cmd_table(struct tablelist *tlist) 388 { 389 struct tablelist *t; 390 for (t = tlist; t != NULL; t = t->t_next) 391 { 392 expand_special_keys(t->t_start, ptr_diff(t->t_end, t->t_start)); 393 } 394 } 395 396 /* 397 * Expand special key abbreviations in all command tables. 398 */ 399 public void expand_cmd_tables(void) 400 { 401 expand_cmd_table(list_fcmd_tables); 402 expand_cmd_table(list_ecmd_tables); 403 expand_cmd_table(list_var_tables); 404 expand_cmd_table(list_sysvar_tables); 405 } 406 407 /* 408 * Initialize the command lists. 409 */ 410 public void init_cmds(void) 411 { 412 struct tablelist *t; 413 unsigned char *udflt_vartable = (unsigned char *) dflt_vartable; 414 415 /* 416 * Add the default command tables. 417 */ 418 add_fcmd_table(cmdtable, sizeof(cmdtable)); 419 add_ecmd_table(edittable, sizeof(edittable)); 420 add_sysvar_table(udflt_vartable, sizeof(dflt_vartable)); 421 #if USERFILE 422 #ifdef BINDIR /* For backwards compatibility */ 423 /* Try to add tables in the OLD system lesskey file. */ 424 add_hometable(lesskey, NULL, BINDIR "/.sysless", TRUE); 425 #endif 426 /* 427 * Try to load lesskey source file or binary file. 428 * If the source file succeeds, don't load binary file. 429 * The binary file is likely to have been generated from 430 * a (possibly out of date) copy of the src file, 431 * so loading it is at best redundant. 432 */ 433 /* 434 * Try to add tables in system lesskey src file. 435 */ 436 #if HAVE_LESSKEYSRC 437 if (add_hometable(lesskey_src, "LESSKEYIN_SYSTEM", LESSKEYINFILE_SYS, TRUE) != 0) 438 #endif 439 { 440 /* 441 * Try to add the tables in the system lesskey binary file. 442 */ 443 add_hometable(lesskey, "LESSKEY_SYSTEM", LESSKEYFILE_SYS, TRUE); 444 } 445 /* 446 * Try to add tables in the lesskey src file "$HOME/.lesskey". 447 */ 448 #if HAVE_LESSKEYSRC 449 if (add_hometable(lesskey_src, "LESSKEYIN", DEF_LESSKEYINFILE, FALSE) != 0) 450 #endif 451 { 452 /* 453 * Try to add the tables in the standard lesskey binary file "$HOME/.less". 454 */ 455 add_hometable(lesskey, "LESSKEY", LESSKEYFILE, FALSE); 456 } 457 458 add_content_table(lesskey_content, "LESSKEY_CONTENT_SYSTEM", TRUE); 459 add_content_table(lesskey_content, "LESSKEY_CONTENT", FALSE); 460 461 /* 462 * If any command table contains a #stop directive, discard all other tables. 463 */ 464 t = find_stop_table(list_fcmd_tables); 465 if (t != NULL) 466 { 467 t->t_next = NULL; 468 list_fcmd_tables = t; 469 } 470 #endif /* USERFILE */ 471 } 472 473 /* 474 * Add a command table. 475 */ 476 static int add_cmd_table(struct tablelist **tlist, unsigned char *buf, size_t len) 477 { 478 struct tablelist *t; 479 480 if (len == 0) 481 return (0); 482 /* 483 * Allocate a tablelist structure, initialize it, 484 * and link it into the list of tables. 485 */ 486 if ((t = (struct tablelist *) 487 calloc(1, sizeof(struct tablelist))) == NULL) 488 { 489 return (-1); 490 } 491 t->t_start = buf; 492 t->t_end = buf + len; 493 t->t_next = NULL; 494 if (*tlist == NULL) 495 *tlist = t; 496 else 497 { 498 struct tablelist *e; 499 for (e = *tlist; e->t_next != NULL; e = e->t_next) 500 continue; 501 e->t_next = t; 502 } 503 return (0); 504 } 505 506 /* 507 * Remove the last command table in a list. 508 */ 509 static void pop_cmd_table(struct tablelist **tlist) 510 { 511 struct tablelist *t; 512 if (*tlist == NULL) 513 return; 514 if ((*tlist)->t_next == NULL) 515 { 516 t = *tlist; 517 *tlist = NULL; 518 } else 519 { 520 struct tablelist *e; 521 for (e = *tlist; e->t_next->t_next != NULL; e = e->t_next) 522 continue; 523 t = e->t_next; 524 e->t_next = NULL; 525 } 526 free(t); 527 } 528 529 /* 530 * Add a command table. 531 */ 532 public void add_fcmd_table(unsigned char *buf, size_t len) 533 { 534 if (add_cmd_table(&list_fcmd_tables, buf, len) < 0) 535 error("Warning: some commands disabled", NULL_PARG); 536 } 537 538 /* 539 * Add an editing command table. 540 */ 541 public void add_ecmd_table(unsigned char *buf, size_t len) 542 { 543 if (add_cmd_table(&list_ecmd_tables, buf, len) < 0) 544 error("Warning: some edit commands disabled", NULL_PARG); 545 } 546 547 /* 548 * Add an environment variable table. 549 */ 550 static void add_var_table(struct tablelist **tlist, mutable unsigned char *buf, size_t len) 551 { 552 struct xbuffer xbuf; 553 554 xbuf_init(&xbuf); 555 expand_evars((mutable char*)buf, len, &xbuf); /*{{unsigned-issue}}*/ 556 /* {{ We leak the table in buf. expand_evars scribbled in it so it's useless anyway. }} */ 557 if (add_cmd_table(tlist, xbuf.data, xbuf.end) < 0) 558 error("Warning: environment variables from lesskey file unavailable", NULL_PARG); 559 } 560 561 public void add_uvar_table(unsigned char *buf, size_t len) 562 { 563 add_var_table(&list_var_tables, buf, len); 564 } 565 566 public void add_sysvar_table(unsigned char *buf, size_t len) 567 { 568 add_var_table(&list_sysvar_tables, buf, len); 569 } 570 571 /* 572 * Return action for a mouse wheel down event. 573 */ 574 static int mouse_wheel_down(void) 575 { 576 if (!(emouse & EMOUSE_VSCROLL)) 577 return A_NOACTION; 578 return mouse_reverse ? A_B_MOUSE : A_F_MOUSE; 579 } 580 581 /* 582 * Return action for a mouse wheel up event. 583 */ 584 static int mouse_wheel_up(void) 585 { 586 if (!(emouse & EMOUSE_VSCROLL)) 587 return A_NOACTION; 588 return mouse_reverse ? A_F_MOUSE : A_B_MOUSE; 589 } 590 591 /* 592 * Return action for a mouse wheel left event. 593 */ 594 static int mouse_wheel_left(void) 595 { 596 if (!(emouse & EMOUSE_HSCROLL)) 597 return A_NOACTION; 598 return mouse_reverse ? A_R_MOUSE : A_L_MOUSE; 599 } 600 601 /* 602 * Return action for a mouse wheel right event. 603 */ 604 static int mouse_wheel_right(void) 605 { 606 if (!(emouse & EMOUSE_HSCROLL)) 607 return A_NOACTION; 608 return mouse_reverse ? A_L_MOUSE : A_R_MOUSE; 609 } 610 611 /* 612 * Return action for the left mouse button trigger. 613 */ 614 static int mouse_button_left(int x, int y, lbool down, lbool drag) 615 { 616 static int last_drag_x = -1; 617 static int last_drag_y = -1; 618 static int last_click_y = -1; 619 620 if (down && !drag) 621 { 622 last_drag_x = x; 623 last_drag_y = last_click_y = y; 624 } 625 if (drag) 626 { 627 if ((emouse & EMOUSE_HDRAG) && last_drag_x >= 0 && x != last_drag_x) { 628 /* Drag text left/right */ 629 pos_rehead(FALSE); 630 if (hshift < x - last_drag_x) 631 hshift = 0; 632 else 633 hshift -= x - last_drag_x; 634 screen_trashed(); 635 cmd_exec(); 636 last_drag_x = x; 637 } 638 if ((emouse & EMOUSE_VDRAG) && last_drag_y >= 0) { 639 /* Drag text up/down */ 640 if (y > last_drag_y) 641 { 642 cmd_exec(); 643 backward(y - last_drag_y, FALSE, FALSE, FALSE); 644 last_drag_y = y; 645 } else if (y < last_drag_y) 646 { 647 cmd_exec(); 648 forward(last_drag_y - y, FALSE, FALSE, FALSE); 649 last_drag_y = y; 650 } 651 } 652 } else if ((emouse & EMOUSE_LCLICK) && !down) 653 { 654 #if OSC8_LINK 655 if (secure_allow(SF_OSC8_OPEN)) 656 { 657 cmd_exec(); 658 if (osc8_click(y, x)) 659 return (A_NOACTION); 660 } 661 #else 662 (void) x; 663 #endif /* OSC8_LINK */ 664 if (y < sc_height-1 && y == last_click_y) 665 { 666 cmd_exec(); 667 setmark('#', y, 0); 668 screen_trashed(); 669 } 670 } 671 return (A_NOACTION); 672 } 673 674 /* 675 * Return action for the right mouse button trigger. 676 */ 677 static int mouse_button_right(int x, int y, lbool down, lbool drag) 678 { 679 (void) x; (void) drag; 680 /* 681 * {{ unlike mouse_button_left, we could return an action, 682 * but keep it near mouse_button_left for readability. }} 683 */ 684 if (!(emouse & EMOUSE_RCLICK)) 685 return (A_NOACTION); 686 if (!down && y < sc_height-1) 687 { 688 gomark('#', 0); 689 screen_trashed(); 690 } 691 return (A_NOACTION); 692 } 693 694 /* 695 * Read a decimal integer. Return the integer and set *pterm to the terminating char. 696 */ 697 static int getcc_int(char *pterm) 698 { 699 int num = 0; 700 int digits = 0; 701 for (;;) 702 { 703 char ch = getcc(); 704 if (ch < '0' || ch > '9') 705 { 706 if (pterm != NULL) *pterm = ch; 707 if (digits == 0) 708 return (-1); 709 return (num); 710 } 711 if (ckd_mul(&num, num, 10) || ckd_add(&num, num, ch - '0')) 712 return -1; 713 ++digits; 714 } 715 } 716 717 static int x11mouse_button(int btn, int x, int y, lbool down, lbool drag) 718 { 719 switch (btn) { 720 case X11MOUSE_BUTTON1: 721 return mouse_button_left(x, y, down, drag); 722 /* is BUTTON2 the rightmost with 2-buttons mouse? */ 723 case X11MOUSE_BUTTON2: 724 case X11MOUSE_BUTTON3: 725 return mouse_button_right(x, y, down, drag); 726 } 727 return (A_NOACTION); 728 } 729 730 /* 731 * Read suffix of mouse input and return the action to take. 732 * The prefix ("\e[M") has already been read. 733 */ 734 static int x11mouse_action(lbool skip) 735 { 736 static int prev_b = X11MOUSE_BUTTON_REL; 737 int x, y; 738 int b = getcc() - X11MOUSE_OFFSET; 739 lbool drag = ((b & X11MOUSE_DRAG) != 0); 740 b &= ~X11MOUSE_DRAG; 741 x = getcc() - X11MOUSE_OFFSET-1; 742 y = getcc() - X11MOUSE_OFFSET-1; 743 if (skip) 744 return (A_NOACTION); 745 switch (b) { 746 case X11MOUSE_WHEEL_DOWN: 747 return mouse_wheel_down(); 748 case X11MOUSE_WHEEL_UP: 749 return mouse_wheel_up(); 750 case X11MOUSE_WHEEL_LEFT: 751 return mouse_wheel_left(); 752 case X11MOUSE_WHEEL_RIGHT: 753 return mouse_wheel_right(); 754 case X11MOUSE_BUTTON1: 755 case X11MOUSE_BUTTON2: 756 case X11MOUSE_BUTTON3: 757 prev_b = b; 758 return x11mouse_button(b, x, y, TRUE, drag); 759 case X11MOUSE_BUTTON_REL: /* button up */ 760 return x11mouse_button(prev_b, x, y, FALSE, drag); 761 } 762 return (A_NOACTION); 763 } 764 765 /* 766 * Read suffix of mouse input and return the action to take. 767 * The prefix ("\e[<") has already been read. 768 */ 769 static int x116mouse_action(lbool skip) 770 { 771 char ch; 772 int x, y; 773 int b = getcc_int(&ch); 774 lbool drag = ((b & X11MOUSE_DRAG) != 0); 775 b &= ~X11MOUSE_DRAG; 776 if (b < 0 || ch != ';') return (A_NOACTION); 777 x = getcc_int(&ch) - 1; 778 if (x < 0 || ch != ';') return (A_NOACTION); 779 y = getcc_int(&ch) - 1; 780 if (y < 0) return (A_NOACTION); 781 if (skip) 782 return (A_NOACTION); 783 switch (b) { 784 case X11MOUSE_WHEEL_DOWN: 785 return mouse_wheel_down(); 786 case X11MOUSE_WHEEL_UP: 787 return mouse_wheel_up(); 788 case X11MOUSE_WHEEL_LEFT: 789 return mouse_wheel_left(); 790 case X11MOUSE_WHEEL_RIGHT: 791 return mouse_wheel_right(); 792 case X11MOUSE_BUTTON1: 793 case X11MOUSE_BUTTON2: 794 case X11MOUSE_BUTTON3: { 795 lbool down = (ch == 'M'); 796 lbool up = (ch == 'm'); 797 if (up || down) 798 return x11mouse_button(b, x, y, down, drag); 799 break; } 800 } 801 return (A_NOACTION); 802 } 803 804 /* 805 * Return the largest N such that the first N chars of goal 806 * are equal to the last N chars of str. 807 */ 808 static size_t cmd_match(constant char *goal, constant char *str) 809 { 810 size_t slen = strlen(str); 811 size_t len; 812 for (len = slen; len > 0; len--) 813 if (strncmp(str + slen - len, goal, len) == 0) 814 break; 815 return len; 816 } 817 818 /* 819 * Return pointer to next command table entry. 820 * Also return the action and the extra string from the entry. 821 */ 822 static constant unsigned char * cmd_next_entry(constant unsigned char *entry, mutable int *action, mutable constant unsigned char **extra, mutable size_t *cmdlen) 823 { 824 int a; 825 constant unsigned char *oentry = entry; 826 while (*entry != '\0') /* skip cmd */ 827 ++entry; 828 if (cmdlen != NULL) 829 *cmdlen = ptr_diff(entry, oentry); 830 do 831 a = *++entry; /* get action */ 832 while (a == A_SKIP); 833 ++entry; /* skip action */ 834 if (extra != NULL) 835 *extra = (a & A_EXTRA) ? entry : NULL; 836 if (a & A_EXTRA) 837 { 838 while (*entry++ != '\0') /* skip extra string */ 839 continue; 840 a &= ~A_EXTRA; 841 } 842 if (action != NULL) 843 *action = a; 844 return entry; 845 } 846 847 /* 848 * Does a command table contain a #stop directive? 849 */ 850 static lbool table_has_stop(struct tablelist *t) 851 { 852 constant unsigned char *entry = t->t_start; 853 854 while (entry < t->t_end) 855 { 856 int action; 857 entry = cmd_next_entry(entry, &action, NULL, NULL); 858 if (action == A_END_LIST) 859 return TRUE; 860 } 861 return FALSE; 862 } 863 864 /* 865 * Find the first command table with a #stop directive, if any. 866 */ 867 static struct tablelist * find_stop_table(struct tablelist *t) 868 { 869 for (; t != NULL; t = t->t_next) 870 { 871 if (table_has_stop(t)) 872 return t; 873 } 874 return NULL; 875 } 876 877 /* 878 * Search a single command table for the command string in cmd. 879 */ 880 static int cmd_decode(struct tablelist *tlist, constant char *cmd, lbool anchored, constant char **extra) 881 { 882 int action = A_INVALID; 883 size_t match_len = 0; 884 constant unsigned char *table = NULL; 885 constant unsigned char *endtable; 886 887 if (extra != NULL) 888 *extra = NULL; 889 while (tlist != NULL) 890 { 891 int taction; 892 constant unsigned char *textra; 893 size_t tcmdlen; 894 size_t tmatch; 895 if (table == NULL) 896 { 897 /* Beginning of table: set start/end pointers. */ 898 table = tlist->t_start; 899 endtable = tlist->t_end; 900 } 901 if (anchored) 902 tmatch = (strcmp((constant char *) table, cmd) == 0) ? strlen(cmd) : 0; 903 else 904 tmatch = cmd_match((constant char *) table, cmd); 905 table = cmd_next_entry(table, &taction, &textra, &tcmdlen); 906 if (table >= endtable) 907 { 908 /* End of table; move to next table. */ 909 tlist = tlist->t_next; 910 table = NULL; 911 } 912 if (taction == A_END_LIST) 913 break; 914 if (tmatch >= match_len) 915 { 916 if (tmatch == tcmdlen) 917 { 918 /* (Last chars of) cmd matches this table entry. */ 919 action = taction; 920 if (extra != NULL) 921 *extra = (constant char *) textra; 922 } else if (tmatch > 0 && (tmatch > match_len || action == A_INVALID)) 923 { 924 /* cmd is a prefix of this table entry */ 925 action = A_PREFIX; 926 if (extra != NULL) 927 *extra = NULL; 928 } 929 match_len = tmatch; 930 } 931 } 932 if (action == A_X11MOUSE_IN) 933 action = x11mouse_action(FALSE); 934 else if (action == A_X116MOUSE_IN) 935 action = x116mouse_action(FALSE); 936 return action; 937 } 938 939 /* 940 * Decode a command from the cmdtables list. 941 */ 942 public int fcmd_decode(constant char *cmd, constant char **sp) 943 { 944 return (cmd_decode(list_fcmd_tables, cmd, FALSE, sp)); 945 } 946 947 /* 948 * Decode a command from the edittables list. 949 */ 950 public int ecmd_decode(constant char *cmd, constant char **sp) 951 { 952 return (cmd_decode(list_ecmd_tables, cmd, FALSE, sp)); 953 } 954 955 /* 956 * Parse a comma-separated list. 957 * Call func repeatedly, passing each item in the list. 958 * Stop and return FALSE if func ever returns FALSE, 959 * otherwise parse the entire list and return TRUE. 960 */ 961 public lbool parse_csl(lbool (*func)(constant char *word, size_t wlen, void *arg), constant char *str, void *arg) 962 { 963 for (;;) 964 { 965 constant char *estr; 966 while (*str == ' ' || *str == ',') ++str; /* skip leading spaces/commas */ 967 if (*str == '\0') break; 968 estr = strchr(str, ','); 969 if (estr == NULL) estr = str + strlen(str); 970 while (estr > str && estr[-1] == ' ') --estr; /* trim trailing spaces */ 971 if (!(*func)(str, ptr_diff(estr, str), arg)) 972 return FALSE; 973 str = estr; 974 } 975 return TRUE; 976 } 977 978 /* 979 * Display error message for parse_csl_bitmap. 980 */ 981 static int csl_bitmap_error(constant char *pfx, constant char *type, size_t len, constant char *name) 982 { 983 PARG parg; 984 size_t msglen = len + strlen(pfx) + strlen(type) + 32; 985 char *msg = ecalloc(msglen, sizeof(char)); 986 SNPRINTF4(msg, msglen, "%s: %s name \"%.*s\"", pfx, type, (int) len, name); 987 parg.p_string = msg; 988 error("%s", &parg); 989 free(msg); 990 return 0; 991 } 992 993 /* 994 * Return the bit value of a csl_bitmap name. 995 */ 996 public int csl_bitmap_bit(constant char *name, size_t len, struct csl_bitmap_def *defs, int num_defs, constant char *pfx) 997 { 998 int i; 999 int match = -1; 1000 1001 for (i = 0; i < num_defs; i++) 1002 { 1003 if (strncmp(defs[i].bit_name, name, len) == 0) 1004 { 1005 if (match >= 0) /* name is ambiguous */ 1006 return csl_bitmap_error(pfx, "ambiguous", len, name); 1007 match = i; 1008 } 1009 } 1010 if (match < 0) 1011 return csl_bitmap_error(pfx, "invalid", len, name); 1012 return defs[match].bit_value; 1013 } 1014 1015 /* State for parse_csl_bitmap. */ 1016 struct csl_bitmap_info 1017 { 1018 int bitmap; /* bitmap being constructed */ 1019 struct csl_bitmap_def *def; /* array of name/value pairs */ 1020 int num_defs; /* length of def array */ 1021 constant char *pfx; /* prefix for error messages */ 1022 }; 1023 1024 /* 1025 * Set a bit in a csl_bitmap_info based on a csl_bitmap name. 1026 */ 1027 static lbool csl_bitmap_set(constant char *word, size_t wlen, void *arg) 1028 { 1029 struct csl_bitmap_info *info = (struct csl_bitmap_info *) arg; 1030 info->bitmap |= csl_bitmap_bit(word, wlen, info->def, info->num_defs, info->pfx); 1031 return TRUE; 1032 } 1033 1034 /* 1035 * Parse a comma-separated list of csl_bitmap names and return 1036 * the combined bitmap value. 1037 */ 1038 public int parse_csl_bitmap(constant char *str, struct csl_bitmap_def *defs, int num_defs, constant char *pfx) 1039 { 1040 struct csl_bitmap_info info; 1041 info.bitmap = 0; 1042 info.def = defs; 1043 info.num_defs = num_defs; 1044 info.pfx = pfx; 1045 if (!parse_csl(csl_bitmap_set, str, &info)) 1046 return -1; 1047 return info.bitmap; 1048 } 1049 1050 /* 1051 * Should we ignore the setting of an environment variable? 1052 */ 1053 static lbool word_no_match(constant char *word, size_t wlen, void *arg) 1054 { 1055 constant char *var = (constant char *) arg; 1056 return !(wlen == strlen(var) && strncmp(var, word, wlen) == 0); 1057 } 1058 static lbool ignore_env(constant char *var) 1059 { 1060 if (isnullenv(no_config)) 1061 return FALSE; /* no_config is not set; don't ignore anything */ 1062 /* no_config is set; ignore any var that does not appear in no_config */ 1063 return parse_csl(word_no_match, no_config, (void*) var); 1064 } 1065 1066 /* 1067 * Get the value of an environment variable. 1068 * Looks first in the lesskey file, then in the real environment. 1069 */ 1070 public constant char * lgetenv(constant char *var) 1071 { 1072 int a; 1073 constant char *s; 1074 1075 if (ignore_env(var)) 1076 return (NULL); 1077 a = cmd_decode(list_var_tables, var, TRUE, &s); 1078 if (a == EV_OK) 1079 return (s); 1080 s = getenv(var); 1081 if (s != NULL && *s != '\0') 1082 return (s); 1083 a = cmd_decode(list_sysvar_tables, var, TRUE, &s); 1084 if (a == EV_OK) 1085 return (s); 1086 return (NULL); 1087 } 1088 1089 /* 1090 * Like lgetenv, but also uses a buffer partially filled with an env table. 1091 */ 1092 public constant char * lgetenv_ext(constant char *var, unsigned char *env_buf, size_t env_buf_len) 1093 { 1094 constant char *r; 1095 size_t e; 1096 size_t env_end = 0; 1097 1098 for (e = 0;;) 1099 { 1100 for (; e < env_buf_len; e++) 1101 if (env_buf[e] == '\0') 1102 break; 1103 if (e >= env_buf_len) break; 1104 if (env_buf[++e] & A_EXTRA) 1105 { 1106 for (e = e+1; e < env_buf_len; e++) 1107 if (env_buf[e] == '\0') 1108 break; 1109 } 1110 e++; 1111 if (e >= env_buf_len) break; 1112 env_end = e; 1113 } 1114 /* Temporarily add env_buf to var_tables, do the lookup, then remove it. */ 1115 add_uvar_table(env_buf, env_end); 1116 r = lgetenv(var); 1117 pop_cmd_table(&list_var_tables); 1118 return r; 1119 } 1120 1121 /* 1122 * Is a string null or empty? 1123 */ 1124 public lbool isnullenv(constant char *s) 1125 { 1126 return (s == NULL || *s == '\0'); 1127 } 1128 1129 #if USERFILE 1130 /* 1131 * Get an "integer" from a lesskey file. 1132 * Integers are stored in a funny format: 1133 * two bytes, low order first, in radix KRADIX. 1134 */ 1135 static size_t gint(unsigned char **sp) 1136 { 1137 size_t n; 1138 1139 n = *(*sp)++; 1140 n += *(*sp)++ * KRADIX; 1141 return (n); 1142 } 1143 1144 /* 1145 * Process an old (pre-v241) lesskey file. 1146 */ 1147 static int old_lesskey(unsigned char *buf, size_t len) 1148 { 1149 /* 1150 * Old-style lesskey file. 1151 * The file must end with either 1152 * ...,cmd,0,action 1153 * or ...,cmd,0,action|A_EXTRA,string,0 1154 * So the last byte or the second to last byte must be zero. 1155 */ 1156 if (buf[len-1] != '\0' && buf[len-2] != '\0') 1157 return (-1); 1158 add_fcmd_table(buf, len); 1159 return (0); 1160 } 1161 1162 /* 1163 * Process a new (post-v241) lesskey file. 1164 */ 1165 static int new_lesskey(unsigned char *buf, size_t len, lbool sysvar) 1166 { 1167 unsigned char *p; 1168 unsigned char *end; 1169 int c; 1170 size_t n; 1171 1172 /* 1173 * New-style lesskey file. 1174 * Extract the pieces. 1175 */ 1176 if (buf[len-3] != C0_END_LESSKEY_MAGIC || 1177 buf[len-2] != C1_END_LESSKEY_MAGIC || 1178 buf[len-1] != C2_END_LESSKEY_MAGIC) 1179 return (-1); 1180 p = buf + 4; 1181 end = buf + len; 1182 for (;;) 1183 { 1184 c = *p++; 1185 switch (c) 1186 { 1187 case CMD_SECTION: 1188 n = gint(&p); 1189 if (p+n >= end) 1190 return (-1); 1191 add_fcmd_table(p, n); 1192 p += n; 1193 break; 1194 case EDIT_SECTION: 1195 n = gint(&p); 1196 if (p+n >= end) 1197 return (-1); 1198 add_ecmd_table(p, n); 1199 p += n; 1200 break; 1201 case VAR_SECTION: 1202 n = gint(&p); 1203 if (p+n >= end) 1204 return (-1); 1205 if (sysvar) 1206 add_sysvar_table(p, n); 1207 else 1208 add_uvar_table(p, n); 1209 p += n; 1210 break; 1211 case END_SECTION: 1212 return (0); 1213 default: 1214 /* 1215 * Unrecognized section type. 1216 */ 1217 return (-1); 1218 } 1219 } 1220 } 1221 1222 /* 1223 * Set up a user command table, based on a "lesskey" file. 1224 */ 1225 public int lesskey(constant char *filename, lbool sysvar) 1226 { 1227 unsigned char *buf; 1228 POSITION len; 1229 ssize_t n; 1230 int f; 1231 1232 if (!secure_allow(SF_LESSKEY) || !isnullenv(no_config)) 1233 return (1); 1234 /* 1235 * Try to open the lesskey file. 1236 */ 1237 f = open(filename, OPEN_READ); 1238 if (f < 0) 1239 return (1); 1240 1241 /* 1242 * Read the file into a buffer. 1243 * We first figure out the size of the file and allocate space for it. 1244 * {{ Minimal error checking is done here. 1245 * A garbage .less file will produce strange results. 1246 * To avoid a large amount of error checking code here, we 1247 * rely on the lesskey program to generate a good .less file. }} 1248 */ 1249 len = filesize(f); 1250 if (len == NULL_POSITION || len < 3) 1251 { 1252 /* 1253 * Bad file (valid file must have at least 3 chars). 1254 */ 1255 close(f); 1256 return (-1); 1257 } 1258 if ((buf = (unsigned char *) calloc((size_t)len, sizeof(char))) == NULL) 1259 { 1260 close(f); 1261 return (-1); 1262 } 1263 if (less_lseek(f, (less_off_t)0, SEEK_SET) == BAD_LSEEK) 1264 { 1265 free(buf); 1266 close(f); 1267 return (-1); 1268 } 1269 n = read(f, buf, (size_t) len); 1270 close(f); 1271 if (n != len) 1272 { 1273 free(buf); 1274 return (-1); 1275 } 1276 1277 /* 1278 * Figure out if this is an old-style (before version 241) 1279 * or new-style lesskey file format. 1280 */ 1281 if (len < 4 || 1282 buf[0] != C0_LESSKEY_MAGIC || buf[1] != C1_LESSKEY_MAGIC || 1283 buf[2] != C2_LESSKEY_MAGIC || buf[3] != C3_LESSKEY_MAGIC) 1284 return (old_lesskey(buf, (size_t) len)); 1285 return (new_lesskey(buf, (size_t) len, sysvar)); 1286 } 1287 1288 #if HAVE_LESSKEYSRC 1289 static int lesskey_text(constant char *filename, lbool sysvar, lbool content) 1290 { 1291 int r; 1292 static struct lesskey_tables tables; 1293 1294 if (!secure_allow(SF_LESSKEY) || !isnullenv(no_config)) 1295 return (1); 1296 r = content ? parse_lesskey_content(filename, &tables) : parse_lesskey(filename, &tables); 1297 if (r != 0) 1298 return (r); 1299 add_fcmd_table(tables.cmdtable.buf.data, tables.cmdtable.buf.end); 1300 add_ecmd_table(tables.edittable.buf.data, tables.edittable.buf.end); 1301 if (sysvar) 1302 add_sysvar_table(tables.vartable.buf.data, tables.vartable.buf.end); 1303 else 1304 add_uvar_table(tables.vartable.buf.data, tables.vartable.buf.end); 1305 return (0); 1306 } 1307 1308 public int lesskey_src(constant char *filename, lbool sysvar) 1309 { 1310 return lesskey_text(filename, sysvar, FALSE); 1311 } 1312 1313 public int lesskey_content(constant char *content, lbool sysvar) 1314 { 1315 return lesskey_text(content, sysvar, TRUE); 1316 } 1317 1318 void lesskey_parse_error(char *s) 1319 { 1320 PARG parg; 1321 parg.p_string = s; 1322 error("%s", &parg); 1323 } 1324 #endif /* HAVE_LESSKEYSRC */ 1325 1326 /* 1327 * Add a lesskey file. 1328 */ 1329 static int add_hometable(int (*call_lesskey)(constant char *, lbool), constant char *envname, constant char *def_filename, lbool sysvar) 1330 { 1331 char *filename = NULL; 1332 constant char *efilename; 1333 int r; 1334 1335 #if LESSTEST 1336 if (is_lesstest()) /* Don't use lesskey files in lesstest */ 1337 return -1; 1338 #endif 1339 if (envname != NULL && (efilename = lgetenv(envname)) != NULL) 1340 filename = save(efilename); 1341 else if (sysvar) /* def_filename is full path */ 1342 filename = save(def_filename); 1343 else /* def_filename is just basename */ 1344 { 1345 /* Remove first char (normally a dot) unless stored in $HOME. */ 1346 constant char *xdg = lgetenv("XDG_CONFIG_HOME"); 1347 if (!isnullenv(xdg)) 1348 filename = dirfile(xdg, &def_filename[1], 1); 1349 if (filename == NULL) 1350 { 1351 constant char *home = lgetenv("HOME"); 1352 if (!isnullenv(home)) 1353 { 1354 char *cfg_dir = dirfile(home, ".config", 0); 1355 filename = dirfile(cfg_dir, &def_filename[1], 1); 1356 free(cfg_dir); 1357 } 1358 } 1359 if (filename == NULL) 1360 filename = homefile(def_filename); 1361 } 1362 if (filename == NULL) 1363 return -1; 1364 r = (*call_lesskey)(filename, sysvar); 1365 free(filename); 1366 return (r); 1367 } 1368 1369 /* 1370 * Add the content of a lesskey source file. 1371 */ 1372 static void add_content_table(int (*call_lesskey)(constant char *, lbool), constant char *envname, lbool sysvar) 1373 { 1374 constant char *content; 1375 1376 (void) call_lesskey; /* not used */ 1377 content = lgetenv(envname); 1378 if (isnullenv(content)) 1379 return; 1380 lesskey_content(content, sysvar); 1381 } 1382 #endif /* USERFILE */ 1383 1384 /* 1385 * See if a char is a special line-editing command. 1386 */ 1387 public int editchar(char c, int flags) 1388 { 1389 int action; 1390 int nch; 1391 constant char *s; 1392 char usercmd[MAX_CMDLEN+1]; 1393 1394 /* 1395 * An editing character could actually be a sequence of characters; 1396 * for example, an escape sequence sent by pressing the uparrow key. 1397 * To match the editing string, we use the command decoder 1398 * but give it the edit-commands command table 1399 * This table is constructed to match the user's keyboard. 1400 */ 1401 if (c == erase_char || c == erase2_char) 1402 return (EC_BACKSPACE); 1403 if (c == kill_char) 1404 { 1405 #if MSDOS_COMPILER==WIN32C 1406 if (!win32_kbhit()) 1407 #endif 1408 return (EC_LINEKILL); 1409 } 1410 1411 /* 1412 * Collect characters in a buffer. 1413 * Start with the one we have, and get more if we need them. 1414 */ 1415 nch = 0; 1416 do { 1417 if (nch > 0) 1418 c = getcc(); 1419 usercmd[nch] = c; 1420 usercmd[nch+1] = '\0'; 1421 nch++; 1422 action = ecmd_decode(usercmd, &s); 1423 } while (action == A_PREFIX && nch < MAX_CMDLEN); 1424 1425 if (action == EC_X11MOUSE) 1426 return (x11mouse_action(TRUE)); 1427 if (action == EC_X116MOUSE) 1428 return (x116mouse_action(TRUE)); 1429 1430 if (flags & ECF_NORIGHTLEFT) 1431 { 1432 switch (action) 1433 { 1434 case EC_RIGHT: 1435 case EC_LEFT: 1436 action = A_INVALID; 1437 break; 1438 } 1439 } 1440 #if CMD_HISTORY 1441 if (flags & ECF_NOHISTORY) 1442 { 1443 /* 1444 * The caller says there is no history list. 1445 * Reject any history-manipulation action. 1446 */ 1447 switch (action) 1448 { 1449 case EC_UP: 1450 case EC_DOWN: 1451 action = A_INVALID; 1452 break; 1453 } 1454 } 1455 #endif 1456 if (flags & ECF_NOCOMPLETE) 1457 { 1458 /* 1459 * The caller says we don't want any filename completion cmds. 1460 * Reject them. 1461 */ 1462 switch (action) 1463 { 1464 case EC_F_COMPLETE: 1465 case EC_B_COMPLETE: 1466 case EC_EXPAND: 1467 action = A_INVALID; 1468 break; 1469 } 1470 } 1471 if ((flags & ECF_PEEK) || action == A_INVALID) 1472 { 1473 /* 1474 * We're just peeking, or we didn't understand the command. 1475 * Unget all the characters we read in the loop above. 1476 * This does NOT include the original character that was 1477 * passed in as a parameter. 1478 */ 1479 while (nch > 1) 1480 { 1481 ungetcc(usercmd[--nch]); 1482 } 1483 } else 1484 { 1485 if (s != NULL) 1486 ungetsc(s); 1487 } 1488 return action; 1489 } 1490 1491