xref: /netbsd/external/bsd/flex/dist/src/scanopt.c (revision b806bde9)
1*b806bde9Schristos /*	$NetBSD: scanopt.c,v 1.3 2017/01/02 17:45:27 christos Exp $	*/
2608ad45eSchristos 
3e7270ba8Schristos /* flex - tool to generate fast lexical analyzers */
4e7270ba8Schristos 
5e7270ba8Schristos /*  Copyright (c) 1990 The Regents of the University of California. */
6e7270ba8Schristos /*  All rights reserved. */
7e7270ba8Schristos 
8e7270ba8Schristos /*  This code is derived from software contributed to Berkeley by */
9e7270ba8Schristos /*  Vern Paxson. */
10e7270ba8Schristos 
11e7270ba8Schristos /*  The United States Government has rights in this work pursuant */
12e7270ba8Schristos /*  to contract no. DE-AC03-76SF00098 between the United States */
13e7270ba8Schristos /*  Department of Energy and the University of California. */
14e7270ba8Schristos 
15e7270ba8Schristos /*  This file is part of flex. */
16e7270ba8Schristos 
17e7270ba8Schristos /*  Redistribution and use in source and binary forms, with or without */
18e7270ba8Schristos /*  modification, are permitted provided that the following conditions */
19e7270ba8Schristos /*  are met: */
20e7270ba8Schristos 
21e7270ba8Schristos /*  1. Redistributions of source code must retain the above copyright */
22e7270ba8Schristos /*     notice, this list of conditions and the following disclaimer. */
23e7270ba8Schristos /*  2. Redistributions in binary form must reproduce the above copyright */
24e7270ba8Schristos /*     notice, this list of conditions and the following disclaimer in the */
25e7270ba8Schristos /*     documentation and/or other materials provided with the distribution. */
26e7270ba8Schristos 
27e7270ba8Schristos /*  Neither the name of the University nor the names of its contributors */
28e7270ba8Schristos /*  may be used to endorse or promote products derived from this software */
29e7270ba8Schristos /*  without specific prior written permission. */
30e7270ba8Schristos 
31e7270ba8Schristos /*  THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */
32e7270ba8Schristos /*  IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */
33e7270ba8Schristos /*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */
34e7270ba8Schristos /*  PURPOSE. */
35e7270ba8Schristos #include "flexdef.h"
36*b806bde9Schristos __RCSID("$NetBSD: scanopt.c,v 1.3 2017/01/02 17:45:27 christos Exp $");
37608ad45eSchristos 
38e7270ba8Schristos #include "scanopt.h"
39e7270ba8Schristos 
40e7270ba8Schristos 
41e7270ba8Schristos /* Internal structures */
42e7270ba8Schristos 
43e7270ba8Schristos #define ARG_NONE 0x01
44e7270ba8Schristos #define ARG_REQ  0x02
45e7270ba8Schristos #define ARG_OPT  0x04
46e7270ba8Schristos #define IS_LONG  0x08
47e7270ba8Schristos 
48e7270ba8Schristos struct _aux {
49e7270ba8Schristos 	int     flags;		/* The above hex flags. */
50e7270ba8Schristos 	int     namelen;	/* Length of the actual option word, e.g., "--file[=foo]" is 4 */
51e7270ba8Schristos 	int     printlen;	/* Length of entire string, e.g., "--file[=foo]" is 12 */
52e7270ba8Schristos };
53e7270ba8Schristos 
54e7270ba8Schristos 
55e7270ba8Schristos struct _scanopt_t {
56e7270ba8Schristos 	const optspec_t *options;	/* List of options. */
57e7270ba8Schristos 	struct _aux *aux;	/* Auxiliary data about options. */
58e7270ba8Schristos 	int     optc;		/* Number of options. */
59e7270ba8Schristos 	int     argc;		/* Number of args. */
60e7270ba8Schristos 	char  **argv;		/* Array of strings. */
61e7270ba8Schristos 	int     index;		/* Used as: argv[index][subscript]. */
62e7270ba8Schristos 	int     subscript;
63e7270ba8Schristos 	char    no_err_msg;	/* If true, do not print errors. */
64e7270ba8Schristos 	char    has_long;
65e7270ba8Schristos 	char    has_short;
66e7270ba8Schristos };
67e7270ba8Schristos 
68e7270ba8Schristos /* Accessor functions. These WOULD be one-liners, but portability calls. */
69*b806bde9Schristos static const char *NAME(struct _scanopt_t *, int);
70*b806bde9Schristos static int PRINTLEN(struct _scanopt_t *, int);
71*b806bde9Schristos static int RVAL(struct _scanopt_t *, int);
72*b806bde9Schristos static int FLAGS(struct _scanopt_t *, int);
73*b806bde9Schristos static const char *DESC(struct _scanopt_t *, int);
74*b806bde9Schristos static int scanopt_err(struct _scanopt_t *, int, int);
75*b806bde9Schristos static int matchlongopt(char *, char **, int *, char **, int *);
76*b806bde9Schristos static int find_opt(struct _scanopt_t *, int, char *, int, int *, int *opt_offset);
77e7270ba8Schristos 
NAME(struct _scanopt_t * s,int i)78*b806bde9Schristos static const char *NAME (struct _scanopt_t *s, int i)
79e7270ba8Schristos {
80e7270ba8Schristos 	return s->options[i].opt_fmt +
81e7270ba8Schristos 		((s->aux[i].flags & IS_LONG) ? 2 : 1);
82e7270ba8Schristos }
83e7270ba8Schristos 
PRINTLEN(struct _scanopt_t * s,int i)84*b806bde9Schristos static int PRINTLEN (struct _scanopt_t *s, int i)
85e7270ba8Schristos {
86e7270ba8Schristos 	return s->aux[i].printlen;
87e7270ba8Schristos }
88e7270ba8Schristos 
RVAL(struct _scanopt_t * s,int i)89*b806bde9Schristos static int RVAL (struct _scanopt_t *s, int i)
90e7270ba8Schristos {
91e7270ba8Schristos 	return s->options[i].r_val;
92e7270ba8Schristos }
93e7270ba8Schristos 
FLAGS(struct _scanopt_t * s,int i)94*b806bde9Schristos static int FLAGS (struct _scanopt_t *s, int i)
95e7270ba8Schristos {
96e7270ba8Schristos 	return s->aux[i].flags;
97e7270ba8Schristos }
98e7270ba8Schristos 
DESC(struct _scanopt_t * s,int i)99*b806bde9Schristos static const char *DESC (struct _scanopt_t *s, int i)
100e7270ba8Schristos {
101e7270ba8Schristos 	return s->options[i].desc ? s->options[i].desc : "";
102e7270ba8Schristos }
103e7270ba8Schristos 
104e7270ba8Schristos #ifndef NO_SCANOPT_USAGE
105*b806bde9Schristos static int get_cols (void);
106e7270ba8Schristos 
get_cols(void)107*b806bde9Schristos static int get_cols (void)
108e7270ba8Schristos {
109e7270ba8Schristos 	char   *env;
110e7270ba8Schristos 	int     cols = 80;	/* default */
111e7270ba8Schristos 
112e7270ba8Schristos #ifdef HAVE_NCURSES_H
113e7270ba8Schristos 	initscr ();
114e7270ba8Schristos 	endwin ();
115e7270ba8Schristos 	if (COLS > 0)
116e7270ba8Schristos 		return COLS;
117e7270ba8Schristos #endif
118e7270ba8Schristos 
119e7270ba8Schristos 	if ((env = getenv ("COLUMNS")) != NULL)
120e7270ba8Schristos 		cols = atoi (env);
121e7270ba8Schristos 
122e7270ba8Schristos 	return cols;
123e7270ba8Schristos }
124e7270ba8Schristos #endif
125e7270ba8Schristos 
126e7270ba8Schristos /* Macro to check for NULL before assigning a value. */
127e7270ba8Schristos #define SAFE_ASSIGN(ptr,val) \
128e7270ba8Schristos     do{                      \
129e7270ba8Schristos         if((ptr)!=NULL)      \
130e7270ba8Schristos             *(ptr) = val;    \
131e7270ba8Schristos     }while(0)
132e7270ba8Schristos 
133e7270ba8Schristos /* Macro to assure we reset subscript whenever we adjust s->index.*/
134e7270ba8Schristos #define INC_INDEX(s,n)     \
135e7270ba8Schristos     do{                    \
136e7270ba8Schristos        (s)->index += (n);  \
137e7270ba8Schristos        (s)->subscript= 0;  \
138e7270ba8Schristos     }while(0)
139e7270ba8Schristos 
scanopt_init(const optspec_t * options,int argc,char ** argv,int flags)140*b806bde9Schristos scanopt_t *scanopt_init (const optspec_t *options, int argc, char **argv, int flags)
141e7270ba8Schristos {
142e7270ba8Schristos 	int     i;
143e7270ba8Schristos 	struct _scanopt_t *s;
144*b806bde9Schristos 	s = malloc(sizeof (struct _scanopt_t));
145e7270ba8Schristos 
146e7270ba8Schristos 	s->options = options;
147e7270ba8Schristos 	s->optc = 0;
148e7270ba8Schristos 	s->argc = argc;
149e7270ba8Schristos 	s->argv = (char **) argv;
150e7270ba8Schristos 	s->index = 1;
151e7270ba8Schristos 	s->subscript = 0;
152e7270ba8Schristos 	s->no_err_msg = (flags & SCANOPT_NO_ERR_MSG);
153e7270ba8Schristos 	s->has_long = 0;
154e7270ba8Schristos 	s->has_short = 0;
155e7270ba8Schristos 
156e7270ba8Schristos 	/* Determine option count. (Find entry with all zeros). */
157e7270ba8Schristos 	s->optc = 0;
158e7270ba8Schristos 	while (options[s->optc].opt_fmt
159e7270ba8Schristos 	       || options[s->optc].r_val || options[s->optc].desc)
160e7270ba8Schristos 		s->optc++;
161e7270ba8Schristos 
162e7270ba8Schristos 	/* Build auxiliary data */
163*b806bde9Schristos 	s->aux = malloc((size_t) s->optc * sizeof (struct _aux));
164e7270ba8Schristos 
165e7270ba8Schristos 	for (i = 0; i < s->optc; i++) {
166*b806bde9Schristos 		const unsigned char *p, *pname;
167e7270ba8Schristos 		const struct optspec_t *opt;
168e7270ba8Schristos 		struct _aux *aux;
169e7270ba8Schristos 
170e7270ba8Schristos 		opt = s->options + i;
171e7270ba8Schristos 		aux = s->aux + i;
172e7270ba8Schristos 
173e7270ba8Schristos 		aux->flags = ARG_NONE;
174e7270ba8Schristos 
175e7270ba8Schristos 		if (opt->opt_fmt[0] == '-' && opt->opt_fmt[1] == '-') {
176e7270ba8Schristos 			aux->flags |= IS_LONG;
177*b806bde9Schristos 			pname = (const unsigned char *)(opt->opt_fmt + 2);
178e7270ba8Schristos 			s->has_long = 1;
179e7270ba8Schristos 		}
180e7270ba8Schristos 		else {
181*b806bde9Schristos 			pname = (const unsigned char *)(opt->opt_fmt + 1);
182e7270ba8Schristos 			s->has_short = 1;
183e7270ba8Schristos 		}
184*b806bde9Schristos 		aux->printlen = (int) strlen (opt->opt_fmt);
185e7270ba8Schristos 
186e7270ba8Schristos 		aux->namelen = 0;
187e7270ba8Schristos 		for (p = pname + 1; *p; p++) {
188e7270ba8Schristos 			/* detect required arg */
189e7270ba8Schristos 			if (*p == '=' || isspace ((unsigned char)*p)
190e7270ba8Schristos 			    || !(aux->flags & IS_LONG)) {
191e7270ba8Schristos 				if (aux->namelen == 0)
192*b806bde9Schristos 					aux->namelen = (int) (p - pname);
193e7270ba8Schristos 				aux->flags |= ARG_REQ;
194e7270ba8Schristos 				aux->flags &= ~ARG_NONE;
195e7270ba8Schristos 			}
196e7270ba8Schristos 			/* detect optional arg. This overrides required arg. */
197e7270ba8Schristos 			if (*p == '[') {
198e7270ba8Schristos 				if (aux->namelen == 0)
199*b806bde9Schristos 					aux->namelen = (int) (p - pname);
200e7270ba8Schristos 				aux->flags &= ~(ARG_REQ | ARG_NONE);
201e7270ba8Schristos 				aux->flags |= ARG_OPT;
202e7270ba8Schristos 				break;
203e7270ba8Schristos 			}
204e7270ba8Schristos 		}
205e7270ba8Schristos 		if (aux->namelen == 0)
206*b806bde9Schristos 			aux->namelen = (int) (p - pname);
207e7270ba8Schristos 	}
208e7270ba8Schristos 	return (scanopt_t *) s;
209e7270ba8Schristos }
210e7270ba8Schristos 
211e7270ba8Schristos #ifndef NO_SCANOPT_USAGE
212e7270ba8Schristos /* these structs are for scanopt_usage(). */
213e7270ba8Schristos struct usg_elem {
214e7270ba8Schristos 	int     idx;
215e7270ba8Schristos 	struct usg_elem *next;
216e7270ba8Schristos 	struct usg_elem *alias;
217e7270ba8Schristos };
218e7270ba8Schristos typedef struct usg_elem usg_elem;
219e7270ba8Schristos 
220e7270ba8Schristos 
221e7270ba8Schristos /* Prints a usage message based on contents of optlist.
222e7270ba8Schristos  * Parameters:
223e7270ba8Schristos  *   scanner  - The scanner, already initialized with scanopt_init().
224e7270ba8Schristos  *   fp       - The file stream to write to.
225e7270ba8Schristos  *   usage    - Text to be prepended to option list.
226e7270ba8Schristos  * Return:  Always returns 0 (zero).
227e7270ba8Schristos  * The output looks something like this:
228e7270ba8Schristos 
229e7270ba8Schristos [indent][option, alias1, alias2...][indent][description line1
230e7270ba8Schristos                                             description line2...]
231e7270ba8Schristos  */
scanopt_usage(scanopt_t * scanner,FILE * fp,const char * usage)232*b806bde9Schristos int     scanopt_usage (scanopt_t *scanner, FILE *fp, const char *usage)
233e7270ba8Schristos {
234e7270ba8Schristos 	struct _scanopt_t *s;
235e7270ba8Schristos 	int     i, columns, indent = 2;
236e7270ba8Schristos 	usg_elem *byr_val = NULL;	/* option indices sorted by r_val */
237e7270ba8Schristos 	usg_elem *store;	/* array of preallocated elements. */
238e7270ba8Schristos 	int     store_idx = 0;
239e7270ba8Schristos 	usg_elem *ue;
240e7270ba8Schristos 	int     maxlen[2];
241e7270ba8Schristos 	int     desccol = 0;
242e7270ba8Schristos 	int     print_run = 0;
243e7270ba8Schristos 
244e7270ba8Schristos 	maxlen[0] = 0;
245e7270ba8Schristos 	maxlen[1] = 0;
246e7270ba8Schristos 
247e7270ba8Schristos 	s = (struct _scanopt_t *) scanner;
248e7270ba8Schristos 
249e7270ba8Schristos 	if (usage) {
250e7270ba8Schristos 		fprintf (fp, "%s\n", usage);
251e7270ba8Schristos 	}
252e7270ba8Schristos 	else {
253e7270ba8Schristos 		/* Find the basename of argv[0] */
254e7270ba8Schristos 		const char *p;
255e7270ba8Schristos 
256e7270ba8Schristos 		p = s->argv[0] + strlen (s->argv[0]);
257e7270ba8Schristos 		while (p != s->argv[0] && *p != '/')
258e7270ba8Schristos 			--p;
259e7270ba8Schristos 		if (*p == '/')
260e7270ba8Schristos 			p++;
261e7270ba8Schristos 
262e7270ba8Schristos 		fprintf (fp, _("Usage: %s [OPTIONS]...\n"), p);
263e7270ba8Schristos 	}
264e7270ba8Schristos 	fprintf (fp, "\n");
265e7270ba8Schristos 
266e7270ba8Schristos 	/* Sort by r_val and string. Yes, this is O(n*n), but n is small. */
267*b806bde9Schristos 	store = malloc((size_t) s->optc * sizeof (usg_elem));
268e7270ba8Schristos 	for (i = 0; i < s->optc; i++) {
269e7270ba8Schristos 
270e7270ba8Schristos 		/* grab the next preallocate node. */
271e7270ba8Schristos 		ue = store + store_idx++;
272e7270ba8Schristos 		ue->idx = i;
273e7270ba8Schristos 		ue->next = ue->alias = NULL;
274e7270ba8Schristos 
275e7270ba8Schristos 		/* insert into list. */
276e7270ba8Schristos 		if (!byr_val)
277e7270ba8Schristos 			byr_val = ue;
278e7270ba8Schristos 		else {
279e7270ba8Schristos 			int     found_alias = 0;
280e7270ba8Schristos 			usg_elem **ue_curr, **ptr_if_no_alias = NULL;
281e7270ba8Schristos 
282e7270ba8Schristos 			ue_curr = &byr_val;
283e7270ba8Schristos 			while (*ue_curr) {
284e7270ba8Schristos 				if (RVAL (s, (*ue_curr)->idx) ==
285e7270ba8Schristos 				    RVAL (s, ue->idx)) {
286e7270ba8Schristos 					/* push onto the alias list. */
287e7270ba8Schristos 					ue_curr = &((*ue_curr)->alias);
288e7270ba8Schristos 					found_alias = 1;
289e7270ba8Schristos 					break;
290e7270ba8Schristos 				}
291e7270ba8Schristos 				if (!ptr_if_no_alias
292e7270ba8Schristos 				    &&
293*b806bde9Schristos 				    strcasecmp (NAME (s, (*ue_curr)->idx),
294e7270ba8Schristos 						NAME (s, ue->idx)) > 0) {
295e7270ba8Schristos 					ptr_if_no_alias = ue_curr;
296e7270ba8Schristos 				}
297e7270ba8Schristos 				ue_curr = &((*ue_curr)->next);
298e7270ba8Schristos 			}
299e7270ba8Schristos 			if (!found_alias && ptr_if_no_alias)
300e7270ba8Schristos 				ue_curr = ptr_if_no_alias;
301e7270ba8Schristos 			ue->next = *ue_curr;
302e7270ba8Schristos 			*ue_curr = ue;
303e7270ba8Schristos 		}
304e7270ba8Schristos 	}
305e7270ba8Schristos 
306e7270ba8Schristos #if 0
307e7270ba8Schristos 	if (1) {
308e7270ba8Schristos 		printf ("ORIGINAL:\n");
309e7270ba8Schristos 		for (i = 0; i < s->optc; i++)
310e7270ba8Schristos 			printf ("%2d: %s\n", i, NAME (s, i));
311e7270ba8Schristos 		printf ("SORTED:\n");
312e7270ba8Schristos 		ue = byr_val;
313e7270ba8Schristos 		while (ue) {
314e7270ba8Schristos 			usg_elem *ue2;
315e7270ba8Schristos 
316e7270ba8Schristos 			printf ("%2d: %s\n", ue->idx, NAME (s, ue->idx));
317e7270ba8Schristos 			for (ue2 = ue->alias; ue2; ue2 = ue2->next)
318e7270ba8Schristos 				printf ("  +---> %2d: %s\n", ue2->idx,
319e7270ba8Schristos 					NAME (s, ue2->idx));
320e7270ba8Schristos 			ue = ue->next;
321e7270ba8Schristos 		}
322e7270ba8Schristos 	}
323e7270ba8Schristos #endif
324e7270ba8Schristos 
325e7270ba8Schristos 	/* Now build each row of output. */
326e7270ba8Schristos 
327e7270ba8Schristos 	/* first pass calculate how much room we need. */
328e7270ba8Schristos 	for (ue = byr_val; ue; ue = ue->next) {
329e7270ba8Schristos 		usg_elem *ap;
330e7270ba8Schristos 		int     len = 0;
331e7270ba8Schristos 		int     nshort = 0, nlong = 0;
332e7270ba8Schristos 
333e7270ba8Schristos 
334e7270ba8Schristos #define CALC_LEN(i) do {\
335e7270ba8Schristos           if(FLAGS(s,i) & IS_LONG) \
336e7270ba8Schristos               len +=  (nlong++||nshort) ? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\
337e7270ba8Schristos           else\
338e7270ba8Schristos               len +=  (nshort++||nlong)? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\
339e7270ba8Schristos         }while(0)
340e7270ba8Schristos 
341e7270ba8Schristos 		if (!(FLAGS (s, ue->idx) & IS_LONG))
342e7270ba8Schristos 			CALC_LEN (ue->idx);
343e7270ba8Schristos 
344e7270ba8Schristos 		/* do short aliases first. */
345e7270ba8Schristos 		for (ap = ue->alias; ap; ap = ap->next) {
346e7270ba8Schristos 			if (FLAGS (s, ap->idx) & IS_LONG)
347e7270ba8Schristos 				continue;
348e7270ba8Schristos 			CALC_LEN (ap->idx);
349e7270ba8Schristos 		}
350e7270ba8Schristos 
351e7270ba8Schristos 		if (FLAGS (s, ue->idx) & IS_LONG)
352e7270ba8Schristos 			CALC_LEN (ue->idx);
353e7270ba8Schristos 
354e7270ba8Schristos 		/* repeat the above loop, this time for long aliases. */
355e7270ba8Schristos 		for (ap = ue->alias; ap; ap = ap->next) {
356e7270ba8Schristos 			if (!(FLAGS (s, ap->idx) & IS_LONG))
357e7270ba8Schristos 				continue;
358e7270ba8Schristos 			CALC_LEN (ap->idx);
359e7270ba8Schristos 		}
360e7270ba8Schristos 
361e7270ba8Schristos 		if (len > maxlen[0])
362e7270ba8Schristos 			maxlen[0] = len;
363e7270ba8Schristos 
364e7270ba8Schristos 		/* It's much easier to calculate length for description column! */
365*b806bde9Schristos 		len = (int) strlen (DESC (s, ue->idx));
366e7270ba8Schristos 		if (len > maxlen[1])
367e7270ba8Schristos 			maxlen[1] = len;
368e7270ba8Schristos 	}
369e7270ba8Schristos 
370e7270ba8Schristos 	/* Determine how much room we have, and how much we will allocate to each col.
371e7270ba8Schristos 	 * Do not address pathological cases. Output will just be ugly. */
372e7270ba8Schristos 	columns = get_cols () - 1;
373e7270ba8Schristos 	if (maxlen[0] + maxlen[1] + indent * 2 > columns) {
374e7270ba8Schristos 		/* col 0 gets whatever it wants. we'll wrap the desc col. */
375e7270ba8Schristos 		maxlen[1] = columns - (maxlen[0] + indent * 2);
376e7270ba8Schristos 		if (maxlen[1] < 14)	/* 14 is arbitrary lower limit on desc width. */
377e7270ba8Schristos 			maxlen[1] = INT_MAX;
378e7270ba8Schristos 	}
379e7270ba8Schristos 	desccol = maxlen[0] + indent * 2;
380e7270ba8Schristos 
381e7270ba8Schristos #define PRINT_SPACES(fp,n)\
382e7270ba8Schristos     do{\
383e7270ba8Schristos         int _n;\
384e7270ba8Schristos         _n=(n);\
385e7270ba8Schristos         while(_n-- > 0)\
386e7270ba8Schristos             fputc(' ',(fp));\
387e7270ba8Schristos     }while(0)
388e7270ba8Schristos 
389e7270ba8Schristos 
390e7270ba8Schristos 	/* Second pass (same as above loop), this time we print. */
391e7270ba8Schristos 	/* Sloppy hack: We iterate twice. The first time we print short and long options.
392e7270ba8Schristos 	   The second time we print those lines that have ONLY long options. */
393e7270ba8Schristos 	while (print_run++ < 2) {
394e7270ba8Schristos 		for (ue = byr_val; ue; ue = ue->next) {
395e7270ba8Schristos 			usg_elem *ap;
396e7270ba8Schristos 			int     nwords = 0, nchars = 0, has_short = 0;
397e7270ba8Schristos 
398e7270ba8Schristos /* TODO: get has_short schtick to work */
399e7270ba8Schristos 			has_short = !(FLAGS (s, ue->idx) & IS_LONG);
400e7270ba8Schristos 			for (ap = ue->alias; ap; ap = ap->next) {
401e7270ba8Schristos 				if (!(FLAGS (s, ap->idx) & IS_LONG)) {
402e7270ba8Schristos 					has_short = 1;
403e7270ba8Schristos 					break;
404e7270ba8Schristos 				}
405e7270ba8Schristos 			}
406e7270ba8Schristos 			if ((print_run == 1 && !has_short) ||
407e7270ba8Schristos 			    (print_run == 2 && has_short))
408e7270ba8Schristos 				continue;
409e7270ba8Schristos 
410e7270ba8Schristos 			PRINT_SPACES (fp, indent);
411e7270ba8Schristos 			nchars += indent;
412e7270ba8Schristos 
413e7270ba8Schristos /* Print, adding a ", " between aliases. */
414e7270ba8Schristos #define PRINT_IT(i) do{\
415e7270ba8Schristos                   if(nwords++)\
416e7270ba8Schristos                       nchars+=fprintf(fp,", ");\
417e7270ba8Schristos                   nchars+=fprintf(fp,"%s",s->options[i].opt_fmt);\
418e7270ba8Schristos             }while(0)
419e7270ba8Schristos 
420e7270ba8Schristos 			if (!(FLAGS (s, ue->idx) & IS_LONG))
421e7270ba8Schristos 				PRINT_IT (ue->idx);
422e7270ba8Schristos 
423e7270ba8Schristos 			/* print short aliases first. */
424e7270ba8Schristos 			for (ap = ue->alias; ap; ap = ap->next) {
425e7270ba8Schristos 				if (!(FLAGS (s, ap->idx) & IS_LONG))
426e7270ba8Schristos 					PRINT_IT (ap->idx);
427e7270ba8Schristos 			}
428e7270ba8Schristos 
429e7270ba8Schristos 
430e7270ba8Schristos 			if (FLAGS (s, ue->idx) & IS_LONG)
431e7270ba8Schristos 				PRINT_IT (ue->idx);
432e7270ba8Schristos 
433e7270ba8Schristos 			/* repeat the above loop, this time for long aliases. */
434e7270ba8Schristos 			for (ap = ue->alias; ap; ap = ap->next) {
435e7270ba8Schristos 				if (FLAGS (s, ap->idx) & IS_LONG)
436e7270ba8Schristos 					PRINT_IT (ap->idx);
437e7270ba8Schristos 			}
438e7270ba8Schristos 
439e7270ba8Schristos 			/* pad to desccol */
440e7270ba8Schristos 			PRINT_SPACES (fp, desccol - nchars);
441e7270ba8Schristos 
442e7270ba8Schristos 			/* Print description, wrapped to maxlen[1] columns. */
443e7270ba8Schristos 			if (1) {
444e7270ba8Schristos 				const char *pstart;
445e7270ba8Schristos 
446e7270ba8Schristos 				pstart = DESC (s, ue->idx);
447e7270ba8Schristos 				while (1) {
448e7270ba8Schristos 					int     n = 0;
449e7270ba8Schristos 					const char *lastws = NULL, *p;
450e7270ba8Schristos 
451e7270ba8Schristos 					p = pstart;
452e7270ba8Schristos 
453e7270ba8Schristos 					while (*p && n < maxlen[1]
454e7270ba8Schristos 					       && *p != '\n') {
455e7270ba8Schristos 						if (isspace ((unsigned char)(*p))
456e7270ba8Schristos 						    || *p == '-') lastws =
457e7270ba8Schristos 								p;
458e7270ba8Schristos 						n++;
459e7270ba8Schristos 						p++;
460e7270ba8Schristos 					}
461e7270ba8Schristos 
462e7270ba8Schristos 					if (!*p) {	/* hit end of desc. done. */
463e7270ba8Schristos 						fprintf (fp, "%s\n",
464e7270ba8Schristos 							 pstart);
465e7270ba8Schristos 						break;
466e7270ba8Schristos 					}
467e7270ba8Schristos 					else if (*p == '\n') {	/* print everything up to here then wrap. */
468e7270ba8Schristos 						fprintf (fp, "%.*s\n", n,
469e7270ba8Schristos 							 pstart);
470e7270ba8Schristos 						PRINT_SPACES (fp, desccol);
471e7270ba8Schristos 						pstart = p + 1;
472e7270ba8Schristos 						continue;
473e7270ba8Schristos 					}
474e7270ba8Schristos 					else {	/* we hit the edge of the screen. wrap at space if possible. */
475e7270ba8Schristos 						if (lastws) {
476e7270ba8Schristos 							fprintf (fp,
477e7270ba8Schristos 								 "%.*s\n",
478e7270ba8Schristos 								 (int)(lastws - pstart),
479e7270ba8Schristos 								 pstart);
480e7270ba8Schristos 							pstart =
481e7270ba8Schristos 								lastws + 1;
482e7270ba8Schristos 						}
483e7270ba8Schristos 						else {
484e7270ba8Schristos 							fprintf (fp,
485e7270ba8Schristos 								 "%.*s\n",
486e7270ba8Schristos 								 n,
487e7270ba8Schristos 								 pstart);
488e7270ba8Schristos 							pstart = p + 1;
489e7270ba8Schristos 						}
490e7270ba8Schristos 						PRINT_SPACES (fp, desccol);
491e7270ba8Schristos 						continue;
492e7270ba8Schristos 					}
493e7270ba8Schristos 				}
494e7270ba8Schristos 			}
495e7270ba8Schristos 		}
496e7270ba8Schristos 	}			/* end while */
497e7270ba8Schristos 	free (store);
498e7270ba8Schristos 	return 0;
499e7270ba8Schristos }
500e7270ba8Schristos #endif /* no scanopt_usage */
501e7270ba8Schristos 
502e7270ba8Schristos 
scanopt_err(struct _scanopt_t * s,int is_short,int err)503*b806bde9Schristos static int scanopt_err (struct _scanopt_t *s, int is_short, int err)
504e7270ba8Schristos {
505e7270ba8Schristos 	const char *optname = "";
506e7270ba8Schristos 	char    optchar[2];
507e7270ba8Schristos 
508e7270ba8Schristos 	if (!s->no_err_msg) {
509e7270ba8Schristos 
510e7270ba8Schristos 		if (s->index > 0 && s->index < s->argc) {
511e7270ba8Schristos 			if (is_short) {
512e7270ba8Schristos 				optchar[0] =
513e7270ba8Schristos 					s->argv[s->index][s->subscript];
514e7270ba8Schristos 				optchar[1] = '\0';
515e7270ba8Schristos 				optname = optchar;
516e7270ba8Schristos 			}
517e7270ba8Schristos 			else {
518e7270ba8Schristos 				optname = s->argv[s->index];
519e7270ba8Schristos 			}
520e7270ba8Schristos 		}
521e7270ba8Schristos 
522e7270ba8Schristos 		fprintf (stderr, "%s: ", s->argv[0]);
523e7270ba8Schristos 		switch (err) {
524e7270ba8Schristos 		case SCANOPT_ERR_ARG_NOT_ALLOWED:
525e7270ba8Schristos 			fprintf (stderr,
526e7270ba8Schristos 				 _
527e7270ba8Schristos 				 ("option `%s' doesn't allow an argument\n"),
528e7270ba8Schristos 				 optname);
529e7270ba8Schristos 			break;
530e7270ba8Schristos 		case SCANOPT_ERR_ARG_NOT_FOUND:
531e7270ba8Schristos 			fprintf (stderr,
532e7270ba8Schristos 				 _("option `%s' requires an argument\n"),
533e7270ba8Schristos 				 optname);
534e7270ba8Schristos 			break;
535e7270ba8Schristos 		case SCANOPT_ERR_OPT_AMBIGUOUS:
536e7270ba8Schristos 			fprintf (stderr, _("option `%s' is ambiguous\n"),
537e7270ba8Schristos 				 optname);
538e7270ba8Schristos 			break;
539e7270ba8Schristos 		case SCANOPT_ERR_OPT_UNRECOGNIZED:
540e7270ba8Schristos 			fprintf (stderr, _("Unrecognized option `%s'\n"),
541e7270ba8Schristos 				 optname);
542e7270ba8Schristos 			break;
543e7270ba8Schristos 		default:
544e7270ba8Schristos 			fprintf (stderr, _("Unknown error=(%d)\n"), err);
545e7270ba8Schristos 			break;
546e7270ba8Schristos 		}
547e7270ba8Schristos 	}
548e7270ba8Schristos 	return err;
549e7270ba8Schristos }
550e7270ba8Schristos 
551e7270ba8Schristos 
552e7270ba8Schristos /* Internal. Match str against the regex  ^--([^=]+)(=(.*))?
553e7270ba8Schristos  * return 1 if *looks* like a long option.
554e7270ba8Schristos  * 'str' is the only input argument, the rest of the arguments are output only.
555e7270ba8Schristos  * optname will point to str + 2
556e7270ba8Schristos  *
557e7270ba8Schristos  */
matchlongopt(char * str,char ** optname,int * optlen,char ** arg,int * arglen)558*b806bde9Schristos static int matchlongopt (char *str, char **optname, int *optlen, char **arg, int *arglen)
559e7270ba8Schristos {
560e7270ba8Schristos 	char   *p;
561e7270ba8Schristos 
562*b806bde9Schristos 	*optname = *arg = NULL;
563e7270ba8Schristos 	*optlen = *arglen = 0;
564e7270ba8Schristos 
565e7270ba8Schristos 	/* Match regex /--./   */
566e7270ba8Schristos 	p = str;
567e7270ba8Schristos 	if (p[0] != '-' || p[1] != '-' || !p[2])
568e7270ba8Schristos 		return 0;
569e7270ba8Schristos 
570e7270ba8Schristos 	p += 2;
571*b806bde9Schristos 	*optname = p;
572e7270ba8Schristos 
573e7270ba8Schristos 	/* find the end of optname */
574e7270ba8Schristos 	while (*p && *p != '=')
575e7270ba8Schristos 		++p;
576e7270ba8Schristos 
577*b806bde9Schristos 	*optlen = (int) (p - *optname);
578e7270ba8Schristos 
579e7270ba8Schristos 	if (!*p)
580e7270ba8Schristos 		/* an option with no '=...' part. */
581e7270ba8Schristos 		return 1;
582e7270ba8Schristos 
583e7270ba8Schristos 
584e7270ba8Schristos 	/* We saw an '=' char. The rest of p is the arg. */
585e7270ba8Schristos 	p++;
586e7270ba8Schristos 	*arg = p;
587e7270ba8Schristos 	while (*p)
588e7270ba8Schristos 		++p;
589*b806bde9Schristos 	*arglen = (int) (p - *arg);
590e7270ba8Schristos 
591e7270ba8Schristos 	return 1;
592e7270ba8Schristos }
593e7270ba8Schristos 
594e7270ba8Schristos 
595e7270ba8Schristos /* Internal. Look up long or short option by name.
596e7270ba8Schristos  * Long options must match a non-ambiguous prefix, or exact match.
597e7270ba8Schristos  * Short options must be exact.
598e7270ba8Schristos  * Return boolean true if found and no error.
599e7270ba8Schristos  * Error stored in err_code or zero if no error. */
find_opt(struct _scanopt_t * s,int lookup_long,char * optstart,int len,int * err_code,int * opt_offset)600*b806bde9Schristos static int find_opt (struct _scanopt_t *s, int lookup_long, char *optstart, int
601*b806bde9Schristos 	len, int *err_code, int *opt_offset)
602e7270ba8Schristos {
603e7270ba8Schristos 	int     nmatch = 0, lastr_val = 0, i;
604e7270ba8Schristos 
605e7270ba8Schristos 	*err_code = 0;
606e7270ba8Schristos 	*opt_offset = -1;
607e7270ba8Schristos 
608e7270ba8Schristos 	if (!optstart)
609e7270ba8Schristos 		return 0;
610e7270ba8Schristos 
611e7270ba8Schristos 	for (i = 0; i < s->optc; i++) {
612*b806bde9Schristos 		const char   *optname;
613e7270ba8Schristos 
614*b806bde9Schristos 		optname = s->options[i].opt_fmt + (lookup_long ? 2 : 1);
615e7270ba8Schristos 
616e7270ba8Schristos 		if (lookup_long && (s->aux[i].flags & IS_LONG)) {
617e7270ba8Schristos 			if (len > s->aux[i].namelen)
618e7270ba8Schristos 				continue;
619e7270ba8Schristos 
620*b806bde9Schristos 			if (strncmp (optname, optstart, (size_t) len) == 0) {
621e7270ba8Schristos 				nmatch++;
622e7270ba8Schristos 				*opt_offset = i;
623e7270ba8Schristos 
624e7270ba8Schristos 				/* exact match overrides all. */
625e7270ba8Schristos 				if (len == s->aux[i].namelen) {
626e7270ba8Schristos 					nmatch = 1;
627e7270ba8Schristos 					break;
628e7270ba8Schristos 				}
629e7270ba8Schristos 
630e7270ba8Schristos 				/* ambiguity is ok between aliases. */
631e7270ba8Schristos 				if (lastr_val
632e7270ba8Schristos 				    && lastr_val ==
633e7270ba8Schristos 				    s->options[i].r_val) nmatch--;
634e7270ba8Schristos 				lastr_val = s->options[i].r_val;
635e7270ba8Schristos 			}
636e7270ba8Schristos 		}
637e7270ba8Schristos 		else if (!lookup_long && !(s->aux[i].flags & IS_LONG)) {
638e7270ba8Schristos 			if (optname[0] == optstart[0]) {
639e7270ba8Schristos 				nmatch++;
640e7270ba8Schristos 				*opt_offset = i;
641e7270ba8Schristos 			}
642e7270ba8Schristos 		}
643e7270ba8Schristos 	}
644e7270ba8Schristos 
645e7270ba8Schristos 	if (nmatch == 0) {
646e7270ba8Schristos 		*err_code = SCANOPT_ERR_OPT_UNRECOGNIZED;
647e7270ba8Schristos 		*opt_offset = -1;
648e7270ba8Schristos 	}
649e7270ba8Schristos 	else if (nmatch > 1) {
650e7270ba8Schristos 		*err_code = SCANOPT_ERR_OPT_AMBIGUOUS;
651e7270ba8Schristos 		*opt_offset = -1;
652e7270ba8Schristos 	}
653e7270ba8Schristos 
654e7270ba8Schristos 	return *err_code ? 0 : 1;
655e7270ba8Schristos }
656e7270ba8Schristos 
657e7270ba8Schristos 
scanopt(scanopt_t * svoid,char ** arg,int * optindex)658*b806bde9Schristos int     scanopt (scanopt_t *svoid, char **arg, int *optindex)
659e7270ba8Schristos {
660e7270ba8Schristos 	char   *optname = NULL, *optarg = NULL, *pstart;
661e7270ba8Schristos 	int     namelen = 0, arglen = 0;
662e7270ba8Schristos 	int     errcode = 0, has_next;
663e7270ba8Schristos 	const optspec_t *optp;
664e7270ba8Schristos 	struct _scanopt_t *s;
665e7270ba8Schristos 	struct _aux *auxp;
666e7270ba8Schristos 	int     is_short;
667e7270ba8Schristos 	int     opt_offset = -1;
668e7270ba8Schristos 
669e7270ba8Schristos 	s = (struct _scanopt_t *) svoid;
670e7270ba8Schristos 
671e7270ba8Schristos 	/* Normalize return-parameters. */
672e7270ba8Schristos 	SAFE_ASSIGN (arg, NULL);
673e7270ba8Schristos 	SAFE_ASSIGN (optindex, s->index);
674e7270ba8Schristos 
675e7270ba8Schristos 	if (s->index >= s->argc)
676e7270ba8Schristos 		return 0;
677e7270ba8Schristos 
678e7270ba8Schristos 	/* pstart always points to the start of our current scan. */
679e7270ba8Schristos 	pstart = s->argv[s->index] + s->subscript;
680e7270ba8Schristos 	if (!pstart)
681e7270ba8Schristos 		return 0;
682e7270ba8Schristos 
683e7270ba8Schristos 	if (s->subscript == 0) {
684e7270ba8Schristos 
685e7270ba8Schristos 		/* test for exact match of "--" */
686e7270ba8Schristos 		if (pstart[0] == '-' && pstart[1] == '-' && !pstart[2]) {
687e7270ba8Schristos 			SAFE_ASSIGN (optindex, s->index + 1);
688e7270ba8Schristos 			INC_INDEX (s, 1);
689e7270ba8Schristos 			return 0;
690e7270ba8Schristos 		}
691e7270ba8Schristos 
692e7270ba8Schristos 		/* Match an opt. */
693e7270ba8Schristos 		if (matchlongopt
694e7270ba8Schristos 		    (pstart, &optname, &namelen, &optarg, &arglen)) {
695e7270ba8Schristos 
696e7270ba8Schristos 			/* it LOOKS like an opt, but is it one?! */
697e7270ba8Schristos 			if (!find_opt
698e7270ba8Schristos 			    (s, 1, optname, namelen, &errcode,
699e7270ba8Schristos 			     &opt_offset)) {
700e7270ba8Schristos 				scanopt_err (s, 0, errcode);
701e7270ba8Schristos 				return errcode;
702e7270ba8Schristos 			}
703e7270ba8Schristos 			/* We handle this below. */
704e7270ba8Schristos 			is_short = 0;
705e7270ba8Schristos 
706e7270ba8Schristos 			/* Check for short opt.  */
707e7270ba8Schristos 		}
708e7270ba8Schristos 		else if (pstart[0] == '-' && pstart[1]) {
709e7270ba8Schristos 			/* Pass through to below. */
710e7270ba8Schristos 			is_short = 1;
711e7270ba8Schristos 			s->subscript++;
712e7270ba8Schristos 			pstart++;
713e7270ba8Schristos 		}
714e7270ba8Schristos 
715e7270ba8Schristos 		else {
716e7270ba8Schristos 			/* It's not an option. We're done. */
717e7270ba8Schristos 			return 0;
718e7270ba8Schristos 		}
719e7270ba8Schristos 	}
720e7270ba8Schristos 
721e7270ba8Schristos 	/* We have to re-check the subscript status because it
722e7270ba8Schristos 	 * may have changed above. */
723e7270ba8Schristos 
724e7270ba8Schristos 	if (s->subscript != 0) {
725e7270ba8Schristos 
726e7270ba8Schristos 		/* we are somewhere in a run of short opts,
727e7270ba8Schristos 		 * e.g., at the 'z' in `tar -xzf` */
728e7270ba8Schristos 
729e7270ba8Schristos 		optname = pstart;
730e7270ba8Schristos 		namelen = 1;
731e7270ba8Schristos 		is_short = 1;
732e7270ba8Schristos 
733e7270ba8Schristos 		if (!find_opt
734e7270ba8Schristos 		    (s, 0, pstart, namelen, &errcode, &opt_offset)) {
735e7270ba8Schristos 			return scanopt_err (s, 1, errcode);
736e7270ba8Schristos 		}
737e7270ba8Schristos 
738e7270ba8Schristos 		optarg = pstart + 1;
739e7270ba8Schristos 		if (!*optarg) {
740e7270ba8Schristos 			optarg = NULL;
741e7270ba8Schristos 			arglen = 0;
742e7270ba8Schristos 		}
743e7270ba8Schristos 		else
744*b806bde9Schristos 			arglen = (int) strlen (optarg);
745e7270ba8Schristos 	}
746e7270ba8Schristos 
747e7270ba8Schristos 	/* At this point, we have a long or short option matched at opt_offset into
748e7270ba8Schristos 	 * the s->options array (and corresponding aux array).
749e7270ba8Schristos 	 * A trailing argument is in {optarg,arglen}, if any.
750e7270ba8Schristos 	 */
751e7270ba8Schristos 
752e7270ba8Schristos 	/* Look ahead in argv[] to see if there is something
753e7270ba8Schristos 	 * that we can use as an argument (if needed). */
754e7270ba8Schristos 	has_next = s->index + 1 < s->argc
755e7270ba8Schristos 		&& strcmp ("--", s->argv[s->index + 1]) != 0;
756e7270ba8Schristos 
757e7270ba8Schristos 	optp = s->options + opt_offset;
758e7270ba8Schristos 	auxp = s->aux + opt_offset;
759e7270ba8Schristos 
760e7270ba8Schristos 	/* case: no args allowed */
761e7270ba8Schristos 	if (auxp->flags & ARG_NONE) {
762e7270ba8Schristos 		if (optarg && !is_short) {
763e7270ba8Schristos 			scanopt_err (s, is_short, errcode = SCANOPT_ERR_ARG_NOT_ALLOWED);
764e7270ba8Schristos 			INC_INDEX (s, 1);
765e7270ba8Schristos 			return errcode;
766e7270ba8Schristos 		}
767e7270ba8Schristos 		else if (!optarg)
768e7270ba8Schristos 			INC_INDEX (s, 1);
769e7270ba8Schristos 		else
770e7270ba8Schristos 			s->subscript++;
771e7270ba8Schristos 		return optp->r_val;
772e7270ba8Schristos 	}
773e7270ba8Schristos 
774e7270ba8Schristos 	/* case: required */
775e7270ba8Schristos 	if (auxp->flags & ARG_REQ) {
776e7270ba8Schristos 		if (!optarg && !has_next)
777e7270ba8Schristos 			return scanopt_err (s, is_short, SCANOPT_ERR_ARG_NOT_FOUND);
778e7270ba8Schristos 
779e7270ba8Schristos 		if (!optarg) {
780e7270ba8Schristos 			/* Let the next argv element become the argument. */
781e7270ba8Schristos 			SAFE_ASSIGN (arg, s->argv[s->index + 1]);
782e7270ba8Schristos 			INC_INDEX (s, 2);
783e7270ba8Schristos 		}
784e7270ba8Schristos 		else {
785e7270ba8Schristos 			SAFE_ASSIGN (arg, (char *) optarg);
786e7270ba8Schristos 			INC_INDEX (s, 1);
787e7270ba8Schristos 		}
788e7270ba8Schristos 		return optp->r_val;
789e7270ba8Schristos 	}
790e7270ba8Schristos 
791e7270ba8Schristos 	/* case: optional */
792e7270ba8Schristos 	if (auxp->flags & ARG_OPT) {
793e7270ba8Schristos 		SAFE_ASSIGN (arg, optarg);
794e7270ba8Schristos 		INC_INDEX (s, 1);
795e7270ba8Schristos 		return optp->r_val;
796e7270ba8Schristos 	}
797e7270ba8Schristos 
798e7270ba8Schristos 
799e7270ba8Schristos 	/* Should not reach here. */
800e7270ba8Schristos 	return 0;
801e7270ba8Schristos }
802e7270ba8Schristos 
803e7270ba8Schristos 
scanopt_destroy(scanopt_t * svoid)804*b806bde9Schristos int     scanopt_destroy (scanopt_t *svoid)
805e7270ba8Schristos {
806e7270ba8Schristos 	struct _scanopt_t *s;
807e7270ba8Schristos 
808e7270ba8Schristos 	s = (struct _scanopt_t *) svoid;
809*b806bde9Schristos 	if (s != NULL) {
810e7270ba8Schristos 		free(s->aux);
811e7270ba8Schristos 		free(s);
812e7270ba8Schristos 	}
813e7270ba8Schristos 	return 0;
814e7270ba8Schristos }
815e7270ba8Schristos 
816e7270ba8Schristos 
817e7270ba8Schristos /* vim:set tabstop=8 softtabstop=4 shiftwidth=4: */
818