|
|
1b12c3 |
/****************************************************************************/
|
|
|
1b12c3 |
/* argv.h: a thread-safe argument vector parser and usage screen generator */
|
|
|
e2e2c2 |
/* Copyright (C) 2015--2016 Z. Gilboa */
|
|
|
1b12c3 |
/* Released under GPLv2 and GPLv3; see COPYING.PERK. */
|
|
|
1b12c3 |
/* This file is (also) part of sofort: portable software project template. */
|
|
|
1b12c3 |
/****************************************************************************/
|
|
|
1b12c3 |
|
|
|
1b12c3 |
#ifndef ARGV_H
|
|
|
1b12c3 |
#define ARGV_H
|
|
|
1b12c3 |
|
|
|
1b12c3 |
#include <stdbool.h>
|
|
|
1b12c3 |
#include <stdint.h>
|
|
|
1b12c3 |
#include <stddef.h>
|
|
|
1b12c3 |
#include <errno.h>
|
|
|
1b12c3 |
#include <string.h>
|
|
|
1b12c3 |
#include <stdlib.h>
|
|
|
1b12c3 |
#include <stdio.h>
|
|
|
1b12c3 |
|
|
|
1b12c3 |
#define ARGV_VERBOSITY_NONE 0x00
|
|
|
1b12c3 |
#define ARGV_VERBOSITY_ERRORS 0x01
|
|
|
1b12c3 |
#define ARGV_VERBOSITY_STATUS 0x02
|
|
|
1b12c3 |
#define ARGV_CLONE_VECTOR 0x80
|
|
|
1b12c3 |
|
|
|
2245d9 |
#ifndef ARGV_TAB_WIDTH
|
|
|
2245d9 |
#define ARGV_TAB_WIDTH 8
|
|
|
a07457 |
#endif
|
|
|
a07457 |
|
|
|
12240e |
/*******************************************/
|
|
|
12240e |
/* */
|
|
|
12240e |
/* support of hybrid options */
|
|
|
12240e |
/* ------------------------- */
|
|
|
12240e |
/* hybrid options are very similar to */
|
|
|
12240e |
/* long options, yet are prefixed by */
|
|
|
12240e |
/* a single dash rather than two */
|
|
|
12240e |
/* (i.e. -std, -isystem). */
|
|
|
12240e |
/* hybrid options are supported by this */
|
|
|
12240e |
/* driver for compatibility with legacy */
|
|
|
12240e |
/* tools; note, however, that the use */
|
|
|
12240e |
/* of hybrid options should be strongly */
|
|
|
12240e |
/* discouraged due to the limitations */
|
|
|
12240e |
/* they impose on short options (for */
|
|
|
12240e |
/* example, a driver implementing -std */
|
|
|
12240e |
/* may not provide -s as a short option */
|
|
|
12240e |
/* that takes an arbitrary value). */
|
|
|
12240e |
/* */
|
|
|
12240e |
/* SPACE: -hybrid VALUE (i.e. -MF file) */
|
|
|
12240e |
/* EQUAL: -hybrid=VALUE (i.e. -std=c99) */
|
|
|
12240e |
/* ONLY: -opt accepted, --opt rejected */
|
|
|
12240e |
/* */
|
|
|
12240e |
/*******************************************/
|
|
|
12240e |
|
|
|
12240e |
|
|
|
12240e |
#define ARGV_OPTION_HYBRID_NONE 0x00
|
|
|
12240e |
#define ARGV_OPTION_HYBRID_ONLY 0x01
|
|
|
12240e |
#define ARGV_OPTION_HYBRID_SPACE 0x02
|
|
|
12240e |
#define ARGV_OPTION_HYBRID_EQUAL 0x04
|
|
|
12240e |
#define ARGV_OPTION_HYBRID_SWITCH (ARGV_OPTION_HYBRID_SPACE \
|
|
|
12240e |
| ARGV_OPTION_HYBRID_EQUAL)
|
|
|
12240e |
|
|
|
1b12c3 |
enum argv_optarg {
|
|
|
1b12c3 |
ARGV_OPTARG_NONE,
|
|
|
1b12c3 |
ARGV_OPTARG_REQUIRED,
|
|
|
1b12c3 |
ARGV_OPTARG_OPTIONAL,
|
|
|
1b12c3 |
};
|
|
|
1b12c3 |
|
|
|
1b12c3 |
enum argv_mode {
|
|
|
1b12c3 |
ARGV_MODE_SCAN,
|
|
|
1b12c3 |
ARGV_MODE_COPY,
|
|
|
1b12c3 |
};
|
|
|
1b12c3 |
|
|
|
1b12c3 |
enum argv_error {
|
|
|
1b12c3 |
ARGV_ERROR_OK,
|
|
|
1b12c3 |
ARGV_ERROR_INTERNAL,
|
|
|
1b12c3 |
ARGV_ERROR_SHORT_OPTION,
|
|
|
1b12c3 |
ARGV_ERROR_LONG_OPTION,
|
|
|
1b12c3 |
ARGV_ERROR_OPTARG_NONE,
|
|
|
1b12c3 |
ARGV_ERROR_OPTARG_REQUIRED,
|
|
|
1b12c3 |
ARGV_ERROR_OPTARG_PARADIGM,
|
|
|
12240e |
ARGV_ERROR_HYBRID_NONE,
|
|
|
12240e |
ARGV_ERROR_HYBRID_ONLY,
|
|
|
12240e |
ARGV_ERROR_HYBRID_SPACE,
|
|
|
12240e |
ARGV_ERROR_HYBRID_EQUAL,
|
|
|
1b12c3 |
};
|
|
|
1b12c3 |
|
|
|
1b12c3 |
struct argv_option {
|
|
|
1b12c3 |
const char * long_name;
|
|
|
1b12c3 |
const char short_name;
|
|
|
1b12c3 |
int tag;
|
|
|
1b12c3 |
enum argv_optarg optarg;
|
|
|
12240e |
int flags;
|
|
|
1b12c3 |
const char * paradigm;
|
|
|
1b12c3 |
const char * argname;
|
|
|
1b12c3 |
const char * description;
|
|
|
1b12c3 |
};
|
|
|
1b12c3 |
|
|
|
1b12c3 |
struct argv_entry {
|
|
|
1b12c3 |
const char * arg;
|
|
|
1b12c3 |
int tag;
|
|
|
1b12c3 |
bool fopt;
|
|
|
1b12c3 |
bool fval;
|
|
|
1b12c3 |
bool fnoscan;
|
|
|
1b12c3 |
enum argv_error errcode;
|
|
|
1b12c3 |
};
|
|
|
1b12c3 |
|
|
|
1b12c3 |
struct argv_meta {
|
|
|
1b12c3 |
const char ** argv;
|
|
|
1b12c3 |
struct argv_entry * entries;
|
|
|
1b12c3 |
};
|
|
|
1b12c3 |
|
|
|
1b12c3 |
struct argv_meta_impl {
|
|
|
1b12c3 |
const char ** argv;
|
|
|
1b12c3 |
char * strbuf;
|
|
|
1b12c3 |
struct argv_meta meta;
|
|
|
1b12c3 |
};
|
|
|
1b12c3 |
|
|
|
1b12c3 |
struct argv_ctx {
|
|
|
1b12c3 |
int flags;
|
|
|
1b12c3 |
int mode;
|
|
|
1b12c3 |
int nentries;
|
|
|
1b12c3 |
enum argv_error errcode;
|
|
|
1b12c3 |
const char * errch;
|
|
|
1b12c3 |
const struct argv_option * erropt;
|
|
|
1b12c3 |
const char * program;
|
|
|
1b12c3 |
};
|
|
|
1b12c3 |
|
|
|
c0d0f3 |
#ifdef ARGV_DRIVER
|
|
|
c0d0f3 |
|
|
|
5888e8 |
static const char * argv_program_name(const char *);
|
|
|
5888e8 |
|
|
|
1b12c3 |
static void argv_usage(
|
|
|
1b12c3 |
FILE *,
|
|
|
1b12c3 |
const char * header,
|
|
|
1b12c3 |
const struct argv_option[],
|
|
|
1b12c3 |
const char * mode);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
static struct argv_meta * argv_get(
|
|
|
1b12c3 |
const char **,
|
|
|
1b12c3 |
const struct argv_option[],
|
|
|
1b12c3 |
int flags);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
static void argv_free(struct argv_meta *);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
|
|
|
1b12c3 |
|
|
|
1b12c3 |
|
|
|
1b12c3 |
/*------------------------------------*/
|
|
|
1b12c3 |
/* implementation of static functions */
|
|
|
1b12c3 |
/*------------------------------------*/
|
|
|
1b12c3 |
|
|
|
1b12c3 |
static const struct argv_option * argv_short_option(
|
|
|
1b12c3 |
const char * ch,
|
|
|
1b12c3 |
const struct argv_option options[],
|
|
|
1b12c3 |
struct argv_entry * entry)
|
|
|
1b12c3 |
{
|
|
|
1b12c3 |
const struct argv_option * option;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
for (option=options; option->long_name || option->short_name; option++) {
|
|
|
1b12c3 |
if (option->short_name == *ch) {
|
|
|
1b12c3 |
entry->tag = option->tag;
|
|
|
1b12c3 |
entry->fopt = true;
|
|
|
1b12c3 |
return option;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
return 0;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
static const struct argv_option * argv_long_option(
|
|
|
1b12c3 |
const char * ch,
|
|
|
1b12c3 |
const struct argv_option options[],
|
|
|
1b12c3 |
struct argv_entry * entry)
|
|
|
1b12c3 |
{
|
|
|
1b12c3 |
const struct argv_option * option;
|
|
|
1b12c3 |
const char * arg;
|
|
|
1b12c3 |
size_t len;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
for (option=options; option->long_name || option->short_name; option++) {
|
|
|
1b12c3 |
len = option->long_name ? strlen(option->long_name) : 0;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (len && !(strncmp(option->long_name,ch,len))) {
|
|
|
1b12c3 |
arg = ch + len;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (!*arg || (*arg == '=')) {
|
|
|
1b12c3 |
entry->tag = option->tag;
|
|
|
1b12c3 |
entry->fopt = true;
|
|
|
1b12c3 |
return option;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
return 0;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
static inline bool is_short_option(const char * arg)
|
|
|
1b12c3 |
{
|
|
|
1b12c3 |
return (arg[0]=='-') && arg[1] && (arg[1]!='-');
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
static inline bool is_long_option(const char * arg)
|
|
|
1b12c3 |
{
|
|
|
1b12c3 |
return (arg[0]=='-') && (arg[1]=='-') && arg[2];
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
static inline bool is_last_option(const char * arg)
|
|
|
1b12c3 |
{
|
|
|
1b12c3 |
return (arg[0]=='-') && (arg[1]=='-') && !arg[2];
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
12240e |
static inline bool is_hybrid_option(
|
|
|
12240e |
const char * arg,
|
|
|
12240e |
const struct argv_option options[])
|
|
|
12240e |
{
|
|
|
12240e |
const struct argv_option * option;
|
|
|
12240e |
struct argv_entry entry;
|
|
|
12240e |
|
|
|
12240e |
if (!is_short_option(arg))
|
|
|
12240e |
return false;
|
|
|
12240e |
|
|
|
12240e |
if (!(option = argv_long_option(++arg,options,&entry)))
|
|
|
12240e |
return false;
|
|
|
12240e |
|
|
|
12240e |
if (!(option->flags & ARGV_OPTION_HYBRID_SWITCH))
|
|
|
12240e |
if (argv_short_option(arg,options,&entry))
|
|
|
12240e |
return false;
|
|
|
12240e |
|
|
|
12240e |
return true;
|
|
|
12240e |
}
|
|
|
12240e |
|
|
|
1b12c3 |
static inline bool is_arg_in_paradigm(const char * arg, const char * paradigm)
|
|
|
1b12c3 |
{
|
|
|
1b12c3 |
size_t len;
|
|
|
1b12c3 |
const char * ch;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
for (ch=paradigm,len=strlen(arg); ch; ) {
|
|
|
1b12c3 |
if (!strncmp(arg,ch,len)) {
|
|
|
1b12c3 |
if (!*(ch += len))
|
|
|
1b12c3 |
return true;
|
|
|
1b12c3 |
else if (*ch == '|')
|
|
|
1b12c3 |
return true;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if ((ch = strchr(ch,'|')))
|
|
|
1b12c3 |
ch++;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
return false;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
static inline const struct argv_option * option_from_tag(
|
|
|
1b12c3 |
const struct argv_option options[],
|
|
|
1b12c3 |
int tag)
|
|
|
1b12c3 |
{
|
|
|
1b12c3 |
const struct argv_option * option;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
for (option=options; option->short_name || option->long_name; option++)
|
|
|
1b12c3 |
if (option->tag == tag)
|
|
|
1b12c3 |
return option;
|
|
|
1b12c3 |
return 0;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
static void argv_scan(
|
|
|
1b12c3 |
const char ** argv,
|
|
|
1b12c3 |
const struct argv_option options[],
|
|
|
1b12c3 |
struct argv_ctx * ctx,
|
|
|
1b12c3 |
struct argv_meta * meta)
|
|
|
1b12c3 |
{
|
|
|
1b12c3 |
const char ** parg;
|
|
|
1b12c3 |
const char * ch;
|
|
|
1b12c3 |
const char * val;
|
|
|
1b12c3 |
const struct argv_option * option;
|
|
|
1b12c3 |
struct argv_entry entry;
|
|
|
1b12c3 |
struct argv_entry * mentry;
|
|
|
1b12c3 |
enum argv_error ferror;
|
|
|
1b12c3 |
bool fval;
|
|
|
1b12c3 |
bool fnext;
|
|
|
1b12c3 |
bool fshort;
|
|
|
12240e |
bool fhybrid;
|
|
|
1b12c3 |
bool fnoscan;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
argv++;
|
|
|
1b12c3 |
parg = argv;
|
|
|
1b12c3 |
ch = *parg;
|
|
|
1b12c3 |
ferror = ARGV_ERROR_OK;
|
|
|
1b12c3 |
fshort = false;
|
|
|
1b12c3 |
fnoscan = false;
|
|
|
1b12c3 |
fval = false;
|
|
|
1b12c3 |
mentry = meta ? meta->entries : 0;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
while (ch && (ferror == ARGV_ERROR_OK)) {
|
|
|
12240e |
option = 0;
|
|
|
12240e |
fhybrid = false;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (fnoscan)
|
|
|
1b12c3 |
fval = true;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
else if (is_last_option(ch))
|
|
|
1b12c3 |
fnoscan = true;
|
|
|
1b12c3 |
|
|
|
12240e |
else if (!fshort && is_hybrid_option(ch,options))
|
|
|
12240e |
fhybrid = true;
|
|
|
12240e |
|
|
|
12240e |
if (!fnoscan && !fhybrid && (fshort || is_short_option(ch))) {
|
|
|
1b12c3 |
if (!fshort)
|
|
|
1b12c3 |
ch++;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if ((option = argv_short_option(ch,options,&entry))) {
|
|
|
1b12c3 |
if (ch[1]) {
|
|
|
1b12c3 |
ch++;
|
|
|
1b12c3 |
fnext = false;
|
|
|
1b12c3 |
fshort = (option->optarg == ARGV_OPTARG_NONE);
|
|
|
1b12c3 |
} else {
|
|
|
1b12c3 |
parg++;
|
|
|
1b12c3 |
ch = *parg;
|
|
|
1b12c3 |
fnext = true;
|
|
|
1b12c3 |
fshort = false;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (option->optarg == ARGV_OPTARG_NONE) {
|
|
|
1b12c3 |
if (!fnext && ch && (*ch == '-'))
|
|
|
1b12c3 |
ferror = ARGV_ERROR_OPTARG_NONE;
|
|
|
1b12c3 |
else
|
|
|
1b12c3 |
fval = false;
|
|
|
1b12c3 |
} else if (!fnext)
|
|
|
1b12c3 |
fval = true;
|
|
|
1b12c3 |
else if (option->optarg == ARGV_OPTARG_REQUIRED) {
|
|
|
1b12c3 |
if (ch && is_short_option(ch))
|
|
|
1b12c3 |
ferror = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
1b12c3 |
else if (ch && is_long_option(ch))
|
|
|
1b12c3 |
ferror = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
1b12c3 |
else if (ch && is_last_option(ch))
|
|
|
1b12c3 |
ferror = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
1b12c3 |
else if (ch)
|
|
|
1b12c3 |
fval = true;
|
|
|
1b12c3 |
else
|
|
|
1b12c3 |
ferror = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
1b12c3 |
} else {
|
|
|
1b12c3 |
/* ARGV_OPTARG_OPTIONAL */
|
|
|
1b12c3 |
if (ch && is_short_option(ch))
|
|
|
1b12c3 |
fval = false;
|
|
|
1b12c3 |
else if (ch && is_long_option(ch))
|
|
|
1b12c3 |
fval = false;
|
|
|
1b12c3 |
else if (ch && is_last_option(ch))
|
|
|
1b12c3 |
fval = false;
|
|
|
1b12c3 |
else
|
|
|
1b12c3 |
fval = ch;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
} else
|
|
|
1b12c3 |
ferror = ARGV_ERROR_SHORT_OPTION;
|
|
|
1b12c3 |
|
|
|
12240e |
} else if (!fnoscan && (fhybrid || is_long_option(ch))) {
|
|
|
12240e |
ch += (fhybrid ? 1 : 2);
|
|
|
12240e |
|
|
|
12240e |
if ((option = argv_long_option(ch,options,&entry))) {
|
|
|
1b12c3 |
val = ch + strlen(option->long_name);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
/* val[0] is either '=' or '\0' */
|
|
|
1b12c3 |
if (!val[0]) {
|
|
|
1b12c3 |
parg++;
|
|
|
1b12c3 |
ch = *parg;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
12240e |
if (fhybrid && !(option->flags & ARGV_OPTION_HYBRID_SWITCH))
|
|
|
12240e |
ferror = ARGV_ERROR_HYBRID_NONE;
|
|
|
12240e |
else if (option->optarg == ARGV_OPTARG_NONE) {
|
|
|
1b12c3 |
if (val[0]) {
|
|
|
1b12c3 |
ferror = ARGV_ERROR_OPTARG_NONE;
|
|
|
1b12c3 |
ctx->errch = val + 1;
|
|
|
1b12c3 |
} else
|
|
|
1b12c3 |
fval = false;
|
|
|
12240e |
} else if (!fhybrid && (option->flags & ARGV_OPTION_HYBRID_ONLY))
|
|
|
12240e |
ferror = ARGV_ERROR_HYBRID_ONLY;
|
|
|
12240e |
else if (fhybrid && !val[0] && !(option->flags & ARGV_OPTION_HYBRID_SPACE))
|
|
|
12240e |
ferror = ARGV_ERROR_HYBRID_SPACE;
|
|
|
12240e |
else if (fhybrid && val[0] && !(option->flags & ARGV_OPTION_HYBRID_EQUAL))
|
|
|
12240e |
ferror = ARGV_ERROR_HYBRID_EQUAL;
|
|
|
12240e |
else if (val[0] && !val[1])
|
|
|
1b12c3 |
ferror = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
1b12c3 |
else if (val[0] && val[1]) {
|
|
|
1b12c3 |
fval = true;
|
|
|
1b12c3 |
ch = ++val;
|
|
|
1b12c3 |
} else if (option->optarg == ARGV_OPTARG_REQUIRED) {
|
|
|
1b12c3 |
if (!val[0] && !*parg)
|
|
|
1b12c3 |
ferror = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
1b12c3 |
else if (*parg && is_short_option(*parg))
|
|
|
1b12c3 |
ferror = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
1b12c3 |
else if (*parg && is_long_option(*parg))
|
|
|
1b12c3 |
ferror = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
1b12c3 |
else if (*parg && is_last_option(*parg))
|
|
|
1b12c3 |
ferror = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
1b12c3 |
else
|
|
|
1b12c3 |
fval = true;
|
|
|
1b12c3 |
} else {
|
|
|
1b12c3 |
/* ARGV_OPTARG_OPTIONAL */
|
|
|
28ba08 |
fval = val[0];
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
} else
|
|
|
1b12c3 |
ferror = ARGV_ERROR_LONG_OPTION;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (ferror == ARGV_ERROR_OK)
|
|
|
1b12c3 |
if (option && fval && option->paradigm)
|
|
|
1b12c3 |
if (!is_arg_in_paradigm(ch,option->paradigm))
|
|
|
1b12c3 |
ferror = ARGV_ERROR_OPTARG_PARADIGM;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (ferror != ARGV_ERROR_OK) {
|
|
|
1b12c3 |
ctx->errcode = ferror;
|
|
|
1b12c3 |
ctx->errch = ctx->errch ? ctx->errch : ch;
|
|
|
1b12c3 |
ctx->erropt = option;
|
|
|
1b12c3 |
return;
|
|
|
1b12c3 |
} else if (ctx->mode == ARGV_MODE_SCAN) {
|
|
|
1b12c3 |
if (!fnoscan)
|
|
|
1b12c3 |
ctx->nentries++;
|
|
|
1b12c3 |
else if (fval)
|
|
|
1b12c3 |
ctx->nentries++;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (fval || !option) {
|
|
|
1b12c3 |
parg++;
|
|
|
1b12c3 |
ch = *parg;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
} else if (ctx->mode == ARGV_MODE_COPY) {
|
|
|
1b12c3 |
if (fnoscan) {
|
|
|
1b12c3 |
if (fval) {
|
|
|
1b12c3 |
mentry->arg = ch;
|
|
|
1b12c3 |
mentry->fnoscan = true;
|
|
|
1b12c3 |
mentry++;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
parg++;
|
|
|
1b12c3 |
ch = *parg;
|
|
|
1b12c3 |
} else if (option) {
|
|
|
1b12c3 |
mentry->arg = fval ? ch : 0;
|
|
|
1b12c3 |
mentry->tag = option->tag;
|
|
|
1b12c3 |
mentry->fopt = true;
|
|
|
1b12c3 |
mentry->fval = fval;
|
|
|
1b12c3 |
mentry++;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (fval) {
|
|
|
1b12c3 |
parg++;
|
|
|
1b12c3 |
ch = *parg;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
} else {
|
|
|
1b12c3 |
mentry->arg = ch;
|
|
|
1b12c3 |
mentry++;
|
|
|
1b12c3 |
parg++;
|
|
|
1b12c3 |
ch = *parg;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
5888e8 |
static const char * argv_program_name(const char * program_path)
|
|
|
5888e8 |
{
|
|
|
5888e8 |
const char * ch;
|
|
|
5888e8 |
|
|
|
5888e8 |
if (program_path) {
|
|
|
5888e8 |
if ((ch = strrchr(program_path,'/')))
|
|
|
5888e8 |
return *(++ch) ? ch : 0;
|
|
|
5888e8 |
|
|
|
5888e8 |
if ((ch = strrchr(program_path,'\\')))
|
|
|
5888e8 |
return *(++ch) ? ch : 0;
|
|
|
5888e8 |
}
|
|
|
5888e8 |
|
|
|
5888e8 |
return program_path;
|
|
|
5888e8 |
}
|
|
|
5888e8 |
|
|
|
1b12c3 |
static void argv_show_error(struct argv_ctx * ctx)
|
|
|
1b12c3 |
{
|
|
|
1b12c3 |
fprintf(stderr,"%s: error: ",ctx->program);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
switch (ctx->errcode) {
|
|
|
1b12c3 |
case ARGV_ERROR_SHORT_OPTION:
|
|
|
6b04c6 |
fprintf(stderr,"'-%c' is not a valid short option\n",*ctx->errch);
|
|
|
1b12c3 |
break;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
case ARGV_ERROR_LONG_OPTION:
|
|
|
1b12c3 |
fprintf(stderr,"'--%s' is not a valid long option\n",ctx->errch);
|
|
|
1b12c3 |
break;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
case ARGV_ERROR_OPTARG_NONE:
|
|
|
76d6a3 |
fprintf(stderr,"'%s' is not a valid option value for [%s%c%s%s%s] "
|
|
|
76d6a3 |
"(option values may not be specified)\n",
|
|
|
1b12c3 |
ctx->errch,
|
|
|
1b12c3 |
ctx->erropt->short_name ? "-" : "",
|
|
|
1b12c3 |
ctx->erropt->short_name,
|
|
|
1b12c3 |
ctx->erropt->short_name ? "," : "",
|
|
|
1b12c3 |
ctx->erropt->long_name ? "--" : "",
|
|
|
1b12c3 |
ctx->erropt->long_name);
|
|
|
1b12c3 |
break;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
case ARGV_ERROR_OPTARG_REQUIRED:
|
|
|
1b12c3 |
fprintf(stderr,"option [%s%c%s%s%s] requires %s %s%s%s\n",
|
|
|
1b12c3 |
ctx->erropt->short_name ? "-" : "",
|
|
|
1b12c3 |
ctx->erropt->short_name,
|
|
|
1b12c3 |
ctx->erropt->short_name ? "," : "",
|
|
|
1b12c3 |
ctx->erropt->long_name ? "--" : "",
|
|
|
1b12c3 |
ctx->erropt->long_name,
|
|
|
1b12c3 |
ctx->erropt->paradigm ? "one of the following values:" : "a value",
|
|
|
1b12c3 |
ctx->erropt->paradigm ? "{" : "",
|
|
|
1b12c3 |
ctx->erropt->paradigm ? ctx->erropt->paradigm : "",
|
|
|
1b12c3 |
ctx->erropt->paradigm ? "}" : "");
|
|
|
1b12c3 |
break;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
case ARGV_ERROR_OPTARG_PARADIGM:
|
|
|
1b12c3 |
fprintf(stderr,"'%s' is not a valid option value for [%s%c%s%s%s]={%s}\n",
|
|
|
1b12c3 |
ctx->errch,
|
|
|
1b12c3 |
ctx->erropt->short_name ? "-" : "",
|
|
|
1b12c3 |
ctx->erropt->short_name,
|
|
|
1b12c3 |
ctx->erropt->short_name ? "," : "",
|
|
|
1b12c3 |
ctx->erropt->long_name ? "--" : "",
|
|
|
1b12c3 |
ctx->erropt->long_name,
|
|
|
1b12c3 |
ctx->erropt->paradigm);
|
|
|
1b12c3 |
break;
|
|
|
1b12c3 |
|
|
|
12240e |
case ARGV_ERROR_HYBRID_NONE:
|
|
|
12240e |
fprintf(stderr,"-%s is not a synonym for --%s\n",
|
|
|
12240e |
ctx->erropt->long_name,
|
|
|
12240e |
ctx->erropt->long_name);
|
|
|
12240e |
break;
|
|
|
12240e |
|
|
|
12240e |
case ARGV_ERROR_HYBRID_ONLY:
|
|
|
12240e |
fprintf(stderr,"--%s is not a synonym for -%s\n",
|
|
|
12240e |
ctx->erropt->long_name,
|
|
|
12240e |
ctx->erropt->long_name);
|
|
|
12240e |
break;
|
|
|
12240e |
|
|
|
12240e |
case ARGV_ERROR_HYBRID_SPACE:
|
|
|
12240e |
fprintf(stderr,"-%s: illegal value assignment; valid syntax is -%s=VAL\n",
|
|
|
12240e |
ctx->erropt->long_name,
|
|
|
12240e |
ctx->erropt->long_name);
|
|
|
12240e |
break;
|
|
|
12240e |
|
|
|
12240e |
case ARGV_ERROR_HYBRID_EQUAL:
|
|
|
12240e |
fprintf(stderr,"-%s: illegal value assignment; valid syntax is -%s VAL\n",
|
|
|
12240e |
ctx->erropt->long_name,
|
|
|
12240e |
ctx->erropt->long_name);
|
|
|
12240e |
break;
|
|
|
12240e |
|
|
|
1b12c3 |
case ARGV_ERROR_INTERNAL:
|
|
|
1b12c3 |
fputs("internal error",stderr);
|
|
|
1b12c3 |
break;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
default:
|
|
|
1b12c3 |
break;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
static void argv_show_status(
|
|
|
1b12c3 |
const struct argv_option options[],
|
|
|
1b12c3 |
struct argv_ctx * ctx,
|
|
|
1b12c3 |
struct argv_meta * meta)
|
|
|
1b12c3 |
{
|
|
|
1b12c3 |
int argc;
|
|
|
1b12c3 |
const char ** argv;
|
|
|
1b12c3 |
struct argv_entry * entry;
|
|
|
1b12c3 |
const struct argv_option * option;
|
|
|
1b12c3 |
char short_name[2] = {0};
|
|
|
1b12c3 |
const char * space = "";
|
|
|
1b12c3 |
|
|
|
1b12c3 |
fputs("\n\nconcatenated command line:\n",stderr);
|
|
|
1b12c3 |
for (argv=meta->argv; *argv; argv++) {
|
|
|
1b12c3 |
fprintf(stderr,"%s%s",space,*argv);
|
|
|
1b12c3 |
space = " ";
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
fputs("\n\nargument vector:\n",stderr);
|
|
|
1b12c3 |
for (argc=0,argv=meta->argv; *argv; argc++,argv++)
|
|
|
1b12c3 |
fprintf(stderr,"argv[%d]: %s\n",argc,*argv);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
fputs("\n\nparsed entries:\n",stderr);
|
|
|
1b12c3 |
for (entry=meta->entries; entry->arg || entry->fopt; entry++)
|
|
|
1b12c3 |
if (entry->fopt) {
|
|
|
1b12c3 |
option = option_from_tag(options,entry->tag);
|
|
|
1b12c3 |
short_name[0] = option->short_name;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (entry->fval)
|
|
|
1b12c3 |
fprintf(stderr,"[-%s,--%s] := %s\n",
|
|
|
1b12c3 |
short_name,option->long_name,entry->arg);
|
|
|
1b12c3 |
else
|
|
|
1b12c3 |
fprintf(stderr,"[-%s,--%s]\n",
|
|
|
1b12c3 |
short_name,option->long_name);
|
|
|
1b12c3 |
} else
|
|
|
1b12c3 |
fprintf(stderr,"<program arg> := %s\n",entry->arg);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
fputs("\n\n",stderr);
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
static struct argv_meta * argv_free_impl(struct argv_meta_impl * imeta)
|
|
|
1b12c3 |
{
|
|
|
1b12c3 |
if (imeta->argv)
|
|
|
1b12c3 |
free(imeta->argv);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (imeta->strbuf)
|
|
|
1b12c3 |
free(imeta->strbuf);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (imeta->meta.entries)
|
|
|
1b12c3 |
free(imeta->meta.entries);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
free(imeta);
|
|
|
1b12c3 |
return 0;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
static struct argv_meta * argv_alloc(const char ** argv, struct argv_ctx * ctx)
|
|
|
1b12c3 |
{
|
|
|
1b12c3 |
struct argv_meta_impl * imeta;
|
|
|
1b12c3 |
const char ** vector;
|
|
|
1b12c3 |
char * dst;
|
|
|
1b12c3 |
size_t size;
|
|
|
1b12c3 |
int argc;
|
|
|
1b12c3 |
int i;
|
|
|
1b12c3 |
|
|
|
17202a |
if (!(imeta = calloc(1,sizeof(*imeta))))
|
|
|
1b12c3 |
return 0;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (ctx->flags & ARGV_CLONE_VECTOR) {
|
|
|
1b12c3 |
for (vector=argv,argc=0,size=0; *vector; vector++) {
|
|
|
1b12c3 |
size += strlen(*vector) + 1;
|
|
|
1b12c3 |
argc++;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
17202a |
if (!(imeta->argv = calloc(argc+1,sizeof(char *))))
|
|
|
1b12c3 |
return argv_free_impl(imeta);
|
|
|
17202a |
else if (!(imeta->strbuf = calloc(1,size+1)))
|
|
|
1b12c3 |
return argv_free_impl(imeta);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
for (i=0,dst=imeta->strbuf; i
|
|
|
1b12c3 |
strcpy(dst,argv[i]);
|
|
|
1b12c3 |
imeta->argv[i] = dst;
|
|
|
1b12c3 |
dst += strlen(dst)+1;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
imeta->meta.argv = imeta->argv;
|
|
|
1b12c3 |
} else
|
|
|
1b12c3 |
imeta->meta.argv = argv;
|
|
|
1b12c3 |
|
|
|
17202a |
if (!(imeta->meta.entries = calloc(
|
|
|
17202a |
ctx->nentries+1,
|
|
|
17202a |
sizeof(struct argv_entry))))
|
|
|
1b12c3 |
return argv_free_impl(imeta);
|
|
|
1b12c3 |
else
|
|
|
1b12c3 |
return &imeta->meta;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
static struct argv_meta * argv_get(
|
|
|
1b12c3 |
const char * argv[],
|
|
|
1b12c3 |
const struct argv_option options[],
|
|
|
1b12c3 |
int flags)
|
|
|
1b12c3 |
{
|
|
|
1b12c3 |
struct argv_meta * meta;
|
|
|
1b12c3 |
struct argv_ctx ctx = {flags,ARGV_MODE_SCAN,0,0,0,0};
|
|
|
1b12c3 |
|
|
|
1b12c3 |
argv_scan(argv,options,&ctx,0);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (ctx.errcode != ARGV_ERROR_OK) {
|
|
|
5888e8 |
if (!ctx.program)
|
|
|
5888e8 |
ctx.program = argv_program_name(argv[0]);
|
|
|
5888e8 |
|
|
|
1b12c3 |
if (ctx.flags & ARGV_VERBOSITY_ERRORS)
|
|
|
1b12c3 |
argv_show_error(&ctx;;
|
|
|
5888e8 |
|
|
|
1b12c3 |
return 0;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (!(meta = argv_alloc(argv,&ctx)))
|
|
|
1b12c3 |
return 0;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
ctx.mode = ARGV_MODE_COPY;
|
|
|
1b12c3 |
argv_scan(meta->argv,options,&ctx,meta);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (ctx.errcode != ARGV_ERROR_OK) {
|
|
|
8f1613 |
if (!ctx.program)
|
|
|
8f1613 |
ctx.program = argv[0];
|
|
|
8f1613 |
|
|
|
8f1613 |
ctx.errcode = ARGV_ERROR_INTERNAL;
|
|
|
8f1613 |
argv_show_error(&ctx;;
|
|
|
1b12c3 |
argv_free(meta);
|
|
|
8f1613 |
|
|
|
1b12c3 |
return 0;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (ctx.flags & ARGV_VERBOSITY_STATUS)
|
|
|
1b12c3 |
argv_show_status(options,&ctx,meta);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
return meta;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
static void argv_free(struct argv_meta * xmeta)
|
|
|
1b12c3 |
{
|
|
|
1b12c3 |
struct argv_meta_impl * imeta;
|
|
|
1b12c3 |
uintptr_t addr;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (xmeta) {
|
|
|
1b12c3 |
addr = (uintptr_t)xmeta - offsetof(struct argv_meta_impl,meta);
|
|
|
1b12c3 |
imeta = (struct argv_meta_impl *)addr;
|
|
|
1b12c3 |
argv_free_impl(imeta);
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
static void argv_usage(
|
|
|
1b12c3 |
FILE * file,
|
|
|
1b12c3 |
const char * header,
|
|
|
1b12c3 |
const struct argv_option options[],
|
|
|
1b12c3 |
const char * mode)
|
|
|
1b12c3 |
{
|
|
|
1b12c3 |
const struct argv_option * option;
|
|
|
1b12c3 |
bool fshort,flong;
|
|
|
1b12c3 |
bool fnewline;
|
|
|
1b12c3 |
size_t len,optlen;
|
|
|
1b12c3 |
size_t paralen,rparalen,mparalen;
|
|
|
1b12c3 |
size_t desclen,rdesclen;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
char * para;
|
|
|
1b12c3 |
char * next_para;
|
|
|
1b12c3 |
char * desc;
|
|
|
1b12c3 |
char * next_desc;
|
|
|
1b12c3 |
char * paradigm;
|
|
|
1b12c3 |
char * buf;
|
|
|
1b12c3 |
size_t buflen;
|
|
|
4e7a71 |
const char * sdescription;
|
|
|
4e7a71 |
const char * sargname;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
const char indent[] = " ";
|
|
|
1b12c3 |
const int rblen = sizeof("}") - sizeof(char);
|
|
|
1b12c3 |
const int rbblen = sizeof("{]") - sizeof(char);
|
|
|
1b12c3 |
const int brcklen= sizeof("[]") - sizeof(char);
|
|
|
1b12c3 |
const int solen = sizeof("-") - sizeof(char);
|
|
|
1b12c3 |
const int lolen = sizeof("--") - sizeof(char);
|
|
|
1b12c3 |
const int slolen = sizeof("-X,--") - sizeof(char);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
fshort = mode ? !strcmp(mode,"short") : 0;
|
|
|
1b12c3 |
flong = fshort ? 0 : mode && !strcmp(mode,"long");
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (header)
|
|
|
1b12c3 |
fprintf(stdout,"%s",header);
|
|
|
1b12c3 |
|
|
|
76d6a3 |
optlen = 0;
|
|
|
76d6a3 |
paralen = 0;
|
|
|
76d6a3 |
|
|
|
76d6a3 |
for (option=options; option->short_name || option->long_name; option++) {
|
|
|
1b12c3 |
if (fshort)
|
|
|
1b12c3 |
len = option->short_name ? sizeof(char) + solen : 0;
|
|
|
1b12c3 |
else if (flong)
|
|
|
1b12c3 |
len = option->long_name ? strlen(option->long_name) + lolen : 0;
|
|
|
1b12c3 |
else
|
|
|
1b12c3 |
len = option->long_name ? strlen(option->long_name) + slolen : 0;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (len) {
|
|
|
1b12c3 |
if (len > optlen)
|
|
|
1b12c3 |
optlen = len;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (option->paradigm)
|
|
|
1b12c3 |
len = strlen(option->paradigm) + strlen("{}");
|
|
|
1b12c3 |
else if (option->argname)
|
|
|
1b12c3 |
len = strlen(option->argname);
|
|
|
1b12c3 |
else if (option->optarg != ARGV_OPTARG_NONE)
|
|
|
1b12c3 |
len = strlen("<val>");
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (option->optarg == ARGV_OPTARG_OPTIONAL)
|
|
|
1b12c3 |
len += strlen("[]");
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (len > paralen)
|
|
|
1b12c3 |
paralen = len;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
2245d9 |
optlen += ARGV_TAB_WIDTH;
|
|
|
2245d9 |
optlen &= (~(ARGV_TAB_WIDTH-1));
|
|
|
1b12c3 |
|
|
|
536c1b |
paradigm = next_para = buf = 0;
|
|
|
536c1b |
fnewline = false;
|
|
|
536c1b |
rparalen = 0;
|
|
|
536c1b |
mparalen = 0;
|
|
|
536c1b |
|
|
|
1b12c3 |
if (paralen) {
|
|
|
2245d9 |
paralen += (ARGV_TAB_WIDTH);
|
|
|
2245d9 |
paralen &= (~(ARGV_TAB_WIDTH-1));
|
|
|
1b12c3 |
mparalen = paralen + 2*rbblen;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (optlen + paralen > 64)
|
|
|
1b12c3 |
paralen = 32;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
/* account for ' ','\t', try to fit in 80 or 96 columns */
|
|
|
2245d9 |
if (optlen+paralen+2+ARGV_TAB_WIDTH < 80-32)
|
|
|
2245d9 |
desclen = 80 - (optlen+paralen+2+ARGV_TAB_WIDTH);
|
|
|
2245d9 |
else if (optlen+paralen+2+ARGV_TAB_WIDTH < 96-32)
|
|
|
2245d9 |
desclen = 96 - (optlen+paralen+2+ARGV_TAB_WIDTH);
|
|
|
1b12c3 |
else
|
|
|
1b12c3 |
desclen = 32;
|
|
|
1b12c3 |
|
|
|
76d6a3 |
buflen = 0;
|
|
|
76d6a3 |
rdesclen = 1;
|
|
|
76d6a3 |
|
|
|
76d6a3 |
for (option=options; option->short_name || option->long_name; option++) {
|
|
|
527ec5 |
if (fshort && !option->short_name)
|
|
|
527ec5 |
continue;
|
|
|
527ec5 |
else if (flong && !option->long_name)
|
|
|
527ec5 |
continue;
|
|
|
527ec5 |
|
|
|
1b12c3 |
if (option->paradigm) {
|
|
|
1b12c3 |
if (option->optarg == ARGV_OPTARG_OPTIONAL)
|
|
|
1b12c3 |
rparalen = strlen(option->paradigm) - 2*rbblen;
|
|
|
1b12c3 |
else
|
|
|
1b12c3 |
rparalen = strlen(option->paradigm) - 2*rblen;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
4e7a71 |
sdescription = option->description ? option->description : "";
|
|
|
4e7a71 |
sargname = option->argname ? option->argname : "";
|
|
|
4e7a71 |
|
|
|
1b12c3 |
if (option->paradigm)
|
|
|
4e7a71 |
rdesclen = snprintf(buf,buflen,sdescription,option->paradigm);
|
|
|
1b12c3 |
else
|
|
|
4e7a71 |
rdesclen = snprintf(buf,buflen,sdescription,sargname);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (fnewline)
|
|
|
2a42e1 |
(void)0;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if ((rparalen > paralen) || (rdesclen > desclen)) {
|
|
|
1b12c3 |
if (!fnewline) {
|
|
|
2a42e1 |
(void)0;
|
|
|
1b12c3 |
fnewline = true;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
} else
|
|
|
1b12c3 |
fnewline = false;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (fshort)
|
|
|
1b12c3 |
fprintf(file,"%s-%-*c",indent,(int)(optlen-solen),option->short_name);
|
|
|
1b12c3 |
else if (flong)
|
|
|
1b12c3 |
fprintf(file,"%s--%-*s",indent,(int)(optlen-lolen),option->long_name);
|
|
|
1b12c3 |
else {
|
|
|
1b12c3 |
if (option->short_name && option->long_name)
|
|
|
76d6a3 |
fprintf(file,"%s-%c,--%-*s",
|
|
|
76d6a3 |
indent,option->short_name,
|
|
|
76d6a3 |
(int)(optlen-slolen),option->long_name);
|
|
|
1b12c3 |
else if (option->short_name)
|
|
|
76d6a3 |
fprintf(file,"%s-%-*c",
|
|
|
76d6a3 |
indent,(int)(optlen-solen),option->short_name);
|
|
|
1b12c3 |
else
|
|
|
76d6a3 |
fprintf(file,"%s%3s--%-*s",
|
|
|
76d6a3 |
indent,"",
|
|
|
76d6a3 |
(int)(optlen-slolen),option->long_name);
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (rdesclen > buflen) {
|
|
|
1b12c3 |
if (buf) {
|
|
|
1b12c3 |
free(buf);
|
|
|
1b12c3 |
buf = 0;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
len = rdesclen + 512;
|
|
|
1b12c3 |
len &= (~511);
|
|
|
1b12c3 |
|
|
|
17202a |
if ((buf = calloc(1,len))) {
|
|
|
1b12c3 |
buflen = len;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (option->paradigm)
|
|
|
76d6a3 |
rdesclen = snprintf(buf,buflen,
|
|
|
76d6a3 |
option->description,
|
|
|
76d6a3 |
option->paradigm);
|
|
|
1b12c3 |
else
|
|
|
76d6a3 |
rdesclen = snprintf(buf,buflen,
|
|
|
76d6a3 |
option->description,
|
|
|
76d6a3 |
option->argname);
|
|
|
1b12c3 |
} else {
|
|
|
1b12c3 |
buflen = 0;
|
|
|
1b12c3 |
continue;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (option->paradigm && (rparalen <= paralen)) {
|
|
|
1b12c3 |
if (option->optarg == ARGV_OPTARG_OPTIONAL)
|
|
|
76d6a3 |
fprintf(file,"[{%s}]%-*c",
|
|
|
76d6a3 |
option->paradigm,
|
|
|
76d6a3 |
(int)(paralen-strlen(option->paradigm)-2*rbblen),' ');
|
|
|
1b12c3 |
else
|
|
|
76d6a3 |
fprintf(file,"{%s}%-*c",
|
|
|
76d6a3 |
option->paradigm,
|
|
|
76d6a3 |
(int)(paralen-strlen(option->paradigm)-rbblen),' ');
|
|
|
1b12c3 |
para = (char *)0;
|
|
|
1b12c3 |
} else if (option->paradigm) {
|
|
|
17202a |
if (!paradigm && !(paradigm = calloc(1,mparalen))) {
|
|
|
1b12c3 |
fputc('\n',file);
|
|
|
1b12c3 |
continue;
|
|
|
1b12c3 |
} else
|
|
|
1b12c3 |
para = strcpy(paradigm,option->paradigm);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (option->optarg == ARGV_OPTARG_OPTIONAL) {
|
|
|
1b12c3 |
fputs("[{",file);
|
|
|
1b12c3 |
rparalen = paralen - rbblen;
|
|
|
1b12c3 |
} else {
|
|
|
1b12c3 |
fputc('{',file);
|
|
|
1b12c3 |
rparalen = paralen - rblen;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
} else if (option->argname) {
|
|
|
1b12c3 |
if (option->optarg == ARGV_OPTARG_OPTIONAL)
|
|
|
76d6a3 |
fprintf(file,"[%s]%-*c",
|
|
|
76d6a3 |
option->argname,
|
|
|
76d6a3 |
(int)(paralen-strlen(option->argname)-brcklen),' ');
|
|
|
1b12c3 |
else
|
|
|
76d6a3 |
fprintf(file,"%s%-*c",
|
|
|
76d6a3 |
option->argname,
|
|
|
76d6a3 |
(int)(paralen-strlen(option->argname)),' ');
|
|
|
1b12c3 |
para = (char *)0;
|
|
|
1b12c3 |
} else {
|
|
|
1b12c3 |
fprintf(file,"%-*c",(int)paralen,' ');
|
|
|
1b12c3 |
para = (char *)0;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (!para && option->description && rdesclen <= desclen) {
|
|
|
1b12c3 |
fputc('\t',file);
|
|
|
1b12c3 |
fputs(buf,file);
|
|
|
1b12c3 |
desc = (char *)0;
|
|
|
1b12c3 |
} else if (option->description)
|
|
|
1b12c3 |
desc = buf;
|
|
|
1b12c3 |
else
|
|
|
1b12c3 |
desc = (char *)0;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
while (para || desc) {
|
|
|
1b12c3 |
if (para) {
|
|
|
76d6a3 |
next_para = para+rparalen-1;
|
|
|
76d6a3 |
|
|
|
76d6a3 |
for (; (next_para>para) && (*next_para!='|'); )
|
|
|
1b12c3 |
next_para--;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (para > paradigm) {
|
|
|
1b12c3 |
if (option->optarg == ARGV_OPTARG_OPTIONAL)
|
|
|
1b12c3 |
fputs(" ",file);
|
|
|
1b12c3 |
else
|
|
|
1b12c3 |
fputc(' ',file);
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (*next_para != '|') {
|
|
|
1b12c3 |
fprintf(file,"%s",para);
|
|
|
1b12c3 |
para = (char *)0;
|
|
|
1b12c3 |
} else if (next_para > para) {
|
|
|
1b12c3 |
*next_para = '\0';
|
|
|
1b12c3 |
fprintf(file,"%-*s",(int)rparalen,para);
|
|
|
1b12c3 |
*next_para = '|';
|
|
|
1b12c3 |
para = next_para;
|
|
|
1b12c3 |
rparalen = strlen(para);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
/* 2*rbblen,2*rblen, etc.: account for indentation */
|
|
|
1b12c3 |
if (option->optarg == ARGV_OPTARG_OPTIONAL)
|
|
|
76d6a3 |
rparalen = (rparalen+2*rbblen > paralen)
|
|
|
76d6a3 |
? paralen-rbblen
|
|
|
76d6a3 |
: rparalen;
|
|
|
1b12c3 |
else
|
|
|
76d6a3 |
rparalen = (rparalen+2*rblen > paralen)
|
|
|
76d6a3 |
? paralen-rblen
|
|
|
76d6a3 |
: rparalen;
|
|
|
1b12c3 |
} else {
|
|
|
1b12c3 |
if (option->optarg == ARGV_OPTARG_OPTIONAL)
|
|
|
76d6a3 |
fprintf(file,"%s}]%-*c",para,
|
|
|
76d6a3 |
(int)(paralen-strlen(para)-rbblen),' ');
|
|
|
1b12c3 |
else
|
|
|
76d6a3 |
fprintf(file,"%s}%-*c",para,
|
|
|
76d6a3 |
(int)(paralen-strlen(para)-rblen),' ');
|
|
|
1b12c3 |
para = (char *)0;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
} else if (desc > buf)
|
|
|
1b12c3 |
fprintf(file,"%-*c",(int)paralen,' ');
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (desc) {
|
|
|
1b12c3 |
if (desc > buf)
|
|
|
1b12c3 |
fputs("\t ",file);
|
|
|
1b12c3 |
else
|
|
|
1b12c3 |
fputc('\t',file);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if ((rdesclen = strlen(desc)+(desc>buf)) <= desclen) {
|
|
|
1b12c3 |
fputs(desc,file);
|
|
|
1b12c3 |
desc = (char *)0;
|
|
|
1b12c3 |
} else {
|
|
|
76d6a3 |
next_desc = desc + desclen - 1;
|
|
|
76d6a3 |
|
|
|
76d6a3 |
for (; (next_desc > desc)
|
|
|
76d6a3 |
&& (*next_desc != ' ')
|
|
|
76d6a3 |
&& (*next_desc != '\n'); )
|
|
|
1b12c3 |
next_desc--;
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if ((*next_desc != ' ') && (*next_desc!='\n')) {
|
|
|
1b12c3 |
fputs(desc,file);
|
|
|
1b12c3 |
desc = (char *)0;
|
|
|
1b12c3 |
} else if (next_desc > desc) {
|
|
|
1b12c3 |
*next_desc = '\0';
|
|
|
1b12c3 |
fputs(desc,file);
|
|
|
1b12c3 |
desc = ++next_desc;
|
|
|
1b12c3 |
} else {
|
|
|
1b12c3 |
fputs(desc,file);
|
|
|
1b12c3 |
desc = (char *)0;
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (para || desc)
|
|
|
1b12c3 |
fprintf(file,"\n%s%-*c",indent,(int)optlen,' ');
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
fputc('\n',file);
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (paradigm)
|
|
|
1b12c3 |
free(paradigm);
|
|
|
1b12c3 |
|
|
|
1b12c3 |
if (buf)
|
|
|
1b12c3 |
free(buf);
|
|
|
1b12c3 |
}
|
|
|
1b12c3 |
|
|
|
1b12c3 |
#endif
|
|
|
249590 |
|
|
|
249590 |
#endif
|