238 lines
4.5 KiB
C
238 lines
4.5 KiB
C
/* ===========================================================================
|
|
* uz80as, an assembler for the Zilog Z80 and several other microprocessors.
|
|
*
|
|
* Handling of command line options, similar to getopt.
|
|
* ===========================================================================
|
|
*/
|
|
|
|
/*
|
|
* Changes:
|
|
*
|
|
* - Jul 22 2018: long options without short option character recognized.
|
|
*
|
|
*/
|
|
|
|
#include "ngetopt.h"
|
|
|
|
#ifndef STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
|
|
static int find_short_opt(int val, struct ngetopt_opt *ops)
|
|
{
|
|
int i;
|
|
|
|
i = 0;
|
|
while (ops[i].name != NULL) {
|
|
if (ops[i].val > 0 && ops[i].val == val)
|
|
return i;
|
|
i++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int find_long_opt(char *str, struct ngetopt_opt *ops)
|
|
{
|
|
int i;
|
|
const char *p, *q;
|
|
|
|
i = 0;
|
|
while (ops[i].name != NULL) {
|
|
p = ops[i].name;
|
|
q = str;
|
|
while (*p != '\0' && *p == *q) {
|
|
p++;
|
|
q++;
|
|
}
|
|
if (*p == '\0' && (*q == '\0' || *q == '=')) {
|
|
return i;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void ngetopt_init(struct ngetopt *p, int argc, char *const *argv,
|
|
struct ngetopt_opt *ops)
|
|
{
|
|
p->argc = argc;
|
|
p->argv = argv;
|
|
p->ops = ops;
|
|
p->optind = 1;
|
|
p->subind = 0;
|
|
strcpy(p->str, "-X");
|
|
}
|
|
|
|
static int get_short_opt(struct ngetopt *p)
|
|
{
|
|
int i;
|
|
char *opt;
|
|
|
|
opt = p->argv[p->optind];
|
|
i = find_short_opt(opt[p->subind], p->ops);
|
|
if (i < 0) {
|
|
/* unrecognized option */
|
|
p->str[1] = (char) opt[p->subind];
|
|
p->optarg = p->str;
|
|
p->subind++;
|
|
return '?';
|
|
}
|
|
|
|
if (!p->ops[i].has_arg) {
|
|
/* it's ok */
|
|
p->subind++;
|
|
return p->ops[i].val;
|
|
}
|
|
|
|
/* needs an argument */
|
|
if (opt[p->subind + 1] != '\0') {
|
|
/* the argument is the suffix */
|
|
p->optarg = &opt[p->subind + 1];
|
|
p->subind = 0;
|
|
p->optind++;
|
|
return p->ops[i].val;
|
|
}
|
|
|
|
/* the argument is the next token */
|
|
p->optind++;
|
|
p->subind = 0;
|
|
if (p->optind < p->argc) {
|
|
p->optarg = p->argv[p->optind];
|
|
p->optind++;
|
|
return p->ops[i].val;
|
|
}
|
|
|
|
/* ups, argument missing */
|
|
p->str[1] = (char) p->ops[i].val;
|
|
p->optarg = p->str;
|
|
return ':';
|
|
}
|
|
|
|
static int get_opt(struct ngetopt *p)
|
|
{
|
|
int i;
|
|
char *opt, *optnext;
|
|
|
|
/* all arguments consumed */
|
|
if (p->optind >= p->argc)
|
|
return -1;
|
|
|
|
opt = p->argv[p->optind];
|
|
if (opt[0] != '-') {
|
|
/* non option */
|
|
return -1;
|
|
}
|
|
|
|
/* - */
|
|
if (opt[1] == '\0') {
|
|
/* stdin */
|
|
return -1;
|
|
}
|
|
|
|
if (opt[1] != '-') {
|
|
/* -xxxxx */
|
|
p->subind = 1;
|
|
return get_short_opt(p);
|
|
}
|
|
|
|
/* -- */
|
|
if (opt[2] == '\0') {
|
|
/* found "--" */
|
|
p->optind++;
|
|
return -1;
|
|
}
|
|
|
|
/* long option */
|
|
i = find_long_opt(&opt[2], p->ops);
|
|
if (i < 0) {
|
|
/* not found */
|
|
p->optind++;
|
|
p->optarg = opt;
|
|
while (*opt != '\0' && *opt != '=') {
|
|
opt++;
|
|
}
|
|
*opt = '\0';
|
|
return '?';
|
|
}
|
|
|
|
/* found, go to end of option */
|
|
optnext = opt + 2 + strlen(p->ops[i].name);
|
|
|
|
if (*optnext == '\0' && !p->ops[i].has_arg) {
|
|
/* doesn't need arguments */
|
|
p->optind++;
|
|
p->optstr = opt + 2;
|
|
return p->ops[i].val;
|
|
}
|
|
|
|
if (*optnext == '=' && !p->ops[i].has_arg) {
|
|
/* does not need arguments but argument supplied */
|
|
*optnext = '\0';
|
|
p->optarg = opt;
|
|
return ';';
|
|
}
|
|
|
|
/* the argument is the next token */
|
|
if (*optnext == '\0') {
|
|
p->optind++;
|
|
if (p->optind < p->argc) {
|
|
p->optstr = opt + 2;
|
|
p->optarg = p->argv[p->optind];
|
|
p->optind++;
|
|
return p->ops[i].val;
|
|
}
|
|
|
|
/* ups, argument missing */
|
|
p->optarg = opt;
|
|
p->optind++;
|
|
return ':';
|
|
}
|
|
|
|
/* *optnext == '=' */
|
|
*optnext = '\0';
|
|
p->optstr = opt + 2;
|
|
p->optarg = optnext + 1;
|
|
p->optind++;
|
|
return p->ops[i].val;
|
|
}
|
|
|
|
/*
|
|
* If ok:
|
|
*
|
|
* - For a long option with a zero value single character option, 0 is
|
|
* returned, optstr is the string of the long option (without '-' or '--')
|
|
* and optarg is the option argument or NULL.
|
|
*
|
|
* - For anything else the single option character is returned and optarg
|
|
* is the option argument or NULL.
|
|
*
|
|
* If the option is not recognized, '?' is returned, and optarg is the
|
|
* literal string of the option not recognized (already with '-' or '--'
|
|
* prefixed).
|
|
*
|
|
* If the option is recognized but the argument is missing, ':' is
|
|
* returned and optarg is the option as supplied (with '-' or '--' prefixed).
|
|
*
|
|
* If the option is recognized and it is a long option followed by '=', but the
|
|
* option does not take arguments, ';' is returned and optarg is the option
|
|
* (with '-' or '--' prefixed).
|
|
*
|
|
* -1 is returned if no more options.
|
|
*/
|
|
int ngetopt_next(struct ngetopt *p)
|
|
{
|
|
if (p->subind == 0)
|
|
return get_opt(p);
|
|
|
|
/* p->subind > 0 */
|
|
if (p->argv[p->optind][p->subind] != '\0')
|
|
return get_short_opt(p);
|
|
|
|
/* no more options in this list of short options */
|
|
p->subind = 0;
|
|
p->optind++;
|
|
return get_opt(p);
|
|
}
|