Edit

IABSD.fr/src/usr.bin/less/option.c

Branch :

  • Show log

    Commit

  • Author : krw
    Date : 2018-03-17 14:03:36
    Hash : 745ece38
    Message : Split only LESS on $. As guenther@ said "STOP SPLITTING ANYTHING BUT $LESS ON '$' !". anton@ came up with the same diff. ok nicm@

  • usr.bin/less/option.c
  • /*
     * Copyright (C) 1984-2012  Mark Nudelman
     * Modified for use with illumos by Garrett D'Amore.
     * Copyright 2014 Garrett D'Amore <garrett@damore.org>
     *
     * 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;
    int plusoption = FALSE;
    
    static char *optstring(char *, char **, char *, char *, int);
    static int flip_triple(int, int);
    
    extern int screen_trashed;
    extern int less_is_more;
    extern int quit_at_eof;
    extern char *every_first_cmd;
    extern int opt_use_backslash;
    
    /*
     * Return a printable description of an option.
     */
    static char *
    opt_desc(struct loption *o)
    {
    	static char buf[OPTNAME_MAX + 10];
    	if (o->oletter == OLETTER_NONE)
    		(void) snprintf(buf, sizeof (buf), "--%s", o->onames->oname);
    	else
    		(void) snprintf(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".
     */
    char *
    propt(int c)
    {
    	static char buf[8];
    
    	(void) snprintf(buf, sizeof (buf), "-%s", prchar(c));
    	return (buf);
    }
    
    /*
     * Scan an argument (either from the command line or from the
     * LESS environment variable) and process it.
     */
    void
    scan_option(char *s, int env)
    {
    	struct loption *o;
    	int optc;
    	char *optname;
    	char *printopt;
    	char *str;
    	int set_default;
    	int lc;
    	int err;
    	int moreopt;
    	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, NULL);
    			break;
    		}
    		pendopt = NULL;
    		return;
    	}
    
    	set_default = FALSE;
    	optname = NULL;
    	moreopt = 0;
    	o = 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 == '-') {
    				if (!less_is_more) {
    					optname = ++s;
    				}
    				break;
    			}
    			/*
    			 * "-+" means set these options back to their defaults.
    			 * (They may have been set otherwise by previous
    			 * options.)
    			 */
    			if (!less_is_more) {
    				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, 0);
    			if (s == NULL)
    				return;
    			if (*str == '+')
    				every_first_cmd = estrdup(str+1);
    			else
    				ungetsc(str);
    			free(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';
    			moreopt = 1;
    			break;
    		case 'n':
    			if (less_is_more) {
    				moreopt = 1;
    				optc = 'z';
    			}
    			break;
    		case 'i':
    			if (less_is_more) {
    				moreopt = 1;
    				optc = 'I';
    			}
    			break;
    		case 'u':
    			if (less_is_more) {
    				moreopt = 1;
    				optc = 'U';
    			}
    			break;
    		case 'e':
    			if (less_is_more) {
    				moreopt = 1;
    				optc = 'E';
    			}
    			break;
    		case 'h':
    			if (less_is_more) {
    				moreopt = 1;
    				optc = '?';
    			}
    			break;
    		case 'd':
    			if (less_is_more) {
    				moreopt = 1;
    				optc = 'M';
    			}
    			break;
    		}
    
    		/*
    		 * Not a special case.
    		 * Look up the option letter in the option table.
    		 */
    		err = 0;
    		if (optname == NULL) {
    			printopt = propt(optc);
    			lc = islower(optc);
    			o = findopt(optc);
    			if (less_is_more && (!moreopt) && (o != NULL) &&
    			    ((o->otype & MORE_OK) == 0)) {
    				o = NULL;
    			}
    		} else {
    			printopt = optname;
    			lc = islower(optname[0]);
    			o = findopt_name(&optname, NULL, &err);
    			s = optname;
    			optname = NULL;
    			switch (*s) {
    			case ' ':	/* name matches exactly */
    			case '\0':
    				break;
    
    			case '=':	/* name 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);
    					return;
    				}
    				s++;
    				break;
    			default:	/* name longer than option, bad */
    				o = NULL;
    			}
    		}
    		if (o == NULL) {
    			parg.p_string = printopt;
    			if (less_is_more) {
    				error("Illegal option %s (more -h for help)",
    				    &parg);
    			} else 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);
    			}
    			return;
    		}
    
    		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], env);
    			if (s == NULL)
    				return;
    			break;
    		case NUMBER:
    			if (*s == '\0') {
    				pendopt = o;
    				return;
    			}
    			*(o->ovar) = getnum(&s, printopt, NULL);
    			break;
    		}
    		/*
    		 * If the option has a handling function, call it.
    		 */
    		if (o->ofunc != NULL)
    			(*o->ofunc)(INIT, str);
    		free(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
     */
    void
    toggle_option(struct loption *o, int lower, char *s, int how_toggle)
    {
    	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);
    		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 (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
    		repaint_hilite(0);
    
    	/*
    	 * 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);
    				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);
    				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 (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
    		chg_hilite();
    
    	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);
    			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(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.
     */
    int
    opt_has_param(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.
     */
    char *
    opt_prompt(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.
     */
    int
    isoptpending(void)
    {
    	return (pendopt != NULL);
    }
    
    /*
     * Print error message about missing string.
     */
    static void
    nostring(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.
     */
    void
    nopendopt(void)
    {
    	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(char *s, char **p_str, char *printopt, char *validchars, int env)
    {
    	char *p;
    	char *out;
    
    	if (*s == '\0') {
    		nostring(printopt);
    		return (NULL);
    	}
    	/* Alloc could be more than needed, but not worth trimming. */
    	*p_str = ecalloc(strlen(s)+1, sizeof (char));
    	out = *p_str;
    
    	for (p = s; *p != '\0'; p++) {
    		if (opt_use_backslash && *p == '\\' && p[1] != '\0') {
    			/* Take next char literally. */
    			++p;
    		} else {
    			if ((*p == END_OPTION_STRING && env == 1) ||
    			    (validchars != NULL &&
    			    strchr(validchars, *p) == NULL))
    				/* End of option string. */
    				break;
    		}
    		*out++ = *p;
    	}
    	*out = '\0';
    	return (p);
    }
    
    /*
     */
    static int
    num_error(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);
    	}
    	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.
     */
    int
    getnum(char **sp, char *printopt, int *errp)
    {
    	char *s;
    	int n;
    	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.
     */
    long
    getfraction(char **sp, char *printopt, int *errp)
    {
    	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.
     */
    int
    get_quit_at_eof(void)
    {
    	return (quit_at_eof);
    }