basic: improve command line parsing
- add qe_strtobool(str, def). Returns boolean value from str: y yes t true 1 evaluate to true any other string evaluates to false the empty string evaluates to def - add bstr_t type: constant bounded string with pointer and length. bstr_make creates a bstr_t from a string pointer or NULL bstr_token creates a bstr_t from a string upto a separator bstr_get_nth creates a bstr_t from a the nth element of a string list bstr_equal compares bstr_t objects for equality - use macros for type safe registation of command line options - combine short, long, argument names and help string - support command line options with optional arguments
This commit is contained in:
parent
f1de1ca561
commit
3dccd7f696
4 changed files with 199 additions and 97 deletions
169
qe.c
169
qe.c
|
|
@ -8176,21 +8176,19 @@ void do_load_qerc(EditState *e, const char *filename)
|
|||
|
||||
/******************************************************/
|
||||
/* command line option handling */
|
||||
static CmdOptionDef *first_cmd_options;
|
||||
static CmdLineOptionDef *first_cmd_options;
|
||||
|
||||
void qe_register_cmd_line_options(CmdOptionDef *table)
|
||||
void qe_register_cmd_line_options(CmdLineOptionDef *table)
|
||||
{
|
||||
CmdOptionDef **pp, *p;
|
||||
CmdLineOptionDef **pp, *p;
|
||||
|
||||
/* link command line options table at end of list */
|
||||
pp = &first_cmd_options;
|
||||
while (*pp != NULL) {
|
||||
for (pp = &first_cmd_options; *pp != NULL; pp = &p->u.next) {
|
||||
p = *pp;
|
||||
if (p == table)
|
||||
return; /* already registered */
|
||||
while (p->name != NULL)
|
||||
if (p == table)
|
||||
return; /* already registered */
|
||||
while (p->desc != NULL)
|
||||
p++;
|
||||
pp = &p->u.next;
|
||||
}
|
||||
*pp = table;
|
||||
}
|
||||
|
|
@ -8213,8 +8211,7 @@ static void show_version(void)
|
|||
|
||||
static void show_usage(void)
|
||||
{
|
||||
CmdOptionDef *p;
|
||||
int pos;
|
||||
CmdLineOptionDef *p;
|
||||
|
||||
printf("Usage: qe [OPTIONS] [filename ...]\n"
|
||||
"\n"
|
||||
|
|
@ -8222,20 +8219,26 @@ static void show_usage(void)
|
|||
"\n");
|
||||
|
||||
/* print all registered command line options */
|
||||
p = first_cmd_options;
|
||||
while (p != NULL) {
|
||||
while (p->name != NULL) {
|
||||
pos = printf("--%s", p->name);
|
||||
if (p->shortname)
|
||||
pos += printf(", -%s", p->shortname);
|
||||
if (p->flags & CMD_OPT_ARG)
|
||||
pos += printf(" %s", p->argname);
|
||||
if (pos < 24)
|
||||
printf("%*s", pos - 24, "");
|
||||
printf("%s\n", p->help);
|
||||
for (p = first_cmd_options; p != NULL; p = p->u.next) {
|
||||
while (p->desc != NULL) {
|
||||
const char *s = p->desc;
|
||||
bstr_t shortname = bstr_token(s, '|', &s);
|
||||
bstr_t name = bstr_token(s, '|', &s);
|
||||
bstr_t argname = bstr_token(s, '|', &s);
|
||||
bstr_t help = bstr_make(s);
|
||||
int pos = printf(" ");
|
||||
|
||||
if (shortname.len)
|
||||
pos += printf(" -%.*s", shortname.len, shortname.s);
|
||||
if (name.len)
|
||||
pos += printf(" --%.*s", name.len, name.s);
|
||||
if (argname.len)
|
||||
pos += printf(" %.*s", argname.len, argname.s);
|
||||
if (pos < 22)
|
||||
printf("%*s", pos - 22, "");
|
||||
printf(" %.*s\n", help.len, help.s);
|
||||
p++;
|
||||
}
|
||||
p = p->u.next;
|
||||
}
|
||||
printf("\n"
|
||||
"Report bugs to bug@qemacs.org. First, please see the Bugs\n"
|
||||
|
|
@ -8247,60 +8250,79 @@ static int parse_command_line(int argc, char **argv)
|
|||
{
|
||||
int _optind;
|
||||
|
||||
_optind = 1;
|
||||
for (;;) {
|
||||
const char *r, *r1, *r2, *_optarg;
|
||||
CmdOptionDef *p;
|
||||
for (_optind = 1; _optind < argc;) {
|
||||
const char *arg, *r, *_optarg;
|
||||
CmdLineOptionDef *p;
|
||||
bstr_t opt1;
|
||||
bstr_t opt2;
|
||||
|
||||
if (_optind >= argc)
|
||||
break;
|
||||
r = argv[_optind];
|
||||
r = arg = argv[_optind];
|
||||
/* stop before first non option */
|
||||
if (r[0] != '-')
|
||||
break;
|
||||
_optind++;
|
||||
|
||||
r2 = r1 = r + 1;
|
||||
if (r2[0] == '-') {
|
||||
r2++;
|
||||
opt1.s = opt2.s = r + 1;
|
||||
if (r[1] == '-') {
|
||||
opt2.s++;
|
||||
/* stop after `--' marker */
|
||||
if (r2[0] == '\0')
|
||||
if (r[2] == '\0')
|
||||
break;
|
||||
}
|
||||
/* parse optional argument specified with opt=arg or opt:arg syntax */
|
||||
_optarg = NULL;
|
||||
while (*r) {
|
||||
if (*r == ':' || *r == '=') {
|
||||
_optarg = r + 1;
|
||||
break;
|
||||
}
|
||||
r++;
|
||||
}
|
||||
opt1.len = r - opt1.s;
|
||||
opt2.len = r - opt2.s;
|
||||
|
||||
p = first_cmd_options;
|
||||
while (p != NULL) {
|
||||
while (p->name != NULL) {
|
||||
if (strequal(p->name, r2) ||
|
||||
(p->shortname && strequal(p->shortname, r1))) {
|
||||
if (p->flags & CMD_OPT_ARG) {
|
||||
for (p = first_cmd_options; p != NULL; p = p->u.next) {
|
||||
while (p->desc != NULL) {
|
||||
const char *s = p->desc;
|
||||
bstr_t shortname = bstr_token(s, '|', &s);
|
||||
bstr_t name = bstr_token(s, '|', &s);
|
||||
bstr_t argname = bstr_token(s, '|', &s);
|
||||
if (bstr_equal(opt1, shortname) || bstr_equal(opt2, name)) {
|
||||
if (argname.len && _optarg == NULL) {
|
||||
if (_optind >= argc) {
|
||||
put_status(NULL,
|
||||
"cmdline argument expected -- %s", r);
|
||||
"cmdline argument %.*s expected for --%.*s",
|
||||
argname.len, argname.s, name.len, name.s);
|
||||
goto next_cmd;
|
||||
}
|
||||
_optarg = argv[_optind++];
|
||||
} else {
|
||||
_optarg = NULL;
|
||||
}
|
||||
if (p->flags & CMD_OPT_BOOL) {
|
||||
*p->u.int_ptr = 1;
|
||||
} else if (p->flags & CMD_OPT_STRING) {
|
||||
*p->u.string_ptr = _optarg;
|
||||
} else if (p->flags & CMD_OPT_INT) {
|
||||
switch (p->type) {
|
||||
case CMD_LINE_TYPE_BOOL:
|
||||
*p->u.int_ptr = qe_strtobool(_optarg, 1);
|
||||
break;
|
||||
case CMD_LINE_TYPE_INT:
|
||||
*p->u.int_ptr = strtol(_optarg, NULL, 0);
|
||||
} else if (p->flags & CMD_OPT_ARG) {
|
||||
p->u.func_arg(_optarg);
|
||||
} else {
|
||||
break;
|
||||
case CMD_LINE_TYPE_STRING:
|
||||
*p->u.string_ptr = _optarg;
|
||||
break;
|
||||
case CMD_LINE_TYPE_FVOID:
|
||||
p->u.func_noarg();
|
||||
break;
|
||||
case CMD_LINE_TYPE_FARG:
|
||||
p->u.func_arg(_optarg);
|
||||
break;
|
||||
case CMD_LINE_TYPE_NONE:
|
||||
case CMD_LINE_TYPE_NEXT:
|
||||
break;
|
||||
}
|
||||
goto next_cmd;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
p = p->u.next;
|
||||
}
|
||||
put_status(NULL, "unknown cmdline option '%s'", r);
|
||||
put_status(NULL, "unknown cmdline option '%s'", arg);
|
||||
next_cmd: ;
|
||||
}
|
||||
|
||||
|
|
@ -8365,28 +8387,29 @@ void set_tty_charset(const char *name)
|
|||
qe_state.tty_charset = qe_strdup(name);
|
||||
}
|
||||
|
||||
static CmdOptionDef cmd_options[] = {
|
||||
{ "help", "h", NULL, 0, "display this help message and exit",
|
||||
{ .func_noarg = show_usage }},
|
||||
{ "no-init-file", "q", NULL, CMD_OPT_BOOL, "do not load config files",
|
||||
{ .int_ptr = &no_init_file }},
|
||||
{ "single-window", "1", NULL, CMD_OPT_BOOL, "keep a single window when loading multiple files",
|
||||
{ .int_ptr = &single_window }},
|
||||
{ "no-windows", "nw", NULL, CMD_OPT_BOOL, "force tty terminal usage",
|
||||
{ .int_ptr = &force_tty }},
|
||||
{ "ttycharset", "c", "CHARSET", CMD_OPT_ARG, "specify tty charset",
|
||||
{ .func_arg = set_tty_charset }},
|
||||
{ "use-session", "s", NULL, CMD_OPT_BOOL, "load and save session files",
|
||||
{ .int_ptr = &use_session_file }},
|
||||
{ "user", "u", "USER", CMD_OPT_ARG, "load ~USER/.qe/config instead of your own",
|
||||
{ .func_arg = set_user_option }},
|
||||
{ "version", "V", NULL, 0, "display version information and exit",
|
||||
{ .func_noarg = show_version }},
|
||||
static CmdLineOptionDef cmd_options[] = {
|
||||
CMD_LINE_FVOID("h", "help", show_usage,
|
||||
"display this help message and exit"),
|
||||
CMD_LINE_FVOID("?", "", show_usage, ""),
|
||||
CMD_LINE_BOOL("q", "no-init-file", &no_init_file,
|
||||
"do not load config files"),
|
||||
CMD_LINE_BOOL("1", "single-window", &single_window,
|
||||
"keep a single window when loading multiple files"),
|
||||
CMD_LINE_BOOL("nw", "no-windows", &force_tty,
|
||||
"force tty terminal usage"),
|
||||
CMD_LINE_FARG("c", "charset", "CHARSET", set_tty_charset,
|
||||
"specify tty charset"),
|
||||
CMD_LINE_BOOL("s", "use-session", &use_session_file,
|
||||
"load and save session files"),
|
||||
CMD_LINE_FARG("u", "user", "USER", set_user_option,
|
||||
"load ~USER/.qe/config instead of your own"),
|
||||
CMD_LINE_FVOID("V", "version", show_version,
|
||||
"display version information and exit"),
|
||||
#ifndef CONFIG_TINY
|
||||
{ "free-all", NULL, NULL, CMD_OPT_BOOL, "free all structures upon exit",
|
||||
{ .int_ptr = &free_everything }},
|
||||
CMD_LINE_BOOL("", "free-all", &free_everything,
|
||||
"free all structures upon exit"),
|
||||
#endif
|
||||
{ NULL, NULL, NULL, 0, NULL, { NULL }},
|
||||
CMD_LINE_LINK()
|
||||
};
|
||||
|
||||
/* default key bindings */
|
||||
|
|
|
|||
60
qe.h
60
qe.h
|
|
@ -434,6 +434,7 @@ static inline int check_fcall(const unsigned int *str, int i) {
|
|||
}
|
||||
|
||||
int qe_strcollate(const char *s1, const char *s2);
|
||||
int qe_strtobool(const char *s, int def);
|
||||
void qe_strtolower(char *buf, int buf_size, const char *str);
|
||||
void skip_spaces(const char **pp);
|
||||
|
||||
|
|
@ -579,32 +580,61 @@ static inline int buf_puts(buf_t *bp, const char *str) {
|
|||
int buf_printf(buf_t *bp, const char *fmt, ...) qe__attr_printf(2,3);
|
||||
int buf_putc_utf8(buf_t *bp, int c);
|
||||
|
||||
/* Bounded constant strings used in various parse functions */
|
||||
typedef struct bstr_t {
|
||||
const char *s;
|
||||
int len;
|
||||
} bstr_t;
|
||||
|
||||
static inline bstr_t bstr_make(const char *s) {
|
||||
bstr_t bs = { s, s ? strlen(s) : 0 };
|
||||
return bs;
|
||||
}
|
||||
|
||||
bstr_t bstr_token(const char *s, int sep, const char **pp);
|
||||
bstr_t bstr_get_nth(const char *s, int n);
|
||||
|
||||
static inline int bstr_equal(bstr_t s1, bstr_t s2) {
|
||||
/* NULL and empty strings are equivalent */
|
||||
return s1.len == s2.len && !memcmp(s1.s, s2.s, s1.len);
|
||||
}
|
||||
|
||||
/* our own implementation of qsort_r() */
|
||||
void qe_qsort_r(void *base, size_t nmemb, size_t size, void *thunk,
|
||||
int (*compar)(void *, const void *, const void *));
|
||||
|
||||
/* command line option */
|
||||
#define CMD_OPT_ARG 0x0001 /* argument */
|
||||
#define CMD_OPT_STRING 0x0002 /* string */
|
||||
#define CMD_OPT_BOOL 0x0004 /* boolean */
|
||||
#define CMD_OPT_INT 0x0008 /* int */
|
||||
/* Command line options */
|
||||
enum CmdLineOptionType {
|
||||
CMD_LINE_TYPE_NONE = 0, /* nothing */
|
||||
CMD_LINE_TYPE_BOOL = 1, /* boolean ptr */
|
||||
CMD_LINE_TYPE_INT = 2, /* int ptr */
|
||||
CMD_LINE_TYPE_STRING = 3, /* string ptr */
|
||||
CMD_LINE_TYPE_FVOID = 4, /* function() */
|
||||
CMD_LINE_TYPE_FARG = 5, /* function(string) */
|
||||
CMD_LINE_TYPE_NEXT = 6, /* next pointer */
|
||||
};
|
||||
|
||||
typedef struct CmdOptionDef {
|
||||
const char *name;
|
||||
const char *shortname;
|
||||
const char *argname;
|
||||
int flags;
|
||||
const char *help;
|
||||
typedef struct CmdLineOptionDef {
|
||||
const char *desc;
|
||||
enum CmdLineOptionType type;
|
||||
union {
|
||||
const char **string_ptr;
|
||||
int *int_ptr;
|
||||
const char **string_ptr;
|
||||
void (*func_noarg)(void);
|
||||
void (*func_arg)(const char *);
|
||||
struct CmdOptionDef *next;
|
||||
struct CmdLineOptionDef *next;
|
||||
} u;
|
||||
} CmdOptionDef;
|
||||
} CmdLineOptionDef;
|
||||
|
||||
void qe_register_cmd_line_options(CmdOptionDef *table);
|
||||
#define CMD_LINE_NONE() { NULL, CMD_LINE_TYPE_NONE, { NULL }}
|
||||
#define CMD_LINE_BOOL(s,n,p,h) { s "|" n "||" h, CMD_LINE_TYPE_BOOL, { .int_ptr = p }}
|
||||
#define CMD_LINE_INT(s,n,a,p,h) { s "|" n "|" a "|" h, CMD_LINE_TYPE_INT, { .int_ptr = p }}
|
||||
#define CMD_LINE_STRING(s,n,a,p,h) { s "|" n "|" a "|" h, CMD_LINE_TYPE_STRING, { .string_ptr = p }}
|
||||
#define CMD_LINE_FVOID(s,n,p,h) { s "|" n "||" h, CMD_LINE_TYPE_FVOID, { .func_noarg = p }}
|
||||
#define CMD_LINE_FARG(s,n,a,p,h) { s "|" n "|" a "|" h, CMD_LINE_TYPE_FARG, { .func_arg = p }}
|
||||
#define CMD_LINE_LINK() { NULL, CMD_LINE_TYPE_NEXT, { NULL }}
|
||||
|
||||
void qe_register_cmd_line_options(CmdLineOptionDef *table);
|
||||
|
||||
int find_resource_file(char *path, int path_size, const char *pattern);
|
||||
|
||||
|
|
|
|||
49
util.c
49
util.c
|
|
@ -470,6 +470,14 @@ int qe_strcollate(const char *s1, const char *s2)
|
|||
|
||||
/* CG: need a local version of strcasecmp: qe_strcasecmp() */
|
||||
|
||||
int qe_strtobool(const char *s, int def) {
|
||||
if (s && *s) {
|
||||
return strxfind("1|y|yes|t|true", s) ? 1 : 0;
|
||||
} else {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
/* Should return int, length of converted string? */
|
||||
void qe_strtolower(char *buf, int size, const char *str)
|
||||
{
|
||||
|
|
@ -2222,6 +2230,47 @@ void *qe_realloc(void *pp, size_t size)
|
|||
return p;
|
||||
}
|
||||
|
||||
/*---------------- bounded strings ----------------*/
|
||||
|
||||
/* get the n-th string from a `|` separated list */
|
||||
bstr_t bstr_get_nth(const char *s, int n) {
|
||||
bstr_t bs;
|
||||
|
||||
for (bs.s = s;; s++) {
|
||||
if (*s == '\0' || *s == '|') {
|
||||
if (n-- == 0) {
|
||||
bs.len = s - bs.s;
|
||||
break;
|
||||
}
|
||||
if (*s) {
|
||||
bs.s = s + 1;
|
||||
} else {
|
||||
bs.len = 0;
|
||||
bs.s = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bs;
|
||||
}
|
||||
|
||||
/* get the first string from a list and push pointer */
|
||||
bstr_t bstr_token(const char *s, int sep, const char **pp) {
|
||||
bstr_t bs = { s, 0 };
|
||||
|
||||
if (s) {
|
||||
/* XXX: should special case spaces? */
|
||||
for (; s != '\0' && *s != sep; s++)
|
||||
continue;
|
||||
|
||||
bs.len = s - bs.s;
|
||||
if (pp) {
|
||||
*pp = *s ? s + 1 : NULL;
|
||||
}
|
||||
}
|
||||
return bs;
|
||||
}
|
||||
|
||||
/*---------------- qe_qsort_r ----------------*/
|
||||
|
||||
/* Our own implementation of qsort_r() since it is not available
|
||||
|
|
|
|||
16
x11.c
16
x11.c
|
|
@ -1911,14 +1911,14 @@ static CmdDef x11_commands[] = {
|
|||
CMD_DEF_END,
|
||||
};
|
||||
|
||||
static CmdOptionDef cmd_options[] = {
|
||||
{ "display", "d", "display", CMD_OPT_STRING | CMD_OPT_ARG, "set X11 display",
|
||||
{ .string_ptr = &display_str }},
|
||||
{ "geometry", "g", "WxH", CMD_OPT_STRING | CMD_OPT_ARG, "set X11 display size",
|
||||
{ .string_ptr = &geometry_str }},
|
||||
{ "font-size", "fs", "ptsize", CMD_OPT_INT | CMD_OPT_ARG, "set default font size",
|
||||
{ .int_ptr = &font_ptsize }},
|
||||
{ NULL, NULL, NULL, 0, NULL, { NULL }},
|
||||
static CmdLineOptionDef cmd_options[] = {
|
||||
CMD_LINE_STRING("d", "display", "DISPLAY", &display_str,
|
||||
"set X11 display"),
|
||||
CMD_LINE_STRING("g", "geometry", "WxH", &geometry_str,
|
||||
"set X11 display size"),
|
||||
CMD_LINE_INT("fs", "font-size", "ptsize", &font_ptsize,
|
||||
"set default font size"),
|
||||
CMD_LINE_LINK()
|
||||
};
|
||||
|
||||
static int x11_init(void)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue