|
|
e9098c |
/****************************************************************************/
|
|
|
e9098c |
/* argv.h: a thread-safe argument vector parser and usage screen generator */
|
|
|
e9098c |
/* Copyright (C) 2015--2024 SysDeer Technologies, LLC */
|
|
|
e9098c |
/* Released under GPLv2 and GPLv3; see COPYING.TREEBNF. */
|
|
|
e9098c |
/****************************************************************************/
|
|
|
e9098c |
|
|
|
e9098c |
#ifndef ARGV_H
|
|
|
e9098c |
#define ARGV_H
|
|
|
e9098c |
|
|
|
e9098c |
#include <stdbool.h>
|
|
|
e9098c |
#include <stdint.h>
|
|
|
e9098c |
#include <stddef.h>
|
|
|
e9098c |
#include <string.h>
|
|
|
e9098c |
#include <stdlib.h>
|
|
|
e9098c |
#include <stdio.h>
|
|
|
917bb6 |
#include <ctype.h>
|
|
|
e9098c |
#include <unistd.h>
|
|
|
e9098c |
|
|
|
e9098c |
#define ARGV_VERBOSITY_NONE 0x00
|
|
|
e9098c |
#define ARGV_VERBOSITY_ERRORS 0x01
|
|
|
e9098c |
#define ARGV_VERBOSITY_STATUS 0x02
|
|
|
e9098c |
#define ARGV_CLONE_VECTOR 0x80
|
|
|
e9098c |
|
|
|
e9098c |
#ifndef ARGV_TAB_WIDTH
|
|
|
e9098c |
#define ARGV_TAB_WIDTH 8
|
|
|
e9098c |
#endif
|
|
|
e9098c |
|
|
|
e9098c |
/*******************************************/
|
|
|
e9098c |
/* */
|
|
|
e9098c |
/* support of hybrid options */
|
|
|
e9098c |
/* ------------------------- */
|
|
|
e9098c |
/* hybrid options are very similar to */
|
|
|
e9098c |
/* long options, yet are prefixed by */
|
|
|
e9098c |
/* a single dash rather than two */
|
|
|
e9098c |
/* (i.e. -std, -isystem). */
|
|
|
e9098c |
/* hybrid options are supported by this */
|
|
|
e9098c |
/* driver for compatibility with legacy */
|
|
|
e9098c |
/* tools; note, however, that the use */
|
|
|
e9098c |
/* of hybrid options should be strongly */
|
|
|
e9098c |
/* discouraged due to the limitations */
|
|
|
e9098c |
/* they impose on short options (for */
|
|
|
e9098c |
/* example, a driver implementing -std */
|
|
|
e9098c |
/* may not provide -s as a short option */
|
|
|
e9098c |
/* that takes an arbitrary value). */
|
|
|
e9098c |
/* */
|
|
|
e9098c |
/* SPACE: -hybrid VALUE (i.e. -MF file) */
|
|
|
e9098c |
/* EQUAL: -hybrid=VALUE (i.e. -std=c99) */
|
|
|
e9098c |
/* COMMA: -hybrid,VALUE (i.e. -Wl,<arg>) */
|
|
|
e9098c |
/* ONLY: -opt accepted, --opt rejected */
|
|
|
e9098c |
/* JOINED: -optVALUE */
|
|
|
e9098c |
/* */
|
|
|
e9098c |
/*******************************************/
|
|
|
e9098c |
|
|
|
e9098c |
|
|
|
e9098c |
#define ARGV_OPTION_HYBRID_NONE 0x00
|
|
|
e9098c |
#define ARGV_OPTION_HYBRID_ONLY 0x01
|
|
|
e9098c |
#define ARGV_OPTION_HYBRID_SPACE 0x02
|
|
|
e9098c |
#define ARGV_OPTION_HYBRID_EQUAL 0x04
|
|
|
e9098c |
#define ARGV_OPTION_HYBRID_COMMA 0x08
|
|
|
e9098c |
#define ARGV_OPTION_HYBRID_JOINED 0x10
|
|
|
917bb6 |
|
|
|
917bb6 |
#define ARGV_OPTION_KEYVAL_PAIR 0X20
|
|
|
917bb6 |
#define ARGV_OPTION_KEYVAL_ARRAY 0X40
|
|
|
917bb6 |
#define ARGV_OPTION_KEYVAL_MASK (ARGV_OPTION_KEYVAL_PAIR \
|
|
|
917bb6 |
| ARGV_OPTION_KEYVAL_ARRAY)
|
|
|
917bb6 |
|
|
|
e9098c |
#define ARGV_OPTION_HYBRID_CIRCUS (ARGV_OPTION_HYBRID_SPACE \
|
|
|
e9098c |
| ARGV_OPTION_HYBRID_JOINED)
|
|
|
917bb6 |
|
|
|
e9098c |
#define ARGV_OPTION_HYBRID_DUAL (ARGV_OPTION_HYBRID_SPACE \
|
|
|
e9098c |
| ARGV_OPTION_HYBRID_EQUAL)
|
|
|
917bb6 |
|
|
|
e9098c |
#define ARGV_OPTION_HYBRID_SWITCH (ARGV_OPTION_HYBRID_ONLY \
|
|
|
e9098c |
| ARGV_OPTION_HYBRID_SPACE \
|
|
|
e9098c |
| ARGV_OPTION_HYBRID_EQUAL \
|
|
|
e9098c |
| ARGV_OPTION_HYBRID_COMMA \
|
|
|
e9098c |
| ARGV_OPTION_HYBRID_JOINED)
|
|
|
e9098c |
|
|
|
917bb6 |
#define ARGV_KEYVAL_ASSIGN 0x01
|
|
|
917bb6 |
#define ARGV_KEYVAL_OVERRIDE 0x02
|
|
|
917bb6 |
|
|
|
e9098c |
enum argv_optarg {
|
|
|
e9098c |
ARGV_OPTARG_NONE,
|
|
|
e9098c |
ARGV_OPTARG_REQUIRED,
|
|
|
e9098c |
ARGV_OPTARG_OPTIONAL,
|
|
|
e9098c |
};
|
|
|
e9098c |
|
|
|
e9098c |
enum argv_mode {
|
|
|
e9098c |
ARGV_MODE_SCAN,
|
|
|
e9098c |
ARGV_MODE_COPY,
|
|
|
e9098c |
};
|
|
|
e9098c |
|
|
|
e9098c |
enum argv_error {
|
|
|
e9098c |
ARGV_ERROR_OK,
|
|
|
e9098c |
ARGV_ERROR_INTERNAL,
|
|
|
e9098c |
ARGV_ERROR_SHORT_OPTION,
|
|
|
e9098c |
ARGV_ERROR_LONG_OPTION,
|
|
|
e9098c |
ARGV_ERROR_VENDOR_OPTION,
|
|
|
e9098c |
ARGV_ERROR_OPTARG_NONE,
|
|
|
e9098c |
ARGV_ERROR_OPTARG_REQUIRED,
|
|
|
e9098c |
ARGV_ERROR_OPTARG_PARADIGM,
|
|
|
e9098c |
ARGV_ERROR_HYBRID_NONE,
|
|
|
e9098c |
ARGV_ERROR_HYBRID_ONLY,
|
|
|
e9098c |
ARGV_ERROR_HYBRID_SPACE,
|
|
|
e9098c |
ARGV_ERROR_HYBRID_EQUAL,
|
|
|
e9098c |
ARGV_ERROR_HYBRID_COMMA,
|
|
|
917bb6 |
ARGV_ERROR_KEYVAL_KEY,
|
|
|
917bb6 |
ARGV_ERROR_KEYVAL_VALUE,
|
|
|
917bb6 |
ARGV_ERROR_KEYVAL_ALLOC,
|
|
|
e9098c |
};
|
|
|
e9098c |
|
|
|
e9098c |
struct argv_option {
|
|
|
e9098c |
const char * long_name;
|
|
|
e9098c |
const char short_name;
|
|
|
e9098c |
int tag;
|
|
|
e9098c |
enum argv_optarg optarg;
|
|
|
e9098c |
int flags;
|
|
|
e9098c |
const char * paradigm;
|
|
|
e9098c |
const char * argname;
|
|
|
e9098c |
const char * description;
|
|
|
e9098c |
};
|
|
|
e9098c |
|
|
|
917bb6 |
struct argv_keyval {
|
|
|
917bb6 |
const char * keyword;
|
|
|
917bb6 |
const char * value;
|
|
|
917bb6 |
int flags;
|
|
|
917bb6 |
};
|
|
|
917bb6 |
|
|
|
e9098c |
struct argv_entry {
|
|
|
609385 |
const char * arg;
|
|
|
917bb6 |
struct argv_keyval * keyv;
|
|
|
609385 |
int tag;
|
|
|
609385 |
bool fopt;
|
|
|
609385 |
bool fval;
|
|
|
609385 |
bool fnoscan;
|
|
|
609385 |
enum argv_error errcode;
|
|
|
e9098c |
};
|
|
|
e9098c |
|
|
|
e9098c |
struct argv_meta {
|
|
|
e9098c |
char ** argv;
|
|
|
e9098c |
struct argv_entry * entries;
|
|
|
e9098c |
};
|
|
|
e9098c |
|
|
|
e9098c |
struct argv_ctx {
|
|
|
e9098c |
int flags;
|
|
|
e9098c |
int mode;
|
|
|
e9098c |
int nentries;
|
|
|
e9098c |
intptr_t unitidx;
|
|
|
e9098c |
intptr_t erridx;
|
|
|
e9098c |
enum argv_error errcode;
|
|
|
e9098c |
const char * errch;
|
|
|
e9098c |
const struct argv_option * erropt;
|
|
|
e9098c |
const char * program;
|
|
|
917bb6 |
size_t keyvlen;
|
|
|
e9098c |
};
|
|
|
e9098c |
|
|
|
e9098c |
#ifdef ARGV_DRIVER
|
|
|
e9098c |
|
|
|
e9098c |
struct argv_meta_impl {
|
|
|
e9098c |
char ** argv;
|
|
|
e9098c |
char * strbuf;
|
|
|
917bb6 |
char * keyvbuf;
|
|
|
917bb6 |
char * keyvmark;
|
|
|
e9098c |
struct argv_meta meta;
|
|
|
e9098c |
};
|
|
|
e9098c |
|
|
|
e9098c |
static int argv_optv_init(
|
|
|
e9098c |
const struct argv_option[],
|
|
|
e9098c |
const struct argv_option **);
|
|
|
e9098c |
|
|
|
e9098c |
static const char * argv_program_name(const char *);
|
|
|
e9098c |
|
|
|
e9098c |
static void argv_usage(
|
|
|
e9098c |
int fd,
|
|
|
e9098c |
const char * header,
|
|
|
e9098c |
const struct argv_option **,
|
|
|
e9098c |
const char * mode);
|
|
|
e9098c |
|
|
|
e9098c |
static void argv_usage_plain(
|
|
|
e9098c |
int fd,
|
|
|
e9098c |
const char * header,
|
|
|
e9098c |
const struct argv_option **,
|
|
|
e9098c |
const char * mode);
|
|
|
e9098c |
|
|
|
e9098c |
static struct argv_meta * argv_get(
|
|
|
e9098c |
char **,
|
|
|
e9098c |
const struct argv_option **,
|
|
|
e9098c |
int flags,
|
|
|
e9098c |
int fd);
|
|
|
e9098c |
|
|
|
e9098c |
static void argv_free(struct argv_meta *);
|
|
|
e9098c |
|
|
|
e9098c |
#ifndef argv_dprintf
|
|
|
e9098c |
#define argv_dprintf dprintf
|
|
|
e9098c |
#endif
|
|
|
e9098c |
|
|
|
e9098c |
|
|
|
e9098c |
|
|
|
e9098c |
/*------------------------------------*/
|
|
|
e9098c |
/* implementation of static functions */
|
|
|
e9098c |
/*------------------------------------*/
|
|
|
e9098c |
|
|
|
e9098c |
static int argv_optv_init(
|
|
|
e9098c |
const struct argv_option options[],
|
|
|
e9098c |
const struct argv_option ** optv)
|
|
|
e9098c |
{
|
|
|
e9098c |
const struct argv_option * option;
|
|
|
e9098c |
int i;
|
|
|
e9098c |
|
|
|
e9098c |
for (option=options,i=0; option->long_name || option->short_name; option++)
|
|
|
e9098c |
optv[i++] = option;
|
|
|
e9098c |
|
|
|
e9098c |
optv[i] = 0;
|
|
|
e9098c |
return i;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
static const struct argv_option * argv_short_option(
|
|
|
e9098c |
const char * ch,
|
|
|
e9098c |
const struct argv_option ** optv,
|
|
|
e9098c |
struct argv_entry * entry)
|
|
|
e9098c |
{
|
|
|
e9098c |
const struct argv_option * option;
|
|
|
e9098c |
|
|
|
e9098c |
for (; *optv; optv++) {
|
|
|
e9098c |
option = *optv;
|
|
|
e9098c |
|
|
|
e9098c |
if (option->short_name == *ch) {
|
|
|
e9098c |
entry->tag = option->tag;
|
|
|
e9098c |
entry->fopt = true;
|
|
|
e9098c |
return option;
|
|
|
e9098c |
}
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
return 0;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
static const struct argv_option * argv_long_option(
|
|
|
e9098c |
const char * ch,
|
|
|
e9098c |
const struct argv_option ** optv,
|
|
|
e9098c |
struct argv_entry * entry)
|
|
|
e9098c |
{
|
|
|
e9098c |
const struct argv_option * option;
|
|
|
e9098c |
const char * arg;
|
|
|
e9098c |
size_t len;
|
|
|
e9098c |
|
|
|
e9098c |
for (; *optv; optv++) {
|
|
|
e9098c |
option = *optv;
|
|
|
e9098c |
len = option->long_name ? strlen(option->long_name) : 0;
|
|
|
e9098c |
|
|
|
e9098c |
if (len && !(strncmp(option->long_name,ch,len))) {
|
|
|
e9098c |
arg = ch + len;
|
|
|
e9098c |
|
|
|
e9098c |
if (!*arg
|
|
|
e9098c |
|| (*arg == '=')
|
|
|
e9098c |
|| (option->flags & ARGV_OPTION_HYBRID_JOINED)
|
|
|
e9098c |
|| ((option->flags & ARGV_OPTION_HYBRID_COMMA)
|
|
|
e9098c |
&& (*arg == ','))) {
|
|
|
e9098c |
entry->tag = option->tag;
|
|
|
e9098c |
entry->fopt = true;
|
|
|
e9098c |
return option;
|
|
|
e9098c |
}
|
|
|
e9098c |
}
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
return 0;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
static inline bool is_short_option(const char * arg)
|
|
|
e9098c |
{
|
|
|
e9098c |
return (arg[0]=='-') && arg[1] && (arg[1]!='-');
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
static inline bool is_long_option(const char * arg)
|
|
|
e9098c |
{
|
|
|
e9098c |
return (arg[0]=='-') && (arg[1]=='-') && arg[2];
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
static inline bool is_last_option(const char * arg)
|
|
|
e9098c |
{
|
|
|
e9098c |
return (arg[0]=='-') && (arg[1]=='-') && !arg[2];
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
static inline bool is_hybrid_option(
|
|
|
e9098c |
const char * arg,
|
|
|
e9098c |
const struct argv_option ** optv)
|
|
|
e9098c |
{
|
|
|
e9098c |
const struct argv_option * option;
|
|
|
e9098c |
struct argv_entry entry;
|
|
|
e9098c |
|
|
|
e9098c |
if (!is_short_option(arg))
|
|
|
e9098c |
return false;
|
|
|
e9098c |
|
|
|
e9098c |
if (!(option = argv_long_option(++arg,optv,&entry)))
|
|
|
e9098c |
return false;
|
|
|
e9098c |
|
|
|
e9098c |
if (!(option->flags & ARGV_OPTION_HYBRID_SWITCH))
|
|
|
e9098c |
if (argv_short_option(arg,optv,&entry))
|
|
|
e9098c |
return false;
|
|
|
e9098c |
|
|
|
e9098c |
return true;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
static inline bool is_arg_in_paradigm(const char * arg, const char * paradigm)
|
|
|
e9098c |
{
|
|
|
e9098c |
size_t len;
|
|
|
e9098c |
const char * ch;
|
|
|
e9098c |
|
|
|
e9098c |
for (ch=paradigm,len=strlen(arg); ch; ) {
|
|
|
e9098c |
if (!strncmp(arg,ch,len)) {
|
|
|
e9098c |
if (!*(ch += len))
|
|
|
e9098c |
return true;
|
|
|
e9098c |
else if (*ch == '|')
|
|
|
e9098c |
return true;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
if ((ch = strchr(ch,'|')))
|
|
|
e9098c |
ch++;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
return false;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
static inline const struct argv_option * option_from_tag(
|
|
|
e9098c |
const struct argv_option ** optv,
|
|
|
e9098c |
int tag)
|
|
|
e9098c |
{
|
|
|
e9098c |
for (; *optv; optv++)
|
|
|
e9098c |
if (optv[0]->tag == tag)
|
|
|
e9098c |
return optv[0];
|
|
|
e9098c |
return 0;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
917bb6 |
static inline int argv_scan_keyval_array(struct argv_meta_impl * meta, struct argv_entry * entry)
|
|
|
917bb6 |
{
|
|
|
917bb6 |
const char * ch;
|
|
|
917bb6 |
char * dst;
|
|
|
917bb6 |
int ncomma;
|
|
|
917bb6 |
int cint;
|
|
|
917bb6 |
struct argv_keyval * keyv;
|
|
|
917bb6 |
|
|
|
917bb6 |
/* count key[val] elements, assume no comma after last element */
|
|
|
917bb6 |
for (ch=entry->arg,ncomma=1; *ch; ch++) {
|
|
|
917bb6 |
if ((ch[0]=='\\') && (ch[1]==',')) {
|
|
|
917bb6 |
ch++;
|
|
|
917bb6 |
|
|
|
917bb6 |
} else if ((ch[0]==',')) {
|
|
|
917bb6 |
ncomma++;
|
|
|
917bb6 |
}
|
|
|
917bb6 |
}
|
|
|
917bb6 |
|
|
|
917bb6 |
/* keyval string buffer */
|
|
|
917bb6 |
dst = meta->keyvmark;
|
|
|
917bb6 |
|
|
|
917bb6 |
/* allocate keyval array */
|
|
|
917bb6 |
if (!(entry->keyv = calloc(ncomma+1,sizeof(*entry->keyv))))
|
|
|
917bb6 |
return ARGV_ERROR_KEYVAL_ALLOC;
|
|
|
917bb6 |
|
|
|
917bb6 |
/* create keyval array */
|
|
|
917bb6 |
entry->keyv->keyword = dst;
|
|
|
917bb6 |
|
|
|
917bb6 |
for (ch=entry->arg,keyv=entry->keyv; *ch; ch++) {
|
|
|
917bb6 |
if ((ch[0]=='\\') && (ch[1]==',')) {
|
|
|
917bb6 |
*dst++ = ',';
|
|
|
917bb6 |
ch++;
|
|
|
917bb6 |
|
|
|
917bb6 |
} else if ((ch[0]==':') && (ch[1]=='=')) {
|
|
|
917bb6 |
if (!keyv->keyword[0])
|
|
|
917bb6 |
return ARGV_ERROR_KEYVAL_KEY;
|
|
|
917bb6 |
|
|
|
917bb6 |
keyv->flags = ARGV_KEYVAL_OVERRIDE;
|
|
|
917bb6 |
keyv->value = ++dst;
|
|
|
917bb6 |
ch++;
|
|
|
917bb6 |
|
|
|
917bb6 |
} else if ((ch[0]=='=')) {
|
|
|
917bb6 |
if (!keyv->keyword[0])
|
|
|
917bb6 |
return ARGV_ERROR_KEYVAL_KEY;
|
|
|
917bb6 |
|
|
|
917bb6 |
keyv->flags = ARGV_KEYVAL_ASSIGN;
|
|
|
917bb6 |
keyv->value = ++dst;
|
|
|
917bb6 |
|
|
|
917bb6 |
} else if ((ch[0]==',')) {
|
|
|
917bb6 |
for (; isblank(cint = ch[1]); )
|
|
|
917bb6 |
ch++;
|
|
|
917bb6 |
|
|
|
917bb6 |
if (ch[1]) {
|
|
|
917bb6 |
keyv++;
|
|
|
917bb6 |
keyv->keyword = ++dst;
|
|
|
917bb6 |
}
|
|
|
917bb6 |
} else {
|
|
|
917bb6 |
*dst++ = *ch;
|
|
|
917bb6 |
}
|
|
|
917bb6 |
}
|
|
|
917bb6 |
|
|
|
917bb6 |
/* keyval string buffer */
|
|
|
917bb6 |
meta->keyvmark = ++dst;
|
|
|
917bb6 |
|
|
|
917bb6 |
return ARGV_ERROR_OK;
|
|
|
917bb6 |
}
|
|
|
917bb6 |
|
|
|
917bb6 |
static inline int argv_scan_keyval_pair(struct argv_meta_impl * meta, struct argv_entry * entry)
|
|
|
917bb6 |
{
|
|
|
917bb6 |
const char * ch;
|
|
|
917bb6 |
char * dst;
|
|
|
917bb6 |
struct argv_keyval * keyv;
|
|
|
917bb6 |
|
|
|
917bb6 |
/* keyval string buffer */
|
|
|
917bb6 |
dst = meta->keyvmark;
|
|
|
917bb6 |
|
|
|
917bb6 |
/* allocate keyval array */
|
|
|
917bb6 |
if (!(entry->keyv = calloc(2,sizeof(*entry->keyv))))
|
|
|
917bb6 |
return ARGV_ERROR_KEYVAL_ALLOC;
|
|
|
917bb6 |
|
|
|
917bb6 |
/* create keyval array */
|
|
|
917bb6 |
entry->keyv->keyword = dst;
|
|
|
917bb6 |
|
|
|
917bb6 |
for (ch=entry->arg,keyv=entry->keyv; *ch && !keyv->flags; ch++) {
|
|
|
917bb6 |
if ((ch[0]=='\\') && (ch[1]==',')) {
|
|
|
917bb6 |
*dst++ = ',';
|
|
|
917bb6 |
ch++;
|
|
|
917bb6 |
|
|
|
917bb6 |
} else if ((ch[0]==':') && (ch[1]=='=')) {
|
|
|
917bb6 |
if (!keyv->keyword[0])
|
|
|
917bb6 |
return ARGV_ERROR_KEYVAL_KEY;
|
|
|
917bb6 |
|
|
|
917bb6 |
keyv->flags = ARGV_KEYVAL_OVERRIDE;
|
|
|
917bb6 |
keyv->value = ++dst;
|
|
|
917bb6 |
ch++;
|
|
|
917bb6 |
|
|
|
917bb6 |
} else if ((ch[0]=='=')) {
|
|
|
917bb6 |
if (!keyv->keyword[0])
|
|
|
917bb6 |
return ARGV_ERROR_KEYVAL_KEY;
|
|
|
917bb6 |
|
|
|
917bb6 |
keyv->flags = ARGV_KEYVAL_ASSIGN;
|
|
|
917bb6 |
keyv->value = ++dst;
|
|
|
917bb6 |
|
|
|
917bb6 |
} else {
|
|
|
917bb6 |
*dst++ = *ch;
|
|
|
917bb6 |
}
|
|
|
917bb6 |
}
|
|
|
917bb6 |
|
|
|
917bb6 |
for (; *ch; )
|
|
|
917bb6 |
*dst++ = *ch++;
|
|
|
917bb6 |
|
|
|
917bb6 |
/* keyval string buffer */
|
|
|
917bb6 |
meta->keyvmark = ++dst;
|
|
|
917bb6 |
|
|
|
917bb6 |
return ARGV_ERROR_OK;
|
|
|
917bb6 |
}
|
|
|
917bb6 |
|
|
|
e9098c |
static void argv_scan(
|
|
|
e9098c |
char ** argv,
|
|
|
e9098c |
const struct argv_option ** optv,
|
|
|
e9098c |
struct argv_ctx * ctx,
|
|
|
e9098c |
struct argv_meta * meta)
|
|
|
e9098c |
{
|
|
|
917bb6 |
struct argv_meta_impl * imeta;
|
|
|
917bb6 |
uintptr_t addr;
|
|
|
e9098c |
char ** parg;
|
|
|
e9098c |
const char * ch;
|
|
|
e9098c |
const char * val;
|
|
|
e9098c |
const struct argv_option * option;
|
|
|
e9098c |
struct argv_entry entry;
|
|
|
e9098c |
struct argv_entry * mentry;
|
|
|
e9098c |
enum argv_error ferr;
|
|
|
e9098c |
bool fval;
|
|
|
e9098c |
bool fnext;
|
|
|
e9098c |
bool fshort;
|
|
|
e9098c |
bool fhybrid;
|
|
|
e9098c |
bool fnoscan;
|
|
|
e9098c |
|
|
|
917bb6 |
addr = (uintptr_t)meta - offsetof(struct argv_meta_impl,meta);
|
|
|
917bb6 |
imeta = (struct argv_meta_impl *)addr;
|
|
|
917bb6 |
|
|
|
e9098c |
parg = &argv[1];
|
|
|
e9098c |
ch = *parg;
|
|
|
e9098c |
ferr = ARGV_ERROR_OK;
|
|
|
e9098c |
fshort = false;
|
|
|
e9098c |
fnoscan = false;
|
|
|
e9098c |
fval = false;
|
|
|
e9098c |
mentry = meta ? meta->entries : 0;
|
|
|
e9098c |
|
|
|
e9098c |
ctx->unitidx = 0;
|
|
|
e9098c |
ctx->erridx = 0;
|
|
|
e9098c |
|
|
|
e9098c |
while (ch && (ferr == ARGV_ERROR_OK)) {
|
|
|
e9098c |
option = 0;
|
|
|
e9098c |
fhybrid = false;
|
|
|
e9098c |
|
|
|
e9098c |
if (fnoscan)
|
|
|
e9098c |
fval = true;
|
|
|
e9098c |
|
|
|
e9098c |
else if (is_last_option(ch))
|
|
|
e9098c |
fnoscan = true;
|
|
|
e9098c |
|
|
|
e9098c |
else if (!fshort && is_hybrid_option(ch,optv))
|
|
|
e9098c |
fhybrid = true;
|
|
|
e9098c |
|
|
|
e9098c |
if (!fnoscan && !fhybrid && (fshort || is_short_option(ch))) {
|
|
|
e9098c |
if (!fshort)
|
|
|
e9098c |
ch++;
|
|
|
e9098c |
|
|
|
e9098c |
if ((option = argv_short_option(ch,optv,&entry))) {
|
|
|
e9098c |
if (ch[1]) {
|
|
|
e9098c |
ch++;
|
|
|
e9098c |
fnext = false;
|
|
|
e9098c |
fshort = (option->optarg == ARGV_OPTARG_NONE);
|
|
|
e9098c |
} else {
|
|
|
e9098c |
parg++;
|
|
|
e9098c |
ch = *parg;
|
|
|
e9098c |
fnext = true;
|
|
|
e9098c |
fshort = false;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
if (option->optarg == ARGV_OPTARG_NONE) {
|
|
|
e9098c |
if (!fnext && ch && (*ch == '-')) {
|
|
|
e9098c |
ferr = ARGV_ERROR_OPTARG_NONE;
|
|
|
e9098c |
} else {
|
|
|
e9098c |
fval = false;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
} else if (!fnext) {
|
|
|
e9098c |
fval = true;
|
|
|
e9098c |
|
|
|
e9098c |
} else if (option->optarg == ARGV_OPTARG_REQUIRED) {
|
|
|
e9098c |
if (ch && is_short_option(ch))
|
|
|
e9098c |
ferr = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
e9098c |
else if (ch && is_long_option(ch))
|
|
|
e9098c |
ferr = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
e9098c |
else if (ch && is_last_option(ch))
|
|
|
e9098c |
ferr = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
e9098c |
else if (ch)
|
|
|
e9098c |
fval = true;
|
|
|
e9098c |
else
|
|
|
e9098c |
ferr = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
e9098c |
} else {
|
|
|
e9098c |
/* ARGV_OPTARG_OPTIONAL */
|
|
|
e9098c |
if (ch && is_short_option(ch))
|
|
|
e9098c |
fval = false;
|
|
|
e9098c |
else if (ch && is_long_option(ch))
|
|
|
e9098c |
fval = false;
|
|
|
e9098c |
else if (ch && is_last_option(ch))
|
|
|
e9098c |
fval = false;
|
|
|
e9098c |
else if (fnext)
|
|
|
e9098c |
fval = false;
|
|
|
e9098c |
else
|
|
|
e9098c |
fval = ch;
|
|
|
e9098c |
}
|
|
|
e9098c |
} else {
|
|
|
e9098c |
if ((ch == &parg[0][1]) && (ch[0] == 'W') && ch[1]) {
|
|
|
e9098c |
ferr = ARGV_ERROR_VENDOR_OPTION;
|
|
|
e9098c |
} else {
|
|
|
e9098c |
ferr = ARGV_ERROR_SHORT_OPTION;
|
|
|
e9098c |
}
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
} else if (!fnoscan && (fhybrid || is_long_option(ch))) {
|
|
|
e9098c |
ch += (fhybrid ? 1 : 2);
|
|
|
e9098c |
|
|
|
e9098c |
if ((option = argv_long_option(ch,optv,&entry))) {
|
|
|
e9098c |
val = ch + strlen(option->long_name);
|
|
|
e9098c |
|
|
|
e9098c |
/* val[0] is either '=' (or ',') or '\0' */
|
|
|
e9098c |
if (!val[0]) {
|
|
|
e9098c |
parg++;
|
|
|
e9098c |
ch = *parg;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
/* now verify the proper setting of option values */
|
|
|
e9098c |
if (fhybrid && !(option->flags & ARGV_OPTION_HYBRID_SWITCH)) {
|
|
|
e9098c |
ferr = ARGV_ERROR_HYBRID_NONE;
|
|
|
e9098c |
|
|
|
e9098c |
} else if (!fhybrid && (option->flags & ARGV_OPTION_HYBRID_ONLY)) {
|
|
|
e9098c |
ferr = ARGV_ERROR_HYBRID_ONLY;
|
|
|
e9098c |
|
|
|
e9098c |
} else if (option->optarg == ARGV_OPTARG_NONE) {
|
|
|
e9098c |
if (val[0]) {
|
|
|
e9098c |
ferr = ARGV_ERROR_OPTARG_NONE;
|
|
|
e9098c |
ctx->errch = val + 1;
|
|
|
e9098c |
} else {
|
|
|
e9098c |
fval = false;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
} else if (val[0] && (option->flags & ARGV_OPTION_HYBRID_JOINED)) {
|
|
|
e9098c |
fval = true;
|
|
|
e9098c |
ch = val;
|
|
|
e9098c |
|
|
|
e9098c |
} else if (fhybrid && !val[0] && !(option->flags & ARGV_OPTION_HYBRID_SPACE)) {
|
|
|
e9098c |
if (option->optarg == ARGV_OPTARG_OPTIONAL) {
|
|
|
e9098c |
fval = false;
|
|
|
e9098c |
|
|
|
e9098c |
} else {
|
|
|
e9098c |
ferr = ARGV_ERROR_HYBRID_SPACE;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
} else if (fhybrid && (val[0]=='=') && !(option->flags & ARGV_OPTION_HYBRID_EQUAL)) {
|
|
|
e9098c |
ferr = ARGV_ERROR_HYBRID_EQUAL;
|
|
|
e9098c |
|
|
|
e9098c |
} else if (fhybrid && (val[0]==',') && !(option->flags & ARGV_OPTION_HYBRID_COMMA)) {
|
|
|
e9098c |
ferr = ARGV_ERROR_HYBRID_COMMA;
|
|
|
e9098c |
|
|
|
e9098c |
} else if (!fhybrid && (val[0]==',')) {
|
|
|
e9098c |
ferr = ARGV_ERROR_HYBRID_COMMA;
|
|
|
e9098c |
|
|
|
e9098c |
} else if (val[0] && !val[1]) {
|
|
|
e9098c |
ferr = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
e9098c |
|
|
|
e9098c |
} else if (val[0] && val[1]) {
|
|
|
e9098c |
fval = true;
|
|
|
e9098c |
ch = ++val;
|
|
|
e9098c |
|
|
|
e9098c |
} else if (option->optarg == ARGV_OPTARG_REQUIRED) {
|
|
|
e9098c |
if (!val[0] && !*parg) {
|
|
|
e9098c |
ferr = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
e9098c |
|
|
|
e9098c |
} else if (*parg && is_short_option(*parg)) {
|
|
|
e9098c |
ferr = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
e9098c |
|
|
|
e9098c |
} else if (*parg && is_long_option(*parg)) {
|
|
|
e9098c |
ferr = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
e9098c |
|
|
|
e9098c |
} else if (*parg && is_last_option(*parg)) {
|
|
|
e9098c |
ferr = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
e9098c |
|
|
|
e9098c |
} else {
|
|
|
e9098c |
fval = true;
|
|
|
e9098c |
}
|
|
|
e9098c |
} else {
|
|
|
e9098c |
/* ARGV_OPTARG_OPTIONAL */
|
|
|
e9098c |
fval = val[0];
|
|
|
e9098c |
}
|
|
|
e9098c |
} else {
|
|
|
e9098c |
ferr = ARGV_ERROR_LONG_OPTION;
|
|
|
e9098c |
}
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
if (ferr == ARGV_ERROR_OK)
|
|
|
e9098c |
if (option && fval && option->paradigm)
|
|
|
e9098c |
if (!is_arg_in_paradigm(ch,option->paradigm))
|
|
|
e9098c |
ferr = ARGV_ERROR_OPTARG_PARADIGM;
|
|
|
e9098c |
|
|
|
e9098c |
if (ferr == ARGV_ERROR_OK)
|
|
|
e9098c |
if (!option && !ctx->unitidx)
|
|
|
e9098c |
ctx->unitidx = parg - argv;
|
|
|
e9098c |
|
|
|
917bb6 |
if (ferr == ARGV_ERROR_OK)
|
|
|
917bb6 |
if (option && (option->flags & ARGV_OPTION_KEYVAL_MASK))
|
|
|
917bb6 |
if (ctx->mode == ARGV_MODE_SCAN)
|
|
|
917bb6 |
ctx->keyvlen += strlen(ch) + 1;
|
|
|
917bb6 |
|
|
|
e9098c |
if (ferr != ARGV_ERROR_OK) {
|
|
|
e9098c |
ctx->errcode = ferr;
|
|
|
e9098c |
ctx->errch = ctx->errch ? ctx->errch : ch;
|
|
|
e9098c |
ctx->erropt = option;
|
|
|
e9098c |
ctx->erridx = parg - argv;
|
|
|
e9098c |
return;
|
|
|
917bb6 |
}
|
|
|
917bb6 |
|
|
|
917bb6 |
if (ctx->mode == ARGV_MODE_SCAN) {
|
|
|
e9098c |
if (!fnoscan)
|
|
|
e9098c |
ctx->nentries++;
|
|
|
e9098c |
else if (fval)
|
|
|
e9098c |
ctx->nentries++;
|
|
|
e9098c |
|
|
|
e9098c |
if (fval || !option) {
|
|
|
e9098c |
parg++;
|
|
|
e9098c |
ch = *parg;
|
|
|
e9098c |
}
|
|
|
917bb6 |
|
|
|
e9098c |
} else if (ctx->mode == ARGV_MODE_COPY) {
|
|
|
e9098c |
if (fnoscan) {
|
|
|
e9098c |
if (fval) {
|
|
|
e9098c |
mentry->arg = ch;
|
|
|
e9098c |
mentry->fnoscan = true;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
parg++;
|
|
|
e9098c |
ch = *parg;
|
|
|
e9098c |
} else if (option) {
|
|
|
e9098c |
mentry->arg = fval ? ch : 0;
|
|
|
e9098c |
mentry->tag = option->tag;
|
|
|
e9098c |
mentry->fopt = true;
|
|
|
e9098c |
mentry->fval = fval;
|
|
|
e9098c |
|
|
|
e9098c |
if (fval) {
|
|
|
e9098c |
parg++;
|
|
|
e9098c |
ch = *parg;
|
|
|
e9098c |
}
|
|
|
e9098c |
} else {
|
|
|
e9098c |
mentry->arg = ch;
|
|
|
e9098c |
parg++;
|
|
|
e9098c |
ch = *parg;
|
|
|
e9098c |
}
|
|
|
e9098c |
}
|
|
|
917bb6 |
|
|
|
917bb6 |
if (option && (option->flags & ARGV_OPTION_KEYVAL_PAIR))
|
|
|
917bb6 |
if (ctx->mode == ARGV_MODE_COPY)
|
|
|
917bb6 |
ferr = argv_scan_keyval_pair(imeta,mentry);
|
|
|
917bb6 |
|
|
|
917bb6 |
if (option && (option->flags & ARGV_OPTION_KEYVAL_ARRAY))
|
|
|
917bb6 |
if (ctx->mode == ARGV_MODE_COPY)
|
|
|
917bb6 |
ferr = argv_scan_keyval_array(imeta,mentry);
|
|
|
917bb6 |
|
|
|
917bb6 |
if (ferr != ARGV_ERROR_OK) {
|
|
|
917bb6 |
ctx->errcode = ferr;
|
|
|
917bb6 |
ctx->errch = ctx->errch ? ctx->errch : ch;
|
|
|
917bb6 |
ctx->erropt = option;
|
|
|
917bb6 |
ctx->erridx = parg - argv;
|
|
|
917bb6 |
return;
|
|
|
917bb6 |
}
|
|
|
917bb6 |
|
|
|
917bb6 |
mentry++;
|
|
|
e9098c |
}
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
static const char * argv_program_name(const char * program_path)
|
|
|
e9098c |
{
|
|
|
e9098c |
const char * ch;
|
|
|
e9098c |
|
|
|
e9098c |
if (program_path) {
|
|
|
e9098c |
if ((ch = strrchr(program_path,'/')))
|
|
|
e9098c |
return *(++ch) ? ch : 0;
|
|
|
e9098c |
|
|
|
e9098c |
if ((ch = strrchr(program_path,'\\')))
|
|
|
e9098c |
return *(++ch) ? ch : 0;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
return program_path;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
static void argv_show_error(int fd, struct argv_ctx * ctx)
|
|
|
e9098c |
{
|
|
|
e9098c |
const char * src;
|
|
|
e9098c |
char * dst;
|
|
|
e9098c |
char * cap;
|
|
|
e9098c |
char opt_vendor_buf[256];
|
|
|
e9098c |
char opt_short_name[2] = {0,0};
|
|
|
e9098c |
|
|
|
e9098c |
if (ctx->erropt && ctx->erropt->short_name)
|
|
|
e9098c |
opt_short_name[0] = ctx->erropt->short_name;
|
|
|
e9098c |
|
|
|
e9098c |
argv_dprintf(fd,"%s: error: ",ctx->program);
|
|
|
e9098c |
|
|
|
e9098c |
switch (ctx->errcode) {
|
|
|
e9098c |
case ARGV_ERROR_SHORT_OPTION:
|
|
|
e9098c |
argv_dprintf(fd,"'-%c' is not a valid short option\n",*ctx->errch);
|
|
|
e9098c |
break;
|
|
|
e9098c |
|
|
|
e9098c |
case ARGV_ERROR_LONG_OPTION:
|
|
|
e9098c |
argv_dprintf(fd,"'--%s' is not a valid long option\n",ctx->errch);
|
|
|
e9098c |
break;
|
|
|
e9098c |
|
|
|
e9098c |
case ARGV_ERROR_VENDOR_OPTION:
|
|
|
e9098c |
src = ctx->errch;
|
|
|
e9098c |
dst = opt_vendor_buf;
|
|
|
e9098c |
cap = &opt_vendor_buf[sizeof(opt_vendor_buf)];
|
|
|
e9098c |
|
|
|
e9098c |
for (; src && *src && dst
|
|
|
e9098c |
if ((*src == '=') || (*src == ',') || (*src == ':')) {
|
|
|
e9098c |
src = 0;
|
|
|
e9098c |
} else {
|
|
|
e9098c |
*dst++ = *src++;
|
|
|
e9098c |
}
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
if (dst == cap)
|
|
|
e9098c |
dst--;
|
|
|
e9098c |
|
|
|
e9098c |
*dst = '\0';
|
|
|
e9098c |
|
|
|
e9098c |
argv_dprintf(fd,"'-%s' is not a valid vendor option\n",opt_vendor_buf);
|
|
|
e9098c |
break;
|
|
|
e9098c |
|
|
|
e9098c |
case ARGV_ERROR_OPTARG_NONE:
|
|
|
e9098c |
argv_dprintf(fd,"'%s' is not a valid option value for [%s%s%s%s%s] "
|
|
|
e9098c |
"(option values may not be specified)\n",
|
|
|
e9098c |
ctx->errch,
|
|
|
e9098c |
opt_short_name[0] ? "-" : "",
|
|
|
e9098c |
opt_short_name,
|
|
|
e9098c |
opt_short_name[0] ? "," : "",
|
|
|
e9098c |
ctx->erropt->long_name ? "--" : "",
|
|
|
e9098c |
ctx->erropt->long_name);
|
|
|
e9098c |
break;
|
|
|
e9098c |
|
|
|
e9098c |
case ARGV_ERROR_OPTARG_REQUIRED:
|
|
|
e9098c |
argv_dprintf(fd,"option [%s%s%s%s%s] requires %s %s%s%s\n",
|
|
|
e9098c |
opt_short_name[0] ? "-" : "",
|
|
|
e9098c |
opt_short_name,
|
|
|
e9098c |
opt_short_name[0] ? "," : "",
|
|
|
e9098c |
ctx->erropt->long_name
|
|
|
e9098c |
? (ctx->erropt->flags & ARGV_OPTION_HYBRID_ONLY) ? "-" : "--"
|
|
|
e9098c |
: "",
|
|
|
e9098c |
ctx->erropt->long_name,
|
|
|
e9098c |
ctx->erropt->paradigm ? "one of the following values:" : "a value",
|
|
|
e9098c |
ctx->erropt->paradigm ? "{" : "",
|
|
|
e9098c |
ctx->erropt->paradigm ? ctx->erropt->paradigm : "",
|
|
|
e9098c |
ctx->erropt->paradigm ? "}" : "");
|
|
|
e9098c |
break;
|
|
|
e9098c |
|
|
|
e9098c |
case ARGV_ERROR_OPTARG_PARADIGM:
|
|
|
e9098c |
argv_dprintf(fd,"'%s' is not a valid option value for [%s%s%s%s%s]={%s}\n",
|
|
|
e9098c |
ctx->errch,
|
|
|
e9098c |
opt_short_name[0] ? "-" : "",
|
|
|
e9098c |
opt_short_name,
|
|
|
e9098c |
opt_short_name[0] ? "," : "",
|
|
|
e9098c |
ctx->erropt->long_name ? "--" : "",
|
|
|
e9098c |
ctx->erropt->long_name,
|
|
|
e9098c |
ctx->erropt->paradigm);
|
|
|
e9098c |
break;
|
|
|
e9098c |
|
|
|
e9098c |
case ARGV_ERROR_HYBRID_NONE:
|
|
|
e9098c |
argv_dprintf(fd,"-%s is not a synonym for --%s\n",
|
|
|
e9098c |
ctx->erropt->long_name,
|
|
|
e9098c |
ctx->erropt->long_name);
|
|
|
e9098c |
break;
|
|
|
e9098c |
|
|
|
e9098c |
case ARGV_ERROR_HYBRID_ONLY:
|
|
|
e9098c |
argv_dprintf(fd,"--%s is not a synonym for -%s\n",
|
|
|
e9098c |
ctx->erropt->long_name,
|
|
|
e9098c |
ctx->erropt->long_name);
|
|
|
e9098c |
break;
|
|
|
e9098c |
|
|
|
e9098c |
case ARGV_ERROR_HYBRID_SPACE:
|
|
|
e9098c |
case ARGV_ERROR_HYBRID_EQUAL:
|
|
|
e9098c |
case ARGV_ERROR_HYBRID_COMMA:
|
|
|
e9098c |
argv_dprintf(fd,"-%s: illegal value assignment; valid syntax is "
|
|
|
e9098c |
"-%s%sVAL\n",
|
|
|
e9098c |
ctx->erropt->long_name,
|
|
|
e9098c |
ctx->erropt->long_name,
|
|
|
e9098c |
(ctx->erropt->flags & ARGV_OPTION_HYBRID_SPACE)
|
|
|
e9098c |
? " " : (ctx->erropt->flags & ARGV_OPTION_HYBRID_EQUAL)
|
|
|
e9098c |
? "=" : (ctx->erropt->flags & ARGV_OPTION_HYBRID_COMMA)
|
|
|
e9098c |
? "," : "");
|
|
|
e9098c |
|
|
|
e9098c |
break;
|
|
|
e9098c |
|
|
|
917bb6 |
case ARGV_ERROR_KEYVAL_KEY:
|
|
|
917bb6 |
argv_dprintf(fd,"illegal key detected in keyval argument\n");
|
|
|
917bb6 |
break;
|
|
|
917bb6 |
|
|
|
917bb6 |
case ARGV_ERROR_KEYVAL_VALUE:
|
|
|
917bb6 |
argv_dprintf(fd,"illegal value detected in keyval argument\n");
|
|
|
917bb6 |
break;
|
|
|
917bb6 |
|
|
|
e9098c |
case ARGV_ERROR_INTERNAL:
|
|
|
e9098c |
argv_dprintf(fd,"internal error");
|
|
|
e9098c |
break;
|
|
|
e9098c |
|
|
|
e9098c |
default:
|
|
|
e9098c |
break;
|
|
|
e9098c |
}
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
static void argv_show_status(
|
|
|
e9098c |
int fd,
|
|
|
e9098c |
const struct argv_option ** optv,
|
|
|
e9098c |
struct argv_ctx * ctx,
|
|
|
e9098c |
struct argv_meta * meta)
|
|
|
e9098c |
{
|
|
|
e9098c |
int argc;
|
|
|
e9098c |
char ** argv;
|
|
|
e9098c |
struct argv_entry * entry;
|
|
|
e9098c |
const struct argv_option * option;
|
|
|
e9098c |
char short_name[2] = {0};
|
|
|
e9098c |
const char * space = "";
|
|
|
e9098c |
|
|
|
e9098c |
(void)ctx;
|
|
|
e9098c |
|
|
|
e9098c |
argv_dprintf(fd,"\n\nconcatenated command line:\n");
|
|
|
e9098c |
for (argv=meta->argv; *argv; argv++) {
|
|
|
e9098c |
argv_dprintf(fd,"%s%s",space,*argv);
|
|
|
e9098c |
space = " ";
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
argv_dprintf(fd,"\n\nargument vector:\n");
|
|
|
e9098c |
for (argc=0,argv=meta->argv; *argv; argc++,argv++)
|
|
|
e9098c |
argv_dprintf(fd,"argv[%d]: %s\n",argc,*argv);
|
|
|
e9098c |
|
|
|
e9098c |
argv_dprintf(fd,"\n\nparsed entries:\n");
|
|
|
e1e5c7 |
for (entry=meta->entries; entry->arg || entry->fopt; entry++) {
|
|
|
e9098c |
if (entry->fopt) {
|
|
|
e9098c |
option = option_from_tag(optv,entry->tag);
|
|
|
e9098c |
short_name[0] = option->short_name;
|
|
|
e9098c |
|
|
|
e9098c |
if (entry->fval)
|
|
|
e9098c |
argv_dprintf(fd,"[-%s,--%s] := %s\n",
|
|
|
e9098c |
short_name,option->long_name,entry->arg);
|
|
|
e9098c |
else
|
|
|
e9098c |
argv_dprintf(fd,"[-%s,--%s]\n",
|
|
|
e9098c |
short_name,option->long_name);
|
|
|
e1e5c7 |
} else {
|
|
|
e9098c |
argv_dprintf(fd,"<program arg> := %s\n",entry->arg);
|
|
|
e1e5c7 |
}
|
|
|
e1e5c7 |
}
|
|
|
e9098c |
|
|
|
e9098c |
argv_dprintf(fd,"\n\n");
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
static struct argv_meta * argv_free_impl(struct argv_meta_impl * imeta)
|
|
|
e9098c |
{
|
|
|
917bb6 |
struct argv_entry * entry;
|
|
|
917bb6 |
void * addr;
|
|
|
917bb6 |
|
|
|
917bb6 |
if (imeta->keyvbuf)
|
|
|
917bb6 |
for (entry=imeta->meta.entries; entry->fopt || entry->arg; entry++)
|
|
|
917bb6 |
if (entry->keyv)
|
|
|
917bb6 |
free((addr = entry->keyv));
|
|
|
917bb6 |
|
|
|
917bb6 |
if (imeta->keyvbuf)
|
|
|
917bb6 |
free(imeta->keyvbuf);
|
|
|
917bb6 |
|
|
|
e9098c |
if (imeta->argv)
|
|
|
e9098c |
free(imeta->argv);
|
|
|
e9098c |
|
|
|
e9098c |
if (imeta->strbuf)
|
|
|
e9098c |
free(imeta->strbuf);
|
|
|
e9098c |
|
|
|
e9098c |
if (imeta->meta.entries)
|
|
|
e9098c |
free(imeta->meta.entries);
|
|
|
e9098c |
|
|
|
e9098c |
free(imeta);
|
|
|
e9098c |
return 0;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
static struct argv_meta * argv_alloc(char ** argv, struct argv_ctx * ctx)
|
|
|
e9098c |
{
|
|
|
e9098c |
struct argv_meta_impl * imeta;
|
|
|
e9098c |
char ** vector;
|
|
|
e9098c |
char * dst;
|
|
|
e9098c |
size_t size;
|
|
|
e9098c |
int argc;
|
|
|
e9098c |
int i;
|
|
|
e9098c |
|
|
|
e9098c |
if (!(imeta = calloc(1,sizeof(*imeta))))
|
|
|
e9098c |
return 0;
|
|
|
e9098c |
|
|
|
e9098c |
if (ctx->flags & ARGV_CLONE_VECTOR) {
|
|
|
e9098c |
for (vector=argv,argc=0,size=0; *vector; vector++) {
|
|
|
e9098c |
size += strlen(*vector) + 1;
|
|
|
e9098c |
argc++;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
if (!(imeta->argv = calloc(argc+1,sizeof(char *))))
|
|
|
e9098c |
return argv_free_impl(imeta);
|
|
|
db73e4 |
|
|
|
db73e4 |
if (!(imeta->strbuf = calloc(1,size+1)))
|
|
|
e9098c |
return argv_free_impl(imeta);
|
|
|
e9098c |
|
|
|
e9098c |
for (i=0,dst=imeta->strbuf; i
|
|
|
e9098c |
strcpy(dst,argv[i]);
|
|
|
e9098c |
imeta->argv[i] = dst;
|
|
|
e9098c |
dst += strlen(dst)+1;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
imeta->meta.argv = imeta->argv;
|
|
|
db73e4 |
} else {
|
|
|
e9098c |
imeta->meta.argv = argv;
|
|
|
db73e4 |
}
|
|
|
e9098c |
|
|
|
db73e4 |
imeta->meta.entries = calloc(
|
|
|
db73e4 |
ctx->nentries+1,
|
|
|
db73e4 |
sizeof(struct argv_entry));
|
|
|
db73e4 |
|
|
|
db73e4 |
if (!imeta->meta.entries)
|
|
|
e9098c |
return argv_free_impl(imeta);
|
|
|
db73e4 |
|
|
|
917bb6 |
if (ctx->keyvlen) {
|
|
|
917bb6 |
imeta->keyvbuf = calloc(
|
|
|
917bb6 |
ctx->keyvlen,
|
|
|
917bb6 |
sizeof(char));
|
|
|
917bb6 |
|
|
|
917bb6 |
if (!(imeta->keyvmark = imeta->keyvbuf))
|
|
|
917bb6 |
return argv_free_impl(imeta);
|
|
|
917bb6 |
}
|
|
|
917bb6 |
|
|
|
db73e4 |
return &imeta->meta;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
static struct argv_meta * argv_get(
|
|
|
e9098c |
char ** argv,
|
|
|
e9098c |
const struct argv_option ** optv,
|
|
|
e9098c |
int flags,
|
|
|
e9098c |
int fd)
|
|
|
e9098c |
{
|
|
|
e9098c |
struct argv_meta * meta;
|
|
|
917bb6 |
struct argv_ctx ctx = {flags,ARGV_MODE_SCAN,0,0,0,0,0,0,0,0};
|
|
|
e9098c |
|
|
|
e9098c |
argv_scan(argv,optv,&ctx,0);
|
|
|
e9098c |
|
|
|
e9098c |
if (ctx.errcode != ARGV_ERROR_OK) {
|
|
|
e9098c |
ctx.program = argv_program_name(argv[0]);
|
|
|
e9098c |
|
|
|
e9098c |
if (ctx.flags & ARGV_VERBOSITY_ERRORS)
|
|
|
e9098c |
argv_show_error(fd,&ctx;;
|
|
|
e9098c |
|
|
|
e9098c |
return 0;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
if (!(meta = argv_alloc(argv,&ctx)))
|
|
|
e9098c |
return 0;
|
|
|
e9098c |
|
|
|
e9098c |
ctx.mode = ARGV_MODE_COPY;
|
|
|
e9098c |
argv_scan(meta->argv,optv,&ctx,meta);
|
|
|
e9098c |
|
|
|
e9098c |
if (ctx.errcode != ARGV_ERROR_OK) {
|
|
|
e9098c |
ctx.program = argv[0];
|
|
|
e9098c |
ctx.errcode = ARGV_ERROR_INTERNAL;
|
|
|
e9098c |
argv_show_error(fd,&ctx;;
|
|
|
e9098c |
argv_free(meta);
|
|
|
e9098c |
|
|
|
e9098c |
return 0;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
if (ctx.flags & ARGV_VERBOSITY_STATUS)
|
|
|
e9098c |
argv_show_status(fd,optv,&ctx,meta);
|
|
|
e9098c |
|
|
|
e9098c |
return meta;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
static void argv_free(struct argv_meta * xmeta)
|
|
|
e9098c |
{
|
|
|
e9098c |
struct argv_meta_impl * imeta;
|
|
|
e9098c |
uintptr_t addr;
|
|
|
e9098c |
|
|
|
e9098c |
if (xmeta) {
|
|
|
e9098c |
addr = (uintptr_t)xmeta - offsetof(struct argv_meta_impl,meta);
|
|
|
e9098c |
imeta = (struct argv_meta_impl *)addr;
|
|
|
e9098c |
argv_free_impl(imeta);
|
|
|
e9098c |
}
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
static void argv_usage_impl(
|
|
|
e9098c |
int fd,
|
|
|
e9098c |
const char * header,
|
|
|
e9098c |
const struct argv_option ** options,
|
|
|
e9098c |
const char * mode,
|
|
|
e9098c |
int fcolor)
|
|
|
e9098c |
{
|
|
|
e9098c |
const struct argv_option ** optv;
|
|
|
e9098c |
const struct argv_option * option;
|
|
|
e80d48 |
int nlong;
|
|
|
e9098c |
bool fshort,flong,fboth;
|
|
|
e9098c |
size_t len,optlen,desclen;
|
|
|
e9098c |
char cache;
|
|
|
e9098c |
char * prefix;
|
|
|
e9098c |
char * desc;
|
|
|
e9098c |
char * mark;
|
|
|
e9098c |
char * cap;
|
|
|
e9098c |
char description[4096];
|
|
|
e9098c |
char optstr [72];
|
|
|
e9098c |
const size_t optcap = 64;
|
|
|
e9098c |
const size_t width = 80;
|
|
|
e9098c |
const char indent[] = " ";
|
|
|
e9098c |
const char creset[] = "\x1b[0m";
|
|
|
e9098c |
const char cbold [] = "\x1b[1m";
|
|
|
e9098c |
const char cgreen[] = "\x1b[32m";
|
|
|
e9098c |
const char cblue [] = "\x1b[34m";
|
|
|
e9098c |
const char ccyan [] = "\x1b[36m";
|
|
|
e9098c |
const char * color = ccyan;
|
|
|
e9098c |
|
|
|
e9098c |
(void)argv_usage;
|
|
|
e9098c |
(void)argv_usage_plain;
|
|
|
e9098c |
|
|
|
e9098c |
fshort = mode ? !strcmp(mode,"short") : 0;
|
|
|
e9098c |
flong = fshort ? 0 : mode && !strcmp(mode,"long");
|
|
|
e9098c |
fboth = !fshort && !flong;
|
|
|
e9098c |
|
|
|
e9098c |
if (fcolor)
|
|
|
e9098c |
argv_dprintf(fd,"%s%s",cbold,cgreen);
|
|
|
e9098c |
|
|
|
e9098c |
if (header)
|
|
|
e9098c |
argv_dprintf(fd,"%s",header);
|
|
|
e9098c |
|
|
|
e80d48 |
for (optlen=0,nlong=0,optv=options; *optv; optv++) {
|
|
|
e9098c |
option = *optv;
|
|
|
e9098c |
|
|
|
e9098c |
/* indent + comma */
|
|
|
e9098c |
len = fboth ? sizeof(indent) + 1 : sizeof(indent);
|
|
|
e9098c |
|
|
|
e9098c |
/* -o */
|
|
|
e9098c |
if (fshort || fboth)
|
|
|
e9098c |
len += option->short_name
|
|
|
e9098c |
? 2 : 0;
|
|
|
e9098c |
|
|
|
e9098c |
/* --option */
|
|
|
e9098c |
if (flong || fboth)
|
|
|
e9098c |
len += option->long_name
|
|
|
e9098c |
? 2 + strlen(option->long_name) : 0;
|
|
|
e9098c |
|
|
|
e9098c |
/* optlen */
|
|
|
e9098c |
if (len > optlen)
|
|
|
e9098c |
optlen = len;
|
|
|
e80d48 |
|
|
|
e80d48 |
/* long (vs. hybrid-only) option? */
|
|
|
e80d48 |
if (option->long_name)
|
|
|
e80d48 |
if (!(option->flags & ARGV_OPTION_HYBRID_ONLY))
|
|
|
e80d48 |
nlong++;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
if (optlen >= optcap) {
|
|
|
e9098c |
argv_dprintf(fd,
|
|
|
e9098c |
"Option strings exceed %zu characters, "
|
|
|
e9098c |
"please generate the usage screen manually.\n",
|
|
|
e9098c |
optcap);
|
|
|
e9098c |
return;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
optlen += ARGV_TAB_WIDTH;
|
|
|
e9098c |
optlen &= (~(ARGV_TAB_WIDTH-1));
|
|
|
e9098c |
desclen = (optlen < width / 2) ? width - optlen : optlen;
|
|
|
e9098c |
|
|
|
e9098c |
for (optv=options; *optv; optv++) {
|
|
|
e9098c |
option = *optv;
|
|
|
e9098c |
|
|
|
e9098c |
/* color */
|
|
|
e9098c |
if (fcolor) {
|
|
|
e9098c |
color = (color == ccyan) ? cblue : ccyan;
|
|
|
93e9d7 |
argv_dprintf(fd,color,0);
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
/* description, using either paradigm or argname if applicable */
|
|
|
e9098c |
snprintf(description,sizeof(description),option->description,
|
|
|
e9098c |
option->paradigm
|
|
|
e9098c |
? option->paradigm
|
|
|
e9098c |
: option->argname ? option->argname : "");
|
|
|
e9098c |
description[sizeof(description)-1] = 0;
|
|
|
e9098c |
|
|
|
e9098c |
/* long/hybrid option prefix (-/--) */
|
|
|
e9098c |
prefix = option->flags & ARGV_OPTION_HYBRID_ONLY
|
|
|
a69ceb |
? " -" : " --";
|
|
|
e9098c |
|
|
|
e80d48 |
/* avoid extra <stace> when all long opts are hybrid-only */
|
|
|
e80d48 |
if (nlong == 0)
|
|
|
e80d48 |
prefix++;
|
|
|
e80d48 |
|
|
|
e9098c |
/* option string */
|
|
|
e9098c |
if (fboth && option->short_name && option->long_name)
|
|
|
e9098c |
sprintf(optstr,"%s-%c,%s%s",
|
|
|
e9098c |
indent,option->short_name,prefix,option->long_name);
|
|
|
e9098c |
|
|
|
e9098c |
else if ((fshort || fboth) && option->short_name)
|
|
|
e9098c |
sprintf(optstr,"%s-%c",indent,option->short_name);
|
|
|
e9098c |
|
|
|
e9098c |
else if (flong && option->long_name)
|
|
|
e9098c |
sprintf(optstr,"%s%s%s",
|
|
|
e9098c |
indent,prefix,option->long_name);
|
|
|
e9098c |
|
|
|
e9098c |
else if (fboth && option->long_name)
|
|
|
e9098c |
sprintf(optstr,"%s %s%s",
|
|
|
e9098c |
indent,prefix,option->long_name);
|
|
|
e9098c |
|
|
|
e9098c |
else
|
|
|
e9098c |
optstr[0] = 0;
|
|
|
e9098c |
|
|
|
e9098c |
/* right-indented option buffer */
|
|
|
e9098c |
if (description[0]) {
|
|
|
e9098c |
len = strlen(optstr);
|
|
|
e9098c |
sprintf(&optstr[len],"%-*c",(int)(optlen-len),' ');
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
/* single line? */
|
|
|
e9098c |
if (optlen + strlen(description) < width) {
|
|
|
e9098c |
argv_dprintf(fd,"%s%s\n",optstr,description);
|
|
|
e9098c |
|
|
|
e9098c |
} else {
|
|
|
e9098c |
desc = description;
|
|
|
e9098c |
cap = desc + strlen(description);
|
|
|
e9098c |
|
|
|
e9098c |
while (desc < cap) {
|
|
|
e9098c |
mark = (desc + desclen >= cap)
|
|
|
e9098c |
? cap : desc + desclen;
|
|
|
e9098c |
|
|
|
e9098c |
while (*mark && (mark > desc)
|
|
|
e9098c |
&& (*mark != ' ')
|
|
|
e9098c |
&& (*mark != '|')
|
|
|
e9098c |
&& (*mark != '\t')
|
|
|
e9098c |
&& (*mark != '\n'))
|
|
|
e9098c |
mark--;
|
|
|
e9098c |
|
|
|
e9098c |
if (mark == desc) {
|
|
|
e9098c |
mark = (desc + desclen >= cap)
|
|
|
e9098c |
? cap : desc + desclen;
|
|
|
e9098c |
cache = *mark;
|
|
|
e9098c |
*mark = 0;
|
|
|
e9098c |
} else if (*mark == '|') {
|
|
|
e9098c |
cache = *mark;
|
|
|
e9098c |
*mark = 0;
|
|
|
e9098c |
} else {
|
|
|
e9098c |
cache = 0;
|
|
|
e9098c |
*mark = 0;
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
/* first line? */
|
|
|
e9098c |
if (desc == description)
|
|
|
e9098c |
argv_dprintf(fd,"%s%s\n",optstr,desc);
|
|
|
e9098c |
else
|
|
|
e9098c |
argv_dprintf(fd,"%-*c %s\n",
|
|
|
e9098c |
(*desc == '|')
|
|
|
e9098c |
? (int)(optlen+1)
|
|
|
e9098c |
: (int)optlen,
|
|
|
e9098c |
' ',desc);
|
|
|
e9098c |
|
|
|
e9098c |
if (cache)
|
|
|
e9098c |
*mark = cache;
|
|
|
e9098c |
else
|
|
|
e9098c |
mark++;
|
|
|
e9098c |
|
|
|
e9098c |
desc = mark;
|
|
|
e9098c |
}
|
|
|
e9098c |
}
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
if (fcolor)
|
|
|
e9098c |
argv_dprintf(fd,creset);
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
static void argv_usage(
|
|
|
e9098c |
int fd,
|
|
|
e9098c |
const char * header,
|
|
|
e9098c |
const struct argv_option ** options,
|
|
|
e9098c |
const char * mode)
|
|
|
e9098c |
{
|
|
|
e9098c |
argv_usage_impl(fd,header,options,mode,isatty(fd));
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
static void argv_usage_plain(
|
|
|
e9098c |
int fd,
|
|
|
e9098c |
const char * header,
|
|
|
e9098c |
const struct argv_option ** options,
|
|
|
e9098c |
const char * mode)
|
|
|
e9098c |
{
|
|
|
e9098c |
argv_usage_impl(fd,header,options,mode,0);
|
|
|
e9098c |
}
|
|
|
e9098c |
|
|
|
e9098c |
#endif
|
|
|
e9098c |
|
|
|
e9098c |
#endif
|