1 /* $NetBSD: scanopt.c,v 1.6 2014/10/30 18:44:05 christos Exp $ */
3 /* flex - tool to generate fast lexical analyzers */
5 /* Copyright (c) 1990 The Regents of the University of California. */
6 /* All rights reserved. */
8 /* This code is derived from software contributed to Berkeley by */
11 /* The United States Government has rights in this work pursuant */
12 /* to contract no. DE-AC03-76SF00098 between the United States */
13 /* Department of Energy and the University of California. */
15 /* This file is part of flex. */
17 /* Redistribution and use in source and binary forms, with or without */
18 /* modification, are permitted provided that the following conditions */
21 /* 1. Redistributions of source code must retain the above copyright */
22 /* notice, this list of conditions and the following disclaimer. */
23 /* 2. Redistributions in binary form must reproduce the above copyright */
24 /* notice, this list of conditions and the following disclaimer in the */
25 /* documentation and/or other materials provided with the distribution. */
27 /* Neither the name of the University nor the names of its contributors */
28 /* may be used to endorse or promote products derived from this software */
29 /* without specific prior written permission. */
31 /* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */
32 /* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */
33 /* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */
36 __RCSID("$NetBSD: scanopt.c,v 1.6 2014/10/30 18:44:05 christos Exp $");
41 /* Internal structures */
43 #ifdef HAVE_STRCASECMP
44 #define STRCASECMP(a,b) strcasecmp(a,b)
46 static int STRCASECMP
PROTO ((const char *, const char *));
48 static int STRCASECMP (a
, b
)
52 while (tolower ((unsigned char)*a
++) == tolower ((unsigned char)*b
++)) ;
63 int flags
; /* The above hex flags. */
64 int namelen
; /* Length of the actual option word, e.g., "--file[=foo]" is 4 */
65 int printlen
; /* Length of entire string, e.g., "--file[=foo]" is 12 */
70 const optspec_t
*options
; /* List of options. */
71 struct _aux
*aux
; /* Auxiliary data about options. */
72 int optc
; /* Number of options. */
73 int argc
; /* Number of args. */
74 char **argv
; /* Array of strings. */
75 int index
; /* Used as: argv[index][subscript]. */
77 char no_err_msg
; /* If true, do not print errors. */
82 /* Accessor functions. These WOULD be one-liners, but portability calls. */
83 static const char *NAME
PROTO ((struct _scanopt_t
*, int));
84 static int PRINTLEN
PROTO ((struct _scanopt_t
*, int));
85 static int RVAL
PROTO ((struct _scanopt_t
*, int));
86 static int FLAGS
PROTO ((struct _scanopt_t
*, int));
87 static const char *DESC
PROTO ((struct _scanopt_t
*, int));
88 static int scanopt_err
PROTO ((struct _scanopt_t
*, int, int, int));
89 static int matchlongopt
PROTO ((char *, char **, int *, char **, int *));
91 PROTO ((struct _scanopt_t
*, int, char *, int, int *, int *opt_offset
));
93 static const char *NAME (s
, i
)
97 return s
->options
[i
].opt_fmt
+
98 ((s
->aux
[i
].flags
& IS_LONG
) ? 2 : 1);
101 static int PRINTLEN (s
, i
)
102 struct _scanopt_t
*s
;
105 return s
->aux
[i
].printlen
;
108 static int RVAL (s
, i
)
109 struct _scanopt_t
*s
;
112 return s
->options
[i
].r_val
;
115 static int FLAGS (s
, i
)
116 struct _scanopt_t
*s
;
119 return s
->aux
[i
].flags
;
122 static const char *DESC (s
, i
)
123 struct _scanopt_t
*s
;
126 return s
->options
[i
].desc
? s
->options
[i
].desc
: "";
129 #ifndef NO_SCANOPT_USAGE
130 static int get_cols
PROTO ((void));
132 static int get_cols ()
135 int cols
= 80; /* default */
137 #ifdef HAVE_NCURSES_H
144 if ((env
= getenv ("COLUMNS")) != NULL
)
151 /* Macro to check for NULL before assigning a value. */
152 #define SAFE_ASSIGN(ptr,val) \
158 /* Macro to assure we reset subscript whenever we adjust s->index.*/
159 #define INC_INDEX(s,n) \
165 scanopt_t
*scanopt_init (options
, argc
, argv
, flags
)
166 const optspec_t
*options
;
172 struct _scanopt_t
*s
;
173 s
= (struct _scanopt_t
*) malloc (sizeof (struct _scanopt_t
));
175 s
->options
= options
;
178 s
->argv
= (char **) argv
;
181 s
->no_err_msg
= (flags
& SCANOPT_NO_ERR_MSG
);
185 /* Determine option count. (Find entry with all zeros). */
187 while (options
[s
->optc
].opt_fmt
188 || options
[s
->optc
].r_val
|| options
[s
->optc
].desc
)
191 /* Build auxiliary data */
192 s
->aux
= (struct _aux
*) malloc (s
->optc
* sizeof (struct _aux
));
194 for (i
= 0; i
< s
->optc
; i
++) {
195 const Char
*p
, *pname
;
196 const struct optspec_t
*opt
;
199 opt
= s
->options
+ i
;
202 aux
->flags
= ARG_NONE
;
204 if (opt
->opt_fmt
[0] == '-' && opt
->opt_fmt
[1] == '-') {
205 aux
->flags
|= IS_LONG
;
206 pname
= (const Char
*)(opt
->opt_fmt
+ 2);
210 pname
= (const Char
*)(opt
->opt_fmt
+ 1);
213 aux
->printlen
= strlen (opt
->opt_fmt
);
216 for (p
= pname
+ 1; *p
; p
++) {
217 /* detect required arg */
218 if (*p
== '=' || isspace ((unsigned char)*p
)
219 || !(aux
->flags
& IS_LONG
)) {
220 if (aux
->namelen
== 0)
221 aux
->namelen
= p
- pname
;
222 aux
->flags
|= ARG_REQ
;
223 aux
->flags
&= ~ARG_NONE
;
225 /* detect optional arg. This overrides required arg. */
227 if (aux
->namelen
== 0)
228 aux
->namelen
= p
- pname
;
229 aux
->flags
&= ~(ARG_REQ
| ARG_NONE
);
230 aux
->flags
|= ARG_OPT
;
234 if (aux
->namelen
== 0)
235 aux
->namelen
= p
- pname
;
237 return (scanopt_t
*) s
;
240 #ifndef NO_SCANOPT_USAGE
241 /* these structs are for scanopt_usage(). */
244 struct usg_elem
*next
;
245 struct usg_elem
*alias
;
247 typedef struct usg_elem usg_elem
;
250 /* Prints a usage message based on contents of optlist.
252 * scanner - The scanner, already initialized with scanopt_init().
253 * fp - The file stream to write to.
254 * usage - Text to be prepended to option list.
255 * Return: Always returns 0 (zero).
256 * The output looks something like this:
258 [indent][option, alias1, alias2...][indent][description line1
259 description line2...]
261 int scanopt_usage (scanner
, fp
, usage
)
266 struct _scanopt_t
*s
;
267 int i
, columns
, indent
= 2;
268 usg_elem
*byr_val
= NULL
; /* option indices sorted by r_val */
269 usg_elem
*store
; /* array of preallocated elements. */
279 s
= (struct _scanopt_t
*) scanner
;
282 fprintf (fp
, "%s\n", usage
);
285 /* Find the basename of argv[0] */
288 p
= s
->argv
[0] + strlen (s
->argv
[0]);
289 while (p
!= s
->argv
[0] && *p
!= '/')
294 fprintf (fp
, _("Usage: %s [OPTIONS]...\n"), p
);
298 /* Sort by r_val and string. Yes, this is O(n*n), but n is small. */
299 store
= (usg_elem
*) malloc (s
->optc
* sizeof (usg_elem
));
300 for (i
= 0; i
< s
->optc
; i
++) {
302 /* grab the next preallocate node. */
303 ue
= store
+ store_idx
++;
305 ue
->next
= ue
->alias
= NULL
;
307 /* insert into list. */
312 usg_elem
**ue_curr
, **ptr_if_no_alias
= NULL
;
316 if (RVAL (s
, (*ue_curr
)->idx
) ==
318 /* push onto the alias list. */
319 ue_curr
= &((*ue_curr
)->alias
);
325 STRCASECMP (NAME (s
, (*ue_curr
)->idx
),
326 NAME (s
, ue
->idx
)) > 0) {
327 ptr_if_no_alias
= ue_curr
;
329 ue_curr
= &((*ue_curr
)->next
);
331 if (!found_alias
&& ptr_if_no_alias
)
332 ue_curr
= ptr_if_no_alias
;
340 printf ("ORIGINAL:\n");
341 for (i
= 0; i
< s
->optc
; i
++)
342 printf ("%2d: %s\n", i
, NAME (s
, i
));
343 printf ("SORTED:\n");
348 printf ("%2d: %s\n", ue
->idx
, NAME (s
, ue
->idx
));
349 for (ue2
= ue
->alias
; ue2
; ue2
= ue2
->next
)
350 printf (" +---> %2d: %s\n", ue2
->idx
,
357 /* Now build each row of output. */
359 /* first pass calculate how much room we need. */
360 for (ue
= byr_val
; ue
; ue
= ue
->next
) {
363 int nshort
= 0, nlong
= 0;
366 #define CALC_LEN(i) do {\
367 if(FLAGS(s,i) & IS_LONG) \
368 len += (nlong++||nshort) ? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\
370 len += (nshort++||nlong)? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\
373 if (!(FLAGS (s
, ue
->idx
) & IS_LONG
))
376 /* do short aliases first. */
377 for (ap
= ue
->alias
; ap
; ap
= ap
->next
) {
378 if (FLAGS (s
, ap
->idx
) & IS_LONG
)
383 if (FLAGS (s
, ue
->idx
) & IS_LONG
)
386 /* repeat the above loop, this time for long aliases. */
387 for (ap
= ue
->alias
; ap
; ap
= ap
->next
) {
388 if (!(FLAGS (s
, ap
->idx
) & IS_LONG
))
396 /* It's much easier to calculate length for description column! */
397 len
= strlen (DESC (s
, ue
->idx
));
402 /* Determine how much room we have, and how much we will allocate to each col.
403 * Do not address pathological cases. Output will just be ugly. */
404 columns
= get_cols () - 1;
405 if (maxlen
[0] + maxlen
[1] + indent
* 2 > columns
) {
406 /* col 0 gets whatever it wants. we'll wrap the desc col. */
407 maxlen
[1] = columns
- (maxlen
[0] + indent
* 2);
408 if (maxlen
[1] < 14) /* 14 is arbitrary lower limit on desc width. */
411 desccol
= maxlen
[0] + indent
* 2;
413 #define PRINT_SPACES(fp,n)\
422 /* Second pass (same as above loop), this time we print. */
423 /* Sloppy hack: We iterate twice. The first time we print short and long options.
424 The second time we print those lines that have ONLY long options. */
425 while (print_run
++ < 2) {
426 for (ue
= byr_val
; ue
; ue
= ue
->next
) {
428 int nwords
= 0, nchars
= 0, has_short
= 0;
430 /* TODO: get has_short schtick to work */
431 has_short
= !(FLAGS (s
, ue
->idx
) & IS_LONG
);
432 for (ap
= ue
->alias
; ap
; ap
= ap
->next
) {
433 if (!(FLAGS (s
, ap
->idx
) & IS_LONG
)) {
438 if ((print_run
== 1 && !has_short
) ||
439 (print_run
== 2 && has_short
))
442 PRINT_SPACES (fp
, indent
);
445 /* Print, adding a ", " between aliases. */
446 #define PRINT_IT(i) do{\
448 nchars+=fprintf(fp,", ");\
449 nchars+=fprintf(fp,"%s",s->options[i].opt_fmt);\
452 if (!(FLAGS (s
, ue
->idx
) & IS_LONG
))
455 /* print short aliases first. */
456 for (ap
= ue
->alias
; ap
; ap
= ap
->next
) {
457 if (!(FLAGS (s
, ap
->idx
) & IS_LONG
))
462 if (FLAGS (s
, ue
->idx
) & IS_LONG
)
465 /* repeat the above loop, this time for long aliases. */
466 for (ap
= ue
->alias
; ap
; ap
= ap
->next
) {
467 if (FLAGS (s
, ap
->idx
) & IS_LONG
)
472 PRINT_SPACES (fp
, desccol
- nchars
);
474 /* Print description, wrapped to maxlen[1] columns. */
478 pstart
= DESC (s
, ue
->idx
);
481 const char *lastws
= NULL
, *p
;
485 while (*p
&& n
< maxlen
[1]
487 if (isspace ((Char
)(*p
))
488 || *p
== '-') lastws
=
494 if (!*p
) { /* hit end of desc. done. */
499 else if (*p
== '\n') { /* print everything up to here then wrap. */
500 fprintf (fp
, "%.*s\n", n
,
502 PRINT_SPACES (fp
, desccol
);
506 else { /* we hit the edge of the screen. wrap at space if possible. */
510 (int)(lastws
- pstart
),
522 PRINT_SPACES (fp
, desccol
);
532 #endif /* no scanopt_usage */
535 static int scanopt_err (s
, opt_offset
, is_short
, err
)
536 struct _scanopt_t
*s
;
541 const char *optname
= "";
544 if (!s
->no_err_msg
) {
546 if (s
->index
> 0 && s
->index
< s
->argc
) {
549 s
->argv
[s
->index
][s
->subscript
];
554 optname
= s
->argv
[s
->index
];
558 fprintf (stderr
, "%s: ", s
->argv
[0]);
560 case SCANOPT_ERR_ARG_NOT_ALLOWED
:
563 ("option `%s' doesn't allow an argument\n"),
566 case SCANOPT_ERR_ARG_NOT_FOUND
:
568 _("option `%s' requires an argument\n"),
571 case SCANOPT_ERR_OPT_AMBIGUOUS
:
572 fprintf (stderr
, _("option `%s' is ambiguous\n"),
575 case SCANOPT_ERR_OPT_UNRECOGNIZED
:
576 fprintf (stderr
, _("Unrecognized option `%s'\n"),
580 fprintf (stderr
, _("Unknown error=(%d)\n"), err
);
588 /* Internal. Match str against the regex ^--([^=]+)(=(.*))?
589 * return 1 if *looks* like a long option.
590 * 'str' is the only input argument, the rest of the arguments are output only.
591 * optname will point to str + 2
594 static int matchlongopt (str
, optname
, optlen
, arg
, arglen
)
603 *optname
= *arg
= (char *) 0;
604 *optlen
= *arglen
= 0;
606 /* Match regex /--./ */
608 if (p
[0] != '-' || p
[1] != '-' || !p
[2])
612 *optname
= (char *) p
;
614 /* find the end of optname */
615 while (*p
&& *p
!= '=')
618 *optlen
= p
- *optname
;
621 /* an option with no '=...' part. */
625 /* We saw an '=' char. The rest of p is the arg. */
636 /* Internal. Look up long or short option by name.
637 * Long options must match a non-ambiguous prefix, or exact match.
638 * Short options must be exact.
639 * Return boolean true if found and no error.
640 * Error stored in err_code or zero if no error. */
641 static int find_opt (s
, lookup_long
, optstart
, len
, err_code
, opt_offset
)
642 struct _scanopt_t
*s
;
649 int nmatch
= 0, lastr_val
= 0, i
;
657 for (i
= 0; i
< s
->optc
; i
++) {
661 (char *) (s
->options
[i
].opt_fmt
+
662 (lookup_long
? 2 : 1));
664 if (lookup_long
&& (s
->aux
[i
].flags
& IS_LONG
)) {
665 if (len
> s
->aux
[i
].namelen
)
668 if (strncmp (optname
, optstart
, len
) == 0) {
672 /* exact match overrides all. */
673 if (len
== s
->aux
[i
].namelen
) {
678 /* ambiguity is ok between aliases. */
681 s
->options
[i
].r_val
) nmatch
--;
682 lastr_val
= s
->options
[i
].r_val
;
685 else if (!lookup_long
&& !(s
->aux
[i
].flags
& IS_LONG
)) {
686 if (optname
[0] == optstart
[0]) {
694 *err_code
= SCANOPT_ERR_OPT_UNRECOGNIZED
;
697 else if (nmatch
> 1) {
698 *err_code
= SCANOPT_ERR_OPT_AMBIGUOUS
;
702 return *err_code
? 0 : 1;
706 int scanopt (svoid
, arg
, optindex
)
711 char *optname
= NULL
, *optarg
= NULL
, *pstart
;
712 int namelen
= 0, arglen
= 0;
713 int errcode
= 0, has_next
;
714 const optspec_t
*optp
;
715 struct _scanopt_t
*s
;
720 /* triggers a 'may be used uninitialized', when compiled with gcc,
721 * asserts off, and -Os. */
723 #endif /* defined(__minix) */
725 s
= (struct _scanopt_t
*) svoid
;
727 /* Normalize return-parameters. */
728 SAFE_ASSIGN (arg
, NULL
);
729 SAFE_ASSIGN (optindex
, s
->index
);
731 if (s
->index
>= s
->argc
)
734 /* pstart always points to the start of our current scan. */
735 pstart
= s
->argv
[s
->index
] + s
->subscript
;
739 if (s
->subscript
== 0) {
741 /* test for exact match of "--" */
742 if (pstart
[0] == '-' && pstart
[1] == '-' && !pstart
[2]) {
743 SAFE_ASSIGN (optindex
, s
->index
+ 1);
750 (pstart
, &optname
, &namelen
, &optarg
, &arglen
)) {
752 /* it LOOKS like an opt, but is it one?! */
754 (s
, 1, optname
, namelen
, &errcode
,
756 scanopt_err (s
, opt_offset
, 0, errcode
);
759 /* We handle this below. */
762 /* Check for short opt. */
764 else if (pstart
[0] == '-' && pstart
[1]) {
765 /* Pass through to below. */
772 /* It's not an option. We're done. */
777 /* We have to re-check the subscript status because it
778 * may have changed above. */
780 if (s
->subscript
!= 0) {
782 /* we are somewhere in a run of short opts,
783 * e.g., at the 'z' in `tar -xzf` */
790 (s
, 0, pstart
, namelen
, &errcode
, &opt_offset
)) {
791 return scanopt_err (s
, opt_offset
, 1, errcode
);
800 arglen
= strlen (optarg
);
803 /* At this point, we have a long or short option matched at opt_offset into
804 * the s->options array (and corresponding aux array).
805 * A trailing argument is in {optarg,arglen}, if any.
808 /* Look ahead in argv[] to see if there is something
809 * that we can use as an argument (if needed). */
810 has_next
= s
->index
+ 1 < s
->argc
811 && strcmp ("--", s
->argv
[s
->index
+ 1]) != 0;
813 optp
= s
->options
+ opt_offset
;
814 auxp
= s
->aux
+ opt_offset
;
816 /* case: no args allowed */
817 if (auxp
->flags
& ARG_NONE
) {
818 if (optarg
&& !is_short
) {
819 scanopt_err (s
, opt_offset
, is_short
, errcode
=
820 SCANOPT_ERR_ARG_NOT_ALLOWED
);
832 if (auxp
->flags
& ARG_REQ
) {
833 if (!optarg
&& !has_next
)
834 return scanopt_err (s
, opt_offset
, is_short
,
835 SCANOPT_ERR_ARG_NOT_FOUND
);
838 /* Let the next argv element become the argument. */
839 SAFE_ASSIGN (arg
, s
->argv
[s
->index
+ 1]);
843 SAFE_ASSIGN (arg
, (char *) optarg
);
850 if (auxp
->flags
& ARG_OPT
) {
851 SAFE_ASSIGN (arg
, optarg
);
857 /* Should not reach here. */
862 int scanopt_destroy (svoid
)
865 struct _scanopt_t
*s
;
867 s
= (struct _scanopt_t
*) svoid
;
877 /* vim:set tabstop=8 softtabstop=4 shiftwidth=4: */