| Commit | Line | Data |
|---|---|---|
| 131ccf9c | 1 | /* |
| a9adbba3 | 2 | * Copyright (C) 1984-2009 Mark Nudelman |
| 131ccf9c PA |
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 about less, or for information on how to | |
| 8 | * contact the author, see the README file. | |
| 9 | */ | |
| 10 | ||
| 11 | ||
| 12 | /* | |
| 13 | * Routines to decode user commands. | |
| 14 | * | |
| 15 | * This is all table driven. | |
| 16 | * A command table is a sequence of command descriptors. | |
| 17 | * Each command descriptor is a sequence of bytes with the following format: | |
| 18 | * <c1><c2>...<cN><0><action> | |
| 19 | * The characters c1,c2,...,cN are the command string; that is, | |
| 20 | * the characters which the user must type. | |
| 21 | * It is terminated by a null <0> byte. | |
| 22 | * The byte after the null byte is the action code associated | |
| 23 | * with the command string. | |
| 24 | * If an action byte is OR-ed with A_EXTRA, this indicates | |
| 25 | * that the option byte is followed by an extra string. | |
| 26 | * | |
| 27 | * There may be many command tables. | |
| 28 | * The first (default) table is built-in. | |
| 29 | * Other tables are read in from "lesskey" files. | |
| 30 | * All the tables are linked together and are searched in order. | |
| 31 | */ | |
| 32 | ||
| 33 | #include "less.h" | |
| 34 | #include "cmd.h" | |
| 35 | #include "lesskey.h" | |
| 36 | ||
| 37 | extern int erase_char, erase2_char, kill_char; | |
| 38 | extern int secure; | |
| 39 | ||
| 40 | #define SK(k) \ | |
| 41 | SK_SPECIAL_KEY, (k), 6, 1, 1, 1 | |
| 42 | /* | |
| 43 | * Command table is ordered roughly according to expected | |
| 44 | * frequency of use, so the common commands are near the beginning. | |
| 45 | */ | |
| 46 | ||
| 47 | static unsigned char cmdtable[] = | |
| 48 | { | |
| 49 | '\r',0, A_F_LINE, | |
| 50 | '\n',0, A_F_LINE, | |
| 51 | 'e',0, A_F_LINE, | |
| 52 | 'j',0, A_F_LINE, | |
| 53 | SK(SK_DOWN_ARROW),0, A_F_LINE, | |
| 54 | CONTROL('E'),0, A_F_LINE, | |
| 55 | CONTROL('N'),0, A_F_LINE, | |
| 56 | 'k',0, A_B_LINE, | |
| 57 | 'y',0, A_B_LINE, | |
| 58 | CONTROL('Y'),0, A_B_LINE, | |
| 59 | SK(SK_CONTROL_K),0, A_B_LINE, | |
| 60 | CONTROL('P'),0, A_B_LINE, | |
| 61 | SK(SK_UP_ARROW),0, A_B_LINE, | |
| 62 | 'J',0, A_FF_LINE, | |
| 63 | 'K',0, A_BF_LINE, | |
| 64 | 'Y',0, A_BF_LINE, | |
| 65 | 'd',0, A_F_SCROLL, | |
| 66 | CONTROL('D'),0, A_F_SCROLL, | |
| 67 | 'u',0, A_B_SCROLL, | |
| 68 | CONTROL('U'),0, A_B_SCROLL, | |
| 69 | ' ',0, A_F_SCREEN, | |
| 70 | 'f',0, A_F_SCREEN, | |
| 71 | CONTROL('F'),0, A_F_SCREEN, | |
| 72 | CONTROL('V'),0, A_F_SCREEN, | |
| 73 | SK(SK_PAGE_DOWN),0, A_F_SCREEN, | |
| 74 | 'b',0, A_B_SCREEN, | |
| 75 | CONTROL('B'),0, A_B_SCREEN, | |
| 76 | ESC,'v',0, A_B_SCREEN, | |
| 77 | SK(SK_PAGE_UP),0, A_B_SCREEN, | |
| 78 | 'z',0, A_F_WINDOW, | |
| 79 | 'w',0, A_B_WINDOW, | |
| 80 | ESC,' ',0, A_FF_SCREEN, | |
| 81 | 'F',0, A_F_FOREVER, | |
| 82 | 'R',0, A_FREPAINT, | |
| 83 | 'r',0, A_REPAINT, | |
| 84 | CONTROL('R'),0, A_REPAINT, | |
| 85 | CONTROL('L'),0, A_REPAINT, | |
| 86 | ESC,'u',0, A_UNDO_SEARCH, | |
| 87 | 'g',0, A_GOLINE, | |
| 88 | SK(SK_HOME),0, A_GOLINE, | |
| 89 | '<',0, A_GOLINE, | |
| 90 | ESC,'<',0, A_GOLINE, | |
| 91 | 'p',0, A_PERCENT, | |
| 92 | '%',0, A_PERCENT, | |
| 93 | ESC,'[',0, A_LSHIFT, | |
| 94 | ESC,']',0, A_RSHIFT, | |
| 95 | ESC,'(',0, A_LSHIFT, | |
| 96 | ESC,')',0, A_RSHIFT, | |
| 97 | SK(SK_RIGHT_ARROW),0, A_RSHIFT, | |
| 98 | SK(SK_LEFT_ARROW),0, A_LSHIFT, | |
| 99 | '{',0, A_F_BRACKET|A_EXTRA, '{','}',0, | |
| 100 | '}',0, A_B_BRACKET|A_EXTRA, '{','}',0, | |
| 101 | '(',0, A_F_BRACKET|A_EXTRA, '(',')',0, | |
| 102 | ')',0, A_B_BRACKET|A_EXTRA, '(',')',0, | |
| 103 | '[',0, A_F_BRACKET|A_EXTRA, '[',']',0, | |
| 104 | ']',0, A_B_BRACKET|A_EXTRA, '[',']',0, | |
| 105 | ESC,CONTROL('F'),0, A_F_BRACKET, | |
| 106 | ESC,CONTROL('B'),0, A_B_BRACKET, | |
| 107 | 'G',0, A_GOEND, | |
| 108 | ESC,'>',0, A_GOEND, | |
| 109 | '>',0, A_GOEND, | |
| 110 | SK(SK_END),0, A_GOEND, | |
| 111 | 'P',0, A_GOPOS, | |
| 112 | ||
| 113 | '0',0, A_DIGIT, | |
| 114 | '1',0, A_DIGIT, | |
| 115 | '2',0, A_DIGIT, | |
| 116 | '3',0, A_DIGIT, | |
| 117 | '4',0, A_DIGIT, | |
| 118 | '5',0, A_DIGIT, | |
| 119 | '6',0, A_DIGIT, | |
| 120 | '7',0, A_DIGIT, | |
| 121 | '8',0, A_DIGIT, | |
| 122 | '9',0, A_DIGIT, | |
| 123 | '.',0, A_DIGIT, | |
| 124 | ||
| 125 | '=',0, A_STAT, | |
| 126 | CONTROL('G'),0, A_STAT, | |
| 127 | ':','f',0, A_STAT, | |
| 128 | '/',0, A_F_SEARCH, | |
| 129 | '?',0, A_B_SEARCH, | |
| 130 | ESC,'/',0, A_F_SEARCH|A_EXTRA, '*',0, | |
| 131 | ESC,'?',0, A_B_SEARCH|A_EXTRA, '*',0, | |
| 132 | 'n',0, A_AGAIN_SEARCH, | |
| 133 | ESC,'n',0, A_T_AGAIN_SEARCH, | |
| 134 | 'N',0, A_REVERSE_SEARCH, | |
| 135 | ESC,'N',0, A_T_REVERSE_SEARCH, | |
| 8be36e5b | 136 | '&',0, A_FILTER, |
| 131ccf9c PA |
137 | 'm',0, A_SETMARK, |
| 138 | '\'',0, A_GOMARK, | |
| 139 | CONTROL('X'),CONTROL('X'),0, A_GOMARK, | |
| 140 | 'E',0, A_EXAMINE, | |
| 141 | ':','e',0, A_EXAMINE, | |
| 142 | CONTROL('X'),CONTROL('V'),0, A_EXAMINE, | |
| 143 | ':','n',0, A_NEXT_FILE, | |
| 144 | ':','p',0, A_PREV_FILE, | |
| 145 | 't',0, A_NEXT_TAG, | |
| 146 | 'T',0, A_PREV_TAG, | |
| 147 | ':','x',0, A_INDEX_FILE, | |
| 148 | ':','d',0, A_REMOVE_FILE, | |
| 149 | '-',0, A_OPT_TOGGLE, | |
| 150 | ':','t',0, A_OPT_TOGGLE|A_EXTRA, 't',0, | |
| 151 | 's',0, A_OPT_TOGGLE|A_EXTRA, 'o',0, | |
| 152 | '_',0, A_DISP_OPTION, | |
| 153 | '|',0, A_PIPE, | |
| 154 | 'v',0, A_VISUAL, | |
| 155 | '!',0, A_SHELL, | |
| 156 | '+',0, A_FIRSTCMD, | |
| 157 | ||
| 158 | 'H',0, A_HELP, | |
| 159 | 'h',0, A_HELP, | |
| 160 | SK(SK_F1),0, A_HELP, | |
| 161 | 'V',0, A_VERSION, | |
| 162 | 'q',0, A_QUIT, | |
| 163 | 'Q',0, A_QUIT, | |
| 164 | ':','q',0, A_QUIT, | |
| 165 | ':','Q',0, A_QUIT, | |
| 166 | 'Z','Z',0, A_QUIT | |
| 167 | }; | |
| 168 | ||
| 169 | static unsigned char edittable[] = | |
| 170 | { | |
| 171 | '\t',0, EC_F_COMPLETE, /* TAB */ | |
| 172 | '\17',0, EC_B_COMPLETE, /* BACKTAB */ | |
| 173 | SK(SK_BACKTAB),0, EC_B_COMPLETE, /* BACKTAB */ | |
| 174 | ESC,'\t',0, EC_B_COMPLETE, /* ESC TAB */ | |
| 175 | CONTROL('L'),0, EC_EXPAND, /* CTRL-L */ | |
| 176 | CONTROL('V'),0, EC_LITERAL, /* BACKSLASH */ | |
| 177 | CONTROL('A'),0, EC_LITERAL, /* BACKSLASH */ | |
| 178 | ESC,'l',0, EC_RIGHT, /* ESC l */ | |
| 179 | SK(SK_RIGHT_ARROW),0, EC_RIGHT, /* RIGHTARROW */ | |
| 180 | ESC,'h',0, EC_LEFT, /* ESC h */ | |
| 181 | SK(SK_LEFT_ARROW),0, EC_LEFT, /* LEFTARROW */ | |
| 182 | ESC,'b',0, EC_W_LEFT, /* ESC b */ | |
| 183 | ESC,SK(SK_LEFT_ARROW),0, EC_W_LEFT, /* ESC LEFTARROW */ | |
| 184 | SK(SK_CTL_LEFT_ARROW),0, EC_W_LEFT, /* CTRL-LEFTARROW */ | |
| 185 | ESC,'w',0, EC_W_RIGHT, /* ESC w */ | |
| 186 | ESC,SK(SK_RIGHT_ARROW),0, EC_W_RIGHT, /* ESC RIGHTARROW */ | |
| 187 | SK(SK_CTL_RIGHT_ARROW),0, EC_W_RIGHT, /* CTRL-RIGHTARROW */ | |
| 188 | ESC,'i',0, EC_INSERT, /* ESC i */ | |
| 189 | SK(SK_INSERT),0, EC_INSERT, /* INSERT */ | |
| 190 | ESC,'x',0, EC_DELETE, /* ESC x */ | |
| 191 | SK(SK_DELETE),0, EC_DELETE, /* DELETE */ | |
| 192 | ESC,'X',0, EC_W_DELETE, /* ESC X */ | |
| 193 | ESC,SK(SK_DELETE),0, EC_W_DELETE, /* ESC DELETE */ | |
| 194 | SK(SK_CTL_DELETE),0, EC_W_DELETE, /* CTRL-DELETE */ | |
| 195 | SK(SK_CTL_BACKSPACE),0, EC_W_BACKSPACE, /* CTRL-BACKSPACE */ | |
| 196 | ESC,'\b',0, EC_W_BACKSPACE, /* ESC BACKSPACE */ | |
| 197 | ESC,'0',0, EC_HOME, /* ESC 0 */ | |
| 198 | SK(SK_HOME),0, EC_HOME, /* HOME */ | |
| 199 | ESC,'$',0, EC_END, /* ESC $ */ | |
| 200 | SK(SK_END),0, EC_END, /* END */ | |
| 201 | ESC,'k',0, EC_UP, /* ESC k */ | |
| 202 | SK(SK_UP_ARROW),0, EC_UP, /* UPARROW */ | |
| 203 | ESC,'j',0, EC_DOWN, /* ESC j */ | |
| 204 | SK(SK_DOWN_ARROW),0, EC_DOWN, /* DOWNARROW */ | |
| 205 | }; | |
| 206 | ||
| 207 | /* | |
| 208 | * Structure to support a list of command tables. | |
| 209 | */ | |
| 210 | struct tablelist | |
| 211 | { | |
| 212 | struct tablelist *t_next; | |
| 213 | char *t_start; | |
| 214 | char *t_end; | |
| 215 | }; | |
| 216 | ||
| 217 | /* | |
| 218 | * List of command tables and list of line-edit tables. | |
| 219 | */ | |
| 220 | static struct tablelist *list_fcmd_tables = NULL; | |
| 221 | static struct tablelist *list_ecmd_tables = NULL; | |
| 222 | static struct tablelist *list_var_tables = NULL; | |
| 223 | static struct tablelist *list_sysvar_tables = NULL; | |
| 224 | ||
| 225 | ||
| 226 | /* | |
| 227 | * Expand special key abbreviations in a command table. | |
| 228 | */ | |
| 229 | static void | |
| 230 | expand_special_keys(table, len) | |
| 231 | char *table; | |
| 232 | int len; | |
| 233 | { | |
| 234 | register char *fm; | |
| 235 | register char *to; | |
| 236 | register int a; | |
| 237 | char *repl; | |
| 238 | int klen; | |
| 239 | ||
| 240 | for (fm = table; fm < table + len; ) | |
| 241 | { | |
| 242 | /* | |
| 243 | * Rewrite each command in the table with any | |
| 244 | * special key abbreviations expanded. | |
| 245 | */ | |
| 246 | for (to = fm; *fm != '\0'; ) | |
| 247 | { | |
| 248 | if (*fm != SK_SPECIAL_KEY) | |
| 249 | { | |
| 250 | *to++ = *fm++; | |
| 251 | continue; | |
| 252 | } | |
| 253 | /* | |
| 254 | * After SK_SPECIAL_KEY, next byte is the type | |
| 255 | * of special key (one of the SK_* contants), | |
| 256 | * and the byte after that is the number of bytes, | |
| 257 | * N, reserved by the abbreviation (including the | |
| 258 | * SK_SPECIAL_KEY and key type bytes). | |
| 259 | * Replace all N bytes with the actual bytes | |
| 260 | * output by the special key on this terminal. | |
| 261 | */ | |
| 262 | repl = special_key_str(fm[1]); | |
| 263 | klen = fm[2] & 0377; | |
| 264 | fm += klen; | |
| 265 | if (repl == NULL || (int) strlen(repl) > klen) | |
| 266 | repl = "\377"; | |
| 267 | while (*repl != '\0') | |
| 268 | *to++ = *repl++; | |
| 269 | } | |
| 270 | *to++ = '\0'; | |
| 271 | /* | |
| 272 | * Fill any unused bytes between end of command and | |
| 273 | * the action byte with A_SKIP. | |
| 274 | */ | |
| 275 | while (to <= fm) | |
| 276 | *to++ = A_SKIP; | |
| 277 | fm++; | |
| 278 | a = *fm++ & 0377; | |
| 279 | if (a & A_EXTRA) | |
| 280 | { | |
| 281 | while (*fm++ != '\0') | |
| 282 | continue; | |
| 283 | } | |
| 284 | } | |
| 285 | } | |
| 286 | ||
| 287 | /* | |
| 288 | * Initialize the command lists. | |
| 289 | */ | |
| 290 | public void | |
| 291 | init_cmds() | |
| 292 | { | |
| 293 | /* | |
| 294 | * Add the default command tables. | |
| 295 | */ | |
| 296 | add_fcmd_table((char*)cmdtable, sizeof(cmdtable)); | |
| 297 | add_ecmd_table((char*)edittable, sizeof(edittable)); | |
| 298 | #if USERFILE | |
| 299 | /* | |
| 300 | * For backwards compatibility, | |
| 301 | * try to add tables in the OLD system lesskey file. | |
| 302 | */ | |
| 303 | #ifdef BINDIR | |
| 304 | add_hometable(NULL, BINDIR "/.sysless", 1); | |
| 305 | #endif | |
| 306 | /* | |
| 307 | * Try to add the tables in the system lesskey file. | |
| 308 | */ | |
| 309 | add_hometable("LESSKEY_SYSTEM", LESSKEYFILE_SYS, 1); | |
| 310 | /* | |
| 311 | * Try to add the tables in the standard lesskey file "$HOME/.less". | |
| 312 | */ | |
| 313 | add_hometable("LESSKEY", LESSKEYFILE, 0); | |
| 314 | #endif | |
| 315 | } | |
| 316 | ||
| 317 | /* | |
| 318 | * Add a command table. | |
| 319 | */ | |
| 320 | static int | |
| 321 | add_cmd_table(tlist, buf, len) | |
| 322 | struct tablelist **tlist; | |
| 323 | char *buf; | |
| 324 | int len; | |
| 325 | { | |
| 326 | register struct tablelist *t; | |
| 327 | ||
| 328 | if (len == 0) | |
| 329 | return (0); | |
| 330 | /* | |
| 331 | * Allocate a tablelist structure, initialize it, | |
| 332 | * and link it into the list of tables. | |
| 333 | */ | |
| 334 | if ((t = (struct tablelist *) | |
| 335 | calloc(1, sizeof(struct tablelist))) == NULL) | |
| 336 | { | |
| 337 | return (-1); | |
| 338 | } | |
| 339 | expand_special_keys(buf, len); | |
| 340 | t->t_start = buf; | |
| 341 | t->t_end = buf + len; | |
| 342 | t->t_next = *tlist; | |
| 343 | *tlist = t; | |
| 344 | return (0); | |
| 345 | } | |
| 346 | ||
| 347 | /* | |
| 348 | * Add a command table. | |
| 349 | */ | |
| 350 | public void | |
| 351 | add_fcmd_table(buf, len) | |
| 352 | char *buf; | |
| 353 | int len; | |
| 354 | { | |
| 355 | if (add_cmd_table(&list_fcmd_tables, buf, len) < 0) | |
| 356 | error("Warning: some commands disabled", NULL_PARG); | |
| 357 | } | |
| 358 | ||
| 359 | /* | |
| 360 | * Add an editing command table. | |
| 361 | */ | |
| 362 | public void | |
| 363 | add_ecmd_table(buf, len) | |
| 364 | char *buf; | |
| 365 | int len; | |
| 366 | { | |
| 367 | if (add_cmd_table(&list_ecmd_tables, buf, len) < 0) | |
| 368 | error("Warning: some edit commands disabled", NULL_PARG); | |
| 369 | } | |
| 370 | ||
| 371 | /* | |
| 372 | * Add an environment variable table. | |
| 373 | */ | |
| 374 | static void | |
| 375 | add_var_table(tlist, buf, len) | |
| 376 | struct tablelist **tlist; | |
| 377 | char *buf; | |
| 378 | int len; | |
| 379 | { | |
| 380 | if (add_cmd_table(tlist, buf, len) < 0) | |
| 381 | error("Warning: environment variables from lesskey file unavailable", NULL_PARG); | |
| 382 | } | |
| 383 | ||
| 384 | /* | |
| 385 | * Search a single command table for the command string in cmd. | |
| 386 | */ | |
| 387 | static int | |
| 388 | cmd_search(cmd, table, endtable, sp) | |
| 389 | char *cmd; | |
| 390 | char *table; | |
| 391 | char *endtable; | |
| 392 | char **sp; | |
| 393 | { | |
| 394 | register char *p; | |
| 395 | register char *q; | |
| 396 | register int a; | |
| 397 | ||
| 398 | *sp = NULL; | |
| 399 | for (p = table, q = cmd; p < endtable; p++, q++) | |
| 400 | { | |
| 401 | if (*p == *q) | |
| 402 | { | |
| 403 | /* | |
| 404 | * Current characters match. | |
| 405 | * If we're at the end of the string, we've found it. | |
| 406 | * Return the action code, which is the character | |
| 407 | * after the null at the end of the string | |
| 408 | * in the command table. | |
| 409 | */ | |
| 410 | if (*p == '\0') | |
| 411 | { | |
| 412 | a = *++p & 0377; | |
| 413 | while (a == A_SKIP) | |
| 414 | a = *++p & 0377; | |
| 415 | if (a == A_END_LIST) | |
| 416 | { | |
| 417 | /* | |
| 418 | * We get here only if the original | |
| 419 | * cmd string passed in was empty (""). | |
| 420 | * I don't think that can happen, | |
| 421 | * but just in case ... | |
| 422 | */ | |
| 423 | return (A_UINVALID); | |
| 424 | } | |
| 425 | /* | |
| 426 | * Check for an "extra" string. | |
| 427 | */ | |
| 428 | if (a & A_EXTRA) | |
| 429 | { | |
| 430 | *sp = ++p; | |
| 431 | a &= ~A_EXTRA; | |
| 432 | } | |
| 433 | return (a); | |
| 434 | } | |
| 435 | } else if (*q == '\0') | |
| 436 | { | |
| 437 | /* | |
| 438 | * Hit the end of the user's command, | |
| 439 | * but not the end of the string in the command table. | |
| 440 | * The user's command is incomplete. | |
| 441 | */ | |
| 442 | return (A_PREFIX); | |
| 443 | } else | |
| 444 | { | |
| 445 | /* | |
| 446 | * Not a match. | |
| 447 | * Skip ahead to the next command in the | |
| 448 | * command table, and reset the pointer | |
| 449 | * to the beginning of the user's command. | |
| 450 | */ | |
| 451 | if (*p == '\0' && p[1] == A_END_LIST) | |
| 452 | { | |
| 453 | /* | |
| 454 | * A_END_LIST is a special marker that tells | |
| 455 | * us to abort the cmd search. | |
| 456 | */ | |
| 457 | return (A_UINVALID); | |
| 458 | } | |
| 459 | while (*p++ != '\0') | |
| 460 | continue; | |
| 461 | while (*p == A_SKIP) | |
| 462 | p++; | |
| 463 | if (*p & A_EXTRA) | |
| 464 | while (*++p != '\0') | |
| 465 | continue; | |
| 466 | q = cmd-1; | |
| 467 | } | |
| 468 | } | |
| 469 | /* | |
| 470 | * No match found in the entire command table. | |
| 471 | */ | |
| 472 | return (A_INVALID); | |
| 473 | } | |
| 474 | ||
| 475 | /* | |
| 476 | * Decode a command character and return the associated action. | |
| 477 | * The "extra" string, if any, is returned in sp. | |
| 478 | */ | |
| 479 | static int | |
| 480 | cmd_decode(tlist, cmd, sp) | |
| 481 | struct tablelist *tlist; | |
| 482 | char *cmd; | |
| 483 | char **sp; | |
| 484 | { | |
| 485 | register struct tablelist *t; | |
| 486 | register int action = A_INVALID; | |
| 487 | ||
| 488 | /* | |
| 489 | * Search thru all the command tables. | |
| 490 | * Stop when we find an action which is not A_INVALID. | |
| 491 | */ | |
| 492 | for (t = tlist; t != NULL; t = t->t_next) | |
| 493 | { | |
| 494 | action = cmd_search(cmd, t->t_start, t->t_end, sp); | |
| 495 | if (action != A_INVALID) | |
| 496 | break; | |
| 497 | } | |
| 498 | if (action == A_UINVALID) | |
| 499 | action = A_INVALID; | |
| 500 | return (action); | |
| 501 | } | |
| 502 | ||
| 503 | /* | |
| 504 | * Decode a command from the cmdtables list. | |
| 505 | */ | |
| 506 | public int | |
| 507 | fcmd_decode(cmd, sp) | |
| 508 | char *cmd; | |
| 509 | char **sp; | |
| 510 | { | |
| 511 | return (cmd_decode(list_fcmd_tables, cmd, sp)); | |
| 512 | } | |
| 513 | ||
| 514 | /* | |
| 515 | * Decode a command from the edittables list. | |
| 516 | */ | |
| 517 | public int | |
| 518 | ecmd_decode(cmd, sp) | |
| 519 | char *cmd; | |
| 520 | char **sp; | |
| 521 | { | |
| 522 | return (cmd_decode(list_ecmd_tables, cmd, sp)); | |
| 523 | } | |
| 524 | ||
| 525 | /* | |
| 526 | * Get the value of an environment variable. | |
| 527 | * Looks first in the lesskey file, then in the real environment. | |
| 528 | */ | |
| 529 | public char * | |
| 530 | lgetenv(var) | |
| 531 | char *var; | |
| 532 | { | |
| 533 | int a; | |
| 534 | char *s; | |
| 535 | ||
| 536 | a = cmd_decode(list_var_tables, var, &s); | |
| 537 | if (a == EV_OK) | |
| 538 | return (s); | |
| 539 | s = getenv(var); | |
| 540 | if (s != NULL && *s != '\0') | |
| 541 | return (s); | |
| 542 | a = cmd_decode(list_sysvar_tables, var, &s); | |
| 543 | if (a == EV_OK) | |
| 544 | return (s); | |
| 545 | return (NULL); | |
| 546 | } | |
| 547 | ||
| 548 | #if USERFILE | |
| 549 | /* | |
| 550 | * Get an "integer" from a lesskey file. | |
| 551 | * Integers are stored in a funny format: | |
| 552 | * two bytes, low order first, in radix KRADIX. | |
| 553 | */ | |
| 554 | static int | |
| 555 | gint(sp) | |
| 556 | char **sp; | |
| 557 | { | |
| 558 | int n; | |
| 559 | ||
| 560 | n = *(*sp)++; | |
| 561 | n += *(*sp)++ * KRADIX; | |
| 562 | return (n); | |
| 563 | } | |
| 564 | ||
| 565 | /* | |
| 566 | * Process an old (pre-v241) lesskey file. | |
| 567 | */ | |
| 568 | static int | |
| 569 | old_lesskey(buf, len) | |
| 570 | char *buf; | |
| 571 | int len; | |
| 572 | { | |
| 573 | /* | |
| 574 | * Old-style lesskey file. | |
| 575 | * The file must end with either | |
| 576 | * ...,cmd,0,action | |
| 577 | * or ...,cmd,0,action|A_EXTRA,string,0 | |
| 578 | * So the last byte or the second to last byte must be zero. | |
| 579 | */ | |
| 580 | if (buf[len-1] != '\0' && buf[len-2] != '\0') | |
| 581 | return (-1); | |
| 582 | add_fcmd_table(buf, len); | |
| 583 | return (0); | |
| 584 | } | |
| 585 | ||
| 586 | /* | |
| 587 | * Process a new (post-v241) lesskey file. | |
| 588 | */ | |
| 589 | static int | |
| 590 | new_lesskey(buf, len, sysvar) | |
| 591 | char *buf; | |
| 592 | int len; | |
| 593 | int sysvar; | |
| 594 | { | |
| 595 | char *p; | |
| 596 | register int c; | |
| 597 | register int n; | |
| 598 | ||
| 599 | /* | |
| 600 | * New-style lesskey file. | |
| 601 | * Extract the pieces. | |
| 602 | */ | |
| 603 | if (buf[len-3] != C0_END_LESSKEY_MAGIC || | |
| 604 | buf[len-2] != C1_END_LESSKEY_MAGIC || | |
| 605 | buf[len-1] != C2_END_LESSKEY_MAGIC) | |
| 606 | return (-1); | |
| 607 | p = buf + 4; | |
| 608 | for (;;) | |
| 609 | { | |
| 610 | c = *p++; | |
| 611 | switch (c) | |
| 612 | { | |
| 613 | case CMD_SECTION: | |
| 614 | n = gint(&p); | |
| 615 | add_fcmd_table(p, n); | |
| 616 | p += n; | |
| 617 | break; | |
| 618 | case EDIT_SECTION: | |
| 619 | n = gint(&p); | |
| 620 | add_ecmd_table(p, n); | |
| 621 | p += n; | |
| 622 | break; | |
| 623 | case VAR_SECTION: | |
| 624 | n = gint(&p); | |
| 625 | add_var_table((sysvar) ? | |
| 626 | &list_sysvar_tables : &list_var_tables, p, n); | |
| 627 | p += n; | |
| 628 | break; | |
| 629 | case END_SECTION: | |
| 630 | return (0); | |
| 631 | default: | |
| 632 | /* | |
| 633 | * Unrecognized section type. | |
| 634 | */ | |
| 635 | return (-1); | |
| 636 | } | |
| 637 | } | |
| 638 | } | |
| 639 | ||
| 640 | /* | |
| 641 | * Set up a user command table, based on a "lesskey" file. | |
| 642 | */ | |
| 643 | public int | |
| 644 | lesskey(filename, sysvar) | |
| 645 | char *filename; | |
| 646 | int sysvar; | |
| 647 | { | |
| 648 | register char *buf; | |
| 649 | register POSITION len; | |
| 650 | register long n; | |
| 651 | register int f; | |
| 652 | ||
| 653 | if (secure) | |
| 654 | return (1); | |
| 655 | /* | |
| 656 | * Try to open the lesskey file. | |
| 657 | */ | |
| 658 | filename = shell_unquote(filename); | |
| 659 | f = open(filename, OPEN_READ); | |
| 660 | free(filename); | |
| 661 | if (f < 0) | |
| 662 | return (1); | |
| 663 | ||
| 664 | /* | |
| 665 | * Read the file into a buffer. | |
| 666 | * We first figure out the size of the file and allocate space for it. | |
| 667 | * {{ Minimal error checking is done here. | |
| 668 | * A garbage .less file will produce strange results. | |
| 669 | * To avoid a large amount of error checking code here, we | |
| 670 | * rely on the lesskey program to generate a good .less file. }} | |
| 671 | */ | |
| 672 | len = filesize(f); | |
| 673 | if (len == NULL_POSITION || len < 3) | |
| 674 | { | |
| 675 | /* | |
| 676 | * Bad file (valid file must have at least 3 chars). | |
| 677 | */ | |
| 678 | close(f); | |
| 679 | return (-1); | |
| 680 | } | |
| 681 | if ((buf = (char *) calloc((int)len, sizeof(char))) == NULL) | |
| 682 | { | |
| 683 | close(f); | |
| 684 | return (-1); | |
| 685 | } | |
| 3336a202 | 686 | if (lseek(f, (off_t)0, SEEK_SET) == BAD_LSEEK) |
| 131ccf9c PA |
687 | { |
| 688 | free(buf); | |
| 689 | close(f); | |
| 690 | return (-1); | |
| 691 | } | |
| 692 | n = read(f, buf, (unsigned int) len); | |
| 693 | close(f); | |
| 694 | if (n != len) | |
| 695 | { | |
| 696 | free(buf); | |
| 697 | return (-1); | |
| 698 | } | |
| 699 | ||
| 700 | /* | |
| 701 | * Figure out if this is an old-style (before version 241) | |
| 702 | * or new-style lesskey file format. | |
| 703 | */ | |
| 704 | if (buf[0] != C0_LESSKEY_MAGIC || buf[1] != C1_LESSKEY_MAGIC || | |
| 705 | buf[2] != C2_LESSKEY_MAGIC || buf[3] != C3_LESSKEY_MAGIC) | |
| 706 | return (old_lesskey(buf, (int)len)); | |
| 707 | return (new_lesskey(buf, (int)len, sysvar)); | |
| 708 | } | |
| 709 | ||
| 710 | /* | |
| 711 | * Add the standard lesskey file "$HOME/.less" | |
| 712 | */ | |
| 713 | public void | |
| 714 | add_hometable(envname, def_filename, sysvar) | |
| 715 | char *envname; | |
| 716 | char *def_filename; | |
| 717 | int sysvar; | |
| 718 | { | |
| 719 | char *filename; | |
| 720 | PARG parg; | |
| 721 | ||
| 722 | if (envname != NULL && (filename = lgetenv(envname)) != NULL) | |
| 723 | filename = save(filename); | |
| 724 | else if (sysvar) | |
| 725 | filename = save(def_filename); | |
| 726 | else | |
| 727 | filename = homefile(def_filename); | |
| 728 | if (filename == NULL) | |
| 729 | return; | |
| 730 | if (lesskey(filename, sysvar) < 0) | |
| 731 | { | |
| 732 | parg.p_string = filename; | |
| 733 | error("Cannot use lesskey file \"%s\"", &parg); | |
| 734 | } | |
| 735 | free(filename); | |
| 736 | } | |
| 737 | #endif | |
| 738 | ||
| 739 | /* | |
| 740 | * See if a char is a special line-editing command. | |
| 741 | */ | |
| 742 | public int | |
| 743 | editchar(c, flags) | |
| 744 | int c; | |
| 745 | int flags; | |
| 746 | { | |
| 747 | int action; | |
| 748 | int nch; | |
| 749 | char *s; | |
| 750 | char usercmd[MAX_CMDLEN+1]; | |
| 751 | ||
| 752 | /* | |
| 753 | * An editing character could actually be a sequence of characters; | |
| 754 | * for example, an escape sequence sent by pressing the uparrow key. | |
| 755 | * To match the editing string, we use the command decoder | |
| 756 | * but give it the edit-commands command table | |
| 757 | * This table is constructed to match the user's keyboard. | |
| 758 | */ | |
| 759 | if (c == erase_char || c == erase2_char) | |
| 760 | return (EC_BACKSPACE); | |
| 761 | if (c == kill_char) | |
| 762 | return (EC_LINEKILL); | |
| 763 | ||
| 764 | /* | |
| 765 | * Collect characters in a buffer. | |
| 766 | * Start with the one we have, and get more if we need them. | |
| 767 | */ | |
| 768 | nch = 0; | |
| 769 | do { | |
| 770 | if (nch > 0) | |
| 771 | c = getcc(); | |
| 772 | usercmd[nch] = c; | |
| 773 | usercmd[nch+1] = '\0'; | |
| 774 | nch++; | |
| 775 | action = ecmd_decode(usercmd, &s); | |
| 776 | } while (action == A_PREFIX); | |
| 777 | ||
| 778 | if (flags & EC_NORIGHTLEFT) | |
| 779 | { | |
| 780 | switch (action) | |
| 781 | { | |
| 782 | case EC_RIGHT: | |
| 783 | case EC_LEFT: | |
| 784 | action = A_INVALID; | |
| 785 | break; | |
| 786 | } | |
| 787 | } | |
| 788 | #if CMD_HISTORY | |
| 789 | if (flags & EC_NOHISTORY) | |
| 790 | { | |
| 791 | /* | |
| 792 | * The caller says there is no history list. | |
| 793 | * Reject any history-manipulation action. | |
| 794 | */ | |
| 795 | switch (action) | |
| 796 | { | |
| 797 | case EC_UP: | |
| 798 | case EC_DOWN: | |
| 799 | action = A_INVALID; | |
| 800 | break; | |
| 801 | } | |
| 802 | } | |
| 803 | #endif | |
| 804 | #if TAB_COMPLETE_FILENAME | |
| 805 | if (flags & EC_NOCOMPLETE) | |
| 806 | { | |
| 807 | /* | |
| 808 | * The caller says we don't want any filename completion cmds. | |
| 809 | * Reject them. | |
| 810 | */ | |
| 811 | switch (action) | |
| 812 | { | |
| 813 | case EC_F_COMPLETE: | |
| 814 | case EC_B_COMPLETE: | |
| 815 | case EC_EXPAND: | |
| 816 | action = A_INVALID; | |
| 817 | break; | |
| 818 | } | |
| 819 | } | |
| 820 | #endif | |
| 821 | if ((flags & EC_PEEK) || action == A_INVALID) | |
| 822 | { | |
| 823 | /* | |
| 824 | * We're just peeking, or we didn't understand the command. | |
| 825 | * Unget all the characters we read in the loop above. | |
| 826 | * This does NOT include the original character that was | |
| 827 | * passed in as a parameter. | |
| 828 | */ | |
| 829 | while (nch > 1) | |
| 830 | { | |
| 831 | ungetcc(usercmd[--nch]); | |
| 832 | } | |
| 833 | } else | |
| 834 | { | |
| 835 | if (s != NULL) | |
| 836 | ungetsc(s); | |
| 837 | } | |
| 838 | return action; | |
| 839 | } | |
| 840 |