1 /* $OpenBSD: option.c,v 1.2 2001/01/29 01:58:03 niklas Exp $ */ 2 3 /* 4 * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice in the documentation and/or other materials provided with 14 * the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 22 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 30 /* 31 * Process command line options. 32 * 33 * Each option is a single letter which controls a program variable. 34 * The options have defaults which may be changed via 35 * the command line option, toggled via the "-" command, 36 * or queried via the "_" command. 37 */ 38 39 #include "less.h" 40 #include "option.h" 41 42 static struct option *pendopt; 43 public int plusoption = FALSE; 44 45 static char *propt(); 46 static char *optstring(); 47 static int flip_triple(); 48 49 extern int screen_trashed; 50 extern char *every_first_cmd; 51 52 /* 53 * Scan an argument (either from the command line or from the 54 * LESS environment variable) and process it. 55 */ 56 public void 57 scan_option(s) 58 char *s; 59 { 60 register struct option *o; 61 register int c; 62 char *str; 63 int set_default; 64 PARG parg; 65 66 if (s == NULL) 67 return; 68 69 /* 70 * If we have a pending string-valued option, handle it now. 71 * This happens if the previous option was, for example, "-P" 72 * without a following string. In that case, the current 73 * option is simply the string for the previous option. 74 */ 75 if (pendopt != NULL) 76 { 77 (*pendopt->ofunc)(INIT, s); 78 pendopt = NULL; 79 return; 80 } 81 82 set_default = FALSE; 83 84 while (*s != '\0') 85 { 86 /* 87 * Check some special cases first. 88 */ 89 switch (c = *s++) 90 { 91 case ' ': 92 case '\t': 93 case END_OPTION_STRING: 94 continue; 95 case '-': 96 /* 97 * "-+" means set these options back to their defaults. 98 * (They may have been set otherwise by previous 99 * options.) 100 */ 101 if (set_default = (*s == '+')) 102 s++; 103 continue; 104 case '+': 105 /* 106 * An option prefixed by a "+" is ungotten, so 107 * that it is interpreted as less commands 108 * processed at the start of the first input file. 109 * "++" means process the commands at the start of 110 * EVERY input file. 111 */ 112 plusoption = TRUE; 113 if (*s == '+') 114 every_first_cmd = save(++s); 115 else 116 ungetsc(s); 117 s = optstring(s, c); 118 continue; 119 case '0': case '1': case '2': case '3': case '4': 120 case '5': case '6': case '7': case '8': case '9': 121 /* 122 * Special "more" compatibility form "-<number>" 123 * instead of -z<number> to set the scrolling 124 * window size. 125 */ 126 s--; 127 c = 'z'; 128 break; 129 } 130 131 /* 132 * Not a special case. 133 * Look up the option letter in the option table. 134 */ 135 o = findopt(c); 136 if (o == NULL) 137 { 138 parg.p_string = propt(c); 139 #if MSOFTC || OS2 140 error("There is no %s flag (\"less -?\" for help)", 141 &parg); 142 #else 143 error("There is no %s flag (\"less -\\?\" for help)", 144 &parg); 145 #endif 146 quit(QUIT_ERROR); 147 } 148 149 switch (o->otype & OTYPE) 150 { 151 case BOOL: 152 if (set_default) 153 *(o->ovar) = o->odefault; 154 else 155 *(o->ovar) = ! o->odefault; 156 break; 157 case TRIPLE: 158 if (set_default) 159 *(o->ovar) = o->odefault; 160 else 161 *(o->ovar) = flip_triple(o->odefault, 162 (o->oletter == c)); 163 break; 164 case STRING: 165 if (*s == '\0') 166 { 167 /* 168 * Set pendopt and return. 169 * We will get the string next time 170 * scan_option is called. 171 */ 172 pendopt = o; 173 return; 174 } 175 /* 176 * Don't do anything here. 177 * All processing of STRING options is done by 178 * the handling function. 179 */ 180 str = s; 181 s = optstring(s, c); 182 break; 183 case NUMBER: 184 *(o->ovar) = getnum(&s, c, (int*)NULL); 185 break; 186 } 187 /* 188 * If the option has a handling function, call it. 189 */ 190 if (o->ofunc != NULL) 191 (*o->ofunc)(INIT, str); 192 } 193 } 194 195 /* 196 * Toggle command line flags from within the program. 197 * Used by the "-" and "_" commands. 198 * how_toggle may be: 199 * OPT_NO_TOGGLE just report the current setting, without changing it. 200 * OPT_TOGGLE invert the current setting 201 * OPT_UNSET set to the default value 202 * OPT_SET set to the inverse of the default value 203 */ 204 public void 205 toggle_option(c, s, how_toggle) 206 int c; 207 char *s; 208 int how_toggle; 209 { 210 register struct option *o; 211 register int num; 212 int err; 213 PARG parg; 214 215 /* 216 * Look up the option letter in the option table. 217 */ 218 o = findopt(c); 219 if (o == NULL) 220 { 221 parg.p_string = propt(c); 222 error("There is no %s flag", &parg); 223 return; 224 } 225 226 if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE)) 227 { 228 parg.p_string = propt(c); 229 error("Cannot change the %s flag", &parg); 230 return; 231 } 232 233 if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY)) 234 { 235 parg.p_string = propt(c); 236 error("Cannot query the %s flag", &parg); 237 return; 238 } 239 240 /* 241 * Check for something which appears to be a do_toggle 242 * (because the "-" command was used), but really is not. 243 * This could be a string option with no string, or 244 * a number option with no number. 245 */ 246 switch (o->otype & OTYPE) 247 { 248 case STRING: 249 case NUMBER: 250 if (how_toggle == OPT_TOGGLE && *s == '\0') 251 how_toggle = OPT_NO_TOGGLE; 252 break; 253 } 254 255 #if HILITE_SEARCH 256 if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT)) 257 repaint_hilite(0); 258 #endif 259 260 /* 261 * Now actually toggle (change) the variable. 262 */ 263 if (how_toggle != OPT_NO_TOGGLE) 264 { 265 switch (o->otype & OTYPE) 266 { 267 case BOOL: 268 /* 269 * Boolean. 270 */ 271 switch (how_toggle) 272 { 273 case OPT_TOGGLE: 274 *(o->ovar) = ! *(o->ovar); 275 break; 276 case OPT_UNSET: 277 *(o->ovar) = o->odefault; 278 break; 279 case OPT_SET: 280 *(o->ovar) = ! o->odefault; 281 break; 282 } 283 break; 284 case TRIPLE: 285 /* 286 * Triple: 287 * If user gave the lower case letter, then switch 288 * to 1 unless already 1, in which case make it 0. 289 * If user gave the upper case letter, then switch 290 * to 2 unless already 2, in which case make it 0. 291 */ 292 switch (how_toggle) 293 { 294 case OPT_TOGGLE: 295 *(o->ovar) = flip_triple(*(o->ovar), 296 o->oletter == c); 297 break; 298 case OPT_UNSET: 299 *(o->ovar) = o->odefault; 300 break; 301 case OPT_SET: 302 *(o->ovar) = flip_triple(o->odefault, 303 o->oletter == c); 304 break; 305 } 306 break; 307 case STRING: 308 /* 309 * String: don't do anything here. 310 * The handling function will do everything. 311 */ 312 switch (how_toggle) 313 { 314 case OPT_SET: 315 case OPT_UNSET: 316 error("Can't use \"-+\" or \"--\" for a string flag", 317 NULL_PARG); 318 return; 319 } 320 break; 321 case NUMBER: 322 /* 323 * Number: set the variable to the given number. 324 */ 325 switch (how_toggle) 326 { 327 case OPT_TOGGLE: 328 num = getnum(&s, '\0', &err); 329 if (!err) 330 *(o->ovar) = num; 331 break; 332 case OPT_UNSET: 333 *(o->ovar) = o->odefault; 334 break; 335 case OPT_SET: 336 error("Can't use \"--\" for a numeric flag", 337 NULL_PARG); 338 return; 339 } 340 break; 341 } 342 } 343 344 /* 345 * Call the handling function for any special action 346 * specific to this option. 347 */ 348 if (o->ofunc != NULL) 349 (*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s); 350 351 #if HILITE_SEARCH 352 if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT)) 353 chg_hilite(); 354 #endif 355 356 /* 357 * Print a message describing the new setting. 358 */ 359 switch (o->otype & OTYPE) 360 { 361 case BOOL: 362 case TRIPLE: 363 /* 364 * Print the odesc message. 365 */ 366 error(o->odesc[*(o->ovar)], NULL_PARG); 367 break; 368 case NUMBER: 369 /* 370 * The message is in odesc[1] and has a %d for 371 * the value of the variable. 372 */ 373 parg.p_int = *(o->ovar); 374 error(o->odesc[1], &parg); 375 break; 376 case STRING: 377 /* 378 * Message was already printed by the handling function. 379 */ 380 break; 381 } 382 383 if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT)) 384 screen_trashed = TRUE; 385 } 386 387 /* 388 * "Toggle" a triple-valued option. 389 */ 390 static int 391 flip_triple(val, lc) 392 int val; 393 int lc; 394 { 395 if (lc) 396 return ((val == OPT_ON) ? OPT_OFF : OPT_ON); 397 else 398 return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS); 399 } 400 401 /* 402 * Return a string suitable for printing as the "name" of an option. 403 * For example, if the option letter is 'x', just return "-x". 404 */ 405 static char * 406 propt(c) 407 int c; 408 { 409 static char buf[8]; 410 411 sprintf(buf, "-%s", prchar(c)); 412 return (buf); 413 } 414 415 /* 416 * Determine if an option is a single character option (BOOL or TRIPLE), 417 * or if it a multi-character option (NUMBER). 418 */ 419 public int 420 single_char_option(c) 421 int c; 422 { 423 register struct option *o; 424 425 o = findopt(c); 426 if (o == NULL) 427 return (TRUE); 428 return ((o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE)) != 0); 429 } 430 431 /* 432 * Return the prompt to be used for a given option letter. 433 * Only string and number valued options have prompts. 434 */ 435 public char * 436 opt_prompt(c) 437 int c; 438 { 439 register struct option *o; 440 441 o = findopt(c); 442 if (o == NULL || (o->otype & (STRING|NUMBER)) == 0) 443 return (NULL); 444 return (o->odesc[0]); 445 } 446 447 /* 448 * Return whether or not there is a string option pending; 449 * that is, if the previous option was a string-valued option letter 450 * (like -P) without a following string. 451 * In that case, the current option is taken to be the string for 452 * the previous option. 453 */ 454 public int 455 isoptpending() 456 { 457 return (pendopt != NULL); 458 } 459 460 /* 461 * Print error message about missing string. 462 */ 463 static void 464 nostring(c) 465 int c; 466 { 467 PARG parg; 468 parg.p_string = propt(c); 469 error("String is required after %s", &parg); 470 } 471 472 /* 473 * Print error message if a STRING type option is not followed by a string. 474 */ 475 public void 476 nopendopt() 477 { 478 nostring(pendopt->oletter); 479 } 480 481 /* 482 * Scan to end of string or to an END_OPTION_STRING character. 483 * In the latter case, replace the char with a null char. 484 * Return a pointer to the remainder of the string, if any. 485 */ 486 static char * 487 optstring(s, c) 488 char *s; 489 int c; 490 { 491 register char *p; 492 493 if (*s == '\0') 494 { 495 nostring(c); 496 quit(QUIT_ERROR); 497 } 498 for (p = s; *p != '\0'; p++) 499 if (*p == END_OPTION_STRING) 500 { 501 *p = '\0'; 502 return (p+1); 503 } 504 return (p); 505 } 506 507 /* 508 * Translate a string into a number. 509 * Like atoi(), but takes a pointer to a char *, and updates 510 * the char * to point after the translated number. 511 */ 512 public int 513 getnum(sp, c, errp) 514 char **sp; 515 int c; 516 int *errp; 517 { 518 register char *s; 519 register int n; 520 register int neg; 521 PARG parg; 522 523 s = skipsp(*sp); 524 neg = FALSE; 525 if (*s == '-') 526 { 527 neg = TRUE; 528 s++; 529 } 530 if (*s < '0' || *s > '9') 531 { 532 if (errp != NULL) 533 { 534 *errp = TRUE; 535 return (-1); 536 } 537 parg.p_string = propt(c); 538 error("Number is required after %s", &parg); 539 quit(QUIT_ERROR); 540 } 541 542 n = 0; 543 while (*s >= '0' && *s <= '9') 544 n = 10 * n + *s++ - '0'; 545 *sp = s; 546 if (errp != NULL) 547 *errp = FALSE; 548 if (neg) 549 n = -n; 550 return (n); 551 } 552