diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2012-07-21 20:40:00 +0000 |
---|---|---|
committer | Lorry <lorry@roadtrain.codethink.co.uk> | 2012-09-24 16:52:48 +0000 |
commit | 09a405d8f652b56944c93ebf5c673cdfe5319b04 (patch) | |
tree | 9cc4518b0a21096735b20ac3204a6fa032f1c566 /option.c | |
download | less-master.tar.gz |
Imported from /srv/lorry/lorry-area/less/less-451.tar.gz.HEADless-451masterbaserock/morph
Diffstat (limited to 'option.c')
-rwxr-xr-x | option.c | 701 |
1 files changed, 701 insertions, 0 deletions
diff --git a/option.c b/option.c new file mode 100755 index 0000000..22de614 --- /dev/null +++ b/option.c @@ -0,0 +1,701 @@ +/* + * Copyright (C) 1984-2012 Mark Nudelman + * + * You may distribute under the terms of either the GNU General Public + * License or the Less License, as specified in the README file. + * + * For more information, see the README file. + */ + + +/* + * Process command line options. + * + * Each option is a single letter which controls a program variable. + * The options have defaults which may be changed via + * the command line option, toggled via the "-" command, + * or queried via the "_" command. + */ + +#include "less.h" +#include "option.h" + +static struct loption *pendopt; +public int plusoption = FALSE; + +static char *optstring(); +static int flip_triple(); + +extern int screen_trashed; +extern int less_is_more; +extern int quit_at_eof; +extern char *every_first_cmd; + +/* + * Return a printable description of an option. + */ + static char * +opt_desc(o) + struct loption *o; +{ + static char buf[OPTNAME_MAX + 10]; + if (o->oletter == OLETTER_NONE) + SNPRINTF1(buf, sizeof(buf), "--%s", o->onames->oname); + else + SNPRINTF2(buf, sizeof(buf), "-%c (--%s)", o->oletter, o->onames->oname); + return (buf); +} + +/* + * Return a string suitable for printing as the "name" of an option. + * For example, if the option letter is 'x', just return "-x". + */ + public char * +propt(c) + int c; +{ + static char buf[8]; + + sprintf(buf, "-%s", prchar(c)); + return (buf); +} + +/* + * Scan an argument (either from the command line or from the + * LESS environment variable) and process it. + */ + public void +scan_option(s) + char *s; +{ + register struct loption *o; + register int optc; + char *optname; + char *printopt; + char *str; + int set_default; + int lc; + int err; + PARG parg; + + if (s == NULL) + return; + + /* + * If we have a pending option which requires an argument, + * handle it now. + * This happens if the previous option was, for example, "-P" + * without a following string. In that case, the current + * option is simply the argument for the previous option. + */ + if (pendopt != NULL) + { + switch (pendopt->otype & OTYPE) + { + case STRING: + (*pendopt->ofunc)(INIT, s); + break; + case NUMBER: + printopt = opt_desc(pendopt); + *(pendopt->ovar) = getnum(&s, printopt, (int*)NULL); + break; + } + pendopt = NULL; + return; + } + + set_default = FALSE; + optname = NULL; + + while (*s != '\0') + { + /* + * Check some special cases first. + */ + switch (optc = *s++) + { + case ' ': + case '\t': + case END_OPTION_STRING: + continue; + case '-': + /* + * "--" indicates an option name instead of a letter. + */ + if (*s == '-') + { + optname = ++s; + break; + } + /* + * "-+" means set these options back to their defaults. + * (They may have been set otherwise by previous + * options.) + */ + set_default = (*s == '+'); + if (set_default) + s++; + continue; + case '+': + /* + * An option prefixed by a "+" is ungotten, so + * that it is interpreted as less commands + * processed at the start of the first input file. + * "++" means process the commands at the start of + * EVERY input file. + */ + plusoption = TRUE; + s = optstring(s, &str, propt('+'), NULL); + if (*str == '+') + every_first_cmd = save(++str); + else + ungetsc(str); + continue; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + /* + * Special "more" compatibility form "-<number>" + * instead of -z<number> to set the scrolling + * window size. + */ + s--; + optc = 'z'; + break; + case 'n': + if (less_is_more) + optc = 'z'; + break; + } + + /* + * Not a special case. + * Look up the option letter in the option table. + */ + err = 0; + if (optname == NULL) + { + printopt = propt(optc); + lc = ASCII_IS_LOWER(optc); + o = findopt(optc); + } else + { + printopt = optname; + lc = ASCII_IS_LOWER(optname[0]); + o = findopt_name(&optname, NULL, &err); + s = optname; + optname = NULL; + if (*s == '\0' || *s == ' ') + { + /* + * The option name matches exactly. + */ + ; + } else if (*s == '=') + { + /* + * The option name is followed by "=value". + */ + if (o != NULL && + (o->otype & OTYPE) != STRING && + (o->otype & OTYPE) != NUMBER) + { + parg.p_string = printopt; + error("The %s option should not be followed by =", + &parg); + quit(QUIT_ERROR); + } + s++; + } else + { + /* + * The specified name is longer than the + * real option name. + */ + o = NULL; + } + } + if (o == NULL) + { + parg.p_string = printopt; + if (err == OPT_AMBIG) + error("%s is an ambiguous abbreviation (\"less --help\" for help)", + &parg); + else + error("There is no %s option (\"less --help\" for help)", + &parg); + quit(QUIT_ERROR); + } + + str = NULL; + switch (o->otype & OTYPE) + { + case BOOL: + if (set_default) + *(o->ovar) = o->odefault; + else + *(o->ovar) = ! o->odefault; + break; + case TRIPLE: + if (set_default) + *(o->ovar) = o->odefault; + else + *(o->ovar) = flip_triple(o->odefault, lc); + break; + case STRING: + if (*s == '\0') + { + /* + * Set pendopt and return. + * We will get the string next time + * scan_option is called. + */ + pendopt = o; + return; + } + /* + * Don't do anything here. + * All processing of STRING options is done by + * the handling function. + */ + while (*s == ' ') + s++; + s = optstring(s, &str, printopt, o->odesc[1]); + break; + case NUMBER: + if (*s == '\0') + { + pendopt = o; + return; + } + *(o->ovar) = getnum(&s, printopt, (int*)NULL); + break; + } + /* + * If the option has a handling function, call it. + */ + if (o->ofunc != NULL) + (*o->ofunc)(INIT, str); + } +} + +/* + * Toggle command line flags from within the program. + * Used by the "-" and "_" commands. + * how_toggle may be: + * OPT_NO_TOGGLE just report the current setting, without changing it. + * OPT_TOGGLE invert the current setting + * OPT_UNSET set to the default value + * OPT_SET set to the inverse of the default value + */ + public void +toggle_option(o, lower, s, how_toggle) + struct loption *o; + int lower; + char *s; + int how_toggle; +{ + register int num; + int no_prompt; + int err; + PARG parg; + + no_prompt = (how_toggle & OPT_NO_PROMPT); + how_toggle &= ~OPT_NO_PROMPT; + + if (o == NULL) + { + error("No such option", NULL_PARG); + return; + } + + if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE)) + { + parg.p_string = opt_desc(o); + error("Cannot change the %s option", &parg); + return; + } + + if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY)) + { + parg.p_string = opt_desc(o); + error("Cannot query the %s option", &parg); + return; + } + + /* + * Check for something which appears to be a do_toggle + * (because the "-" command was used), but really is not. + * This could be a string option with no string, or + * a number option with no number. + */ + switch (o->otype & OTYPE) + { + case STRING: + case NUMBER: + if (how_toggle == OPT_TOGGLE && *s == '\0') + how_toggle = OPT_NO_TOGGLE; + break; + } + +#if HILITE_SEARCH + if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT)) + repaint_hilite(0); +#endif + + /* + * Now actually toggle (change) the variable. + */ + if (how_toggle != OPT_NO_TOGGLE) + { + switch (o->otype & OTYPE) + { + case BOOL: + /* + * Boolean. + */ + switch (how_toggle) + { + case OPT_TOGGLE: + *(o->ovar) = ! *(o->ovar); + break; + case OPT_UNSET: + *(o->ovar) = o->odefault; + break; + case OPT_SET: + *(o->ovar) = ! o->odefault; + break; + } + break; + case TRIPLE: + /* + * Triple: + * If user gave the lower case letter, then switch + * to 1 unless already 1, in which case make it 0. + * If user gave the upper case letter, then switch + * to 2 unless already 2, in which case make it 0. + */ + switch (how_toggle) + { + case OPT_TOGGLE: + *(o->ovar) = flip_triple(*(o->ovar), lower); + break; + case OPT_UNSET: + *(o->ovar) = o->odefault; + break; + case OPT_SET: + *(o->ovar) = flip_triple(o->odefault, lower); + break; + } + break; + case STRING: + /* + * String: don't do anything here. + * The handling function will do everything. + */ + switch (how_toggle) + { + case OPT_SET: + case OPT_UNSET: + error("Cannot use \"-+\" or \"--\" for a string option", + NULL_PARG); + return; + } + break; + case NUMBER: + /* + * Number: set the variable to the given number. + */ + switch (how_toggle) + { + case OPT_TOGGLE: + num = getnum(&s, NULL, &err); + if (!err) + *(o->ovar) = num; + break; + case OPT_UNSET: + *(o->ovar) = o->odefault; + break; + case OPT_SET: + error("Can't use \"-!\" for a numeric option", + NULL_PARG); + return; + } + break; + } + } + + /* + * Call the handling function for any special action + * specific to this option. + */ + if (o->ofunc != NULL) + (*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s); + +#if HILITE_SEARCH + if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT)) + chg_hilite(); +#endif + + if (!no_prompt) + { + /* + * Print a message describing the new setting. + */ + switch (o->otype & OTYPE) + { + case BOOL: + case TRIPLE: + /* + * Print the odesc message. + */ + error(o->odesc[*(o->ovar)], NULL_PARG); + break; + case NUMBER: + /* + * The message is in odesc[1] and has a %d for + * the value of the variable. + */ + parg.p_int = *(o->ovar); + error(o->odesc[1], &parg); + break; + case STRING: + /* + * Message was already printed by the handling function. + */ + break; + } + } + + if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT)) + screen_trashed = TRUE; +} + +/* + * "Toggle" a triple-valued option. + */ + static int +flip_triple(val, lc) + int val; + int lc; +{ + if (lc) + return ((val == OPT_ON) ? OPT_OFF : OPT_ON); + else + return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS); +} + +/* + * Determine if an option takes a parameter. + */ + public int +opt_has_param(o) + struct loption *o; +{ + if (o == NULL) + return (0); + if (o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE)) + return (0); + return (1); +} + +/* + * Return the prompt to be used for a given option letter. + * Only string and number valued options have prompts. + */ + public char * +opt_prompt(o) + struct loption *o; +{ + if (o == NULL || (o->otype & (STRING|NUMBER)) == 0) + return ("?"); + return (o->odesc[0]); +} + +/* + * Return whether or not there is a string option pending; + * that is, if the previous option was a string-valued option letter + * (like -P) without a following string. + * In that case, the current option is taken to be the string for + * the previous option. + */ + public int +isoptpending() +{ + return (pendopt != NULL); +} + +/* + * Print error message about missing string. + */ + static void +nostring(printopt) + char *printopt; +{ + PARG parg; + parg.p_string = printopt; + error("Value is required after %s", &parg); +} + +/* + * Print error message if a STRING type option is not followed by a string. + */ + public void +nopendopt() +{ + nostring(opt_desc(pendopt)); +} + +/* + * Scan to end of string or to an END_OPTION_STRING character. + * In the latter case, replace the char with a null char. + * Return a pointer to the remainder of the string, if any. + */ + static char * +optstring(s, p_str, printopt, validchars) + char *s; + char **p_str; + char *printopt; + char *validchars; +{ + register char *p; + + if (*s == '\0') + { + nostring(printopt); + quit(QUIT_ERROR); + } + *p_str = s; + for (p = s; *p != '\0'; p++) + { + if (*p == END_OPTION_STRING || + (validchars != NULL && strchr(validchars, *p) == NULL)) + { + switch (*p) + { + case END_OPTION_STRING: + case ' ': case '\t': case '-': + /* Replace the char with a null to terminate string. */ + *p++ = '\0'; + break; + default: + /* Cannot replace char; make a copy of the string. */ + *p_str = (char *) ecalloc(p-s+1, sizeof(char)); + strncpy(*p_str, s, p-s); + (*p_str)[p-s] = '\0'; + break; + } + break; + } + } + return (p); +} + +/* + */ + static int +num_error(printopt, errp) + char *printopt; + int *errp; +{ + PARG parg; + + if (errp != NULL) + { + *errp = TRUE; + return (-1); + } + if (printopt != NULL) + { + parg.p_string = printopt; + error("Number is required after %s", &parg); + } + quit(QUIT_ERROR); + /* NOTREACHED */ + return (-1); +} + +/* + * Translate a string into a number. + * Like atoi(), but takes a pointer to a char *, and updates + * the char * to point after the translated number. + */ + public int +getnum(sp, printopt, errp) + char **sp; + char *printopt; + int *errp; +{ + register char *s; + register int n; + register int neg; + + s = skipsp(*sp); + neg = FALSE; + if (*s == '-') + { + neg = TRUE; + s++; + } + if (*s < '0' || *s > '9') + return (num_error(printopt, errp)); + + n = 0; + while (*s >= '0' && *s <= '9') + n = 10 * n + *s++ - '0'; + *sp = s; + if (errp != NULL) + *errp = FALSE; + if (neg) + n = -n; + return (n); +} + +/* + * Translate a string into a fraction, represented by the part of a + * number which would follow a decimal point. + * The value of the fraction is returned as parts per NUM_FRAC_DENOM. + * That is, if "n" is returned, the fraction intended is n/NUM_FRAC_DENOM. + */ + public long +getfraction(sp, printopt, errp) + char **sp; + char *printopt; + int *errp; +{ + register char *s; + long frac = 0; + int fraclen = 0; + + s = skipsp(*sp); + if (*s < '0' || *s > '9') + return (num_error(printopt, errp)); + + for ( ; *s >= '0' && *s <= '9'; s++) + { + frac = (frac * 10) + (*s - '0'); + fraclen++; + } + if (fraclen > NUM_LOG_FRAC_DENOM) + while (fraclen-- > NUM_LOG_FRAC_DENOM) + frac /= 10; + else + while (fraclen++ < NUM_LOG_FRAC_DENOM) + frac *= 10; + *sp = s; + if (errp != NULL) + *errp = FALSE; + return (frac); +} + + +/* + * Get the value of the -e flag. + */ + public int +get_quit_at_eof() +{ + if (!less_is_more) + return quit_at_eof; + /* When less_is_more is set, the -e flag semantics are different. */ + return quit_at_eof ? OPT_ON : OPT_ONPLUS; +} |