1 /* 2 * Copyright (C) 1984-2002 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 about less, or for information on how to 8 * contact the author, see the README file. 9 */ 10 11 12 /* 13 * The option table. 14 */ 15 16 #include "less.h" 17 #include "option.h" 18 19 /* 20 * Variables controlled by command line options. 21 */ 22 public int quiet; /* Should we suppress the audible bell? */ 23 public int how_search; /* Where should forward searches start? */ 24 public int top_scroll; /* Repaint screen from top? 25 (alternative is scroll from bottom) */ 26 public int pr_type; /* Type of prompt (short, medium, long) */ 27 public int bs_mode; /* How to process backspaces */ 28 public int know_dumb; /* Don't complain about dumb terminals */ 29 public int quit_at_eof; /* Quit after hitting end of file twice */ 30 public int quit_if_one_screen; /* Quit if EOF on first screen */ 31 public int squeeze; /* Squeeze multiple blank lines into one */ 32 public int tabstop; /* Tab settings */ 33 public int back_scroll; /* Repaint screen on backwards movement */ 34 public int forw_scroll; /* Repaint screen on forward movement */ 35 public int caseless; /* Do "caseless" searches */ 36 public int linenums; /* Use line numbers */ 37 public int autobuf; /* Automatically allocate buffers as needed */ 38 public int bufspace; /* Max buffer space per file (K) */ 39 public int ctldisp; /* Send control chars to screen untranslated */ 40 public int force_open; /* Open the file even if not regular file */ 41 public int swindow; /* Size of scrolling window */ 42 public int jump_sline; /* Screen line of "jump target" */ 43 public int chopline; /* Truncate displayed lines at screen width */ 44 public int no_init; /* Disable sending ti/te termcap strings */ 45 public int no_keypad; /* Disable sending ks/ke termcap strings */ 46 public int twiddle; /* Show tildes after EOF */ 47 public int show_attn; /* Hilite first unread line */ 48 public int shift_count; /* Number of positions to shift horizontally */ 49 public int status_col; /* Display a status column */ 50 public int use_lessopen; /* Use the LESSOPEN filter */ 51 #if HILITE_SEARCH 52 public int hilite_search; /* Highlight matched search patterns? */ 53 #endif 54 55 /* 56 * Long option names. 57 */ 58 static struct optname a_optname = { "search-skip-screen", NULL }; 59 static struct optname b_optname = { "buffers", NULL }; 60 static struct optname B__optname = { "auto-buffers", NULL }; 61 static struct optname c_optname = { "clear-screen", NULL }; 62 static struct optname d_optname = { "dumb", NULL }; 63 #if MSDOS_COMPILER 64 static struct optname D__optname = { "color", NULL }; 65 #endif 66 static struct optname e_optname = { "quit-at-eof", NULL }; 67 static struct optname f_optname = { "force", NULL }; 68 static struct optname F__optname = { "quit-if-one-screen", NULL }; 69 #if HILITE_SEARCH 70 static struct optname g_optname = { "hilite-search", NULL }; 71 #endif 72 static struct optname h_optname = { "max-back-scroll", NULL }; 73 static struct optname i_optname = { "ignore-case", NULL }; 74 static struct optname j_optname = { "jump-target", NULL }; 75 static struct optname J__optname = { "status-column", NULL }; 76 #if USERFILE 77 static struct optname k_optname = { "lesskey-file", NULL }; 78 #endif 79 static struct optname L__optname = { "no-lessopen", NULL }; 80 static struct optname m_optname = { "long-prompt", NULL }; 81 static struct optname n_optname = { "line-numbers", NULL }; 82 #if LOGFILE 83 static struct optname o_optname = { "log-file", NULL }; 84 static struct optname O__optname = { "LOG-FILE", NULL }; 85 #endif 86 static struct optname p_optname = { "pattern", NULL }; 87 static struct optname P__optname = { "prompt", NULL }; 88 static struct optname q2_optname = { "silent", NULL }; 89 static struct optname q_optname = { "quiet", &q2_optname }; 90 static struct optname r_optname = { "raw-control-chars", NULL }; 91 static struct optname s_optname = { "squeeze-blank-lines", NULL }; 92 static struct optname S__optname = { "chop-long-lines", NULL }; 93 #if TAGS 94 static struct optname t_optname = { "tag", NULL }; 95 static struct optname T__optname = { "tag-file", NULL }; 96 #endif 97 static struct optname u_optname = { "underline-special", NULL }; 98 static struct optname V__optname = { "version", NULL }; 99 static struct optname w_optname = { "hilite-unread", NULL }; 100 static struct optname x_optname = { "tabs", NULL }; 101 static struct optname X__optname = { "no-init", NULL }; 102 static struct optname y_optname = { "max-forw-scroll", NULL }; 103 static struct optname z_optname = { "window", NULL }; 104 static struct optname quote_optname = { "quotes", NULL }; 105 static struct optname tilde_optname = { "tilde", NULL }; 106 static struct optname query_optname = { "help", NULL }; 107 static struct optname pound_optname = { "shift", NULL }; 108 static struct optname keypad_optname = { "no-keypad", NULL }; 109 110 111 /* 112 * Table of all options and their semantics. 113 * 114 * For BOOL and TRIPLE options, odesc[0], odesc[1], odesc[2] are 115 * the description of the option when set to 0, 1 or 2, respectively. 116 * For NUMBER options, odesc[0] is the prompt to use when entering 117 * a new value, and odesc[1] is the description, which should contain 118 * one %d which is replaced by the value of the number. 119 * For STRING options, odesc[0] is the prompt to use when entering 120 * a new value, and odesc[1], if not NULL, is the set of characters 121 * that are valid in the string. 122 */ 123 static struct loption option[] = 124 { 125 { 'a', &a_optname, 126 BOOL, OPT_OFF, &how_search, NULL, 127 { 128 "Search includes displayed screen", 129 "Search skips displayed screen", 130 NULL 131 } 132 }, 133 134 { 'b', &b_optname, 135 NUMBER|INIT_HANDLER, 64, &bufspace, opt_b, 136 { 137 "Max buffer space per file (K): ", 138 "Max buffer space per file: %dK", 139 NULL 140 } 141 }, 142 { 'B', &B__optname, 143 BOOL, OPT_ON, &autobuf, NULL, 144 { 145 "Don't automatically allocate buffers", 146 "Automatically allocate buffers when needed", 147 NULL 148 } 149 }, 150 { 'c', &c_optname, 151 TRIPLE, OPT_OFF, &top_scroll, NULL, 152 { 153 "Repaint by scrolling from bottom of screen", 154 "Repaint by clearing each line", 155 "Repaint by painting from top of screen" 156 } 157 }, 158 { 'd', &d_optname, 159 BOOL|NO_TOGGLE, OPT_OFF, &know_dumb, NULL, 160 { 161 "Assume intelligent terminal", 162 "Assume dumb terminal", 163 NULL 164 } 165 }, 166 #if MSDOS_COMPILER 167 { 'D', &D__optname, 168 STRING|REPAINT|NO_QUERY, 0, NULL, opt_D, 169 { 170 "color desc: ", 171 "Ddknsu0123456789.", 172 NULL 173 } 174 }, 175 #endif 176 { 'e', &e_optname, 177 TRIPLE, OPT_OFF, &quit_at_eof, NULL, 178 { 179 "Don't quit at end-of-file", 180 "Quit at end-of-file", 181 "Quit immediately at end-of-file" 182 } 183 }, 184 { 'f', &f_optname, 185 BOOL, OPT_OFF, &force_open, NULL, 186 { 187 "Open only regular files", 188 "Open even non-regular files", 189 NULL 190 } 191 }, 192 { 'F', &F__optname, 193 BOOL, OPT_OFF, &quit_if_one_screen, NULL, 194 { 195 "Don't quit if end-of-file on first screen", 196 "Quit if end-of-file on first screen", 197 NULL 198 } 199 }, 200 #if HILITE_SEARCH 201 { 'g', &g_optname, 202 TRIPLE|HL_REPAINT, OPT_ONPLUS, &hilite_search, NULL, 203 { 204 "Don't highlight search matches", 205 "Highlight matches for previous search only", 206 "Highlight all matches for previous search pattern", 207 } 208 }, 209 #endif 210 { 'h', &h_optname, 211 NUMBER, -1, &back_scroll, NULL, 212 { 213 "Backwards scroll limit: ", 214 "Backwards scroll limit is %d lines", 215 NULL 216 } 217 }, 218 { 'i', &i_optname, 219 TRIPLE|HL_REPAINT, OPT_OFF, &caseless, opt_i, 220 { 221 "Case is significant in searches", 222 "Ignore case in searches", 223 "Ignore case in searches and in patterns" 224 } 225 }, 226 { 'j', &j_optname, 227 NUMBER, 1, &jump_sline, NULL, 228 { 229 "Target line: ", 230 "Position target at screen line %d", 231 NULL 232 } 233 }, 234 { 'J', &J__optname, 235 BOOL|REPAINT, OPT_OFF, &status_col, NULL, 236 { 237 "Don't display a status column", 238 "Display a status column", 239 NULL 240 } 241 }, 242 #if USERFILE 243 { 'k', &k_optname, 244 STRING|NO_TOGGLE|NO_QUERY, 0, NULL, opt_k, 245 { NULL, NULL, NULL } 246 }, 247 #endif 248 { 'l', NULL, 249 STRING|NO_TOGGLE|NO_QUERY, 0, NULL, opt_l, 250 { NULL, NULL, NULL } 251 }, 252 { 'L', &L__optname, 253 BOOL, OPT_ON, &use_lessopen, NULL, 254 { 255 "Don't use the LESSOPEN filter", 256 "Use the LESSOPEN filter", 257 NULL 258 } 259 }, 260 { 'm', &m_optname, 261 TRIPLE, OPT_OFF, &pr_type, NULL, 262 { 263 "Short prompt", 264 "Medium prompt", 265 "Long prompt" 266 } 267 }, 268 { 'n', &n_optname, 269 TRIPLE|REPAINT, OPT_ON, &linenums, NULL, 270 { 271 "Don't use line numbers", 272 "Use line numbers", 273 "Constantly display line numbers" 274 } 275 }, 276 #if LOGFILE 277 { 'o', &o_optname, 278 STRING, 0, NULL, opt_o, 279 { "log file: ", NULL, NULL } 280 }, 281 { 'O', &O__optname, 282 STRING, 0, NULL, opt__O, 283 { "Log file: ", NULL, NULL } 284 }, 285 #endif 286 { 'p', &p_optname, 287 STRING|NO_TOGGLE|NO_QUERY, 0, NULL, opt_p, 288 { NULL, NULL, NULL } 289 }, 290 { 'P', &P__optname, 291 STRING, 0, NULL, opt__P, 292 { "prompt: ", NULL, NULL } 293 }, 294 { 'q', &q_optname, 295 TRIPLE, OPT_OFF, &quiet, NULL, 296 { 297 "Ring the bell for errors AND at eof/bof", 298 "Ring the bell for errors but not at eof/bof", 299 "Never ring the bell" 300 } 301 }, 302 { 'r', &r_optname, 303 TRIPLE|REPAINT, OPT_OFF, &ctldisp, NULL, 304 { 305 "Display control characters as ^X", 306 "Display control characters directly", 307 "Display control characters directly, processing ANSI sequences" 308 } 309 }, 310 { 's', &s_optname, 311 BOOL|REPAINT, OPT_OFF, &squeeze, NULL, 312 { 313 "Display all blank lines", 314 "Squeeze multiple blank lines", 315 NULL 316 } 317 }, 318 { 'S', &S__optname, 319 BOOL|REPAINT, OPT_OFF, &chopline, NULL, 320 { 321 "Fold long lines", 322 "Chop long lines", 323 NULL 324 } 325 }, 326 #if TAGS 327 { 't', &t_optname, 328 STRING|NO_QUERY, 0, NULL, opt_t, 329 { "tag: ", NULL, NULL } 330 }, 331 { 'T', &T__optname, 332 STRING, 0, NULL, opt__T, 333 { "tags file: ", NULL, NULL } 334 }, 335 #endif 336 { 'u', &u_optname, 337 TRIPLE|REPAINT, OPT_OFF, &bs_mode, NULL, 338 { 339 "Display underlined text in underline mode", 340 "Backspaces cause overstrike", 341 "Print backspace as ^H" 342 } 343 }, 344 { 'V', &V__optname, 345 NOVAR, 0, NULL, opt__V, 346 { NULL, NULL, NULL } 347 }, 348 { 'w', &w_optname, 349 TRIPLE|REPAINT, OPT_OFF, &show_attn, NULL, 350 { 351 "Don't highlight first unread line", 352 "Highlight first unread line after forward-screen", 353 "Highlight first unread line after any forward movement", 354 } 355 }, 356 { 'x', &x_optname, 357 STRING|REPAINT, 0, NULL, opt_x, 358 { 359 "Tab stops: ", 360 "0123456789,", 361 NULL 362 } 363 }, 364 { 'X', &X__optname, 365 BOOL|NO_TOGGLE, OPT_OFF, &no_init, NULL, 366 { 367 "Send init/deinit strings to terminal", 368 "Don't use init/deinit strings", 369 NULL 370 } 371 }, 372 { 'y', &y_optname, 373 NUMBER, -1, &forw_scroll, NULL, 374 { 375 "Forward scroll limit: ", 376 "Forward scroll limit is %d lines", 377 NULL 378 } 379 }, 380 { 'z', &z_optname, 381 NUMBER, -1, &swindow, NULL, 382 { 383 "Scroll window size: ", 384 "Scroll window size is %d lines", 385 NULL 386 } 387 }, 388 { '"', "e_optname, 389 STRING, 0, NULL, opt_quote, 390 { "quotes: ", NULL, NULL } 391 }, 392 { '~', &tilde_optname, 393 BOOL|REPAINT, OPT_ON, &twiddle, NULL, 394 { 395 "Don't show tildes after end of file", 396 "Show tildes after end of file", 397 NULL 398 } 399 }, 400 { '?', &query_optname, 401 NOVAR, 0, NULL, opt_query, 402 { NULL, NULL, NULL } 403 }, 404 { '#', £_optname, 405 NUMBER, 0, &shift_count, NULL, 406 { 407 "Horizontal shift: ", 408 "Horizontal shift %d positions", 409 NULL 410 } 411 }, 412 { '.', &keypad_optname, 413 BOOL|NO_TOGGLE, OPT_OFF, &no_keypad, NULL, 414 { 415 "Use keypad mode", 416 "Don't use keypad mode", 417 NULL 418 } 419 }, 420 { '\0', NULL, NOVAR, 0, NULL, NULL, { NULL, NULL, NULL } } 421 }; 422 423 424 /* 425 * Initialize each option to its default value. 426 */ 427 public void 428 init_option() 429 { 430 register struct loption *o; 431 432 for (o = option; o->oletter != '\0'; o++) 433 { 434 /* 435 * Set each variable to its default. 436 */ 437 if (o->ovar != NULL) 438 *(o->ovar) = o->odefault; 439 if (o->otype & INIT_HANDLER) 440 (*(o->ofunc))(INIT, (char *) NULL); 441 } 442 } 443 444 /* 445 * Find an option in the option table, given its option letter. 446 */ 447 public struct loption * 448 findopt(c) 449 int c; 450 { 451 register struct loption *o; 452 453 for (o = option; o->oletter != '\0'; o++) 454 { 455 if (o->oletter == c) 456 return (o); 457 if ((o->otype & TRIPLE) && toupper(o->oletter) == c) 458 return (o); 459 } 460 return (NULL); 461 } 462 463 /* 464 * 465 */ 466 static int 467 is_optchar(c) 468 char c; 469 { 470 if (SIMPLE_IS_UPPER(c)) 471 return 1; 472 if (SIMPLE_IS_LOWER(c)) 473 return 1; 474 if (c == '-') 475 return 1; 476 return 0; 477 } 478 479 /* 480 * Find an option in the option table, given its option name. 481 * p_optname is the (possibly partial) name to look for, and 482 * is updated to point after the matched name. 483 * p_oname if non-NULL is set to point to the full option name. 484 */ 485 public struct loption * 486 findopt_name(p_optname, p_oname, p_err) 487 char **p_optname; 488 char **p_oname; 489 int *p_err; 490 { 491 char *optname = *p_optname; 492 register struct loption *o; 493 register struct optname *oname; 494 register int len; 495 int uppercase; 496 struct loption *maxo = NULL; 497 struct optname *maxoname = NULL; 498 int maxlen = 0; 499 int ambig = 0; 500 int exact = 0; 501 char *eq; 502 503 /* 504 * Check all options. 505 */ 506 for (o = option; o->oletter != '\0'; o++) 507 { 508 /* 509 * Check all names for this option. 510 */ 511 for (oname = o->onames; oname != NULL; oname = oname->onext) 512 { 513 /* 514 * Try normal match first (uppercase == 0), 515 * then, then if it's a TRIPLE option, 516 * try uppercase match (uppercase == 1). 517 */ 518 for (uppercase = 0; uppercase <= 1; uppercase++) 519 { 520 len = sprefix(optname, oname->oname, uppercase); 521 if (len <= 0 || is_optchar(optname[len])) 522 { 523 /* 524 * We didn't use all of the option name. 525 */ 526 continue; 527 } 528 if (!exact && len == maxlen) 529 /* 530 * Already had a partial match, 531 * and now there's another one that 532 * matches the same length. 533 */ 534 ambig = 1; 535 else if (len > maxlen) 536 { 537 /* 538 * Found a better match than 539 * the one we had. 540 */ 541 maxo = o; 542 maxoname = oname; 543 maxlen = len; 544 ambig = 0; 545 exact = (len == (int)strlen(oname->oname)); 546 } 547 if (!(o->otype & TRIPLE)) 548 break; 549 } 550 } 551 } 552 if (ambig) 553 { 554 /* 555 * Name matched more than one option. 556 */ 557 if (p_err != NULL) 558 *p_err = OPT_AMBIG; 559 return (NULL); 560 } 561 *p_optname = optname + maxlen; 562 if (p_oname != NULL) 563 *p_oname = maxoname == NULL ? NULL : maxoname->oname; 564 return (maxo); 565 } 566