1 /* 2 * Copyright (C) 1984-2015 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 * Handling functions for command line options. 13 * 14 * Most options are handled by the generic code in option.c. 15 * But all string options, and a few non-string options, require 16 * special handling specific to the particular option. 17 * This special processing is done by the "handling functions" in this file. 18 * 19 * Each handling function is passed a "type" and, if it is a string 20 * option, the string which should be "assigned" to the option. 21 * The type may be one of: 22 * INIT The option is being initialized from the command line. 23 * TOGGLE The option is being changed from within the program. 24 * QUERY The setting of the option is merely being queried. 25 */ 26 27 #include "less.h" 28 #include "option.h" 29 30 extern int nbufs; 31 extern int bufspace; 32 extern int pr_type; 33 extern int plusoption; 34 extern int swindow; 35 extern int sc_width; 36 extern int sc_height; 37 extern int secure; 38 extern int dohelp; 39 extern int any_display; 40 extern char openquote; 41 extern char closequote; 42 extern char *prproto[]; 43 extern char *eqproto; 44 extern char *hproto; 45 extern char *wproto; 46 extern char *every_first_cmd; 47 extern IFILE curr_ifile; 48 extern char version[]; 49 extern int jump_sline; 50 extern int jump_sline_fraction; 51 extern int shift_count; 52 extern int shift_count_fraction; 53 extern int less_is_more; 54 #if LOGFILE 55 extern char *namelogfile; 56 extern int force_logfile; 57 extern int logfile; 58 #endif 59 #if TAGS 60 public char *tagoption = NULL; 61 extern char *tags; 62 extern char ztags[]; 63 #endif 64 #if MSDOS_COMPILER 65 extern int nm_fg_color, nm_bg_color; 66 extern int bo_fg_color, bo_bg_color; 67 extern int ul_fg_color, ul_bg_color; 68 extern int so_fg_color, so_bg_color; 69 extern int bl_fg_color, bl_bg_color; 70 #endif 71 72 73 #if LOGFILE 74 /* 75 * Handler for -o option. 76 */ 77 public void 78 opt_o(int type, char *s) 79 { 80 PARG parg; 81 82 if (secure) 83 { 84 error("log file support is not available", NULL_PARG); 85 return; 86 } 87 switch (type) 88 { 89 case INIT: 90 namelogfile = save(s); 91 break; 92 case TOGGLE: 93 if (ch_getflags() & CH_CANSEEK) 94 { 95 error("Input is not a pipe", NULL_PARG); 96 return; 97 } 98 if (logfile >= 0) 99 { 100 error("Log file is already in use", NULL_PARG); 101 return; 102 } 103 s = skipsp(s); 104 if (namelogfile != NULL) 105 free(namelogfile); 106 namelogfile = lglob(s); 107 use_logfile(namelogfile); 108 sync_logfile(); 109 break; 110 case QUERY: 111 if (logfile < 0) 112 error("No log file", NULL_PARG); 113 else 114 { 115 parg.p_string = namelogfile; 116 error("Log file \"%s\"", &parg); 117 } 118 break; 119 } 120 } 121 122 /* 123 * Handler for -O option. 124 */ 125 public void 126 opt__O(int type, char *s) 127 { 128 force_logfile = TRUE; 129 opt_o(type, s); 130 } 131 #endif 132 133 /* 134 * Handlers for -j option. 135 */ 136 public void 137 opt_j(int type, char *s) 138 { 139 PARG parg; 140 char buf[16]; 141 int len; 142 int err; 143 144 switch (type) 145 { 146 case INIT: 147 case TOGGLE: 148 if (*s == '.') 149 { 150 s++; 151 jump_sline_fraction = getfraction(&s, "j", &err); 152 if (err) 153 error("Invalid line fraction", NULL_PARG); 154 else 155 calc_jump_sline(); 156 } else 157 { 158 int sline = getnum(&s, "j", &err); 159 if (err) 160 error("Invalid line number", NULL_PARG); 161 else 162 { 163 jump_sline = sline; 164 jump_sline_fraction = -1; 165 } 166 } 167 break; 168 case QUERY: 169 if (jump_sline_fraction < 0) 170 { 171 parg.p_int = jump_sline; 172 error("Position target at screen line %d", &parg); 173 } else 174 { 175 176 sprintf(buf, ".%06d", jump_sline_fraction); 177 len = (int) strlen(buf); 178 while (len > 2 && buf[len-1] == '0') 179 len--; 180 buf[len] = '\0'; 181 parg.p_string = buf; 182 error("Position target at screen position %s", &parg); 183 } 184 break; 185 } 186 } 187 188 public void 189 calc_jump_sline(void) 190 { 191 if (jump_sline_fraction < 0) 192 return; 193 jump_sline = sc_height * jump_sline_fraction / NUM_FRAC_DENOM; 194 } 195 196 /* 197 * Handlers for -# option. 198 */ 199 public void 200 opt_shift(int type, char *s) 201 { 202 PARG parg; 203 char buf[16]; 204 int len; 205 int err; 206 207 switch (type) 208 { 209 case INIT: 210 case TOGGLE: 211 if (*s == '.') 212 { 213 s++; 214 shift_count_fraction = getfraction(&s, "#", &err); 215 if (err) 216 error("Invalid column fraction", NULL_PARG); 217 else 218 calc_shift_count(); 219 } else 220 { 221 int hs = getnum(&s, "#", &err); 222 if (err) 223 error("Invalid column number", NULL_PARG); 224 else 225 { 226 shift_count = hs; 227 shift_count_fraction = -1; 228 } 229 } 230 break; 231 case QUERY: 232 if (shift_count_fraction < 0) 233 { 234 parg.p_int = shift_count; 235 error("Horizontal shift %d columns", &parg); 236 } else 237 { 238 239 sprintf(buf, ".%06d", shift_count_fraction); 240 len = (int) strlen(buf); 241 while (len > 2 && buf[len-1] == '0') 242 len--; 243 buf[len] = '\0'; 244 parg.p_string = buf; 245 error("Horizontal shift %s of screen width", &parg); 246 } 247 break; 248 } 249 } 250 public void 251 calc_shift_count(void) 252 { 253 if (shift_count_fraction < 0) 254 return; 255 shift_count = sc_width * shift_count_fraction / NUM_FRAC_DENOM; 256 } 257 258 #if USERFILE 259 public void 260 opt_k(int type, char *s) 261 { 262 PARG parg; 263 264 switch (type) 265 { 266 case INIT: 267 if (lesskey(s, 0)) 268 { 269 parg.p_string = s; 270 error("Cannot use lesskey file \"%s\"", &parg); 271 } 272 break; 273 } 274 } 275 #endif 276 277 #if TAGS 278 /* 279 * Handler for -t option. 280 */ 281 public void 282 opt_t(int type, char *s) 283 { 284 IFILE save_ifile; 285 POSITION pos; 286 287 switch (type) 288 { 289 case INIT: 290 tagoption = save(s); 291 /* Do the rest in main() */ 292 break; 293 case TOGGLE: 294 if (secure) 295 { 296 error("tags support is not available", NULL_PARG); 297 break; 298 } 299 findtag(skipsp(s)); 300 save_ifile = save_curr_ifile(); 301 /* 302 * Try to open the file containing the tag 303 * and search for the tag in that file. 304 */ 305 if (edit_tagfile() || (pos = tagsearch()) == NULL_POSITION) 306 { 307 /* Failed: reopen the old file. */ 308 reedit_ifile(save_ifile); 309 break; 310 } 311 unsave_ifile(save_ifile); 312 jump_loc(pos, jump_sline); 313 break; 314 } 315 } 316 317 /* 318 * Handler for -T option. 319 */ 320 public void 321 opt__T(int type, char *s) 322 { 323 PARG parg; 324 325 switch (type) 326 { 327 case INIT: 328 tags = save(s); 329 break; 330 case TOGGLE: 331 s = skipsp(s); 332 if (tags != NULL && tags != ztags) 333 free(tags); 334 tags = lglob(s); 335 break; 336 case QUERY: 337 parg.p_string = tags; 338 error("Tags file \"%s\"", &parg); 339 break; 340 } 341 } 342 #endif 343 344 /* 345 * Handler for -p option. 346 */ 347 public void 348 opt_p(int type, char *s) 349 { 350 switch (type) 351 { 352 case INIT: 353 /* 354 * Unget a command for the specified string. 355 */ 356 if (less_is_more) 357 { 358 /* 359 * In "more" mode, the -p argument is a command, 360 * not a search string, so we don't need a slash. 361 */ 362 every_first_cmd = save(s); 363 } else 364 { 365 plusoption = TRUE; 366 ungetcc(CHAR_END_COMMAND); 367 ungetsc(s); 368 /* 369 * {{ This won't work if the "/" command is 370 * changed or invalidated by a .lesskey file. }} 371 */ 372 ungetsc("/"); 373 } 374 break; 375 } 376 } 377 378 /* 379 * Handler for -P option. 380 */ 381 public void 382 opt__P(int type, char *s) 383 { 384 char **proto; 385 PARG parg; 386 387 switch (type) 388 { 389 case INIT: 390 case TOGGLE: 391 /* 392 * Figure out which prototype string should be changed. 393 */ 394 switch (*s) 395 { 396 case 's': proto = &prproto[PR_SHORT]; s++; break; 397 case 'm': proto = &prproto[PR_MEDIUM]; s++; break; 398 case 'M': proto = &prproto[PR_LONG]; s++; break; 399 case '=': proto = &eqproto; s++; break; 400 case 'h': proto = &hproto; s++; break; 401 case 'w': proto = &wproto; s++; break; 402 default: proto = &prproto[PR_SHORT]; break; 403 } 404 free(*proto); 405 *proto = save(s); 406 break; 407 case QUERY: 408 parg.p_string = prproto[pr_type]; 409 error("%s", &parg); 410 break; 411 } 412 } 413 414 /* 415 * Handler for the -b option. 416 */ 417 /*ARGSUSED*/ 418 public void 419 opt_b(int type, char *s) 420 { 421 switch (type) 422 { 423 case INIT: 424 case TOGGLE: 425 /* 426 * Set the new number of buffers. 427 */ 428 ch_setbufspace(bufspace); 429 break; 430 case QUERY: 431 break; 432 } 433 } 434 435 /* 436 * Handler for the -i option. 437 */ 438 /*ARGSUSED*/ 439 public void 440 opt_i(int type, char *s) 441 { 442 switch (type) 443 { 444 case TOGGLE: 445 chg_caseless(); 446 break; 447 case QUERY: 448 case INIT: 449 break; 450 } 451 } 452 453 /* 454 * Handler for the -V option. 455 */ 456 /*ARGSUSED*/ 457 public void 458 opt__V(int type, char *s) 459 { 460 switch (type) 461 { 462 case TOGGLE: 463 case QUERY: 464 dispversion(); 465 break; 466 case INIT: 467 /* 468 * Force output to stdout per GNU standard for --version output. 469 */ 470 any_display = 1; 471 putstr("less "); 472 putstr(version); 473 putstr(" ("); 474 #if HAVE_GNU_REGEX 475 putstr("GNU "); 476 #endif 477 #if HAVE_POSIX_REGCOMP 478 putstr("POSIX "); 479 #endif 480 #if HAVE_PCRE 481 putstr("PCRE "); 482 #endif 483 #if HAVE_RE_COMP 484 putstr("BSD "); 485 #endif 486 #if HAVE_REGCMP 487 putstr("V8 "); 488 #endif 489 #if HAVE_V8_REGCOMP 490 putstr("Spencer V8 "); 491 #endif 492 #if !HAVE_GNU_REGEX && !HAVE_POSIX_REGCOMP && !HAVE_PCRE && !HAVE_RE_COMP && !HAVE_REGCMP && !HAVE_V8_REGCOMP 493 putstr("no "); 494 #endif 495 putstr("regular expressions)\n"); 496 putstr("Copyright (C) 1984-2015 Mark Nudelman\n\n"); 497 putstr("less comes with NO WARRANTY, to the extent permitted by law.\n"); 498 putstr("For information about the terms of redistribution,\n"); 499 putstr("see the file named README in the less distribution.\n"); 500 putstr("Homepage: http://www.greenwoodsoftware.com/less\n"); 501 quit(QUIT_OK); 502 break; 503 } 504 } 505 506 #if MSDOS_COMPILER 507 /* 508 * Parse an MSDOS color descriptor. 509 */ 510 static void 511 colordesc(char *s, int *fg_color, int *bg_color) 512 { 513 int fg, bg; 514 int err; 515 516 fg = getnum(&s, "D", &err); 517 if (err) 518 { 519 error("Missing fg color in -D", NULL_PARG); 520 return; 521 } 522 if (*s != '.') 523 bg = nm_bg_color; 524 else 525 { 526 s++; 527 bg = getnum(&s, "D", &err); 528 if (err) 529 { 530 error("Missing bg color in -D", NULL_PARG); 531 return; 532 } 533 } 534 if (*s != '\0') 535 error("Extra characters at end of -D option", NULL_PARG); 536 *fg_color = fg; 537 *bg_color = bg; 538 } 539 540 /* 541 * Handler for the -D option. 542 */ 543 /*ARGSUSED*/ 544 public void 545 opt_D(int type, char *s) 546 { 547 switch (type) 548 { 549 case INIT: 550 case TOGGLE: 551 switch (*s++) 552 { 553 case 'n': 554 colordesc(s, &nm_fg_color, &nm_bg_color); 555 break; 556 case 'd': 557 colordesc(s, &bo_fg_color, &bo_bg_color); 558 break; 559 case 'u': 560 colordesc(s, &ul_fg_color, &ul_bg_color); 561 break; 562 case 'k': 563 colordesc(s, &bl_fg_color, &bl_bg_color); 564 break; 565 case 's': 566 colordesc(s, &so_fg_color, &so_bg_color); 567 break; 568 default: 569 error("-D must be followed by n, d, u, k or s", NULL_PARG); 570 break; 571 } 572 if (type == TOGGLE) 573 { 574 at_enter(AT_STANDOUT); 575 at_exit(); 576 } 577 break; 578 case QUERY: 579 break; 580 } 581 } 582 #endif 583 584 /* 585 * Handler for the -x option. 586 */ 587 public void 588 opt_x(int type, char *s) 589 { 590 extern int tabstops[]; 591 extern int ntabstops; 592 extern int tabdefault; 593 char msg[60+(4*TABSTOP_MAX)]; 594 int i; 595 PARG p; 596 597 switch (type) 598 { 599 case INIT: 600 case TOGGLE: 601 /* Start at 1 because tabstops[0] is always zero. */ 602 for (i = 1; i < TABSTOP_MAX; ) 603 { 604 int n = 0; 605 s = skipsp(s); 606 while (*s >= '0' && *s <= '9') 607 n = (10 * n) + (*s++ - '0'); 608 if (n > tabstops[i-1]) 609 tabstops[i++] = n; 610 s = skipsp(s); 611 if (*s++ != ',') 612 break; 613 } 614 if (i < 2) 615 return; 616 ntabstops = i; 617 tabdefault = tabstops[ntabstops-1] - tabstops[ntabstops-2]; 618 break; 619 case QUERY: 620 strcpy(msg, "Tab stops "); 621 if (ntabstops > 2) 622 { 623 for (i = 1; i < ntabstops; i++) 624 { 625 if (i > 1) 626 strcat(msg, ","); 627 sprintf(msg+strlen(msg), "%d", tabstops[i]); 628 } 629 sprintf(msg+strlen(msg), " and then "); 630 } 631 sprintf(msg+strlen(msg), "every %d spaces", 632 tabdefault); 633 p.p_string = msg; 634 error("%s", &p); 635 break; 636 } 637 } 638 639 640 /* 641 * Handler for the -" option. 642 */ 643 public void 644 opt_quote(int type, char *s) 645 { 646 char buf[3]; 647 PARG parg; 648 649 switch (type) 650 { 651 case INIT: 652 case TOGGLE: 653 if (s[0] == '\0') 654 { 655 openquote = closequote = '\0'; 656 break; 657 } 658 if (s[1] != '\0' && s[2] != '\0') 659 { 660 error("-\" must be followed by 1 or 2 chars", NULL_PARG); 661 return; 662 } 663 openquote = s[0]; 664 if (s[1] == '\0') 665 closequote = openquote; 666 else 667 closequote = s[1]; 668 break; 669 case QUERY: 670 buf[0] = openquote; 671 buf[1] = closequote; 672 buf[2] = '\0'; 673 parg.p_string = buf; 674 error("quotes %s", &parg); 675 break; 676 } 677 } 678 679 /* 680 * "-?" means display a help message. 681 * If from the command line, exit immediately. 682 */ 683 /*ARGSUSED*/ 684 public void 685 opt_query(int type, char *s) 686 { 687 switch (type) 688 { 689 case QUERY: 690 case TOGGLE: 691 error("Use \"h\" for help", NULL_PARG); 692 break; 693 case INIT: 694 dohelp = 1; 695 } 696 } 697 698 /* 699 * Get the "screen window" size. 700 */ 701 public int 702 get_swindow(void) 703 { 704 if (swindow > 0) 705 return (swindow); 706 return (sc_height + swindow); 707 } 708 709