freebsd-dev/contrib/less/optfunc.c
Robert Watson 1ea316270f Currently, less(1) uses K&R prototypes, which both fails to provide useful
compiler-time type checking, and also causes problems for targets where
multiple incompatible calling conventions may be selected based on argument
types.  This change switches less(1) to ANSI prototypes.

While there, we also remove use of "register", and attempt to use "const" a
bit better now that the compiler can check argument types.

Reviewed by:	cem, emaste
MFC after:	3 weeks
Sponsored by:	DARPA, AFRL
Differential Revision:	https://reviews.freebsd.org/D10152
2017-03-31 21:29:43 +00:00

709 lines
12 KiB
C

/*
* Copyright (C) 1984-2015 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.
*/
/*
* Handling functions for command line options.
*
* Most options are handled by the generic code in option.c.
* But all string options, and a few non-string options, require
* special handling specific to the particular option.
* This special processing is done by the "handling functions" in this file.
*
* Each handling function is passed a "type" and, if it is a string
* option, the string which should be "assigned" to the option.
* The type may be one of:
* INIT The option is being initialized from the command line.
* TOGGLE The option is being changed from within the program.
* QUERY The setting of the option is merely being queried.
*/
#include "less.h"
#include "option.h"
extern int nbufs;
extern int bufspace;
extern int pr_type;
extern int plusoption;
extern int swindow;
extern int sc_width;
extern int sc_height;
extern int secure;
extern int dohelp;
extern int any_display;
extern char openquote;
extern char closequote;
extern char *prproto[];
extern char *eqproto;
extern char *hproto;
extern char *wproto;
extern char *every_first_cmd;
extern IFILE curr_ifile;
extern char version[];
extern int jump_sline;
extern int jump_sline_fraction;
extern int shift_count;
extern int shift_count_fraction;
extern int less_is_more;
#if LOGFILE
extern char *namelogfile;
extern int force_logfile;
extern int logfile;
#endif
#if TAGS
public char *tagoption = NULL;
extern char *tags;
extern char ztags[];
#endif
#if MSDOS_COMPILER
extern int nm_fg_color, nm_bg_color;
extern int bo_fg_color, bo_bg_color;
extern int ul_fg_color, ul_bg_color;
extern int so_fg_color, so_bg_color;
extern int bl_fg_color, bl_bg_color;
#endif
#if LOGFILE
/*
* Handler for -o option.
*/
public void
opt_o(int type, char *s)
{
PARG parg;
if (secure)
{
error("log file support is not available", NULL_PARG);
return;
}
switch (type)
{
case INIT:
namelogfile = save(s);
break;
case TOGGLE:
if (ch_getflags() & CH_CANSEEK)
{
error("Input is not a pipe", NULL_PARG);
return;
}
if (logfile >= 0)
{
error("Log file is already in use", NULL_PARG);
return;
}
s = skipsp(s);
if (namelogfile != NULL)
free(namelogfile);
namelogfile = lglob(s);
use_logfile(namelogfile);
sync_logfile();
break;
case QUERY:
if (logfile < 0)
error("No log file", NULL_PARG);
else
{
parg.p_string = namelogfile;
error("Log file \"%s\"", &parg);
}
break;
}
}
/*
* Handler for -O option.
*/
public void
opt__O(int type, char *s)
{
force_logfile = TRUE;
opt_o(type, s);
}
#endif
/*
* Handlers for -j option.
*/
public void
opt_j(int type, char *s)
{
PARG parg;
char buf[16];
int len;
int err;
switch (type)
{
case INIT:
case TOGGLE:
if (*s == '.')
{
s++;
jump_sline_fraction = getfraction(&s, "j", &err);
if (err)
error("Invalid line fraction", NULL_PARG);
else
calc_jump_sline();
} else
{
int sline = getnum(&s, "j", &err);
if (err)
error("Invalid line number", NULL_PARG);
else
{
jump_sline = sline;
jump_sline_fraction = -1;
}
}
break;
case QUERY:
if (jump_sline_fraction < 0)
{
parg.p_int = jump_sline;
error("Position target at screen line %d", &parg);
} else
{
sprintf(buf, ".%06d", jump_sline_fraction);
len = (int) strlen(buf);
while (len > 2 && buf[len-1] == '0')
len--;
buf[len] = '\0';
parg.p_string = buf;
error("Position target at screen position %s", &parg);
}
break;
}
}
public void
calc_jump_sline(void)
{
if (jump_sline_fraction < 0)
return;
jump_sline = sc_height * jump_sline_fraction / NUM_FRAC_DENOM;
}
/*
* Handlers for -# option.
*/
public void
opt_shift(int type, char *s)
{
PARG parg;
char buf[16];
int len;
int err;
switch (type)
{
case INIT:
case TOGGLE:
if (*s == '.')
{
s++;
shift_count_fraction = getfraction(&s, "#", &err);
if (err)
error("Invalid column fraction", NULL_PARG);
else
calc_shift_count();
} else
{
int hs = getnum(&s, "#", &err);
if (err)
error("Invalid column number", NULL_PARG);
else
{
shift_count = hs;
shift_count_fraction = -1;
}
}
break;
case QUERY:
if (shift_count_fraction < 0)
{
parg.p_int = shift_count;
error("Horizontal shift %d columns", &parg);
} else
{
sprintf(buf, ".%06d", shift_count_fraction);
len = (int) strlen(buf);
while (len > 2 && buf[len-1] == '0')
len--;
buf[len] = '\0';
parg.p_string = buf;
error("Horizontal shift %s of screen width", &parg);
}
break;
}
}
public void
calc_shift_count(void)
{
if (shift_count_fraction < 0)
return;
shift_count = sc_width * shift_count_fraction / NUM_FRAC_DENOM;
}
#if USERFILE
public void
opt_k(int type, char *s)
{
PARG parg;
switch (type)
{
case INIT:
if (lesskey(s, 0))
{
parg.p_string = s;
error("Cannot use lesskey file \"%s\"", &parg);
}
break;
}
}
#endif
#if TAGS
/*
* Handler for -t option.
*/
public void
opt_t(int type, char *s)
{
IFILE save_ifile;
POSITION pos;
switch (type)
{
case INIT:
tagoption = save(s);
/* Do the rest in main() */
break;
case TOGGLE:
if (secure)
{
error("tags support is not available", NULL_PARG);
break;
}
findtag(skipsp(s));
save_ifile = save_curr_ifile();
/*
* Try to open the file containing the tag
* and search for the tag in that file.
*/
if (edit_tagfile() || (pos = tagsearch()) == NULL_POSITION)
{
/* Failed: reopen the old file. */
reedit_ifile(save_ifile);
break;
}
unsave_ifile(save_ifile);
jump_loc(pos, jump_sline);
break;
}
}
/*
* Handler for -T option.
*/
public void
opt__T(int type, char *s)
{
PARG parg;
switch (type)
{
case INIT:
tags = save(s);
break;
case TOGGLE:
s = skipsp(s);
if (tags != NULL && tags != ztags)
free(tags);
tags = lglob(s);
break;
case QUERY:
parg.p_string = tags;
error("Tags file \"%s\"", &parg);
break;
}
}
#endif
/*
* Handler for -p option.
*/
public void
opt_p(int type, char *s)
{
switch (type)
{
case INIT:
/*
* Unget a command for the specified string.
*/
if (less_is_more)
{
/*
* In "more" mode, the -p argument is a command,
* not a search string, so we don't need a slash.
*/
every_first_cmd = save(s);
} else
{
plusoption = TRUE;
ungetcc(CHAR_END_COMMAND);
ungetsc(s);
/*
* {{ This won't work if the "/" command is
* changed or invalidated by a .lesskey file. }}
*/
ungetsc("/");
}
break;
}
}
/*
* Handler for -P option.
*/
public void
opt__P(int type, char *s)
{
char **proto;
PARG parg;
switch (type)
{
case INIT:
case TOGGLE:
/*
* Figure out which prototype string should be changed.
*/
switch (*s)
{
case 's': proto = &prproto[PR_SHORT]; s++; break;
case 'm': proto = &prproto[PR_MEDIUM]; s++; break;
case 'M': proto = &prproto[PR_LONG]; s++; break;
case '=': proto = &eqproto; s++; break;
case 'h': proto = &hproto; s++; break;
case 'w': proto = &wproto; s++; break;
default: proto = &prproto[PR_SHORT]; break;
}
free(*proto);
*proto = save(s);
break;
case QUERY:
parg.p_string = prproto[pr_type];
error("%s", &parg);
break;
}
}
/*
* Handler for the -b option.
*/
/*ARGSUSED*/
public void
opt_b(int type, char *s)
{
switch (type)
{
case INIT:
case TOGGLE:
/*
* Set the new number of buffers.
*/
ch_setbufspace(bufspace);
break;
case QUERY:
break;
}
}
/*
* Handler for the -i option.
*/
/*ARGSUSED*/
public void
opt_i(int type, char *s)
{
switch (type)
{
case TOGGLE:
chg_caseless();
break;
case QUERY:
case INIT:
break;
}
}
/*
* Handler for the -V option.
*/
/*ARGSUSED*/
public void
opt__V(int type, char *s)
{
switch (type)
{
case TOGGLE:
case QUERY:
dispversion();
break;
case INIT:
/*
* Force output to stdout per GNU standard for --version output.
*/
any_display = 1;
putstr("less ");
putstr(version);
putstr(" (");
#if HAVE_GNU_REGEX
putstr("GNU ");
#endif
#if HAVE_POSIX_REGCOMP
putstr("POSIX ");
#endif
#if HAVE_PCRE
putstr("PCRE ");
#endif
#if HAVE_RE_COMP
putstr("BSD ");
#endif
#if HAVE_REGCMP
putstr("V8 ");
#endif
#if HAVE_V8_REGCOMP
putstr("Spencer V8 ");
#endif
#if !HAVE_GNU_REGEX && !HAVE_POSIX_REGCOMP && !HAVE_PCRE && !HAVE_RE_COMP && !HAVE_REGCMP && !HAVE_V8_REGCOMP
putstr("no ");
#endif
putstr("regular expressions)\n");
putstr("Copyright (C) 1984-2015 Mark Nudelman\n\n");
putstr("less comes with NO WARRANTY, to the extent permitted by law.\n");
putstr("For information about the terms of redistribution,\n");
putstr("see the file named README in the less distribution.\n");
putstr("Homepage: http://www.greenwoodsoftware.com/less\n");
quit(QUIT_OK);
break;
}
}
#if MSDOS_COMPILER
/*
* Parse an MSDOS color descriptor.
*/
static void
colordesc(char *s, int *fg_color, int *bg_color)
{
int fg, bg;
int err;
fg = getnum(&s, "D", &err);
if (err)
{
error("Missing fg color in -D", NULL_PARG);
return;
}
if (*s != '.')
bg = nm_bg_color;
else
{
s++;
bg = getnum(&s, "D", &err);
if (err)
{
error("Missing bg color in -D", NULL_PARG);
return;
}
}
if (*s != '\0')
error("Extra characters at end of -D option", NULL_PARG);
*fg_color = fg;
*bg_color = bg;
}
/*
* Handler for the -D option.
*/
/*ARGSUSED*/
public void
opt_D(int type, char *s)
{
switch (type)
{
case INIT:
case TOGGLE:
switch (*s++)
{
case 'n':
colordesc(s, &nm_fg_color, &nm_bg_color);
break;
case 'd':
colordesc(s, &bo_fg_color, &bo_bg_color);
break;
case 'u':
colordesc(s, &ul_fg_color, &ul_bg_color);
break;
case 'k':
colordesc(s, &bl_fg_color, &bl_bg_color);
break;
case 's':
colordesc(s, &so_fg_color, &so_bg_color);
break;
default:
error("-D must be followed by n, d, u, k or s", NULL_PARG);
break;
}
if (type == TOGGLE)
{
at_enter(AT_STANDOUT);
at_exit();
}
break;
case QUERY:
break;
}
}
#endif
/*
* Handler for the -x option.
*/
public void
opt_x(int type, char *s)
{
extern int tabstops[];
extern int ntabstops;
extern int tabdefault;
char msg[60+(4*TABSTOP_MAX)];
int i;
PARG p;
switch (type)
{
case INIT:
case TOGGLE:
/* Start at 1 because tabstops[0] is always zero. */
for (i = 1; i < TABSTOP_MAX; )
{
int n = 0;
s = skipsp(s);
while (*s >= '0' && *s <= '9')
n = (10 * n) + (*s++ - '0');
if (n > tabstops[i-1])
tabstops[i++] = n;
s = skipsp(s);
if (*s++ != ',')
break;
}
if (i < 2)
return;
ntabstops = i;
tabdefault = tabstops[ntabstops-1] - tabstops[ntabstops-2];
break;
case QUERY:
strcpy(msg, "Tab stops ");
if (ntabstops > 2)
{
for (i = 1; i < ntabstops; i++)
{
if (i > 1)
strcat(msg, ",");
sprintf(msg+strlen(msg), "%d", tabstops[i]);
}
sprintf(msg+strlen(msg), " and then ");
}
sprintf(msg+strlen(msg), "every %d spaces",
tabdefault);
p.p_string = msg;
error("%s", &p);
break;
}
}
/*
* Handler for the -" option.
*/
public void
opt_quote(int type, char *s)
{
char buf[3];
PARG parg;
switch (type)
{
case INIT:
case TOGGLE:
if (s[0] == '\0')
{
openquote = closequote = '\0';
break;
}
if (s[1] != '\0' && s[2] != '\0')
{
error("-\" must be followed by 1 or 2 chars", NULL_PARG);
return;
}
openquote = s[0];
if (s[1] == '\0')
closequote = openquote;
else
closequote = s[1];
break;
case QUERY:
buf[0] = openquote;
buf[1] = closequote;
buf[2] = '\0';
parg.p_string = buf;
error("quotes %s", &parg);
break;
}
}
/*
* "-?" means display a help message.
* If from the command line, exit immediately.
*/
/*ARGSUSED*/
public void
opt_query(int type, char *s)
{
switch (type)
{
case QUERY:
case TOGGLE:
error("Use \"h\" for help", NULL_PARG);
break;
case INIT:
dohelp = 1;
}
}
/*
* Get the "screen window" size.
*/
public int
get_swindow(void)
{
if (swindow > 0)
return (swindow);
return (sc_height + swindow);
}