Blame src/scanopt.c

Packit f00812
/* flex - tool to generate fast lexical analyzers */
Packit f00812
Packit f00812
/*  Copyright (c) 1990 The Regents of the University of California. */
Packit f00812
/*  All rights reserved. */
Packit f00812
Packit f00812
/*  This code is derived from software contributed to Berkeley by */
Packit f00812
/*  Vern Paxson. */
Packit f00812
Packit f00812
/*  The United States Government has rights in this work pursuant */
Packit f00812
/*  to contract no. DE-AC03-76SF00098 between the United States */
Packit f00812
/*  Department of Energy and the University of California. */
Packit f00812
Packit f00812
/*  This file is part of flex. */
Packit f00812
Packit f00812
/*  Redistribution and use in source and binary forms, with or without */
Packit f00812
/*  modification, are permitted provided that the following conditions */
Packit f00812
/*  are met: */
Packit f00812
Packit f00812
/*  1. Redistributions of source code must retain the above copyright */
Packit f00812
/*     notice, this list of conditions and the following disclaimer. */
Packit f00812
/*  2. Redistributions in binary form must reproduce the above copyright */
Packit f00812
/*     notice, this list of conditions and the following disclaimer in the */
Packit f00812
/*     documentation and/or other materials provided with the distribution. */
Packit f00812
Packit f00812
/*  Neither the name of the University nor the names of its contributors */
Packit f00812
/*  may be used to endorse or promote products derived from this software */
Packit f00812
/*  without specific prior written permission. */
Packit f00812
Packit f00812
/*  THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */
Packit f00812
/*  IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */
Packit f00812
/*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */
Packit f00812
/*  PURPOSE. */
Packit f00812

Packit f00812
#include "flexdef.h"
Packit f00812
#include "scanopt.h"
Packit f00812
Packit f00812
Packit f00812
/* Internal structures */
Packit f00812
Packit f00812
#define ARG_NONE 0x01
Packit f00812
#define ARG_REQ  0x02
Packit f00812
#define ARG_OPT  0x04
Packit f00812
#define IS_LONG  0x08
Packit f00812
Packit f00812
struct _aux {
Packit f00812
	int     flags;		/* The above hex flags. */
Packit f00812
	int     namelen;	/* Length of the actual option word, e.g., "--file[=foo]" is 4 */
Packit f00812
	int     printlen;	/* Length of entire string, e.g., "--file[=foo]" is 12 */
Packit f00812
};
Packit f00812
Packit f00812
Packit f00812
struct _scanopt_t {
Packit f00812
	const optspec_t *options;	/* List of options. */
Packit f00812
	struct _aux *aux;	/* Auxiliary data about options. */
Packit f00812
	int     optc;		/* Number of options. */
Packit f00812
	int     argc;		/* Number of args. */
Packit f00812
	char  **argv;		/* Array of strings. */
Packit f00812
	int     index;		/* Used as: argv[index][subscript]. */
Packit f00812
	int     subscript;
Packit f00812
	char    no_err_msg;	/* If true, do not print errors. */
Packit f00812
	char    has_long;
Packit f00812
	char    has_short;
Packit f00812
};
Packit f00812
Packit f00812
/* Accessor functions. These WOULD be one-liners, but portability calls. */
Packit f00812
static const char *NAME(struct _scanopt_t *, int);
Packit f00812
static int PRINTLEN(struct _scanopt_t *, int);
Packit f00812
static int RVAL(struct _scanopt_t *, int);
Packit f00812
static int FLAGS(struct _scanopt_t *, int);
Packit f00812
static const char *DESC(struct _scanopt_t *, int);
Packit f00812
static int scanopt_err(struct _scanopt_t *, int, int);
Packit f00812
static int matchlongopt(char *, char **, int *, char **, int *);
Packit f00812
static int find_opt(struct _scanopt_t *, int, char *, int, int *, int *opt_offset);
Packit f00812
Packit f00812
static const char *NAME (struct _scanopt_t *s, int i)
Packit f00812
{
Packit f00812
	return s->options[i].opt_fmt +
Packit f00812
		((s->aux[i].flags & IS_LONG) ? 2 : 1);
Packit f00812
}
Packit f00812
Packit f00812
static int PRINTLEN (struct _scanopt_t *s, int i)
Packit f00812
{
Packit f00812
	return s->aux[i].printlen;
Packit f00812
}
Packit f00812
Packit f00812
static int RVAL (struct _scanopt_t *s, int i)
Packit f00812
{
Packit f00812
	return s->options[i].r_val;
Packit f00812
}
Packit f00812
Packit f00812
static int FLAGS (struct _scanopt_t *s, int i)
Packit f00812
{
Packit f00812
	return s->aux[i].flags;
Packit f00812
}
Packit f00812
Packit f00812
static const char *DESC (struct _scanopt_t *s, int i)
Packit f00812
{
Packit f00812
	return s->options[i].desc ? s->options[i].desc : "";
Packit f00812
}
Packit f00812
Packit f00812
#ifndef NO_SCANOPT_USAGE
Packit f00812
static int get_cols (void);
Packit f00812
Packit f00812
static int get_cols (void)
Packit f00812
{
Packit f00812
	char   *env;
Packit f00812
	int     cols = 80;	/* default */
Packit f00812
Packit f00812
#ifdef HAVE_NCURSES_H
Packit f00812
	initscr ();
Packit f00812
	endwin ();
Packit f00812
	if (COLS > 0)
Packit f00812
		return COLS;
Packit f00812
#endif
Packit f00812
Packit f00812
	if ((env = getenv ("COLUMNS")) != NULL)
Packit f00812
		cols = atoi (env);
Packit f00812
Packit f00812
	return cols;
Packit f00812
}
Packit f00812
#endif
Packit f00812
Packit f00812
/* Macro to check for NULL before assigning a value. */
Packit f00812
#define SAFE_ASSIGN(ptr,val) \
Packit f00812
    do{                      \
Packit f00812
        if((ptr)!=NULL)      \
Packit f00812
            *(ptr) = val;    \
Packit f00812
    }while(0)
Packit f00812
Packit f00812
/* Macro to assure we reset subscript whenever we adjust s->index.*/
Packit f00812
#define INC_INDEX(s,n)     \
Packit f00812
    do{                    \
Packit f00812
       (s)->index += (n);  \
Packit f00812
       (s)->subscript= 0;  \
Packit f00812
    }while(0)
Packit f00812
Packit f00812
scanopt_t *scanopt_init (const optspec_t *options, int argc, char **argv, int flags)
Packit f00812
{
Packit f00812
	int     i;
Packit f00812
	struct _scanopt_t *s;
Packit f00812
	s = malloc(sizeof (struct _scanopt_t));
Packit f00812
Packit f00812
	s->options = options;
Packit f00812
	s->optc = 0;
Packit f00812
	s->argc = argc;
Packit f00812
	s->argv = (char **) argv;
Packit f00812
	s->index = 1;
Packit f00812
	s->subscript = 0;
Packit f00812
	s->no_err_msg = (flags & SCANOPT_NO_ERR_MSG);
Packit f00812
	s->has_long = 0;
Packit f00812
	s->has_short = 0;
Packit f00812
Packit f00812
	/* Determine option count. (Find entry with all zeros). */
Packit f00812
	s->optc = 0;
Packit f00812
	while (options[s->optc].opt_fmt
Packit f00812
	       || options[s->optc].r_val || options[s->optc].desc)
Packit f00812
		s->optc++;
Packit f00812
Packit f00812
	/* Build auxiliary data */
Packit f00812
	s->aux = malloc((size_t) s->optc * sizeof (struct _aux));
Packit f00812
Packit f00812
	for (i = 0; i < s->optc; i++) {
Packit f00812
		const unsigned char *p, *pname;
Packit f00812
		const struct optspec_t *opt;
Packit f00812
		struct _aux *aux;
Packit f00812
Packit f00812
		opt = s->options + i;
Packit f00812
		aux = s->aux + i;
Packit f00812
Packit f00812
		aux->flags = ARG_NONE;
Packit f00812
Packit f00812
		if (opt->opt_fmt[0] == '-' && opt->opt_fmt[1] == '-') {
Packit f00812
			aux->flags |= IS_LONG;
Packit f00812
			pname = (const unsigned char *)(opt->opt_fmt + 2);
Packit f00812
			s->has_long = 1;
Packit f00812
		}
Packit f00812
		else {
Packit f00812
			pname = (const unsigned char *)(opt->opt_fmt + 1);
Packit f00812
			s->has_short = 1;
Packit f00812
		}
Packit f00812
		aux->printlen = (int) strlen (opt->opt_fmt);
Packit f00812
Packit f00812
		aux->namelen = 0;
Packit f00812
		for (p = pname + 1; *p; p++) {
Packit f00812
			/* detect required arg */
Packit f00812
			if (*p == '=' || isspace ((unsigned char)*p)
Packit f00812
			    || !(aux->flags & IS_LONG)) {
Packit f00812
				if (aux->namelen == 0)
Packit f00812
					aux->namelen = p - pname;
Packit f00812
				aux->flags |= ARG_REQ;
Packit f00812
				aux->flags &= ~ARG_NONE;
Packit f00812
			}
Packit f00812
			/* detect optional arg. This overrides required arg. */
Packit f00812
			if (*p == '[') {
Packit f00812
				if (aux->namelen == 0)
Packit f00812
					aux->namelen = p - pname;
Packit f00812
				aux->flags &= ~(ARG_REQ | ARG_NONE);
Packit f00812
				aux->flags |= ARG_OPT;
Packit f00812
				break;
Packit f00812
			}
Packit f00812
		}
Packit f00812
		if (aux->namelen == 0)
Packit f00812
			aux->namelen = p - pname;
Packit f00812
	}
Packit f00812
	return (scanopt_t *) s;
Packit f00812
}
Packit f00812
Packit f00812
#ifndef NO_SCANOPT_USAGE
Packit f00812
/* these structs are for scanopt_usage(). */
Packit f00812
struct usg_elem {
Packit f00812
	int     idx;
Packit f00812
	struct usg_elem *next;
Packit f00812
	struct usg_elem *alias;
Packit f00812
};
Packit f00812
typedef struct usg_elem usg_elem;
Packit f00812
Packit f00812
Packit f00812
/* Prints a usage message based on contents of optlist.
Packit f00812
 * Parameters:
Packit f00812
 *   scanner  - The scanner, already initialized with scanopt_init().
Packit f00812
 *   fp       - The file stream to write to.
Packit f00812
 *   usage    - Text to be prepended to option list.
Packit f00812
 * Return:  Always returns 0 (zero).
Packit f00812
 * The output looks something like this:
Packit f00812
Packit f00812
[indent][option, alias1, alias2...][indent][description line1
Packit f00812
                                            description line2...]
Packit f00812
 */
Packit f00812
int     scanopt_usage (scanopt_t *scanner, FILE *fp, const char *usage)
Packit f00812
{
Packit f00812
	struct _scanopt_t *s;
Packit f00812
	int     i, columns, indent = 2;
Packit f00812
	usg_elem *byr_val = NULL;	/* option indices sorted by r_val */
Packit f00812
	usg_elem *store;	/* array of preallocated elements. */
Packit f00812
	int     store_idx = 0;
Packit f00812
	usg_elem *ue;
Packit f00812
	int     maxlen[2];
Packit f00812
	int     desccol = 0;
Packit f00812
	int     print_run = 0;
Packit f00812
Packit f00812
	maxlen[0] = 0;
Packit f00812
	maxlen[1] = 0;
Packit f00812
Packit f00812
	s = (struct _scanopt_t *) scanner;
Packit f00812
Packit f00812
	if (usage) {
Packit f00812
		fprintf (fp, "%s\n", usage);
Packit f00812
	}
Packit f00812
	else {
Packit f00812
		/* Find the basename of argv[0] */
Packit f00812
		const char *p;
Packit f00812
Packit f00812
		p = s->argv[0] + strlen (s->argv[0]);
Packit f00812
		while (p != s->argv[0] && *p != '/')
Packit f00812
			--p;
Packit f00812
		if (*p == '/')
Packit f00812
			p++;
Packit f00812
Packit f00812
		fprintf (fp, _("Usage: %s [OPTIONS]...\n"), p);
Packit f00812
	}
Packit f00812
	fprintf (fp, "\n");
Packit f00812
Packit f00812
	/* Sort by r_val and string. Yes, this is O(n*n), but n is small. */
Packit f00812
	store = malloc((size_t) s->optc * sizeof (usg_elem));
Packit f00812
	for (i = 0; i < s->optc; i++) {
Packit f00812
Packit f00812
		/* grab the next preallocate node. */
Packit f00812
		ue = store + store_idx++;
Packit f00812
		ue->idx = i;
Packit f00812
		ue->next = ue->alias = NULL;
Packit f00812
Packit f00812
		/* insert into list. */
Packit f00812
		if (!byr_val)
Packit f00812
			byr_val = ue;
Packit f00812
		else {
Packit f00812
			int     found_alias = 0;
Packit f00812
			usg_elem **ue_curr, **ptr_if_no_alias = NULL;
Packit f00812
Packit f00812
			ue_curr = &byr_val;
Packit f00812
			while (*ue_curr) {
Packit f00812
				if (RVAL (s, (*ue_curr)->idx) ==
Packit f00812
				    RVAL (s, ue->idx)) {
Packit f00812
					/* push onto the alias list. */
Packit f00812
					ue_curr = &((*ue_curr)->alias);
Packit f00812
					found_alias = 1;
Packit f00812
					break;
Packit f00812
				}
Packit f00812
				if (!ptr_if_no_alias
Packit f00812
				    &&
Packit f00812
				    strcasecmp (NAME (s, (*ue_curr)->idx),
Packit f00812
						NAME (s, ue->idx)) > 0) {
Packit f00812
					ptr_if_no_alias = ue_curr;
Packit f00812
				}
Packit f00812
				ue_curr = &((*ue_curr)->next);
Packit f00812
			}
Packit f00812
			if (!found_alias && ptr_if_no_alias)
Packit f00812
				ue_curr = ptr_if_no_alias;
Packit f00812
			ue->next = *ue_curr;
Packit f00812
			*ue_curr = ue;
Packit f00812
		}
Packit f00812
	}
Packit f00812
Packit f00812
#if 0
Packit f00812
	if (1) {
Packit f00812
		printf ("ORIGINAL:\n");
Packit f00812
		for (i = 0; i < s->optc; i++)
Packit f00812
			printf ("%2d: %s\n", i, NAME (s, i));
Packit f00812
		printf ("SORTED:\n");
Packit f00812
		ue = byr_val;
Packit f00812
		while (ue) {
Packit f00812
			usg_elem *ue2;
Packit f00812
Packit f00812
			printf ("%2d: %s\n", ue->idx, NAME (s, ue->idx));
Packit f00812
			for (ue2 = ue->alias; ue2; ue2 = ue2->next)
Packit f00812
				printf ("  +---> %2d: %s\n", ue2->idx,
Packit f00812
					NAME (s, ue2->idx));
Packit f00812
			ue = ue->next;
Packit f00812
		}
Packit f00812
	}
Packit f00812
#endif
Packit f00812
Packit f00812
	/* Now build each row of output. */
Packit f00812
Packit f00812
	/* first pass calculate how much room we need. */
Packit f00812
	for (ue = byr_val; ue; ue = ue->next) {
Packit f00812
		usg_elem *ap;
Packit f00812
		int     len = 0;
Packit f00812
		int     nshort = 0, nlong = 0;
Packit f00812
Packit f00812
Packit f00812
#define CALC_LEN(i) do {\
Packit f00812
          if(FLAGS(s,i) & IS_LONG) \
Packit f00812
              len +=  (nlong++||nshort) ? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\
Packit f00812
          else\
Packit f00812
              len +=  (nshort++||nlong)? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\
Packit f00812
        }while(0)
Packit f00812
Packit f00812
		if (!(FLAGS (s, ue->idx) & IS_LONG))
Packit f00812
			CALC_LEN (ue->idx);
Packit f00812
Packit f00812
		/* do short aliases first. */
Packit f00812
		for (ap = ue->alias; ap; ap = ap->next) {
Packit f00812
			if (FLAGS (s, ap->idx) & IS_LONG)
Packit f00812
				continue;
Packit f00812
			CALC_LEN (ap->idx);
Packit f00812
		}
Packit f00812
Packit f00812
		if (FLAGS (s, ue->idx) & IS_LONG)
Packit f00812
			CALC_LEN (ue->idx);
Packit f00812
Packit f00812
		/* repeat the above loop, this time for long aliases. */
Packit f00812
		for (ap = ue->alias; ap; ap = ap->next) {
Packit f00812
			if (!(FLAGS (s, ap->idx) & IS_LONG))
Packit f00812
				continue;
Packit f00812
			CALC_LEN (ap->idx);
Packit f00812
		}
Packit f00812
Packit f00812
		if (len > maxlen[0])
Packit f00812
			maxlen[0] = len;
Packit f00812
Packit f00812
		/* It's much easier to calculate length for description column! */
Packit f00812
		len = (int) strlen (DESC (s, ue->idx));
Packit f00812
		if (len > maxlen[1])
Packit f00812
			maxlen[1] = len;
Packit f00812
	}
Packit f00812
Packit f00812
	/* Determine how much room we have, and how much we will allocate to each col.
Packit f00812
	 * Do not address pathological cases. Output will just be ugly. */
Packit f00812
	columns = get_cols () - 1;
Packit f00812
	if (maxlen[0] + maxlen[1] + indent * 2 > columns) {
Packit f00812
		/* col 0 gets whatever it wants. we'll wrap the desc col. */
Packit f00812
		maxlen[1] = columns - (maxlen[0] + indent * 2);
Packit f00812
		if (maxlen[1] < 14)	/* 14 is arbitrary lower limit on desc width. */
Packit f00812
			maxlen[1] = INT_MAX;
Packit f00812
	}
Packit f00812
	desccol = maxlen[0] + indent * 2;
Packit f00812
Packit f00812
#define PRINT_SPACES(fp,n)\
Packit f00812
    do{\
Packit f00812
        int _n;\
Packit f00812
        _n=(n);\
Packit f00812
        while(_n-- > 0)\
Packit f00812
            fputc(' ',(fp));\
Packit f00812
    }while(0)
Packit f00812
Packit f00812
Packit f00812
	/* Second pass (same as above loop), this time we print. */
Packit f00812
	/* Sloppy hack: We iterate twice. The first time we print short and long options.
Packit f00812
	   The second time we print those lines that have ONLY long options. */
Packit f00812
	while (print_run++ < 2) {
Packit f00812
		for (ue = byr_val; ue; ue = ue->next) {
Packit f00812
			usg_elem *ap;
Packit f00812
			int     nwords = 0, nchars = 0, has_short = 0;
Packit f00812
Packit f00812
/* TODO: get has_short schtick to work */
Packit f00812
			has_short = !(FLAGS (s, ue->idx) & IS_LONG);
Packit f00812
			for (ap = ue->alias; ap; ap = ap->next) {
Packit f00812
				if (!(FLAGS (s, ap->idx) & IS_LONG)) {
Packit f00812
					has_short = 1;
Packit f00812
					break;
Packit f00812
				}
Packit f00812
			}
Packit f00812
			if ((print_run == 1 && !has_short) ||
Packit f00812
			    (print_run == 2 && has_short))
Packit f00812
				continue;
Packit f00812
Packit f00812
			PRINT_SPACES (fp, indent);
Packit f00812
			nchars += indent;
Packit f00812
Packit f00812
/* Print, adding a ", " between aliases. */
Packit f00812
#define PRINT_IT(i) do{\
Packit f00812
                  if(nwords++)\
Packit f00812
                      nchars+=fprintf(fp,", ");\
Packit f00812
                  nchars+=fprintf(fp,"%s",s->options[i].opt_fmt);\
Packit f00812
            }while(0)
Packit f00812
Packit f00812
			if (!(FLAGS (s, ue->idx) & IS_LONG))
Packit f00812
				PRINT_IT (ue->idx);
Packit f00812
Packit f00812
			/* print short aliases first. */
Packit f00812
			for (ap = ue->alias; ap; ap = ap->next) {
Packit f00812
				if (!(FLAGS (s, ap->idx) & IS_LONG))
Packit f00812
					PRINT_IT (ap->idx);
Packit f00812
			}
Packit f00812
Packit f00812
Packit f00812
			if (FLAGS (s, ue->idx) & IS_LONG)
Packit f00812
				PRINT_IT (ue->idx);
Packit f00812
Packit f00812
			/* repeat the above loop, this time for long aliases. */
Packit f00812
			for (ap = ue->alias; ap; ap = ap->next) {
Packit f00812
				if (FLAGS (s, ap->idx) & IS_LONG)
Packit f00812
					PRINT_IT (ap->idx);
Packit f00812
			}
Packit f00812
Packit f00812
			/* pad to desccol */
Packit f00812
			PRINT_SPACES (fp, desccol - nchars);
Packit f00812
Packit f00812
			/* Print description, wrapped to maxlen[1] columns. */
Packit f00812
			if (1) {
Packit f00812
				const char *pstart;
Packit f00812
Packit f00812
				pstart = DESC (s, ue->idx);
Packit f00812
				while (1) {
Packit f00812
					int     n = 0;
Packit f00812
					const char *lastws = NULL, *p;
Packit f00812
Packit f00812
					p = pstart;
Packit f00812
Packit f00812
					while (*p && n < maxlen[1]
Packit f00812
					       && *p != '\n') {
Packit f00812
						if (isspace ((unsigned char)(*p))
Packit f00812
						    || *p == '-') lastws =
Packit f00812
								p;
Packit f00812
						n++;
Packit f00812
						p++;
Packit f00812
					}
Packit f00812
Packit f00812
					if (!*p) {	/* hit end of desc. done. */
Packit f00812
						fprintf (fp, "%s\n",
Packit f00812
							 pstart);
Packit f00812
						break;
Packit f00812
					}
Packit f00812
					else if (*p == '\n') {	/* print everything up to here then wrap. */
Packit f00812
						fprintf (fp, "%.*s\n", n,
Packit f00812
							 pstart);
Packit f00812
						PRINT_SPACES (fp, desccol);
Packit f00812
						pstart = p + 1;
Packit f00812
						continue;
Packit f00812
					}
Packit f00812
					else {	/* we hit the edge of the screen. wrap at space if possible. */
Packit f00812
						if (lastws) {
Packit f00812
							fprintf (fp,
Packit f00812
								 "%.*s\n",
Packit f00812
								 (int)(lastws - pstart),
Packit f00812
								 pstart);
Packit f00812
							pstart =
Packit f00812
								lastws + 1;
Packit f00812
						}
Packit f00812
						else {
Packit f00812
							fprintf (fp,
Packit f00812
								 "%.*s\n",
Packit f00812
								 n,
Packit f00812
								 pstart);
Packit f00812
							pstart = p + 1;
Packit f00812
						}
Packit f00812
						PRINT_SPACES (fp, desccol);
Packit f00812
						continue;
Packit f00812
					}
Packit f00812
				}
Packit f00812
			}
Packit f00812
		}
Packit f00812
	}			/* end while */
Packit f00812
	free (store);
Packit f00812
	return 0;
Packit f00812
}
Packit f00812
#endif /* no scanopt_usage */
Packit f00812
Packit f00812
Packit f00812
static int scanopt_err (struct _scanopt_t *s, int is_short, int err)
Packit f00812
{
Packit f00812
	const char *optname = "";
Packit f00812
	char    optchar[2];
Packit f00812
Packit f00812
	if (!s->no_err_msg) {
Packit f00812
Packit f00812
		if (s->index > 0 && s->index < s->argc) {
Packit f00812
			if (is_short) {
Packit f00812
				optchar[0] =
Packit f00812
					s->argv[s->index][s->subscript];
Packit f00812
				optchar[1] = '\0';
Packit f00812
				optname = optchar;
Packit f00812
			}
Packit f00812
			else {
Packit f00812
				optname = s->argv[s->index];
Packit f00812
			}
Packit f00812
		}
Packit f00812
Packit f00812
		fprintf (stderr, "%s: ", s->argv[0]);
Packit f00812
		switch (err) {
Packit f00812
		case SCANOPT_ERR_ARG_NOT_ALLOWED:
Packit f00812
			fprintf (stderr,
Packit f00812
				 _
Packit f00812
				 ("option `%s' doesn't allow an argument\n"),
Packit f00812
				 optname);
Packit f00812
			break;
Packit f00812
		case SCANOPT_ERR_ARG_NOT_FOUND:
Packit f00812
			fprintf (stderr,
Packit f00812
				 _("option `%s' requires an argument\n"),
Packit f00812
				 optname);
Packit f00812
			break;
Packit f00812
		case SCANOPT_ERR_OPT_AMBIGUOUS:
Packit f00812
			fprintf (stderr, _("option `%s' is ambiguous\n"),
Packit f00812
				 optname);
Packit f00812
			break;
Packit f00812
		case SCANOPT_ERR_OPT_UNRECOGNIZED:
Packit f00812
			fprintf (stderr, _("Unrecognized option `%s'\n"),
Packit f00812
				 optname);
Packit f00812
			break;
Packit f00812
		default:
Packit f00812
			fprintf (stderr, _("Unknown error=(%d)\n"), err);
Packit f00812
			break;
Packit f00812
		}
Packit f00812
	}
Packit f00812
	return err;
Packit f00812
}
Packit f00812

Packit f00812
Packit f00812
/* Internal. Match str against the regex  ^--([^=]+)(=(.*))?
Packit f00812
 * return 1 if *looks* like a long option.
Packit f00812
 * 'str' is the only input argument, the rest of the arguments are output only.
Packit f00812
 * optname will point to str + 2
Packit f00812
 *
Packit f00812
 */
Packit f00812
static int matchlongopt (char *str, char **optname, int *optlen, char **arg, int *arglen)
Packit f00812
{
Packit f00812
	char   *p;
Packit f00812
Packit f00812
	*optname = *arg = NULL;
Packit f00812
	*optlen = *arglen = 0;
Packit f00812
Packit f00812
	/* Match regex /--./   */
Packit f00812
	p = str;
Packit f00812
	if (p[0] != '-' || p[1] != '-' || !p[2])
Packit f00812
		return 0;
Packit f00812
Packit f00812
	p += 2;
Packit f00812
	*optname = p;
Packit f00812
Packit f00812
	/* find the end of optname */
Packit f00812
	while (*p && *p != '=')
Packit f00812
		++p;
Packit f00812
Packit f00812
	*optlen = p - *optname;
Packit f00812
Packit f00812
	if (!*p)
Packit f00812
		/* an option with no '=...' part. */
Packit f00812
		return 1;
Packit f00812
Packit f00812
Packit f00812
	/* We saw an '=' char. The rest of p is the arg. */
Packit f00812
	p++;
Packit f00812
	*arg = p;
Packit f00812
	while (*p)
Packit f00812
		++p;
Packit f00812
	*arglen = p - *arg;
Packit f00812
Packit f00812
	return 1;
Packit f00812
}
Packit f00812

Packit f00812
Packit f00812
/* Internal. Look up long or short option by name.
Packit f00812
 * Long options must match a non-ambiguous prefix, or exact match.
Packit f00812
 * Short options must be exact.
Packit f00812
 * Return boolean true if found and no error.
Packit f00812
 * Error stored in err_code or zero if no error. */
Packit f00812
static int find_opt (struct _scanopt_t *s, int lookup_long, char *optstart, int
Packit f00812
	len, int *err_code, int *opt_offset)
Packit f00812
{
Packit f00812
	int     nmatch = 0, lastr_val = 0, i;
Packit f00812
Packit f00812
	*err_code = 0;
Packit f00812
	*opt_offset = -1;
Packit f00812
Packit f00812
	if (!optstart)
Packit f00812
		return 0;
Packit f00812
Packit f00812
	for (i = 0; i < s->optc; i++) {
Packit f00812
		const char   *optname;
Packit f00812
Packit f00812
		optname = s->options[i].opt_fmt + (lookup_long ? 2 : 1);
Packit f00812
Packit f00812
		if (lookup_long && (s->aux[i].flags & IS_LONG)) {
Packit f00812
			if (len > s->aux[i].namelen)
Packit f00812
				continue;
Packit f00812
Packit f00812
			if (strncmp (optname, optstart, len) == 0) {
Packit f00812
				nmatch++;
Packit f00812
				*opt_offset = i;
Packit f00812
Packit f00812
				/* exact match overrides all. */
Packit f00812
				if (len == s->aux[i].namelen) {
Packit f00812
					nmatch = 1;
Packit f00812
					break;
Packit f00812
				}
Packit f00812
Packit f00812
				/* ambiguity is ok between aliases. */
Packit f00812
				if (lastr_val
Packit f00812
				    && lastr_val ==
Packit f00812
				    s->options[i].r_val) nmatch--;
Packit f00812
				lastr_val = s->options[i].r_val;
Packit f00812
			}
Packit f00812
		}
Packit f00812
		else if (!lookup_long && !(s->aux[i].flags & IS_LONG)) {
Packit f00812
			if (optname[0] == optstart[0]) {
Packit f00812
				nmatch++;
Packit f00812
				*opt_offset = i;
Packit f00812
			}
Packit f00812
		}
Packit f00812
	}
Packit f00812
Packit f00812
	if (nmatch == 0) {
Packit f00812
		*err_code = SCANOPT_ERR_OPT_UNRECOGNIZED;
Packit f00812
		*opt_offset = -1;
Packit f00812
	}
Packit f00812
	else if (nmatch > 1) {
Packit f00812
		*err_code = SCANOPT_ERR_OPT_AMBIGUOUS;
Packit f00812
		*opt_offset = -1;
Packit f00812
	}
Packit f00812
Packit f00812
	return *err_code ? 0 : 1;
Packit f00812
}
Packit f00812

Packit f00812
Packit f00812
int     scanopt (scanopt_t *svoid, char **arg, int *optindex)
Packit f00812
{
Packit f00812
	char   *optname = NULL, *optarg = NULL, *pstart;
Packit f00812
	int     namelen = 0, arglen = 0;
Packit f00812
	int     errcode = 0, has_next;
Packit f00812
	const optspec_t *optp;
Packit f00812
	struct _scanopt_t *s;
Packit f00812
	struct _aux *auxp;
Packit f00812
	int     is_short;
Packit f00812
	int     opt_offset = -1;
Packit f00812
Packit f00812
	s = (struct _scanopt_t *) svoid;
Packit f00812
Packit f00812
	/* Normalize return-parameters. */
Packit f00812
	SAFE_ASSIGN (arg, NULL);
Packit f00812
	SAFE_ASSIGN (optindex, s->index);
Packit f00812
Packit f00812
	if (s->index >= s->argc)
Packit f00812
		return 0;
Packit f00812
Packit f00812
	/* pstart always points to the start of our current scan. */
Packit f00812
	pstart = s->argv[s->index] + s->subscript;
Packit f00812
	if (!pstart)
Packit f00812
		return 0;
Packit f00812
Packit f00812
	if (s->subscript == 0) {
Packit f00812
Packit f00812
		/* test for exact match of "--" */
Packit f00812
		if (pstart[0] == '-' && pstart[1] == '-' && !pstart[2]) {
Packit f00812
			SAFE_ASSIGN (optindex, s->index + 1);
Packit f00812
			INC_INDEX (s, 1);
Packit f00812
			return 0;
Packit f00812
		}
Packit f00812
Packit f00812
		/* Match an opt. */
Packit f00812
		if (matchlongopt
Packit f00812
		    (pstart, &optname, &namelen, &optarg, &arglen)) {
Packit f00812
Packit f00812
			/* it LOOKS like an opt, but is it one?! */
Packit f00812
			if (!find_opt
Packit f00812
			    (s, 1, optname, namelen, &errcode,
Packit f00812
			     &opt_offset)) {
Packit f00812
				scanopt_err (s, 0, errcode);
Packit f00812
				return errcode;
Packit f00812
			}
Packit f00812
			/* We handle this below. */
Packit f00812
			is_short = 0;
Packit f00812
Packit f00812
			/* Check for short opt.  */
Packit f00812
		}
Packit f00812
		else if (pstart[0] == '-' && pstart[1]) {
Packit f00812
			/* Pass through to below. */
Packit f00812
			is_short = 1;
Packit f00812
			s->subscript++;
Packit f00812
			pstart++;
Packit f00812
		}
Packit f00812
Packit f00812
		else {
Packit f00812
			/* It's not an option. We're done. */
Packit f00812
			return 0;
Packit f00812
		}
Packit f00812
	}
Packit f00812
Packit f00812
	/* We have to re-check the subscript status because it
Packit f00812
	 * may have changed above. */
Packit f00812
Packit f00812
	if (s->subscript != 0) {
Packit f00812
Packit f00812
		/* we are somewhere in a run of short opts,
Packit f00812
		 * e.g., at the 'z' in `tar -xzf` */
Packit f00812
Packit f00812
		optname = pstart;
Packit f00812
		namelen = 1;
Packit f00812
		is_short = 1;
Packit f00812
Packit f00812
		if (!find_opt
Packit f00812
		    (s, 0, pstart, namelen, &errcode, &opt_offset)) {
Packit f00812
			return scanopt_err (s, 1, errcode);
Packit f00812
		}
Packit f00812
Packit f00812
		optarg = pstart + 1;
Packit f00812
		if (!*optarg) {
Packit f00812
			optarg = NULL;
Packit f00812
			arglen = 0;
Packit f00812
		}
Packit f00812
		else
Packit f00812
			arglen = (int) strlen (optarg);
Packit f00812
	}
Packit f00812
Packit f00812
	/* At this point, we have a long or short option matched at opt_offset into
Packit f00812
	 * the s->options array (and corresponding aux array).
Packit f00812
	 * A trailing argument is in {optarg,arglen}, if any.
Packit f00812
	 */
Packit f00812
Packit f00812
	/* Look ahead in argv[] to see if there is something
Packit f00812
	 * that we can use as an argument (if needed). */
Packit f00812
	has_next = s->index + 1 < s->argc
Packit f00812
		&& strcmp ("--", s->argv[s->index + 1]) != 0;
Packit f00812
Packit f00812
	optp = s->options + opt_offset;
Packit f00812
	auxp = s->aux + opt_offset;
Packit f00812
Packit f00812
	/* case: no args allowed */
Packit f00812
	if (auxp->flags & ARG_NONE) {
Packit f00812
		if (optarg && !is_short) {
Packit f00812
			scanopt_err (s, is_short, errcode = SCANOPT_ERR_ARG_NOT_ALLOWED);
Packit f00812
			INC_INDEX (s, 1);
Packit f00812
			return errcode;
Packit f00812
		}
Packit f00812
		else if (!optarg)
Packit f00812
			INC_INDEX (s, 1);
Packit f00812
		else
Packit f00812
			s->subscript++;
Packit f00812
		return optp->r_val;
Packit f00812
	}
Packit f00812
Packit f00812
	/* case: required */
Packit f00812
	if (auxp->flags & ARG_REQ) {
Packit f00812
		if (!optarg && !has_next)
Packit f00812
			return scanopt_err (s, is_short, SCANOPT_ERR_ARG_NOT_FOUND);
Packit f00812
Packit f00812
		if (!optarg) {
Packit f00812
			/* Let the next argv element become the argument. */
Packit f00812
			SAFE_ASSIGN (arg, s->argv[s->index + 1]);
Packit f00812
			INC_INDEX (s, 2);
Packit f00812
		}
Packit f00812
		else {
Packit f00812
			SAFE_ASSIGN (arg, (char *) optarg);
Packit f00812
			INC_INDEX (s, 1);
Packit f00812
		}
Packit f00812
		return optp->r_val;
Packit f00812
	}
Packit f00812
Packit f00812
	/* case: optional */
Packit f00812
	if (auxp->flags & ARG_OPT) {
Packit f00812
		SAFE_ASSIGN (arg, optarg);
Packit f00812
		INC_INDEX (s, 1);
Packit f00812
		return optp->r_val;
Packit f00812
	}
Packit f00812
Packit f00812
Packit f00812
	/* Should not reach here. */
Packit f00812
	return 0;
Packit f00812
}
Packit f00812
Packit f00812
Packit f00812
int     scanopt_destroy (scanopt_t *svoid)
Packit f00812
{
Packit f00812
	struct _scanopt_t *s;
Packit f00812
Packit f00812
	s = (struct _scanopt_t *) svoid;
Packit f00812
	if (s != NULL) {
Packit f00812
		free(s->aux);
Packit f00812
		free(s);
Packit f00812
	}
Packit f00812
	return 0;
Packit f00812
}
Packit f00812
Packit f00812
Packit f00812
/* vim:set tabstop=8 softtabstop=4 shiftwidth=4: */