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