|
|
383aa6 |
/****************************************************************************/
|
|
|
383aa6 |
/* argv.h: a thread-safe argument vector parser and usage screen generator */
|
|
|
dfda6f |
/* Copyright (C) 2015--2016 Z. Gilboa */
|
|
|
383aa6 |
/* Released under GPLv2 and GPLv3; see COPYING.APIMAGIC */
|
|
|
383aa6 |
/* This file is (also) part of sofort: portable software project template. */
|
|
|
383aa6 |
/****************************************************************************/
|
|
|
383aa6 |
|
|
|
383aa6 |
#ifndef ARGV_H
|
|
|
383aa6 |
#define ARGV_H
|
|
|
383aa6 |
|
|
|
383aa6 |
#include <stdbool.h>
|
|
|
383aa6 |
#include <stdint.h>
|
|
|
383aa6 |
#include <stddef.h>
|
|
|
383aa6 |
#include <errno.h>
|
|
|
383aa6 |
#include <string.h>
|
|
|
383aa6 |
#include <stdlib.h>
|
|
|
383aa6 |
#include <stdio.h>
|
|
|
84eb70 |
#include <unistd.h>
|
|
|
383aa6 |
|
|
|
383aa6 |
#define ARGV_VERBOSITY_NONE 0x00
|
|
|
383aa6 |
#define ARGV_VERBOSITY_ERRORS 0x01
|
|
|
383aa6 |
#define ARGV_VERBOSITY_STATUS 0x02
|
|
|
383aa6 |
#define ARGV_CLONE_VECTOR 0x80
|
|
|
383aa6 |
|
|
|
a1c3aa |
#ifndef ARGV_TAB_WIDTH
|
|
|
a1c3aa |
#define ARGV_TAB_WIDTH 8
|
|
|
383aa6 |
#endif
|
|
|
383aa6 |
|
|
|
5ec0ca |
/*******************************************/
|
|
|
5ec0ca |
/* */
|
|
|
5ec0ca |
/* support of hybrid options */
|
|
|
5ec0ca |
/* ------------------------- */
|
|
|
5ec0ca |
/* hybrid options are very similar to */
|
|
|
5ec0ca |
/* long options, yet are prefixed by */
|
|
|
5ec0ca |
/* a single dash rather than two */
|
|
|
5ec0ca |
/* (i.e. -std, -isystem). */
|
|
|
5ec0ca |
/* hybrid options are supported by this */
|
|
|
5ec0ca |
/* driver for compatibility with legacy */
|
|
|
5ec0ca |
/* tools; note, however, that the use */
|
|
|
5ec0ca |
/* of hybrid options should be strongly */
|
|
|
5ec0ca |
/* discouraged due to the limitations */
|
|
|
5ec0ca |
/* they impose on short options (for */
|
|
|
5ec0ca |
/* example, a driver implementing -std */
|
|
|
5ec0ca |
/* may not provide -s as a short option */
|
|
|
5ec0ca |
/* that takes an arbitrary value). */
|
|
|
5ec0ca |
/* */
|
|
|
5ec0ca |
/* SPACE: -hybrid VALUE (i.e. -MF file) */
|
|
|
5ec0ca |
/* EQUAL: -hybrid=VALUE (i.e. -std=c99) */
|
|
|
d27fb4 |
/* COMMA: -hybrid,VALUE (i.e. -Wl,<arg>) */
|
|
|
5ec0ca |
/* ONLY: -opt accepted, --opt rejected */
|
|
|
500c8e |
/* JOINED: -optVALUE */
|
|
|
5ec0ca |
/* */
|
|
|
5ec0ca |
/*******************************************/
|
|
|
5ec0ca |
|
|
|
5ec0ca |
|
|
|
5ec0ca |
#define ARGV_OPTION_HYBRID_NONE 0x00
|
|
|
5ec0ca |
#define ARGV_OPTION_HYBRID_ONLY 0x01
|
|
|
5ec0ca |
#define ARGV_OPTION_HYBRID_SPACE 0x02
|
|
|
5ec0ca |
#define ARGV_OPTION_HYBRID_EQUAL 0x04
|
|
|
d27fb4 |
#define ARGV_OPTION_HYBRID_COMMA 0x08
|
|
|
500c8e |
#define ARGV_OPTION_HYBRID_JOINED 0x10
|
|
|
500c8e |
#define ARGV_OPTION_HYBRID_CIRCUS (ARGV_OPTION_HYBRID_SPACE \
|
|
|
500c8e |
| ARGV_OPTION_HYBRID_JOINED)
|
|
|
d27fb4 |
#define ARGV_OPTION_HYBRID_DUAL (ARGV_OPTION_HYBRID_SPACE \
|
|
|
5ec0ca |
| ARGV_OPTION_HYBRID_EQUAL)
|
|
|
2b3950 |
#define ARGV_OPTION_HYBRID_SWITCH (ARGV_OPTION_HYBRID_ONLY \
|
|
|
2b3950 |
| ARGV_OPTION_HYBRID_SPACE \
|
|
|
d27fb4 |
| ARGV_OPTION_HYBRID_EQUAL \
|
|
|
500c8e |
| ARGV_OPTION_HYBRID_COMMA \
|
|
|
500c8e |
| ARGV_OPTION_HYBRID_JOINED)
|
|
|
5ec0ca |
|
|
|
383aa6 |
enum argv_optarg {
|
|
|
383aa6 |
ARGV_OPTARG_NONE,
|
|
|
383aa6 |
ARGV_OPTARG_REQUIRED,
|
|
|
383aa6 |
ARGV_OPTARG_OPTIONAL,
|
|
|
383aa6 |
};
|
|
|
383aa6 |
|
|
|
383aa6 |
enum argv_mode {
|
|
|
383aa6 |
ARGV_MODE_SCAN,
|
|
|
383aa6 |
ARGV_MODE_COPY,
|
|
|
383aa6 |
};
|
|
|
383aa6 |
|
|
|
383aa6 |
enum argv_error {
|
|
|
383aa6 |
ARGV_ERROR_OK,
|
|
|
383aa6 |
ARGV_ERROR_INTERNAL,
|
|
|
383aa6 |
ARGV_ERROR_SHORT_OPTION,
|
|
|
383aa6 |
ARGV_ERROR_LONG_OPTION,
|
|
|
383aa6 |
ARGV_ERROR_OPTARG_NONE,
|
|
|
383aa6 |
ARGV_ERROR_OPTARG_REQUIRED,
|
|
|
383aa6 |
ARGV_ERROR_OPTARG_PARADIGM,
|
|
|
5ec0ca |
ARGV_ERROR_HYBRID_NONE,
|
|
|
5ec0ca |
ARGV_ERROR_HYBRID_ONLY,
|
|
|
5ec0ca |
ARGV_ERROR_HYBRID_SPACE,
|
|
|
5ec0ca |
ARGV_ERROR_HYBRID_EQUAL,
|
|
|
d27fb4 |
ARGV_ERROR_HYBRID_COMMA,
|
|
|
383aa6 |
};
|
|
|
383aa6 |
|
|
|
383aa6 |
struct argv_option {
|
|
|
383aa6 |
const char * long_name;
|
|
|
383aa6 |
const char short_name;
|
|
|
383aa6 |
int tag;
|
|
|
383aa6 |
enum argv_optarg optarg;
|
|
|
5ec0ca |
int flags;
|
|
|
383aa6 |
const char * paradigm;
|
|
|
383aa6 |
const char * argname;
|
|
|
383aa6 |
const char * description;
|
|
|
383aa6 |
};
|
|
|
383aa6 |
|
|
|
383aa6 |
struct argv_entry {
|
|
|
383aa6 |
const char * arg;
|
|
|
383aa6 |
int tag;
|
|
|
383aa6 |
bool fopt;
|
|
|
383aa6 |
bool fval;
|
|
|
383aa6 |
bool fnoscan;
|
|
|
383aa6 |
enum argv_error errcode;
|
|
|
383aa6 |
};
|
|
|
383aa6 |
|
|
|
383aa6 |
struct argv_meta {
|
|
|
10a518 |
char ** argv;
|
|
|
383aa6 |
struct argv_entry * entries;
|
|
|
383aa6 |
};
|
|
|
383aa6 |
|
|
|
383aa6 |
struct argv_meta_impl {
|
|
|
10a518 |
char ** argv;
|
|
|
383aa6 |
char * strbuf;
|
|
|
383aa6 |
struct argv_meta meta;
|
|
|
383aa6 |
};
|
|
|
383aa6 |
|
|
|
383aa6 |
struct argv_ctx {
|
|
|
383aa6 |
int flags;
|
|
|
383aa6 |
int mode;
|
|
|
383aa6 |
int nentries;
|
|
|
4e499f |
int unitidx;
|
|
|
4e499f |
int erridx;
|
|
|
383aa6 |
enum argv_error errcode;
|
|
|
383aa6 |
const char * errch;
|
|
|
383aa6 |
const struct argv_option * erropt;
|
|
|
383aa6 |
const char * program;
|
|
|
383aa6 |
};
|
|
|
383aa6 |
|
|
|
383aa6 |
#ifdef ARGV_DRIVER
|
|
|
383aa6 |
|
|
|
383aa6 |
static const char * argv_program_name(const char *);
|
|
|
383aa6 |
|
|
|
383aa6 |
static void argv_usage(
|
|
|
383aa6 |
FILE *,
|
|
|
383aa6 |
const char * header,
|
|
|
383aa6 |
const struct argv_option[],
|
|
|
383aa6 |
const char * mode);
|
|
|
383aa6 |
|
|
|
383aa6 |
static struct argv_meta * argv_get(
|
|
|
10a518 |
char **,
|
|
|
383aa6 |
const struct argv_option[],
|
|
|
383aa6 |
int flags);
|
|
|
383aa6 |
|
|
|
383aa6 |
static void argv_free(struct argv_meta *);
|
|
|
383aa6 |
|
|
|
383aa6 |
|
|
|
383aa6 |
|
|
|
383aa6 |
|
|
|
383aa6 |
/*------------------------------------*/
|
|
|
383aa6 |
/* implementation of static functions */
|
|
|
383aa6 |
/*------------------------------------*/
|
|
|
383aa6 |
|
|
|
383aa6 |
static const struct argv_option * argv_short_option(
|
|
|
383aa6 |
const char * ch,
|
|
|
383aa6 |
const struct argv_option options[],
|
|
|
383aa6 |
struct argv_entry * entry)
|
|
|
383aa6 |
{
|
|
|
383aa6 |
const struct argv_option * option;
|
|
|
383aa6 |
|
|
|
383aa6 |
for (option=options; option->long_name || option->short_name; option++) {
|
|
|
383aa6 |
if (option->short_name == *ch) {
|
|
|
383aa6 |
entry->tag = option->tag;
|
|
|
383aa6 |
entry->fopt = true;
|
|
|
383aa6 |
return option;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
return 0;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
static const struct argv_option * argv_long_option(
|
|
|
383aa6 |
const char * ch,
|
|
|
383aa6 |
const struct argv_option options[],
|
|
|
383aa6 |
struct argv_entry * entry)
|
|
|
383aa6 |
{
|
|
|
383aa6 |
const struct argv_option * option;
|
|
|
383aa6 |
const char * arg;
|
|
|
383aa6 |
size_t len;
|
|
|
383aa6 |
|
|
|
383aa6 |
for (option=options; option->long_name || option->short_name; option++) {
|
|
|
383aa6 |
len = option->long_name ? strlen(option->long_name) : 0;
|
|
|
383aa6 |
|
|
|
383aa6 |
if (len && !(strncmp(option->long_name,ch,len))) {
|
|
|
383aa6 |
arg = ch + len;
|
|
|
383aa6 |
|
|
|
500c8e |
if (!*arg
|
|
|
500c8e |
|| (*arg == '=')
|
|
|
500c8e |
|| (option->flags & ARGV_OPTION_HYBRID_JOINED)
|
|
|
500c8e |
|| ((option->flags & ARGV_OPTION_HYBRID_COMMA)
|
|
|
500c8e |
&& (*arg == ','))) {
|
|
|
d27fb4 |
entry->tag = option->tag;
|
|
|
d27fb4 |
entry->fopt = true;
|
|
|
d27fb4 |
return option;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
}
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
return 0;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
static inline bool is_short_option(const char * arg)
|
|
|
383aa6 |
{
|
|
|
383aa6 |
return (arg[0]=='-') && arg[1] && (arg[1]!='-');
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
static inline bool is_long_option(const char * arg)
|
|
|
383aa6 |
{
|
|
|
383aa6 |
return (arg[0]=='-') && (arg[1]=='-') && arg[2];
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
static inline bool is_last_option(const char * arg)
|
|
|
383aa6 |
{
|
|
|
383aa6 |
return (arg[0]=='-') && (arg[1]=='-') && !arg[2];
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
5ec0ca |
static inline bool is_hybrid_option(
|
|
|
5ec0ca |
const char * arg,
|
|
|
5ec0ca |
const struct argv_option options[])
|
|
|
5ec0ca |
{
|
|
|
5ec0ca |
const struct argv_option * option;
|
|
|
5ec0ca |
struct argv_entry entry;
|
|
|
5ec0ca |
|
|
|
5ec0ca |
if (!is_short_option(arg))
|
|
|
5ec0ca |
return false;
|
|
|
5ec0ca |
|
|
|
5ec0ca |
if (!(option = argv_long_option(++arg,options,&entry)))
|
|
|
5ec0ca |
return false;
|
|
|
5ec0ca |
|
|
|
5ec0ca |
if (!(option->flags & ARGV_OPTION_HYBRID_SWITCH))
|
|
|
5ec0ca |
if (argv_short_option(arg,options,&entry))
|
|
|
5ec0ca |
return false;
|
|
|
5ec0ca |
|
|
|
5ec0ca |
return true;
|
|
|
5ec0ca |
}
|
|
|
5ec0ca |
|
|
|
383aa6 |
static inline bool is_arg_in_paradigm(const char * arg, const char * paradigm)
|
|
|
383aa6 |
{
|
|
|
383aa6 |
size_t len;
|
|
|
383aa6 |
const char * ch;
|
|
|
383aa6 |
|
|
|
383aa6 |
for (ch=paradigm,len=strlen(arg); ch; ) {
|
|
|
383aa6 |
if (!strncmp(arg,ch,len)) {
|
|
|
383aa6 |
if (!*(ch += len))
|
|
|
383aa6 |
return true;
|
|
|
383aa6 |
else if (*ch == '|')
|
|
|
383aa6 |
return true;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
if ((ch = strchr(ch,'|')))
|
|
|
383aa6 |
ch++;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
return false;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
static inline const struct argv_option * option_from_tag(
|
|
|
383aa6 |
const struct argv_option options[],
|
|
|
383aa6 |
int tag)
|
|
|
383aa6 |
{
|
|
|
383aa6 |
const struct argv_option * option;
|
|
|
383aa6 |
|
|
|
383aa6 |
for (option=options; option->short_name || option->long_name; option++)
|
|
|
383aa6 |
if (option->tag == tag)
|
|
|
383aa6 |
return option;
|
|
|
383aa6 |
return 0;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
static void argv_scan(
|
|
|
10a518 |
char ** argv,
|
|
|
383aa6 |
const struct argv_option options[],
|
|
|
383aa6 |
struct argv_ctx * ctx,
|
|
|
383aa6 |
struct argv_meta * meta)
|
|
|
383aa6 |
{
|
|
|
10a518 |
char ** parg;
|
|
|
383aa6 |
const char * ch;
|
|
|
383aa6 |
const char * val;
|
|
|
383aa6 |
const struct argv_option * option;
|
|
|
383aa6 |
struct argv_entry entry;
|
|
|
383aa6 |
struct argv_entry * mentry;
|
|
|
e775c9 |
enum argv_error ferr;
|
|
|
383aa6 |
bool fval;
|
|
|
383aa6 |
bool fnext;
|
|
|
383aa6 |
bool fshort;
|
|
|
5ec0ca |
bool fhybrid;
|
|
|
383aa6 |
bool fnoscan;
|
|
|
383aa6 |
|
|
|
4e499f |
parg = &argv[1];
|
|
|
383aa6 |
ch = *parg;
|
|
|
e775c9 |
ferr = ARGV_ERROR_OK;
|
|
|
383aa6 |
fshort = false;
|
|
|
383aa6 |
fnoscan = false;
|
|
|
383aa6 |
fval = false;
|
|
|
383aa6 |
mentry = meta ? meta->entries : 0;
|
|
|
383aa6 |
|
|
|
4e499f |
ctx->unitidx = 0;
|
|
|
4e499f |
ctx->erridx = 0;
|
|
|
4e499f |
|
|
|
e775c9 |
while (ch && (ferr == ARGV_ERROR_OK)) {
|
|
|
5ec0ca |
option = 0;
|
|
|
5ec0ca |
fhybrid = false;
|
|
|
383aa6 |
|
|
|
383aa6 |
if (fnoscan)
|
|
|
383aa6 |
fval = true;
|
|
|
383aa6 |
|
|
|
383aa6 |
else if (is_last_option(ch))
|
|
|
383aa6 |
fnoscan = true;
|
|
|
383aa6 |
|
|
|
5ec0ca |
else if (!fshort && is_hybrid_option(ch,options))
|
|
|
5ec0ca |
fhybrid = true;
|
|
|
5ec0ca |
|
|
|
5ec0ca |
if (!fnoscan && !fhybrid && (fshort || is_short_option(ch))) {
|
|
|
383aa6 |
if (!fshort)
|
|
|
383aa6 |
ch++;
|
|
|
383aa6 |
|
|
|
383aa6 |
if ((option = argv_short_option(ch,options,&entry))) {
|
|
|
383aa6 |
if (ch[1]) {
|
|
|
383aa6 |
ch++;
|
|
|
383aa6 |
fnext = false;
|
|
|
383aa6 |
fshort = (option->optarg == ARGV_OPTARG_NONE);
|
|
|
383aa6 |
} else {
|
|
|
383aa6 |
parg++;
|
|
|
383aa6 |
ch = *parg;
|
|
|
383aa6 |
fnext = true;
|
|
|
383aa6 |
fshort = false;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
if (option->optarg == ARGV_OPTARG_NONE) {
|
|
|
383aa6 |
if (!fnext && ch && (*ch == '-'))
|
|
|
e775c9 |
ferr = ARGV_ERROR_OPTARG_NONE;
|
|
|
383aa6 |
else
|
|
|
383aa6 |
fval = false;
|
|
|
383aa6 |
} else if (!fnext)
|
|
|
383aa6 |
fval = true;
|
|
|
383aa6 |
else if (option->optarg == ARGV_OPTARG_REQUIRED) {
|
|
|
383aa6 |
if (ch && is_short_option(ch))
|
|
|
e775c9 |
ferr = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
383aa6 |
else if (ch && is_long_option(ch))
|
|
|
e775c9 |
ferr = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
383aa6 |
else if (ch && is_last_option(ch))
|
|
|
e775c9 |
ferr = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
383aa6 |
else if (ch)
|
|
|
383aa6 |
fval = true;
|
|
|
383aa6 |
else
|
|
|
e775c9 |
ferr = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
383aa6 |
} else {
|
|
|
383aa6 |
/* ARGV_OPTARG_OPTIONAL */
|
|
|
383aa6 |
if (ch && is_short_option(ch))
|
|
|
383aa6 |
fval = false;
|
|
|
383aa6 |
else if (ch && is_long_option(ch))
|
|
|
383aa6 |
fval = false;
|
|
|
383aa6 |
else if (ch && is_last_option(ch))
|
|
|
383aa6 |
fval = false;
|
|
|
383aa6 |
else
|
|
|
383aa6 |
fval = ch;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
} else
|
|
|
e775c9 |
ferr = ARGV_ERROR_SHORT_OPTION;
|
|
|
383aa6 |
|
|
|
5ec0ca |
} else if (!fnoscan && (fhybrid || is_long_option(ch))) {
|
|
|
5ec0ca |
ch += (fhybrid ? 1 : 2);
|
|
|
5ec0ca |
|
|
|
5ec0ca |
if ((option = argv_long_option(ch,options,&entry))) {
|
|
|
383aa6 |
val = ch + strlen(option->long_name);
|
|
|
383aa6 |
|
|
|
d27fb4 |
/* val[0] is either '=' (or ',') or '\0' */
|
|
|
383aa6 |
if (!val[0]) {
|
|
|
383aa6 |
parg++;
|
|
|
383aa6 |
ch = *parg;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
5ec0ca |
if (fhybrid && !(option->flags & ARGV_OPTION_HYBRID_SWITCH))
|
|
|
e775c9 |
ferr = ARGV_ERROR_HYBRID_NONE;
|
|
|
f291eb |
else if (!fhybrid && (option->flags & ARGV_OPTION_HYBRID_ONLY))
|
|
|
f291eb |
ferr = ARGV_ERROR_HYBRID_ONLY;
|
|
|
5ec0ca |
else if (option->optarg == ARGV_OPTARG_NONE) {
|
|
|
383aa6 |
if (val[0]) {
|
|
|
e775c9 |
ferr = ARGV_ERROR_OPTARG_NONE;
|
|
|
383aa6 |
ctx->errch = val + 1;
|
|
|
383aa6 |
} else
|
|
|
383aa6 |
fval = false;
|
|
|
f291eb |
} else if (val[0] && (option->flags & ARGV_OPTION_HYBRID_JOINED)) {
|
|
|
500c8e |
fval = true;
|
|
|
500c8e |
ch = val;
|
|
|
500c8e |
} else if (fhybrid && !val[0] && !(option->flags & ARGV_OPTION_HYBRID_SPACE))
|
|
|
e775c9 |
ferr = ARGV_ERROR_HYBRID_SPACE;
|
|
|
d27fb4 |
else if (fhybrid && (val[0]=='=') && !(option->flags & ARGV_OPTION_HYBRID_EQUAL))
|
|
|
e775c9 |
ferr = ARGV_ERROR_HYBRID_EQUAL;
|
|
|
d27fb4 |
else if (fhybrid && (val[0]==',') && !(option->flags & ARGV_OPTION_HYBRID_COMMA))
|
|
|
e775c9 |
ferr = ARGV_ERROR_HYBRID_COMMA;
|
|
|
d27fb4 |
else if (!fhybrid && (val[0]==','))
|
|
|
e775c9 |
ferr = ARGV_ERROR_HYBRID_COMMA;
|
|
|
5ec0ca |
else if (val[0] && !val[1])
|
|
|
e775c9 |
ferr = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
383aa6 |
else if (val[0] && val[1]) {
|
|
|
383aa6 |
fval = true;
|
|
|
383aa6 |
ch = ++val;
|
|
|
383aa6 |
} else if (option->optarg == ARGV_OPTARG_REQUIRED) {
|
|
|
383aa6 |
if (!val[0] && !*parg)
|
|
|
e775c9 |
ferr = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
383aa6 |
else if (*parg && is_short_option(*parg))
|
|
|
e775c9 |
ferr = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
383aa6 |
else if (*parg && is_long_option(*parg))
|
|
|
e775c9 |
ferr = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
383aa6 |
else if (*parg && is_last_option(*parg))
|
|
|
e775c9 |
ferr = ARGV_ERROR_OPTARG_REQUIRED;
|
|
|
383aa6 |
else
|
|
|
383aa6 |
fval = true;
|
|
|
383aa6 |
} else {
|
|
|
383aa6 |
/* ARGV_OPTARG_OPTIONAL */
|
|
|
6fbd83 |
fval = val[0];
|
|
|
383aa6 |
}
|
|
|
383aa6 |
} else
|
|
|
e775c9 |
ferr = ARGV_ERROR_LONG_OPTION;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
e775c9 |
if (ferr == ARGV_ERROR_OK)
|
|
|
383aa6 |
if (option && fval && option->paradigm)
|
|
|
383aa6 |
if (!is_arg_in_paradigm(ch,option->paradigm))
|
|
|
e775c9 |
ferr = ARGV_ERROR_OPTARG_PARADIGM;
|
|
|
383aa6 |
|
|
|
4e499f |
if (ferr == ARGV_ERROR_OK)
|
|
|
4e499f |
if (!option && !ctx->unitidx)
|
|
|
4e499f |
ctx->unitidx = parg - argv;
|
|
|
4e499f |
|
|
|
e775c9 |
if (ferr != ARGV_ERROR_OK) {
|
|
|
e775c9 |
ctx->errcode = ferr;
|
|
|
383aa6 |
ctx->errch = ctx->errch ? ctx->errch : ch;
|
|
|
383aa6 |
ctx->erropt = option;
|
|
|
4e499f |
ctx->erridx = parg - argv;
|
|
|
383aa6 |
return;
|
|
|
383aa6 |
} else if (ctx->mode == ARGV_MODE_SCAN) {
|
|
|
383aa6 |
if (!fnoscan)
|
|
|
383aa6 |
ctx->nentries++;
|
|
|
383aa6 |
else if (fval)
|
|
|
383aa6 |
ctx->nentries++;
|
|
|
383aa6 |
|
|
|
383aa6 |
if (fval || !option) {
|
|
|
383aa6 |
parg++;
|
|
|
383aa6 |
ch = *parg;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
} else if (ctx->mode == ARGV_MODE_COPY) {
|
|
|
383aa6 |
if (fnoscan) {
|
|
|
383aa6 |
if (fval) {
|
|
|
383aa6 |
mentry->arg = ch;
|
|
|
383aa6 |
mentry->fnoscan = true;
|
|
|
383aa6 |
mentry++;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
parg++;
|
|
|
383aa6 |
ch = *parg;
|
|
|
383aa6 |
} else if (option) {
|
|
|
383aa6 |
mentry->arg = fval ? ch : 0;
|
|
|
383aa6 |
mentry->tag = option->tag;
|
|
|
383aa6 |
mentry->fopt = true;
|
|
|
383aa6 |
mentry->fval = fval;
|
|
|
383aa6 |
mentry++;
|
|
|
383aa6 |
|
|
|
383aa6 |
if (fval) {
|
|
|
383aa6 |
parg++;
|
|
|
383aa6 |
ch = *parg;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
} else {
|
|
|
383aa6 |
mentry->arg = ch;
|
|
|
383aa6 |
mentry++;
|
|
|
383aa6 |
parg++;
|
|
|
383aa6 |
ch = *parg;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
}
|
|
|
383aa6 |
}
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
static const char * argv_program_name(const char * program_path)
|
|
|
383aa6 |
{
|
|
|
383aa6 |
const char * ch;
|
|
|
383aa6 |
|
|
|
383aa6 |
if (program_path) {
|
|
|
383aa6 |
if ((ch = strrchr(program_path,'/')))
|
|
|
383aa6 |
return *(++ch) ? ch : 0;
|
|
|
383aa6 |
|
|
|
383aa6 |
if ((ch = strrchr(program_path,'\\')))
|
|
|
383aa6 |
return *(++ch) ? ch : 0;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
return program_path;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
static void argv_show_error(struct argv_ctx * ctx)
|
|
|
383aa6 |
{
|
|
|
383aa6 |
fprintf(stderr,"%s: error: ",ctx->program);
|
|
|
383aa6 |
|
|
|
383aa6 |
switch (ctx->errcode) {
|
|
|
383aa6 |
case ARGV_ERROR_SHORT_OPTION:
|
|
|
d83bc0 |
fprintf(stderr,"'-%c' is not a valid short option\n",*ctx->errch);
|
|
|
383aa6 |
break;
|
|
|
383aa6 |
|
|
|
383aa6 |
case ARGV_ERROR_LONG_OPTION:
|
|
|
383aa6 |
fprintf(stderr,"'--%s' is not a valid long option\n",ctx->errch);
|
|
|
383aa6 |
break;
|
|
|
383aa6 |
|
|
|
383aa6 |
case ARGV_ERROR_OPTARG_NONE:
|
|
|
135c1e |
fprintf(stderr,"'%s' is not a valid option value for [%s%c%s%s%s] "
|
|
|
135c1e |
"(option values may not be specified)\n",
|
|
|
383aa6 |
ctx->errch,
|
|
|
383aa6 |
ctx->erropt->short_name ? "-" : "",
|
|
|
383aa6 |
ctx->erropt->short_name,
|
|
|
383aa6 |
ctx->erropt->short_name ? "," : "",
|
|
|
383aa6 |
ctx->erropt->long_name ? "--" : "",
|
|
|
383aa6 |
ctx->erropt->long_name);
|
|
|
383aa6 |
break;
|
|
|
383aa6 |
|
|
|
383aa6 |
case ARGV_ERROR_OPTARG_REQUIRED:
|
|
|
383aa6 |
fprintf(stderr,"option [%s%c%s%s%s] requires %s %s%s%s\n",
|
|
|
383aa6 |
ctx->erropt->short_name ? "-" : "",
|
|
|
383aa6 |
ctx->erropt->short_name,
|
|
|
383aa6 |
ctx->erropt->short_name ? "," : "",
|
|
|
383aa6 |
ctx->erropt->long_name ? "--" : "",
|
|
|
383aa6 |
ctx->erropt->long_name,
|
|
|
383aa6 |
ctx->erropt->paradigm ? "one of the following values:" : "a value",
|
|
|
383aa6 |
ctx->erropt->paradigm ? "{" : "",
|
|
|
383aa6 |
ctx->erropt->paradigm ? ctx->erropt->paradigm : "",
|
|
|
383aa6 |
ctx->erropt->paradigm ? "}" : "");
|
|
|
383aa6 |
break;
|
|
|
383aa6 |
|
|
|
383aa6 |
case ARGV_ERROR_OPTARG_PARADIGM:
|
|
|
383aa6 |
fprintf(stderr,"'%s' is not a valid option value for [%s%c%s%s%s]={%s}\n",
|
|
|
383aa6 |
ctx->errch,
|
|
|
383aa6 |
ctx->erropt->short_name ? "-" : "",
|
|
|
383aa6 |
ctx->erropt->short_name,
|
|
|
383aa6 |
ctx->erropt->short_name ? "," : "",
|
|
|
383aa6 |
ctx->erropt->long_name ? "--" : "",
|
|
|
383aa6 |
ctx->erropt->long_name,
|
|
|
383aa6 |
ctx->erropt->paradigm);
|
|
|
383aa6 |
break;
|
|
|
383aa6 |
|
|
|
5ec0ca |
case ARGV_ERROR_HYBRID_NONE:
|
|
|
5ec0ca |
fprintf(stderr,"-%s is not a synonym for --%s\n",
|
|
|
5ec0ca |
ctx->erropt->long_name,
|
|
|
5ec0ca |
ctx->erropt->long_name);
|
|
|
5ec0ca |
break;
|
|
|
5ec0ca |
|
|
|
5ec0ca |
case ARGV_ERROR_HYBRID_ONLY:
|
|
|
5ec0ca |
fprintf(stderr,"--%s is not a synonym for -%s\n",
|
|
|
5ec0ca |
ctx->erropt->long_name,
|
|
|
5ec0ca |
ctx->erropt->long_name);
|
|
|
5ec0ca |
break;
|
|
|
5ec0ca |
|
|
|
5ec0ca |
case ARGV_ERROR_HYBRID_SPACE:
|
|
|
5ec0ca |
case ARGV_ERROR_HYBRID_EQUAL:
|
|
|
d27fb4 |
case ARGV_ERROR_HYBRID_COMMA:
|
|
|
d27fb4 |
fprintf(stderr,"-%s: illegal value assignment; valid syntax is "
|
|
|
d27fb4 |
"-%s%sVAL\n",
|
|
|
5ec0ca |
ctx->erropt->long_name,
|
|
|
d27fb4 |
ctx->erropt->long_name,
|
|
|
d27fb4 |
(ctx->erropt->flags & ARGV_OPTION_HYBRID_SPACE)
|
|
|
d27fb4 |
? " " : (ctx->erropt->flags & ARGV_OPTION_HYBRID_EQUAL)
|
|
|
500c8e |
? "=" : (ctx->erropt->flags & ARGV_OPTION_HYBRID_COMMA)
|
|
|
500c8e |
? "," : "");
|
|
|
d27fb4 |
|
|
|
5ec0ca |
break;
|
|
|
5ec0ca |
|
|
|
383aa6 |
case ARGV_ERROR_INTERNAL:
|
|
|
383aa6 |
fputs("internal error",stderr);
|
|
|
383aa6 |
break;
|
|
|
383aa6 |
|
|
|
383aa6 |
default:
|
|
|
383aa6 |
break;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
static void argv_show_status(
|
|
|
383aa6 |
const struct argv_option options[],
|
|
|
383aa6 |
struct argv_ctx * ctx,
|
|
|
383aa6 |
struct argv_meta * meta)
|
|
|
383aa6 |
{
|
|
|
383aa6 |
int argc;
|
|
|
10a518 |
char ** argv;
|
|
|
383aa6 |
struct argv_entry * entry;
|
|
|
383aa6 |
const struct argv_option * option;
|
|
|
383aa6 |
char short_name[2] = {0};
|
|
|
383aa6 |
const char * space = "";
|
|
|
383aa6 |
|
|
|
4cf844 |
(void)ctx;
|
|
|
4cf844 |
|
|
|
383aa6 |
fputs("\n\nconcatenated command line:\n",stderr);
|
|
|
383aa6 |
for (argv=meta->argv; *argv; argv++) {
|
|
|
383aa6 |
fprintf(stderr,"%s%s",space,*argv);
|
|
|
383aa6 |
space = " ";
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
fputs("\n\nargument vector:\n",stderr);
|
|
|
383aa6 |
for (argc=0,argv=meta->argv; *argv; argc++,argv++)
|
|
|
383aa6 |
fprintf(stderr,"argv[%d]: %s\n",argc,*argv);
|
|
|
383aa6 |
|
|
|
383aa6 |
fputs("\n\nparsed entries:\n",stderr);
|
|
|
383aa6 |
for (entry=meta->entries; entry->arg || entry->fopt; entry++)
|
|
|
383aa6 |
if (entry->fopt) {
|
|
|
383aa6 |
option = option_from_tag(options,entry->tag);
|
|
|
383aa6 |
short_name[0] = option->short_name;
|
|
|
383aa6 |
|
|
|
383aa6 |
if (entry->fval)
|
|
|
383aa6 |
fprintf(stderr,"[-%s,--%s] := %s\n",
|
|
|
383aa6 |
short_name,option->long_name,entry->arg);
|
|
|
383aa6 |
else
|
|
|
383aa6 |
fprintf(stderr,"[-%s,--%s]\n",
|
|
|
383aa6 |
short_name,option->long_name);
|
|
|
383aa6 |
} else
|
|
|
383aa6 |
fprintf(stderr,"<program arg> := %s\n",entry->arg);
|
|
|
383aa6 |
|
|
|
383aa6 |
fputs("\n\n",stderr);
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
static struct argv_meta * argv_free_impl(struct argv_meta_impl * imeta)
|
|
|
383aa6 |
{
|
|
|
383aa6 |
if (imeta->argv)
|
|
|
383aa6 |
free(imeta->argv);
|
|
|
383aa6 |
|
|
|
383aa6 |
if (imeta->strbuf)
|
|
|
383aa6 |
free(imeta->strbuf);
|
|
|
383aa6 |
|
|
|
383aa6 |
if (imeta->meta.entries)
|
|
|
383aa6 |
free(imeta->meta.entries);
|
|
|
383aa6 |
|
|
|
383aa6 |
free(imeta);
|
|
|
383aa6 |
return 0;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
10a518 |
static struct argv_meta * argv_alloc(char ** argv, struct argv_ctx * ctx)
|
|
|
383aa6 |
{
|
|
|
383aa6 |
struct argv_meta_impl * imeta;
|
|
|
10a518 |
char ** vector;
|
|
|
383aa6 |
char * dst;
|
|
|
383aa6 |
size_t size;
|
|
|
383aa6 |
int argc;
|
|
|
383aa6 |
int i;
|
|
|
383aa6 |
|
|
|
654e64 |
if (!(imeta = calloc(1,sizeof(*imeta))))
|
|
|
383aa6 |
return 0;
|
|
|
383aa6 |
|
|
|
383aa6 |
if (ctx->flags & ARGV_CLONE_VECTOR) {
|
|
|
383aa6 |
for (vector=argv,argc=0,size=0; *vector; vector++) {
|
|
|
383aa6 |
size += strlen(*vector) + 1;
|
|
|
383aa6 |
argc++;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
654e64 |
if (!(imeta->argv = calloc(argc+1,sizeof(char *))))
|
|
|
383aa6 |
return argv_free_impl(imeta);
|
|
|
654e64 |
else if (!(imeta->strbuf = calloc(1,size+1)))
|
|
|
383aa6 |
return argv_free_impl(imeta);
|
|
|
383aa6 |
|
|
|
383aa6 |
for (i=0,dst=imeta->strbuf; i
|
|
|
383aa6 |
strcpy(dst,argv[i]);
|
|
|
383aa6 |
imeta->argv[i] = dst;
|
|
|
383aa6 |
dst += strlen(dst)+1;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
imeta->meta.argv = imeta->argv;
|
|
|
383aa6 |
} else
|
|
|
383aa6 |
imeta->meta.argv = argv;
|
|
|
383aa6 |
|
|
|
654e64 |
if (!(imeta->meta.entries = calloc(
|
|
|
654e64 |
ctx->nentries+1,
|
|
|
654e64 |
sizeof(struct argv_entry))))
|
|
|
383aa6 |
return argv_free_impl(imeta);
|
|
|
383aa6 |
else
|
|
|
383aa6 |
return &imeta->meta;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
static struct argv_meta * argv_get(
|
|
|
10a518 |
char * argv[],
|
|
|
383aa6 |
const struct argv_option options[],
|
|
|
383aa6 |
int flags)
|
|
|
383aa6 |
{
|
|
|
383aa6 |
struct argv_meta * meta;
|
|
|
4e499f |
struct argv_ctx ctx = {flags,ARGV_MODE_SCAN,0,0,0,0,0,0,0};
|
|
|
383aa6 |
|
|
|
383aa6 |
argv_scan(argv,options,&ctx,0);
|
|
|
383aa6 |
|
|
|
383aa6 |
if (ctx.errcode != ARGV_ERROR_OK) {
|
|
|
383aa6 |
if (!ctx.program)
|
|
|
383aa6 |
ctx.program = argv_program_name(argv[0]);
|
|
|
383aa6 |
|
|
|
383aa6 |
if (ctx.flags & ARGV_VERBOSITY_ERRORS)
|
|
|
383aa6 |
argv_show_error(&ctx;;
|
|
|
383aa6 |
|
|
|
383aa6 |
return 0;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
if (!(meta = argv_alloc(argv,&ctx)))
|
|
|
383aa6 |
return 0;
|
|
|
383aa6 |
|
|
|
383aa6 |
ctx.mode = ARGV_MODE_COPY;
|
|
|
383aa6 |
argv_scan(meta->argv,options,&ctx,meta);
|
|
|
383aa6 |
|
|
|
383aa6 |
if (ctx.errcode != ARGV_ERROR_OK) {
|
|
|
383aa6 |
if (!ctx.program)
|
|
|
383aa6 |
ctx.program = argv[0];
|
|
|
383aa6 |
|
|
|
383aa6 |
ctx.errcode = ARGV_ERROR_INTERNAL;
|
|
|
383aa6 |
argv_show_error(&ctx;;
|
|
|
383aa6 |
argv_free(meta);
|
|
|
383aa6 |
|
|
|
383aa6 |
return 0;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
if (ctx.flags & ARGV_VERBOSITY_STATUS)
|
|
|
383aa6 |
argv_show_status(options,&ctx,meta);
|
|
|
383aa6 |
|
|
|
383aa6 |
return meta;
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
static void argv_free(struct argv_meta * xmeta)
|
|
|
383aa6 |
{
|
|
|
383aa6 |
struct argv_meta_impl * imeta;
|
|
|
383aa6 |
uintptr_t addr;
|
|
|
383aa6 |
|
|
|
383aa6 |
if (xmeta) {
|
|
|
383aa6 |
addr = (uintptr_t)xmeta - offsetof(struct argv_meta_impl,meta);
|
|
|
383aa6 |
imeta = (struct argv_meta_impl *)addr;
|
|
|
383aa6 |
argv_free_impl(imeta);
|
|
|
383aa6 |
}
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
static void argv_usage(
|
|
|
383aa6 |
FILE * file,
|
|
|
383aa6 |
const char * header,
|
|
|
383aa6 |
const struct argv_option options[],
|
|
|
383aa6 |
const char * mode)
|
|
|
383aa6 |
{
|
|
|
f47680 |
const struct argv_option * option;
|
|
|
f47680 |
bool fshort,flong,fboth;
|
|
|
f47680 |
size_t len,optlen,desclen;
|
|
|
f47680 |
char cache;
|
|
|
f47680 |
char * prefix;
|
|
|
f47680 |
char * desc;
|
|
|
f47680 |
char * mark;
|
|
|
f47680 |
char * cap;
|
|
|
f47680 |
char description[4096];
|
|
|
f47680 |
char optstr [72];
|
|
|
f47680 |
const size_t optcap = 64;
|
|
|
f47680 |
const size_t width = 80;
|
|
|
f47680 |
const char indent[] = " ";
|
|
|
84eb70 |
const char creset[] = "\x1b[0m";
|
|
|
84eb70 |
const char cbold [] = "\x1b[1m";
|
|
|
84eb70 |
const char cgreen[] = "\x1b[32m";
|
|
|
84eb70 |
const char cblue [] = "\x1b[34m";
|
|
|
84eb70 |
const char ccyan [] = "\x1b[36m";
|
|
|
84eb70 |
const char * color = ccyan;
|
|
|
84eb70 |
bool fcolor;
|
|
|
f47680 |
|
|
|
f47680 |
fshort = mode ? !strcmp(mode,"short") : 0;
|
|
|
f47680 |
flong = fshort ? 0 : mode && !strcmp(mode,"long");
|
|
|
f47680 |
fboth = !fshort && !flong;
|
|
|
b78670 |
fcolor = isatty(fileno(file));
|
|
|
84eb70 |
|
|
|
84eb70 |
if (fcolor)
|
|
|
b78670 |
fprintf(file,"%s%s",cbold,cgreen);
|
|
|
f47680 |
|
|
|
f47680 |
if (header)
|
|
|
b78670 |
fprintf(file,"%s",header);
|
|
|
f47680 |
|
|
|
f47680 |
option = options;
|
|
|
f47680 |
optlen = 0;
|
|
|
f47680 |
|
|
|
f47680 |
for (; option->short_name || option->long_name; option++) {
|
|
|
f47680 |
/* indent + comma */
|
|
|
f47680 |
len = fboth ? sizeof(indent) + 1 : sizeof(indent);
|
|
|
f47680 |
|
|
|
f47680 |
/* -o */
|
|
|
f47680 |
if (fshort || fboth)
|
|
|
f47680 |
len += option->short_name
|
|
|
f47680 |
? 2 : 0;
|
|
|
f47680 |
|
|
|
f47680 |
/* --option */
|
|
|
f47680 |
if (flong || fboth)
|
|
|
f47680 |
len += option->long_name
|
|
|
f47680 |
? 2 + strlen(option->long_name) : 0;
|
|
|
f47680 |
|
|
|
f47680 |
/* optlen */
|
|
|
f47680 |
if (len > optlen)
|
|
|
f47680 |
optlen = len;
|
|
|
f47680 |
}
|
|
|
f47680 |
|
|
|
f47680 |
if (optlen >= optcap) {
|
|
|
f47680 |
fprintf(stderr,
|
|
|
f47680 |
"Option strings exceed %zu characters, "
|
|
|
f47680 |
"please generate the usage screen manually.\n",
|
|
|
f47680 |
optcap);
|
|
|
f47680 |
return;
|
|
|
f47680 |
}
|
|
|
f47680 |
|
|
|
f47680 |
optlen += ARGV_TAB_WIDTH;
|
|
|
f47680 |
optlen &= (~(ARGV_TAB_WIDTH-1));
|
|
|
f47680 |
desclen = (optlen < width / 2) ? width - optlen : optlen;
|
|
|
f47680 |
|
|
|
f47680 |
for (option=options; option->short_name || option->long_name; option++) {
|
|
|
84eb70 |
/* color */
|
|
|
84eb70 |
if (fcolor) {
|
|
|
84eb70 |
color = (color == ccyan) ? cblue : ccyan;
|
|
|
b78670 |
fputs(color,file);
|
|
|
84eb70 |
}
|
|
|
84eb70 |
|
|
|
f47680 |
/* description, using either paradigm or argname if applicable */
|
|
|
f47680 |
snprintf(description,sizeof(description),option->description,
|
|
|
f47680 |
option->paradigm
|
|
|
f47680 |
? option->paradigm
|
|
|
f47680 |
: option->argname ? option->argname : "");
|
|
|
f47680 |
description[sizeof(description)-1] = 0;
|
|
|
f47680 |
|
|
|
f47680 |
/* long/hybrid option prefix (-/--) */
|
|
|
f47680 |
prefix = option->flags & ARGV_OPTION_HYBRID_ONLY
|
|
|
f47680 |
? " -" : "--";
|
|
|
f47680 |
|
|
|
f47680 |
/* option string */
|
|
|
f47680 |
if (fboth && option->short_name && option->long_name)
|
|
|
f47680 |
sprintf(optstr,"%s-%c,%s%s",
|
|
|
f47680 |
indent,option->short_name,prefix,option->long_name);
|
|
|
f47680 |
|
|
|
f47680 |
else if ((fshort || fboth) && option->short_name)
|
|
|
f47680 |
sprintf(optstr,"%s-%d",indent,option->short_name);
|
|
|
f47680 |
|
|
|
f47680 |
else if (flong && option->long_name)
|
|
|
f47680 |
sprintf(optstr,"%s%s%s",
|
|
|
f47680 |
indent,prefix,option->long_name);
|
|
|
f47680 |
|
|
|
f47680 |
else if (fboth && option->long_name)
|
|
|
f47680 |
sprintf(optstr,"%s %s%s",
|
|
|
f47680 |
indent,prefix,option->long_name);
|
|
|
f47680 |
|
|
|
f47680 |
else
|
|
|
f47680 |
optstr[0] = 0;
|
|
|
f47680 |
|
|
|
f47680 |
/* right-indented option buffer */
|
|
|
f47680 |
if (description[0]) {
|
|
|
f47680 |
len = strlen(optstr);
|
|
|
f47680 |
sprintf(&optstr[len],"%-*c",(int)(optlen-len),' ');
|
|
|
f47680 |
}
|
|
|
f47680 |
|
|
|
f47680 |
/* single line? */
|
|
|
f47680 |
if (optlen + strlen(description) < width) {
|
|
|
b78670 |
fprintf(file,"%s%s\n",optstr,description);
|
|
|
f47680 |
|
|
|
f47680 |
} else {
|
|
|
f47680 |
desc = description;
|
|
|
f47680 |
cap = desc + strlen(description);
|
|
|
f47680 |
|
|
|
f47680 |
while (desc < cap) {
|
|
|
f47680 |
mark = (desc + desclen >= cap)
|
|
|
f47680 |
? cap : desc + desclen;
|
|
|
f47680 |
|
|
|
f47680 |
while (*mark && (mark > desc)
|
|
|
f47680 |
&& (*mark != ' ')
|
|
|
df1c7b |
&& (*mark != '|')
|
|
|
f47680 |
&& (*mark != '\t')
|
|
|
f47680 |
&& (*mark != '\n'))
|
|
|
f47680 |
mark--;
|
|
|
f47680 |
|
|
|
f47680 |
if (mark == desc) {
|
|
|
f47680 |
mark = (desc + desclen >= cap)
|
|
|
f47680 |
? cap : desc + desclen;
|
|
|
f47680 |
cache = *mark;
|
|
|
f47680 |
*mark = 0;
|
|
|
df1c7b |
} else if (*mark == '|') {
|
|
|
df1c7b |
cache = *mark;
|
|
|
df1c7b |
*mark = 0;
|
|
|
f47680 |
} else {
|
|
|
f47680 |
cache = 0;
|
|
|
f47680 |
*mark = 0;
|
|
|
f47680 |
}
|
|
|
f47680 |
|
|
|
f47680 |
/* first line? */
|
|
|
f47680 |
if (desc == description)
|
|
|
b78670 |
fprintf(file,"%s%s\n",optstr,desc);
|
|
|
f47680 |
else
|
|
|
b78670 |
fprintf(file,"%-*c %s\n",
|
|
|
df1c7b |
(*desc == '|')
|
|
|
df1c7b |
? (int)(optlen+1)
|
|
|
df1c7b |
: (int)optlen,
|
|
|
df1c7b |
' ',desc);
|
|
|
f47680 |
|
|
|
f47680 |
if (cache)
|
|
|
f47680 |
*mark = cache;
|
|
|
f47680 |
else
|
|
|
f47680 |
mark++;
|
|
|
f47680 |
|
|
|
f47680 |
desc = mark;
|
|
|
f47680 |
}
|
|
|
f47680 |
}
|
|
|
f47680 |
}
|
|
|
84eb70 |
|
|
|
84eb70 |
if (fcolor)
|
|
|
b78670 |
fputs(creset,file);
|
|
|
383aa6 |
}
|
|
|
383aa6 |
|
|
|
383aa6 |
#endif
|
|
|
383aa6 |
|
|
|
383aa6 |
#endif
|