1228072Sbapt/* flex - tool to generate fast lexical analyzers */
2228072Sbapt
3228072Sbapt/*  Copyright (c) 1990 The Regents of the University of California. */
4228072Sbapt/*  All rights reserved. */
5228072Sbapt
6228072Sbapt/*  This code is derived from software contributed to Berkeley by */
7228072Sbapt/*  Vern Paxson. */
8228072Sbapt
9228072Sbapt/*  The United States Government has rights in this work pursuant */
10228072Sbapt/*  to contract no. DE-AC03-76SF00098 between the United States */
11228072Sbapt/*  Department of Energy and the University of California. */
12228072Sbapt
13228072Sbapt/*  This file is part of flex. */
14228072Sbapt
15228072Sbapt/*  Redistribution and use in source and binary forms, with or without */
16228072Sbapt/*  modification, are permitted provided that the following conditions */
17228072Sbapt/*  are met: */
18228072Sbapt
19228072Sbapt/*  1. Redistributions of source code must retain the above copyright */
20228072Sbapt/*     notice, this list of conditions and the following disclaimer. */
21228072Sbapt/*  2. Redistributions in binary form must reproduce the above copyright */
22228072Sbapt/*     notice, this list of conditions and the following disclaimer in the */
23228072Sbapt/*     documentation and/or other materials provided with the distribution. */
24228072Sbapt
25228072Sbapt/*  Neither the name of the University nor the names of its contributors */
26228072Sbapt/*  may be used to endorse or promote products derived from this software */
27228072Sbapt/*  without specific prior written permission. */
28228072Sbapt
29228072Sbapt/*  THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */
30228072Sbapt/*  IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */
31228072Sbapt/*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */
32228072Sbapt/*  PURPOSE. */
33228072Sbapt
34228072Sbapt#include "flexdef.h"
35228072Sbapt#include "scanopt.h"
36228072Sbapt
37228072Sbapt
38228072Sbapt/* Internal structures */
39228072Sbapt
40228072Sbapt#ifdef HAVE_STRCASECMP
41228072Sbapt#define STRCASECMP(a,b) strcasecmp(a,b)
42228072Sbapt#else
43228072Sbaptstatic int STRCASECMP PROTO ((const char *, const char *));
44228072Sbapt
45228072Sbaptstatic int STRCASECMP (a, b)
46228072Sbapt     const char *a;
47228072Sbapt     const char *b;
48228072Sbapt{
49228072Sbapt	while (tolower (*a++) == tolower (*b++)) ;
50228072Sbapt	return b - a;
51228072Sbapt}
52228072Sbapt#endif
53228072Sbapt
54228072Sbapt#define ARG_NONE 0x01
55228072Sbapt#define ARG_REQ  0x02
56228072Sbapt#define ARG_OPT  0x04
57228072Sbapt#define IS_LONG  0x08
58228072Sbapt
59228072Sbaptstruct _aux {
60228072Sbapt	int     flags;		/* The above hex flags. */
61228072Sbapt	int     namelen;	/* Length of the actual option word, e.g., "--file[=foo]" is 4 */
62228072Sbapt	int     printlen;	/* Length of entire string, e.g., "--file[=foo]" is 12 */
63228072Sbapt};
64228072Sbapt
65228072Sbapt
66228072Sbaptstruct _scanopt_t {
67228072Sbapt	const optspec_t *options;	/* List of options. */
68228072Sbapt	struct _aux *aux;	/* Auxiliary data about options. */
69228072Sbapt	int     optc;		/* Number of options. */
70228072Sbapt	int     argc;		/* Number of args. */
71228072Sbapt	char  **argv;		/* Array of strings. */
72228072Sbapt	int     index;		/* Used as: argv[index][subscript]. */
73228072Sbapt	int     subscript;
74228072Sbapt	char    no_err_msg;	/* If true, do not print errors. */
75228072Sbapt	char    has_long;
76228072Sbapt	char    has_short;
77228072Sbapt};
78228072Sbapt
79228072Sbapt/* Accessor functions. These WOULD be one-liners, but portability calls. */
80228072Sbaptstatic const char *NAME PROTO ((struct _scanopt_t *, int));
81228072Sbaptstatic int PRINTLEN PROTO ((struct _scanopt_t *, int));
82228072Sbaptstatic int RVAL PROTO ((struct _scanopt_t *, int));
83228072Sbaptstatic int FLAGS PROTO ((struct _scanopt_t *, int));
84228072Sbaptstatic const char *DESC PROTO ((struct _scanopt_t *, int));
85228072Sbaptstatic int scanopt_err PROTO ((struct _scanopt_t *, int, int, int));
86228072Sbaptstatic int matchlongopt PROTO ((char *, char **, int *, char **, int *));
87228072Sbaptstatic int find_opt
88228072SbaptPROTO ((struct _scanopt_t *, int, char *, int, int *, int *opt_offset));
89228072Sbapt
90228072Sbaptstatic const char *NAME (s, i)
91228072Sbapt     struct _scanopt_t *s;
92228072Sbapt     int     i;
93228072Sbapt{
94228072Sbapt	return s->options[i].opt_fmt +
95228072Sbapt		((s->aux[i].flags & IS_LONG) ? 2 : 1);
96228072Sbapt}
97228072Sbapt
98228072Sbaptstatic int PRINTLEN (s, i)
99228072Sbapt     struct _scanopt_t *s;
100228072Sbapt     int     i;
101228072Sbapt{
102228072Sbapt	return s->aux[i].printlen;
103228072Sbapt}
104228072Sbapt
105228072Sbaptstatic int RVAL (s, i)
106228072Sbapt     struct _scanopt_t *s;
107228072Sbapt     int     i;
108228072Sbapt{
109228072Sbapt	return s->options[i].r_val;
110228072Sbapt}
111228072Sbapt
112228072Sbaptstatic int FLAGS (s, i)
113228072Sbapt     struct _scanopt_t *s;
114228072Sbapt     int     i;
115228072Sbapt{
116228072Sbapt	return s->aux[i].flags;
117228072Sbapt}
118228072Sbapt
119228072Sbaptstatic const char *DESC (s, i)
120228072Sbapt     struct _scanopt_t *s;
121228072Sbapt     int     i;
122228072Sbapt{
123228072Sbapt	return s->options[i].desc ? s->options[i].desc : "";
124228072Sbapt}
125228072Sbapt
126228072Sbapt#ifndef NO_SCANOPT_USAGE
127228072Sbaptstatic int get_cols PROTO ((void));
128228072Sbapt
129228072Sbaptstatic int get_cols ()
130228072Sbapt{
131228072Sbapt	char   *env;
132228072Sbapt	int     cols = 80;	/* default */
133228072Sbapt
134228072Sbapt#ifdef HAVE_NCURSES_H
135228072Sbapt	initscr ();
136228072Sbapt	endwin ();
137228072Sbapt	if (COLS > 0)
138228072Sbapt		return COLS;
139228072Sbapt#endif
140228072Sbapt
141228072Sbapt	if ((env = getenv ("COLUMNS")) != NULL)
142228072Sbapt		cols = atoi (env);
143228072Sbapt
144228072Sbapt	return cols;
145228072Sbapt}
146228072Sbapt#endif
147228072Sbapt
148228072Sbapt/* Macro to check for NULL before assigning a value. */
149228072Sbapt#define SAFE_ASSIGN(ptr,val) \
150228072Sbapt    do{                      \
151228072Sbapt        if((ptr)!=NULL)      \
152228072Sbapt            *(ptr) = val;    \
153228072Sbapt    }while(0)
154228072Sbapt
155228072Sbapt/* Macro to assure we reset subscript whenever we adjust s->index.*/
156228072Sbapt#define INC_INDEX(s,n)     \
157228072Sbapt    do{                    \
158228072Sbapt       (s)->index += (n);  \
159228072Sbapt       (s)->subscript= 0;  \
160228072Sbapt    }while(0)
161228072Sbapt
162228072Sbaptscanopt_t *scanopt_init (options, argc, argv, flags)
163228072Sbapt     const optspec_t *options;
164228072Sbapt     int     argc;
165228072Sbapt     char  **argv;
166228072Sbapt     int     flags;
167228072Sbapt{
168228072Sbapt	int     i;
169228072Sbapt	struct _scanopt_t *s;
170228072Sbapt	s = (struct _scanopt_t *) malloc (sizeof (struct _scanopt_t));
171228072Sbapt
172228072Sbapt	s->options = options;
173228072Sbapt	s->optc = 0;
174228072Sbapt	s->argc = argc;
175228072Sbapt	s->argv = (char **) argv;
176228072Sbapt	s->index = 1;
177228072Sbapt	s->subscript = 0;
178228072Sbapt	s->no_err_msg = (flags & SCANOPT_NO_ERR_MSG);
179228072Sbapt	s->has_long = 0;
180228072Sbapt	s->has_short = 0;
181228072Sbapt
182228072Sbapt	/* Determine option count. (Find entry with all zeros). */
183228072Sbapt	s->optc = 0;
184228072Sbapt	while (options[s->optc].opt_fmt
185228072Sbapt	       || options[s->optc].r_val || options[s->optc].desc)
186228072Sbapt		s->optc++;
187228072Sbapt
188228072Sbapt	/* Build auxiliary data */
189228072Sbapt	s->aux = (struct _aux *) malloc (s->optc * sizeof (struct _aux));
190228072Sbapt
191228072Sbapt	for (i = 0; i < s->optc; i++) {
192250125Sjkim		const Char *p, *pname;
193228072Sbapt		const struct optspec_t *opt;
194228072Sbapt		struct _aux *aux;
195228072Sbapt
196228072Sbapt		opt = s->options + i;
197228072Sbapt		aux = s->aux + i;
198228072Sbapt
199228072Sbapt		aux->flags = ARG_NONE;
200228072Sbapt
201228072Sbapt		if (opt->opt_fmt[0] == '-' && opt->opt_fmt[1] == '-') {
202228072Sbapt			aux->flags |= IS_LONG;
203250125Sjkim			pname = (const Char *)(opt->opt_fmt + 2);
204228072Sbapt			s->has_long = 1;
205228072Sbapt		}
206228072Sbapt		else {
207250125Sjkim			pname = (const Char *)(opt->opt_fmt + 1);
208228072Sbapt			s->has_short = 1;
209228072Sbapt		}
210228072Sbapt		aux->printlen = strlen (opt->opt_fmt);
211228072Sbapt
212228072Sbapt		aux->namelen = 0;
213228072Sbapt		for (p = pname + 1; *p; p++) {
214228072Sbapt			/* detect required arg */
215228072Sbapt			if (*p == '=' || isspace (*p)
216228072Sbapt			    || !(aux->flags & IS_LONG)) {
217228072Sbapt				if (aux->namelen == 0)
218228072Sbapt					aux->namelen = p - pname;
219228072Sbapt				aux->flags |= ARG_REQ;
220228072Sbapt				aux->flags &= ~ARG_NONE;
221228072Sbapt			}
222228072Sbapt			/* detect optional arg. This overrides required arg. */
223228072Sbapt			if (*p == '[') {
224228072Sbapt				if (aux->namelen == 0)
225228072Sbapt					aux->namelen = p - pname;
226228072Sbapt				aux->flags &= ~(ARG_REQ | ARG_NONE);
227228072Sbapt				aux->flags |= ARG_OPT;
228228072Sbapt				break;
229228072Sbapt			}
230228072Sbapt		}
231228072Sbapt		if (aux->namelen == 0)
232228072Sbapt			aux->namelen = p - pname;
233228072Sbapt	}
234228072Sbapt	return (scanopt_t *) s;
235228072Sbapt}
236228072Sbapt
237228072Sbapt#ifndef NO_SCANOPT_USAGE
238228072Sbapt/* these structs are for scanopt_usage(). */
239228072Sbaptstruct usg_elem {
240228072Sbapt	int     idx;
241228072Sbapt	struct usg_elem *next;
242228072Sbapt	struct usg_elem *alias;
243228072Sbapt};
244228072Sbapttypedef struct usg_elem usg_elem;
245228072Sbapt
246228072Sbapt
247228072Sbapt/* Prints a usage message based on contents of optlist.
248228072Sbapt * Parameters:
249228072Sbapt *   scanner  - The scanner, already initialized with scanopt_init().
250228072Sbapt *   fp       - The file stream to write to.
251228072Sbapt *   usage    - Text to be prepended to option list.
252228072Sbapt * Return:  Always returns 0 (zero).
253228072Sbapt * The output looks something like this:
254228072Sbapt
255228072Sbapt[indent][option, alias1, alias2...][indent][description line1
256228072Sbapt                                            description line2...]
257228072Sbapt */
258228072Sbaptint     scanopt_usage (scanner, fp, usage)
259228072Sbapt     scanopt_t *scanner;
260228072Sbapt     FILE   *fp;
261228072Sbapt     const char *usage;
262228072Sbapt{
263228072Sbapt	struct _scanopt_t *s;
264228072Sbapt	int     i, columns, indent = 2;
265228072Sbapt	usg_elem *byr_val = NULL;	/* option indices sorted by r_val */
266228072Sbapt	usg_elem *store;	/* array of preallocated elements. */
267228072Sbapt	int     store_idx = 0;
268228072Sbapt	usg_elem *ue;
269228072Sbapt	int     maxlen[2];
270228072Sbapt	int     desccol = 0;
271228072Sbapt	int     print_run = 0;
272228072Sbapt
273228072Sbapt	maxlen[0] = 0;
274228072Sbapt	maxlen[1] = 0;
275228072Sbapt
276228072Sbapt	s = (struct _scanopt_t *) scanner;
277228072Sbapt
278228072Sbapt	if (usage) {
279228072Sbapt		fprintf (fp, "%s\n", usage);
280228072Sbapt	}
281228072Sbapt	else {
282228072Sbapt		/* Find the basename of argv[0] */
283228072Sbapt		const char *p;
284228072Sbapt
285228072Sbapt		p = s->argv[0] + strlen (s->argv[0]);
286228072Sbapt		while (p != s->argv[0] && *p != '/')
287228072Sbapt			--p;
288228072Sbapt		if (*p == '/')
289228072Sbapt			p++;
290228072Sbapt
291228072Sbapt		fprintf (fp, _("Usage: %s [OPTIONS]...\n"), p);
292228072Sbapt	}
293228072Sbapt	fprintf (fp, "\n");
294228072Sbapt
295228072Sbapt	/* Sort by r_val and string. Yes, this is O(n*n), but n is small. */
296228072Sbapt	store = (usg_elem *) malloc (s->optc * sizeof (usg_elem));
297228072Sbapt	for (i = 0; i < s->optc; i++) {
298228072Sbapt
299228072Sbapt		/* grab the next preallocate node. */
300228072Sbapt		ue = store + store_idx++;
301228072Sbapt		ue->idx = i;
302228072Sbapt		ue->next = ue->alias = NULL;
303228072Sbapt
304228072Sbapt		/* insert into list. */
305228072Sbapt		if (!byr_val)
306228072Sbapt			byr_val = ue;
307228072Sbapt		else {
308228072Sbapt			int     found_alias = 0;
309228072Sbapt			usg_elem **ue_curr, **ptr_if_no_alias = NULL;
310228072Sbapt
311228072Sbapt			ue_curr = &byr_val;
312228072Sbapt			while (*ue_curr) {
313228072Sbapt				if (RVAL (s, (*ue_curr)->idx) ==
314228072Sbapt				    RVAL (s, ue->idx)) {
315228072Sbapt					/* push onto the alias list. */
316228072Sbapt					ue_curr = &((*ue_curr)->alias);
317228072Sbapt					found_alias = 1;
318228072Sbapt					break;
319228072Sbapt				}
320228072Sbapt				if (!ptr_if_no_alias
321228072Sbapt				    &&
322228072Sbapt				    STRCASECMP (NAME (s, (*ue_curr)->idx),
323228072Sbapt						NAME (s, ue->idx)) > 0) {
324228072Sbapt					ptr_if_no_alias = ue_curr;
325228072Sbapt				}
326228072Sbapt				ue_curr = &((*ue_curr)->next);
327228072Sbapt			}
328228072Sbapt			if (!found_alias && ptr_if_no_alias)
329228072Sbapt				ue_curr = ptr_if_no_alias;
330228072Sbapt			ue->next = *ue_curr;
331228072Sbapt			*ue_curr = ue;
332228072Sbapt		}
333228072Sbapt	}
334228072Sbapt
335228072Sbapt#if 0
336228072Sbapt	if (1) {
337228072Sbapt		printf ("ORIGINAL:\n");
338228072Sbapt		for (i = 0; i < s->optc; i++)
339228072Sbapt			printf ("%2d: %s\n", i, NAME (s, i));
340228072Sbapt		printf ("SORTED:\n");
341228072Sbapt		ue = byr_val;
342228072Sbapt		while (ue) {
343228072Sbapt			usg_elem *ue2;
344228072Sbapt
345228072Sbapt			printf ("%2d: %s\n", ue->idx, NAME (s, ue->idx));
346228072Sbapt			for (ue2 = ue->alias; ue2; ue2 = ue2->next)
347228072Sbapt				printf ("  +---> %2d: %s\n", ue2->idx,
348228072Sbapt					NAME (s, ue2->idx));
349228072Sbapt			ue = ue->next;
350228072Sbapt		}
351228072Sbapt	}
352228072Sbapt#endif
353228072Sbapt
354228072Sbapt	/* Now build each row of output. */
355228072Sbapt
356228072Sbapt	/* first pass calculate how much room we need. */
357228072Sbapt	for (ue = byr_val; ue; ue = ue->next) {
358228072Sbapt		usg_elem *ap;
359228072Sbapt		int     len = 0;
360228072Sbapt		int     nshort = 0, nlong = 0;
361228072Sbapt
362228072Sbapt
363228072Sbapt#define CALC_LEN(i) do {\
364228072Sbapt          if(FLAGS(s,i) & IS_LONG) \
365228072Sbapt              len +=  (nlong++||nshort) ? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\
366228072Sbapt          else\
367228072Sbapt              len +=  (nshort++||nlong)? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\
368228072Sbapt        }while(0)
369228072Sbapt
370228072Sbapt		if (!(FLAGS (s, ue->idx) & IS_LONG))
371228072Sbapt			CALC_LEN (ue->idx);
372228072Sbapt
373228072Sbapt		/* do short aliases first. */
374228072Sbapt		for (ap = ue->alias; ap; ap = ap->next) {
375228072Sbapt			if (FLAGS (s, ap->idx) & IS_LONG)
376228072Sbapt				continue;
377228072Sbapt			CALC_LEN (ap->idx);
378228072Sbapt		}
379228072Sbapt
380228072Sbapt		if (FLAGS (s, ue->idx) & IS_LONG)
381228072Sbapt			CALC_LEN (ue->idx);
382228072Sbapt
383228072Sbapt		/* repeat the above loop, this time for long aliases. */
384228072Sbapt		for (ap = ue->alias; ap; ap = ap->next) {
385228072Sbapt			if (!(FLAGS (s, ap->idx) & IS_LONG))
386228072Sbapt				continue;
387228072Sbapt			CALC_LEN (ap->idx);
388228072Sbapt		}
389228072Sbapt
390228072Sbapt		if (len > maxlen[0])
391228072Sbapt			maxlen[0] = len;
392228072Sbapt
393228072Sbapt		/* It's much easier to calculate length for description column! */
394228072Sbapt		len = strlen (DESC (s, ue->idx));
395228072Sbapt		if (len > maxlen[1])
396228072Sbapt			maxlen[1] = len;
397228072Sbapt	}
398228072Sbapt
399228072Sbapt	/* Determine how much room we have, and how much we will allocate to each col.
400228072Sbapt	 * Do not address pathological cases. Output will just be ugly. */
401228072Sbapt	columns = get_cols () - 1;
402228072Sbapt	if (maxlen[0] + maxlen[1] + indent * 2 > columns) {
403228072Sbapt		/* col 0 gets whatever it wants. we'll wrap the desc col. */
404228072Sbapt		maxlen[1] = columns - (maxlen[0] + indent * 2);
405228072Sbapt		if (maxlen[1] < 14)	/* 14 is arbitrary lower limit on desc width. */
406228072Sbapt			maxlen[1] = INT_MAX;
407228072Sbapt	}
408228072Sbapt	desccol = maxlen[0] + indent * 2;
409228072Sbapt
410228072Sbapt#define PRINT_SPACES(fp,n)\
411228072Sbapt    do{\
412228072Sbapt        int _n;\
413228072Sbapt        _n=(n);\
414228072Sbapt        while(_n-- > 0)\
415228072Sbapt            fputc(' ',(fp));\
416228072Sbapt    }while(0)
417228072Sbapt
418228072Sbapt
419228072Sbapt	/* Second pass (same as above loop), this time we print. */
420228072Sbapt	/* Sloppy hack: We iterate twice. The first time we print short and long options.
421228072Sbapt	   The second time we print those lines that have ONLY long options. */
422228072Sbapt	while (print_run++ < 2) {
423228072Sbapt		for (ue = byr_val; ue; ue = ue->next) {
424228072Sbapt			usg_elem *ap;
425228072Sbapt			int     nwords = 0, nchars = 0, has_short = 0;
426228072Sbapt
427228072Sbapt/* TODO: get has_short schtick to work */
428228072Sbapt			has_short = !(FLAGS (s, ue->idx) & IS_LONG);
429228072Sbapt			for (ap = ue->alias; ap; ap = ap->next) {
430228072Sbapt				if (!(FLAGS (s, ap->idx) & IS_LONG)) {
431228072Sbapt					has_short = 1;
432228072Sbapt					break;
433228072Sbapt				}
434228072Sbapt			}
435228072Sbapt			if ((print_run == 1 && !has_short) ||
436228072Sbapt			    (print_run == 2 && has_short))
437228072Sbapt				continue;
438228072Sbapt
439228072Sbapt			PRINT_SPACES (fp, indent);
440228072Sbapt			nchars += indent;
441228072Sbapt
442228072Sbapt/* Print, adding a ", " between aliases. */
443228072Sbapt#define PRINT_IT(i) do{\
444228072Sbapt                  if(nwords++)\
445228072Sbapt                      nchars+=fprintf(fp,", ");\
446228072Sbapt                  nchars+=fprintf(fp,"%s",s->options[i].opt_fmt);\
447228072Sbapt            }while(0)
448228072Sbapt
449228072Sbapt			if (!(FLAGS (s, ue->idx) & IS_LONG))
450228072Sbapt				PRINT_IT (ue->idx);
451228072Sbapt
452228072Sbapt			/* print short aliases first. */
453228072Sbapt			for (ap = ue->alias; ap; ap = ap->next) {
454228072Sbapt				if (!(FLAGS (s, ap->idx) & IS_LONG))
455228072Sbapt					PRINT_IT (ap->idx);
456228072Sbapt			}
457228072Sbapt
458228072Sbapt
459228072Sbapt			if (FLAGS (s, ue->idx) & IS_LONG)
460228072Sbapt				PRINT_IT (ue->idx);
461228072Sbapt
462228072Sbapt			/* repeat the above loop, this time for long aliases. */
463228072Sbapt			for (ap = ue->alias; ap; ap = ap->next) {
464228072Sbapt				if (FLAGS (s, ap->idx) & IS_LONG)
465228072Sbapt					PRINT_IT (ap->idx);
466228072Sbapt			}
467228072Sbapt
468228072Sbapt			/* pad to desccol */
469228072Sbapt			PRINT_SPACES (fp, desccol - nchars);
470228072Sbapt
471228072Sbapt			/* Print description, wrapped to maxlen[1] columns. */
472228072Sbapt			if (1) {
473228072Sbapt				const char *pstart;
474228072Sbapt
475228072Sbapt				pstart = DESC (s, ue->idx);
476228072Sbapt				while (1) {
477228072Sbapt					int     n = 0;
478228072Sbapt					const char *lastws = NULL, *p;
479228072Sbapt
480228072Sbapt					p = pstart;
481228072Sbapt
482228072Sbapt					while (*p && n < maxlen[1]
483228072Sbapt					       && *p != '\n') {
484250125Sjkim						if (isspace ((Char)(*p))
485228072Sbapt						    || *p == '-') lastws =
486228072Sbapt								p;
487228072Sbapt						n++;
488228072Sbapt						p++;
489228072Sbapt					}
490228072Sbapt
491228072Sbapt					if (!*p) {	/* hit end of desc. done. */
492228072Sbapt						fprintf (fp, "%s\n",
493228072Sbapt							 pstart);
494228072Sbapt						break;
495228072Sbapt					}
496228072Sbapt					else if (*p == '\n') {	/* print everything up to here then wrap. */
497228072Sbapt						fprintf (fp, "%.*s\n", n,
498228072Sbapt							 pstart);
499228072Sbapt						PRINT_SPACES (fp, desccol);
500228072Sbapt						pstart = p + 1;
501228072Sbapt						continue;
502228072Sbapt					}
503228072Sbapt					else {	/* we hit the edge of the screen. wrap at space if possible. */
504228072Sbapt						if (lastws) {
505228072Sbapt							fprintf (fp,
506228072Sbapt								 "%.*s\n",
507250125Sjkim								 (int)(lastws - pstart),
508228072Sbapt								 pstart);
509228072Sbapt							pstart =
510228072Sbapt								lastws + 1;
511228072Sbapt						}
512228072Sbapt						else {
513228072Sbapt							fprintf (fp,
514228072Sbapt								 "%.*s\n",
515228072Sbapt								 n,
516228072Sbapt								 pstart);
517228072Sbapt							pstart = p + 1;
518228072Sbapt						}
519228072Sbapt						PRINT_SPACES (fp, desccol);
520228072Sbapt						continue;
521228072Sbapt					}
522228072Sbapt				}
523228072Sbapt			}
524228072Sbapt		}
525228072Sbapt	}			/* end while */
526228072Sbapt	free (store);
527228072Sbapt	return 0;
528228072Sbapt}
529228072Sbapt#endif /* no scanopt_usage */
530228072Sbapt
531228072Sbapt
532228072Sbaptstatic int scanopt_err (s, opt_offset, is_short, err)
533228072Sbapt     struct _scanopt_t *s;
534228072Sbapt     int     opt_offset;
535228072Sbapt     int     is_short;
536228072Sbapt     int     err;
537228072Sbapt{
538228072Sbapt	const char *optname = "";
539228072Sbapt	char    optchar[2];
540228072Sbapt	const optspec_t *opt = NULL;
541228072Sbapt
542228072Sbapt	if (opt_offset >= 0)
543228072Sbapt		opt = s->options + opt_offset;
544228072Sbapt
545228072Sbapt	if (!s->no_err_msg) {
546228072Sbapt
547228072Sbapt		if (s->index > 0 && s->index < s->argc) {
548228072Sbapt			if (is_short) {
549228072Sbapt				optchar[0] =
550228072Sbapt					s->argv[s->index][s->subscript];
551228072Sbapt				optchar[1] = '\0';
552228072Sbapt				optname = optchar;
553228072Sbapt			}
554228072Sbapt			else {
555228072Sbapt				optname = s->argv[s->index];
556228072Sbapt			}
557228072Sbapt		}
558228072Sbapt
559228072Sbapt		fprintf (stderr, "%s: ", s->argv[0]);
560228072Sbapt		switch (err) {
561228072Sbapt		case SCANOPT_ERR_ARG_NOT_ALLOWED:
562228072Sbapt			fprintf (stderr,
563228072Sbapt				 _
564228072Sbapt				 ("option `%s' doesn't allow an argument\n"),
565228072Sbapt				 optname);
566228072Sbapt			break;
567228072Sbapt		case SCANOPT_ERR_ARG_NOT_FOUND:
568228072Sbapt			fprintf (stderr,
569228072Sbapt				 _("option `%s' requires an argument\n"),
570228072Sbapt				 optname);
571228072Sbapt			break;
572228072Sbapt		case SCANOPT_ERR_OPT_AMBIGUOUS:
573228072Sbapt			fprintf (stderr, _("option `%s' is ambiguous\n"),
574228072Sbapt				 optname);
575228072Sbapt			break;
576228072Sbapt		case SCANOPT_ERR_OPT_UNRECOGNIZED:
577228072Sbapt			fprintf (stderr, _("Unrecognized option `%s'\n"),
578228072Sbapt				 optname);
579228072Sbapt			break;
580228072Sbapt		default:
581228072Sbapt			fprintf (stderr, _("Unknown error=(%d)\n"), err);
582228072Sbapt			break;
583228072Sbapt		}
584228072Sbapt	}
585228072Sbapt	return err;
586228072Sbapt}
587228072Sbapt
588228072Sbapt
589228072Sbapt/* Internal. Match str against the regex  ^--([^=]+)(=(.*))?
590228072Sbapt * return 1 if *looks* like a long option.
591228072Sbapt * 'str' is the only input argument, the rest of the arguments are output only.
592228072Sbapt * optname will point to str + 2
593228072Sbapt *
594228072Sbapt */
595228072Sbaptstatic int matchlongopt (str, optname, optlen, arg, arglen)
596228072Sbapt     char   *str;
597228072Sbapt     char  **optname;
598228072Sbapt     int    *optlen;
599228072Sbapt     char  **arg;
600228072Sbapt     int    *arglen;
601228072Sbapt{
602228072Sbapt	char   *p;
603228072Sbapt
604228072Sbapt	*optname = *arg = (char *) 0;
605228072Sbapt	*optlen = *arglen = 0;
606228072Sbapt
607228072Sbapt	/* Match regex /--./   */
608228072Sbapt	p = str;
609228072Sbapt	if (p[0] != '-' || p[1] != '-' || !p[2])
610228072Sbapt		return 0;
611228072Sbapt
612228072Sbapt	p += 2;
613228072Sbapt	*optname = (char *) p;
614228072Sbapt
615228072Sbapt	/* find the end of optname */
616228072Sbapt	while (*p && *p != '=')
617228072Sbapt		++p;
618228072Sbapt
619228072Sbapt	*optlen = p - *optname;
620228072Sbapt
621228072Sbapt	if (!*p)
622228072Sbapt		/* an option with no '=...' part. */
623228072Sbapt		return 1;
624228072Sbapt
625228072Sbapt
626228072Sbapt	/* We saw an '=' char. The rest of p is the arg. */
627228072Sbapt	p++;
628228072Sbapt	*arg = p;
629228072Sbapt	while (*p)
630228072Sbapt		++p;
631228072Sbapt	*arglen = p - *arg;
632228072Sbapt
633228072Sbapt	return 1;
634228072Sbapt}
635228072Sbapt
636228072Sbapt
637228072Sbapt/* Internal. Look up long or short option by name.
638228072Sbapt * Long options must match a non-ambiguous prefix, or exact match.
639228072Sbapt * Short options must be exact.
640228072Sbapt * Return boolean true if found and no error.
641228072Sbapt * Error stored in err_code or zero if no error. */
642228072Sbaptstatic int find_opt (s, lookup_long, optstart, len, err_code, opt_offset)
643228072Sbapt     struct _scanopt_t *s;
644228072Sbapt     int     lookup_long;
645228072Sbapt     char   *optstart;
646228072Sbapt     int     len;
647228072Sbapt     int    *err_code;
648228072Sbapt     int    *opt_offset;
649228072Sbapt{
650228072Sbapt	int     nmatch = 0, lastr_val = 0, i;
651228072Sbapt
652228072Sbapt	*err_code = 0;
653228072Sbapt	*opt_offset = -1;
654228072Sbapt
655228072Sbapt	if (!optstart)
656228072Sbapt		return 0;
657228072Sbapt
658228072Sbapt	for (i = 0; i < s->optc; i++) {
659228072Sbapt		char   *optname;
660228072Sbapt
661228072Sbapt		optname =
662228072Sbapt			(char *) (s->options[i].opt_fmt +
663228072Sbapt				  (lookup_long ? 2 : 1));
664228072Sbapt
665228072Sbapt		if (lookup_long && (s->aux[i].flags & IS_LONG)) {
666228072Sbapt			if (len > s->aux[i].namelen)
667228072Sbapt				continue;
668228072Sbapt
669228072Sbapt			if (strncmp (optname, optstart, len) == 0) {
670228072Sbapt				nmatch++;
671228072Sbapt				*opt_offset = i;
672228072Sbapt
673228072Sbapt				/* exact match overrides all. */
674228072Sbapt				if (len == s->aux[i].namelen) {
675228072Sbapt					nmatch = 1;
676228072Sbapt					break;
677228072Sbapt				}
678228072Sbapt
679228072Sbapt				/* ambiguity is ok between aliases. */
680228072Sbapt				if (lastr_val
681228072Sbapt				    && lastr_val ==
682228072Sbapt				    s->options[i].r_val) nmatch--;
683228072Sbapt				lastr_val = s->options[i].r_val;
684228072Sbapt			}
685228072Sbapt		}
686228072Sbapt		else if (!lookup_long && !(s->aux[i].flags & IS_LONG)) {
687228072Sbapt			if (optname[0] == optstart[0]) {
688228072Sbapt				nmatch++;
689228072Sbapt				*opt_offset = i;
690228072Sbapt			}
691228072Sbapt		}
692228072Sbapt	}
693228072Sbapt
694228072Sbapt	if (nmatch == 0) {
695228072Sbapt		*err_code = SCANOPT_ERR_OPT_UNRECOGNIZED;
696228072Sbapt		*opt_offset = -1;
697228072Sbapt	}
698228072Sbapt	else if (nmatch > 1) {
699228072Sbapt		*err_code = SCANOPT_ERR_OPT_AMBIGUOUS;
700228072Sbapt		*opt_offset = -1;
701228072Sbapt	}
702228072Sbapt
703228072Sbapt	return *err_code ? 0 : 1;
704228072Sbapt}
705228072Sbapt
706228072Sbapt
707228072Sbaptint     scanopt (svoid, arg, optindex)
708228072Sbapt     scanopt_t *svoid;
709228072Sbapt     char  **arg;
710228072Sbapt     int    *optindex;
711228072Sbapt{
712228072Sbapt	char   *optname = NULL, *optarg = NULL, *pstart;
713228072Sbapt	int     namelen = 0, arglen = 0;
714228072Sbapt	int     errcode = 0, has_next;
715228072Sbapt	const optspec_t *optp;
716228072Sbapt	struct _scanopt_t *s;
717228072Sbapt	struct _aux *auxp;
718228072Sbapt	int     is_short;
719228072Sbapt	int     opt_offset = -1;
720228072Sbapt
721228072Sbapt	s = (struct _scanopt_t *) svoid;
722228072Sbapt
723228072Sbapt	/* Normalize return-parameters. */
724228072Sbapt	SAFE_ASSIGN (arg, NULL);
725228072Sbapt	SAFE_ASSIGN (optindex, s->index);
726228072Sbapt
727228072Sbapt	if (s->index >= s->argc)
728228072Sbapt		return 0;
729228072Sbapt
730228072Sbapt	/* pstart always points to the start of our current scan. */
731228072Sbapt	pstart = s->argv[s->index] + s->subscript;
732228072Sbapt	if (!pstart)
733228072Sbapt		return 0;
734228072Sbapt
735228072Sbapt	if (s->subscript == 0) {
736228072Sbapt
737228072Sbapt		/* test for exact match of "--" */
738228072Sbapt		if (pstart[0] == '-' && pstart[1] == '-' && !pstart[2]) {
739228072Sbapt			SAFE_ASSIGN (optindex, s->index + 1);
740228072Sbapt			INC_INDEX (s, 1);
741228072Sbapt			return 0;
742228072Sbapt		}
743228072Sbapt
744228072Sbapt		/* Match an opt. */
745228072Sbapt		if (matchlongopt
746228072Sbapt		    (pstart, &optname, &namelen, &optarg, &arglen)) {
747228072Sbapt
748228072Sbapt			/* it LOOKS like an opt, but is it one?! */
749228072Sbapt			if (!find_opt
750228072Sbapt			    (s, 1, optname, namelen, &errcode,
751228072Sbapt			     &opt_offset)) {
752228072Sbapt				scanopt_err (s, opt_offset, 0, errcode);
753228072Sbapt				return errcode;
754228072Sbapt			}
755228072Sbapt			/* We handle this below. */
756228072Sbapt			is_short = 0;
757228072Sbapt
758228072Sbapt			/* Check for short opt.  */
759228072Sbapt		}
760228072Sbapt		else if (pstart[0] == '-' && pstart[1]) {
761228072Sbapt			/* Pass through to below. */
762228072Sbapt			is_short = 1;
763228072Sbapt			s->subscript++;
764228072Sbapt			pstart++;
765228072Sbapt		}
766228072Sbapt
767228072Sbapt		else {
768228072Sbapt			/* It's not an option. We're done. */
769228072Sbapt			return 0;
770228072Sbapt		}
771228072Sbapt	}
772228072Sbapt
773228072Sbapt	/* We have to re-check the subscript status because it
774228072Sbapt	 * may have changed above. */
775228072Sbapt
776228072Sbapt	if (s->subscript != 0) {
777228072Sbapt
778228072Sbapt		/* we are somewhere in a run of short opts,
779228072Sbapt		 * e.g., at the 'z' in `tar -xzf` */
780228072Sbapt
781228072Sbapt		optname = pstart;
782228072Sbapt		namelen = 1;
783228072Sbapt		is_short = 1;
784228072Sbapt
785228072Sbapt		if (!find_opt
786228072Sbapt		    (s, 0, pstart, namelen, &errcode, &opt_offset)) {
787228072Sbapt			return scanopt_err (s, opt_offset, 1, errcode);
788228072Sbapt		}
789228072Sbapt
790228072Sbapt		optarg = pstart + 1;
791228072Sbapt		if (!*optarg) {
792228072Sbapt			optarg = NULL;
793228072Sbapt			arglen = 0;
794228072Sbapt		}
795228072Sbapt		else
796228072Sbapt			arglen = strlen (optarg);
797228072Sbapt	}
798228072Sbapt
799228072Sbapt	/* At this point, we have a long or short option matched at opt_offset into
800228072Sbapt	 * the s->options array (and corresponding aux array).
801228072Sbapt	 * A trailing argument is in {optarg,arglen}, if any.
802228072Sbapt	 */
803228072Sbapt
804228072Sbapt	/* Look ahead in argv[] to see if there is something
805228072Sbapt	 * that we can use as an argument (if needed). */
806228072Sbapt	has_next = s->index + 1 < s->argc
807228072Sbapt		&& strcmp ("--", s->argv[s->index + 1]) != 0;
808228072Sbapt
809228072Sbapt	optp = s->options + opt_offset;
810228072Sbapt	auxp = s->aux + opt_offset;
811228072Sbapt
812228072Sbapt	/* case: no args allowed */
813228072Sbapt	if (auxp->flags & ARG_NONE) {
814228072Sbapt		if (optarg && !is_short) {
815228072Sbapt			scanopt_err (s, opt_offset, is_short, errcode =
816228072Sbapt				     SCANOPT_ERR_ARG_NOT_ALLOWED);
817228072Sbapt			INC_INDEX (s, 1);
818228072Sbapt			return errcode;
819228072Sbapt		}
820228072Sbapt		else if (!optarg)
821228072Sbapt			INC_INDEX (s, 1);
822228072Sbapt		else
823228072Sbapt			s->subscript++;
824228072Sbapt		return optp->r_val;
825228072Sbapt	}
826228072Sbapt
827228072Sbapt	/* case: required */
828228072Sbapt	if (auxp->flags & ARG_REQ) {
829228072Sbapt		if (!optarg && !has_next)
830228072Sbapt			return scanopt_err (s, opt_offset, is_short,
831228072Sbapt					    SCANOPT_ERR_ARG_NOT_FOUND);
832228072Sbapt
833228072Sbapt		if (!optarg) {
834228072Sbapt			/* Let the next argv element become the argument. */
835228072Sbapt			SAFE_ASSIGN (arg, s->argv[s->index + 1]);
836228072Sbapt			INC_INDEX (s, 2);
837228072Sbapt		}
838228072Sbapt		else {
839228072Sbapt			SAFE_ASSIGN (arg, (char *) optarg);
840228072Sbapt			INC_INDEX (s, 1);
841228072Sbapt		}
842228072Sbapt		return optp->r_val;
843228072Sbapt	}
844228072Sbapt
845228072Sbapt	/* case: optional */
846228072Sbapt	if (auxp->flags & ARG_OPT) {
847228072Sbapt		SAFE_ASSIGN (arg, optarg);
848228072Sbapt		INC_INDEX (s, 1);
849228072Sbapt		return optp->r_val;
850228072Sbapt	}
851228072Sbapt
852228072Sbapt
853228072Sbapt	/* Should not reach here. */
854228072Sbapt	return 0;
855228072Sbapt}
856228072Sbapt
857228072Sbapt
858228072Sbaptint     scanopt_destroy (svoid)
859228072Sbapt     scanopt_t *svoid;
860228072Sbapt{
861228072Sbapt	struct _scanopt_t *s;
862228072Sbapt
863228072Sbapt	s = (struct _scanopt_t *) svoid;
864228072Sbapt	if (s) {
865228072Sbapt		if (s->aux)
866228072Sbapt			free (s->aux);
867228072Sbapt		free (s);
868228072Sbapt	}
869228072Sbapt	return 0;
870228072Sbapt}
871228072Sbapt
872228072Sbapt
873228072Sbapt/* vim:set tabstop=8 softtabstop=4 shiftwidth=4: */
874