summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormillaway <millaway>2001-10-13 00:44:01 +0000
committermillaway <millaway>2001-10-13 00:44:01 +0000
commitf1898377bd36ff56829266d423aba8fe3d984579 (patch)
tree60252575d588ec4cf099e77fdb00b0f41d01b0ee
parentca1dbf6aeaf3179add162e1112e8524e4827fdc4 (diff)
downloadflex-f1898377bd36ff56829266d423aba8fe3d984579.tar.gz
Added long option parsing.
All positive %options are now accessible from command line. Added option -D, (like gcc's -D) to define a preprocessor symbol. Added option --header=FILE to specify a C .h file to generate. Options -n and -c, previously deprecated, now cause flex to abort. Options are now parsed left to right.
-rw-r--r--Makefile.am10
-rw-r--r--flexdef.h6
-rw-r--r--main.c521
-rw-r--r--options.c78
-rw-r--r--options.h58
-rw-r--r--scanopt.c801
-rw-r--r--scanopt.h128
7 files changed, 1358 insertions, 244 deletions
diff --git a/Makefile.am b/Makefile.am
index 79795cc..d9f9883 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -57,7 +57,9 @@ flex_SOURCES = \
skel.c \
sym.c \
tblcmp.c \
- yylex.c
+ yylex.c \
+ options.c \
+ scanopt.c
libfl_a_SOURCES = \
libmain.c \
@@ -66,7 +68,9 @@ libfl_a_SOURCES = \
noinst_HEADERS = \
flexdef.h \
parse.h \
- version.h
+ version.h \
+ options.h \
+ scanopt.h
include_HEADERS = \
FlexLexer.h
@@ -111,6 +115,8 @@ parse.o: parse.c flexdef.h config.h
skel.o: skel.c flexdef.h config.h
sym.o: sym.c flexdef.h config.h
tblcmp.o: tblcmp.c flexdef.h config.h
+options.o: options.c options.h scanopt.h flexdef.h config.h
+scanopt.o: scanopt.c scanopt.h flexdef.h config.h
#The below recipe for making the ChangeLog will only work if you have a copy of the cvs tree handy so let it fail gracefully.
diff --git a/flexdef.h b/flexdef.h
index 0ccc5d9..983d7e5 100644
--- a/flexdef.h
+++ b/flexdef.h
@@ -31,8 +31,12 @@
/* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */
/* PURPOSE. */
+#ifndef FLEXDEF_H
+#define FLEXDEF_H 1
+
#include <stdio.h>
#include <ctype.h>
+#include <limits.h>
#include "config.h"
@@ -1022,3 +1026,5 @@ extern void stack1 PROTO((int, int, int, int));
/* from file yylex.c */
extern int yylex PROTO((void));
+
+#endif /* not defined FLEXDEF_H */
diff --git a/main.c b/main.c
index ea1785f..5b771f0 100644
--- a/main.c
+++ b/main.c
@@ -39,6 +39,7 @@ char copyright[] =
#include "flexdef.h"
#include "version.h"
+#include "options.h"
static char flex_version[] = FLEX_VERSION;
@@ -244,10 +245,10 @@ void check_options()
yytext_is_array = false;
}
- if ( C_plus_plus && (reentrant || reentrant_bison_pure) )
+ if ( C_plus_plus && (reentrant || reentrant_bison_pure) )
flexerror( _( "Options -+ and -R are mutually exclusive." ) );
-
-
+
+
if ( useecs )
{ /* Set up doubly-linked equivalence classes. */
@@ -362,7 +363,7 @@ void check_options()
if ( do_yylineno )
GEN_PREFIX( "lineno" );
-
+
if ( do_yylineno && reentrant)
outn ( "#define YY_USE_LINENO 1");
}
@@ -461,12 +462,12 @@ int exit_status;
putc( 'p', stderr );
if ( performance_report > 1 )
putc( 'p', stderr );
- if ( spprdflt )
+ if ( spprdflt )
putc( 's', stderr );
- if ( reentrant )
+ if ( reentrant )
{
putc( 'R', stderr );
-
+
if( reentrant_bison_pure )
putc( 'b', stderr );
}
@@ -629,8 +630,9 @@ void flexinit( argc, argv )
int argc;
char **argv;
{
- int i, sawcmpflag;
+ int i, sawcmpflag, rv, optind;
char *arg;
+ scanopt_t sopt;
printstats = syntaxerror = trace = spprdflt = caseins = false;
lex_compat = C_plus_plus = backing_up_report = ddebug = fulltbl = false;
@@ -662,240 +664,275 @@ char **argv;
C_plus_plus = true;
/* read flags */
- for ( --argc, ++argv; argc ; --argc, ++argv )
- {
- arg = argv[0];
-
- /* Stop at first non-option. */
- if ( arg[0] != '-' || arg[1] == '\0' )
- break;
-
- if ( arg[1] == '-' )
- { /* --option */
- if ( ! strcmp( arg, "--help" ) )
- arg = "-h";
-
- else if ( ! strcmp( arg, "--version" ) )
- arg = "-V";
-
- else if ( ! strcmp( arg, "--" ) )
- { /* end of options */
- --argc;
- ++argv;
- break;
- }
- }
+ sopt = scanopt_init(flexopts, argc, argv, 0);
+ if (!sopt) {
+ /* This will only happen when flexopts array is altered. */
+ fprintf(stderr,
+ _("Internal error. flexopts are malformed.\n"));
+ exit(1);
+ }
+
+ while((rv=scanopt(sopt, &arg, &optind)) != 0){
+
+ if (rv < 0) {
+ /* Scanopt has already printed an option-specific error message. */
+ fprintf( stderr, _( "For usage, try\n\t%s --help\n" ),
+ program_name );
+ exit( 1 );
+ break;
+ }
- for ( i = 1; arg[i] != '\0'; ++i )
- switch ( arg[i] )
- {
- case '+':
- C_plus_plus = true;
- break;
-
- case 'B':
- interactive = false;
- break;
-
- case 'b':
- backing_up_report = true;
- break;
-
- case 'c':
- break;
-
- case 'C':
- if ( i != 1 )
- flexerror(
- _( "-C flag must be given separately" ) );
-
- if ( ! sawcmpflag )
- {
- useecs = false;
- usemecs = false;
- fulltbl = false;
- sawcmpflag = true;
- }
-
- for ( ++i; arg[i] != '\0'; ++i )
- switch ( arg[i] )
- {
- case 'a':
- long_align =
- true;
- break;
-
- case 'e':
- useecs = true;
- break;
-
- case 'F':
- fullspd = true;
- break;
-
- case 'f':
- fulltbl = true;
- break;
-
- case 'm':
- usemecs = true;
- break;
-
- case 'r':
- use_read = true;
- break;
-
- default:
- lerrif(
- _( "unknown -C option '%c'" ),
- (int) arg[i] );
- break;
- }
-
- goto get_next_arg;
-
- case 'd':
- ddebug = true;
- break;
-
- case 'f':
- useecs = usemecs = false;
- use_read = fulltbl = true;
- break;
-
- case 'F':
- useecs = usemecs = false;
- use_read = fullspd = true;
- break;
-
- case '?':
- case 'h':
- usage();
- exit( 0 );
-
- case 'I':
- interactive = true;
- break;
-
- case 'i':
- caseins = true;
- break;
-
- case 'l':
- lex_compat = true;
- break;
-
- case 'L':
- gen_line_dirs = false;
- break;
-
- case 'n':
- /* Stupid do-nothing deprecated
- * option.
- */
- break;
-
- case 'o':
- if ( i != 1 )
- flexerror(
- _( "-o flag must be given separately" ) );
-
- outfilename = arg + i + 1;
- did_outfilename = 1;
- goto get_next_arg;
-
- case 'P':
- if ( i != 1 )
- flexerror(
- _( "-P flag must be given separately" ) );
-
- prefix = arg + i + 1;
- goto get_next_arg;
-
- case 'p':
- ++performance_report;
- break;
-
- case 'R':
- if ( i != 1 )
- flexerror(
- _( "-R flag must be given separately" ) );
+ switch ((enum flexopt_flag_t)rv){
+ case OPT_CPLUSPLUS:
+ C_plus_plus = true;
+ break;
+
+ case OPT_BATCH:
+ interactive = false;
+ break;
+
+ case OPT_BACKUP:
+ backing_up_report = true;
+ break;
+
+ case OPT_DEPRECATED:
+ flexerror(_("Options -c and -n are deprecated. Do not use."));
+ break;
+
+ case OPT_COMPRESSION:
+ if ( ! sawcmpflag )
+ {
+ useecs = false;
+ usemecs = false;
+ fulltbl = false;
+ sawcmpflag = true;
+ }
+
+ for( i=0 ; arg && arg[i] != '\0'; i++)
+ switch ( arg[i] )
+ {
+ case 'a':
+ long_align = true;
+ break;
+
+ case 'e':
+ useecs = true;
+ break;
+
+ case 'F':
+ fullspd = true;
+ break;
+
+ case 'f':
+ fulltbl = true;
+ break;
+
+ case 'm':
+ usemecs = true;
+ break;
+
+ case 'r':
+ use_read = true;
+ break;
+
+ default:
+ lerrif(
+ _( "unknown -C option '%c'" ),
+ (int) arg[i] );
+ break;
+ }
+ break;
+
+ case OPT_DEBUG:
+ ddebug = true;
+ break;
+
+ case OPT_FULL:
+ useecs = usemecs = false;
+ use_read = fulltbl = true;
+ break;
+
+ case OPT_FAST:
+ useecs = usemecs = false;
+ use_read = fullspd = true;
+ break;
+
+ case OPT_HELP:
+ usage();
+ exit( 0 );
+
+ case OPT_INTERACTIVE:
+ interactive = true;
+ break;
+
+ case OPT_CASE_INSENSITIVE:
+ caseins = true;
+ break;
+
+ case OPT_LEX_COMPAT:
+ lex_compat = true;
+ break;
+
+ case OPT_MAIN:
+ action_define( "YY_MAIN", 1);
+ break;
+
+ case OPT_NOLINE:
+ gen_line_dirs = false;
+ break;
+
+ case OPT_OUTFILE:
+ outfilename = arg;
+ did_outfilename = 1;
+ break;
+
+ case OPT_PREFIX:
+ prefix = arg;
+ break;
+
+ case OPT_PERF_REPORT:
+ ++performance_report;
+ break;
+
+ case OPT_REENTRANT_BISON:
+ reentrant = true;
+ reentrant_bison_pure = true;
+ break;
+
+ case OPT_REENTRANT:
reentrant = true;
-
- /* Optional arguments follow -R */
-
- for ( ++i; arg[i] != '\0'; ++i )
- switch ( arg[i] )
- {
- case 'b':
- reentrant_bison_pure = true;
- break;
-
- default:
- lerrif(
- _( "unknown -R option '%c'" ),
- (int) arg[i] );
- break;
- }
- goto get_next_arg;
-
- case 'S':
- if ( i != 1 )
- flexerror(
- _( "-S flag must be given separately" ) );
-
- skelname = arg + i + 1;
- goto get_next_arg;
-
- case 's':
- spprdflt = true;
- break;
-
- case 't':
- use_stdout = true;
- break;
-
- case 'T':
- trace = true;
- break;
-
- case 'v':
- printstats = true;
- break;
-
- case 'V':
- printf( _( "%s version %s\n" ),
- program_name, flex_version );
- exit( 0 );
-
- case 'w':
- nowarn = true;
- break;
-
- case '7':
- csize = 128;
- break;
-
- case '8':
- csize = CSIZE;
- break;
-
- default:
- fprintf( stderr,
- _( "%s: unknown flag '%c'. For usage, try\n\t%s --help\n" ),
- program_name, (int) arg[i],
- program_name );
- exit( 1 );
- }
-
- /* Used by -C, -S, -o, and -P flags in lieu of a "continue 2"
- * control.
- */
- get_next_arg: ;
- }
- num_input_files = argc;
- input_files = argv;
+ /* Optional 'b' follows -R */
+ if (arg) {
+ if (strcmp(arg,"b")==0)
+ reentrant_bison_pure = true;
+ else
+ lerrif(_( "unknown -R option '%c'" ),(int)arg[i]);
+ }
+ break;
+
+ case OPT_SKEL:
+ skelname = arg;
+ break;
+
+ case OPT_DEFAULT:
+ spprdflt = false;
+ break;
+
+ case OPT_NODEFAULT:
+ spprdflt = true;
+ break;
+
+ case OPT_STDOUT:
+ use_stdout = true;
+ break;
+
+ case OPT_TRACE:
+ trace = true;
+ break;
+
+ case OPT_VERBOSE:
+ printstats = true;
+ break;
+
+ case OPT_VERSION:
+ printf( _( "%s version %s\n" ),
+ program_name, flex_version );
+ exit( 0 );
+
+ case OPT_NOWARN:
+ nowarn = true;
+ break;
+
+ case OPT_7BIT:
+ csize = 128;
+ break;
+
+ case OPT_8BIT:
+ csize = CSIZE;
+ break;
+
+ case OPT_ALIGN:
+ long_align = true;
+ break;
+
+ case OPT_ALWAYS_INTERACTIVE:
+ action_define("YY_ALWAYS_INTERACTIVE", 1);
+ break;
+
+ case OPT_NEVER_INTERACTIVE:
+ action_define( "YY_NEVER_INTERACTIVE", 1 );
+ break;
+
+ case OPT_ARRAY:
+ yytext_is_array = true;
+ break;
+
+ case OPT_POINTER:
+ yytext_is_array = false;
+ break;
+
+ case OPT_ECS:
+ useecs = true;
+ break;
+
+ case OPT_HEADER: /*TODO*/
+ break;
+
+ case OPT_META_ECS:
+ usemecs = true;
+
+
+ case OPT_PREPROCDEFINE:
+ {
+ /* arg is "symbol" or "symbol=definition".
+ Replace the first '=' with ' '.
+ I'm not sure that argv[] is writable. So use a copy. */
+ char *sym, *p;
+ sym = (char*)malloc(strlen("#define ")+strlen(arg)+1);
+ sprintf(sym,"#define %s\n",arg);
+ for(p=sym; *p != '\0'; ++p)
+ if (*p == '='){
+ *p = ' ';
+ break;
+ }
+
+ add_action(sym);
+ free(sym);
+ }
+ break;
+
+ case OPT_READ:
+ use_read = true;
+ break;
+
+ case OPT_STACK:
+ action_define( "YY_STACK_USED", 1 );
+ break;
+
+ case OPT_STDINIT:
+ do_stdinit = true;
+ break;
+
+ case OPT_YYCLASS:
+ yyclass = arg;
+ break;
+
+ case OPT_YYLINENO:
+ do_yylineno = true;
+ break;
+
+ case OPT_YYWRAP:
+ do_yywrap = true;
+ break;
+
+ } /* switch */
+ } /* while scanopt() */
+
+ scanopt_destroy(sopt);
+
+ num_input_files = argc - optind;
+ input_files = argv + optind;
set_input_file( num_input_files > 0 ? input_files[0] : NULL );
lastccl = lastsc = lastdfa = lastnfa = 0;
@@ -1040,7 +1077,7 @@ _( "Variable trailing context rules entail a large performance penalty\n" ) );
}
else
- {
+ {
/* In reentrant scanner, stdinit is handled in flex.skl. */
if ( do_stdinit )
{
@@ -1065,7 +1102,7 @@ _( "Variable trailing context rules entail a large performance penalty\n" ) );
outn( "#endif" );
}
- else
+ else
{
outn( "#ifndef YY_REENTRANT" );
outn( yy_nostdinit );
@@ -1102,7 +1139,7 @@ _( "Variable trailing context rules entail a large performance penalty\n" ) );
"\tLexerError( \"yyFlexLexer::yylex invoked but %option yyclass used\" );" );
outn( "\treturn 0;" );
outn( "\t}" );
-
+
out_str( "\n#define YY_DECL int %s::yylex()\n",
yyclass );
}
diff --git a/options.c b/options.c
new file mode 100644
index 0000000..bd0af32
--- /dev/null
+++ b/options.c
@@ -0,0 +1,78 @@
+#include "options.h"
+
+optspec_t flexopts[] = {
+
+{"--7bit", OPT_7BIT,0},/* Generate 7-bit scanner. */
+{"-7", OPT_7BIT,0},
+{"--8bit", OPT_8BIT,0},/* Generate 8-bit scanner. */
+{"-8", OPT_8BIT,0},
+{"--align", OPT_ALIGN,0},/* Trade off larger tables for better memory alignment. */
+{"--always-interactive",OPT_ALWAYS_INTERACTIVE,0},
+{"--array", OPT_ARRAY,0},
+{"--backup", OPT_BACKUP,0},/* Generate backing-up information to lex.backup. */
+{"-b", OPT_BACKUP,0},
+{"--batch", OPT_BATCH,0},/* Generate batch scanner (opposite of -I). */
+{"-B", OPT_BATCH,0},
+{"--case-insensitive", OPT_CASE_INSENSITIVE,0},/* Generate case-insensitive scanner. */
+{"-i", OPT_CASE_INSENSITIVE,0},
+{"-C[aefFmr]", OPT_COMPRESSION,"Specify degree of table compression (default is -Cem)"},
+{"--c++", OPT_CPLUSPLUS,0},/* Generate C++ scanner class. */
+{"-+", OPT_CPLUSPLUS,0},
+{"--debug", OPT_DEBUG,0},/* Turn on debug mode in generated scanner. */
+{"-d", OPT_DEBUG,0},
+{"--default", OPT_DEFAULT,0},
+{"-c", OPT_DEPRECATED,0},/* Deprecated. Do not use. */
+{"-n", OPT_DEPRECATED,0},/* Deprecated. Do not use. */
+{"--ecs", OPT_ECS,0},/* Construct equivalence classes. */
+{"--fast", OPT_FAST,0},/* Same as -CFr. */
+{"-F", OPT_FAST,0},
+{"--full", OPT_FULL,0},/* Same as -Cfr. */
+{"-f", OPT_FULL,0},
+{"--header[=FILE]", OPT_HEADER,0},
+{"--help", OPT_HELP,0},/* Produce this help message. */
+{"-?", OPT_HELP,0},
+{"-h", OPT_HELP,0},
+{"--interactive", OPT_INTERACTIVE,0},/* Generate interactive scanner (opposite of -B). */
+{"-I", OPT_INTERACTIVE,0},
+{"--lex-compat", OPT_LEX_COMPAT,0},/* Maximal compatibility with original lex. */
+{"-l", OPT_LEX_COMPAT,0},
+{"--main", OPT_MAIN,0}, /* use built-in main() function. */
+{"--meta-ecs", OPT_META_ECS,0},/* Construct meta-equivalence classes. */
+{"--never-interactive", OPT_NEVER_INTERACTIVE,0},
+{"--no-default", OPT_NODEFAULT,0},/* Suppress default rule to ECHO unmatched text. */
+{"-s", OPT_NODEFAULT,0},
+{"--no-line", OPT_NOLINE,0},/* Suppress #line directives in scanner. */
+{"-L", OPT_NOLINE,0},/* Suppress #line directives in scanner. */
+{"--no-warn", OPT_NOWARN,0},/* Suppress warning messages. */
+{"-w", OPT_NOWARN,0},
+{"--outfile=FILE", OPT_OUTFILE,0},/* Write to FILE (default is lex.yy.c) */
+{"-o FILE", OPT_OUTFILE,0},
+{"--perf-report", OPT_PERF_REPORT,0},/* Generate performance report to stderr. */
+{"-p", OPT_PERF_REPORT,0},
+{"--pointer", OPT_POINTER,0},
+{"--prefix=PREFIX", OPT_PREFIX,0},/* Use PREFIX (default is yy) */
+{"-P PREFIX", OPT_PREFIX,0},
+{"-D", OPT_PREPROCDEFINE,0},/* Define a preprocessor symbol. */
+{"--read", OPT_READ,0},/* Use read(2) instead of stdio. */
+{"--reentrant", OPT_REENTRANT,0},/* Generate a reentrant C scanner. */
+{"-R[b]", OPT_REENTRANT,0},
+{"--reentrant-bison", OPT_REENTRANT_BISON,0},/* Reentrant scanner to be called by a bison pure parser. */
+{"--skel=FILE", OPT_SKEL,0},/* Use skeleton from FILE */
+{"-S FILE", OPT_SKEL,0},
+{"--stack", OPT_STACK,0},
+{"--stdinit", OPT_STDINIT,0},
+{"--stdout", OPT_STDOUT,0},/* Write generated scanner to stdout. */
+{"-t", OPT_STDOUT,0},
+{"--trace", OPT_TRACE,0},/* Flex should run in trace mode. */
+{"-T", OPT_TRACE,0},
+{"--verbose", OPT_VERBOSE,0},/* Write summary of scanner statistics to stdout. */
+{"-v", OPT_VERBOSE,0},
+{"--version", OPT_VERSION,0},/* Report flex version. */
+{"-V", OPT_VERSION,0},
+{"--yyclass=NAME", OPT_YYCLASS,0},
+{"--yylineno", OPT_YYLINENO,0},
+{"--yywrap" , OPT_YYWRAP,0},
+{0,0,0} /* required final NULL entry.*/
+};
+
+/* vim:set tabstop=8 softtabstop=4 shiftwidth=4: */
diff --git a/options.h b/options.h
new file mode 100644
index 0000000..bdfdcd7
--- /dev/null
+++ b/options.h
@@ -0,0 +1,58 @@
+#ifndef OPTIONS_H
+#define OPTIONS_H
+#include "scanopt.h"
+
+extern optspec_t flexopts[];
+
+enum flexopt_flag_t {
+ /* Use positive integers only, since they are return codes for scanopt.
+ * Order is not important. */
+ OPT_7BIT=1,
+ OPT_8BIT,
+ OPT_ALIGN,
+ OPT_ALWAYS_INTERACTIVE,
+ OPT_ARRAY,
+ OPT_BACKUP,
+ OPT_BATCH,
+ OPT_CASE_INSENSITIVE,
+ OPT_COMPRESSION,
+ OPT_CPLUSPLUS,
+ OPT_DEBUG,
+ OPT_DEFAULT,
+ OPT_DEPRECATED,
+ OPT_ECS,
+ OPT_FAST,
+ OPT_FULL,
+ OPT_HEADER,
+ OPT_HELP,
+ OPT_INTERACTIVE,
+ OPT_LEX_COMPAT,
+ OPT_MAIN,
+ OPT_META_ECS,
+ OPT_NEVER_INTERACTIVE,
+ OPT_NODEFAULT,
+ OPT_NOLINE,
+ OPT_NOWARN,
+ OPT_OUTFILE,
+ OPT_PERF_REPORT,
+ OPT_POINTER,
+ OPT_PREFIX,
+ OPT_PREPROCDEFINE,
+ OPT_READ,
+ OPT_REENTRANT,
+ OPT_REENTRANT_BISON,
+ OPT_SKEL,
+ OPT_STACK,
+ OPT_STDINIT,
+ OPT_STDOUT,
+ OPT_TRACE,
+ OPT_VERBOSE,
+ OPT_VERSION,
+ OPT_YYCLASS,
+ OPT_YYLINENO,
+ OPT_YYWRAP
+};
+
+#endif
+
+/* vim:set tabstop=8 softtabstop=4 shiftwidth=4 textwidth=0: */
diff --git a/scanopt.c b/scanopt.c
new file mode 100644
index 0000000..bf63728
--- /dev/null
+++ b/scanopt.c
@@ -0,0 +1,801 @@
+/*
+ * Copyright (c) 2001, John W. Millaway <john43@astro.temple.edu>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "scanopt.h"
+
+
+/* Internal structures */
+
+#ifdef HAVE_STRCASECMP
+#define STRCASECMP(a,b) strcasecmp(a,b)
+#else
+static int STRCASECMP(a,b)
+ const char* a;
+ const char* b;
+{
+ while(tolower(*a++) == tolower(*b++))
+ ;
+ return b-a;
+}
+#endif
+
+#define ARG_NONE 0x01
+#define ARG_REQ 0x02
+#define ARG_OPT 0x04
+#define IS_LONG 0x08
+
+struct _aux {
+ int flags; /* The above hex flags. */
+ int namelen; /* Length of the actual option word, e.g., "--file[=foo]" is 4 */
+ int printlen; /* Length of entire string, e.g., "--file[=foo]" is 12 */
+};
+
+
+struct _scanopt_t
+{
+ const optspec_t * options; /* List of options. */
+ struct _aux * aux; /* Auxiliary data about options. */
+ int optc; /* Number of options. */
+ int argc; /* Number of args. */
+ char ** argv; /* Array of strings. */
+ int index; /* Used as: argv[index][subscript]. */
+ int subscript;
+ char no_err_msg; /* If true, do not print errors. */
+ char has_long;
+ char has_short;
+};
+
+/* Accessor functions. These WOULD be one-liners, but portability calls. */
+static const char* NAME(s,i)
+ struct _scanopt_t *s; int i;
+{
+ return s->options[i].opt_fmt + ((s->aux[i].flags & IS_LONG)?2:1);
+}
+
+static int PRINTLEN(s,i)
+ struct _scanopt_t *s; int i;
+{
+ return s->aux[i].printlen;
+}
+
+static int RVAL(s,i)
+ struct _scanopt_t *s; int i;
+{
+ return s->options[i].r_val;
+}
+
+static int FLAGS(s,i)
+ struct _scanopt_t *s; int i;
+{
+ return s->aux[i].flags;
+}
+static const char* DESC(s,i)
+ struct _scanopt_t *s; int i;
+{
+ return s->options[i].desc ? s->options[i].desc : "";
+}
+
+#ifndef NO_SCANOPT_USAGE
+static int get_cols()
+{
+ char *env;
+ int cols = 80; /* default */
+
+#ifdef HAVE_NCURSES_H
+ initscr();
+ endwin();
+ if ( COLS > 0 )
+ return COLS;
+#endif
+
+ if((env = getenv("COLUMNS"))!=NULL)
+ cols=atoi(env);
+
+ return cols;
+}
+#endif
+
+/* Macro to check for NULL before assigning a value. */
+#define SAFE_ASSIGN(ptr,val) \
+ do{ \
+ if((ptr)!=NULL) \
+ *(ptr) = val; \
+ }while(0)
+
+/* Macro to assure we reset subscript whenever we adjust s->index.*/
+#define INC_INDEX(s,n) \
+ do{ \
+ (s)->index += (n); \
+ (s)->subscript= 0; \
+ }while(0)
+
+scanopt_t *
+scanopt_init ( options, argc, argv, flags)
+ const optspec_t* options;
+ int argc;
+ char** argv;
+ int flags;
+{
+ int i;
+ struct _scanopt_t * s;
+ s = (struct _scanopt_t*)malloc(sizeof(struct _scanopt_t));
+
+ s->options = options;
+ s->optc = 0;
+ s->argc = argc;
+ s->argv = (char**)argv;
+ s->index = 1;
+ s->subscript = 0;
+ s->no_err_msg = (flags & SCANOPT_NO_ERR_MSG);
+ s->has_long = 0;
+ s->has_short = 0;
+
+ /* Determine option count. (Find entry with all zeros).*/
+ s->optc = 0;
+ while ( options[s->optc].opt_fmt
+ || options[s->optc].r_val
+ || options[s->optc].desc )
+ s->optc++;
+
+ /* Build auxiliary data */
+ s->aux = (struct _aux*)malloc(s->optc * sizeof(struct _aux));
+
+ for (i=0; i < s->optc; i++) {
+ const char * p, *pname;
+ const struct optspec_t* opt;
+ struct _aux * aux;
+
+ opt = s->options + i;
+ aux = s->aux + i;
+
+ aux->flags = ARG_NONE;
+
+ if( opt->opt_fmt[0] == '-' && opt->opt_fmt[1] == '-') {
+ aux->flags |= IS_LONG;
+ pname = opt->opt_fmt + 2;
+ s->has_long = 1;
+ }else{
+ pname = opt->opt_fmt + 1;
+ s->has_short = 1;
+ }
+ aux->printlen = strlen(opt->opt_fmt);
+
+ aux->namelen = 0;
+ for (p=pname; *p; p++) {
+ if (*p == '=' || isspace(*p)) {
+ if (aux->namelen==0)
+ aux->namelen = p - pname;
+ aux->flags |= ARG_REQ;
+ aux->flags &= ~ARG_NONE;
+ }
+ if (*p == '[') {
+ if (aux->namelen==0)
+ aux->namelen = p - pname;
+ aux->flags &= ~(ARG_REQ|ARG_NONE);
+ aux->flags |= ARG_OPT;
+ break;
+ }
+ }
+ if (aux->namelen ==0)
+ aux->namelen = p - pname;
+ }
+ return (scanopt_t*)s;
+}
+
+#ifndef NO_SCANOPT_USAGE
+/* these structs are for scanopt_usage(). */
+struct usg_elem {
+ int idx;
+ struct usg_elem * next;
+ struct usg_elem * alias;
+};
+typedef struct usg_elem usg_elem;
+
+
+/* Prints a usage message based on contents of optlist.
+ * Parameters:
+ * scanner - The scanner, already initialized with scanopt_init().
+ * fp - The file stream to write to.
+ * usage - Text to be prepended to option list.
+ * Return: Always returns 0 (zero).
+ * The output looks something like this:
+
+[indent][option, alias1, alias2...][indent][description line1
+ description line2...]
+ */
+int scanopt_usage (scanner,fp,usage)
+ scanopt_t* scanner;
+ FILE* fp;
+ const char* usage;
+{
+ struct _scanopt_t * s;
+ int i,columns,indent=2;
+ usg_elem *byr_val=NULL; /* option indices sorted by r_val */
+ usg_elem *store; /* array of preallocated elements. */
+ int store_idx=0;
+ usg_elem *ue;
+ int maxlen[2] = {0,0};
+ int desccol=0;
+ int print_run=0;
+
+
+ s = (struct _scanopt_t*)scanner;
+
+ if (usage){
+ fprintf(fp,"%s\n",usage);
+ }else{
+ /* Find the basename of argv[0] */
+ const char * p;
+ p = s->argv[0] + strlen(s->argv[0]);
+ while(p != s->argv[0] && *p != '/')
+ --p;
+ if (*p == '/')
+ p++;
+
+ fprintf(fp,"Usage: %s [OPTIONS]...\n", p);
+ }
+ fprintf(fp,"\n");
+
+ /* Sort by r_val and string. Yes, this is O(n*n), but n is small. */
+ store = (usg_elem*)malloc(s->optc*sizeof(usg_elem));
+ for (i=0; i < s->optc; i++) {
+
+ /* grab the next preallocate node. */
+ ue = store + store_idx++;
+ ue->idx = i;
+ ue->next = ue->alias = NULL;
+
+ /* insert into list.*/
+ if( !byr_val )
+ byr_val = ue;
+ else {
+ int found_alias=0;
+ usg_elem **ue_curr, **ptr_if_no_alias=NULL;
+ ue_curr = &byr_val;
+ while (*ue_curr) {
+ if( RVAL(s,(*ue_curr)->idx) == RVAL(s,ue->idx)) {
+ /* push onto the alias list. */
+ ue_curr = &((*ue_curr)->alias);
+ found_alias=1;
+ break;
+ }
+ if( !ptr_if_no_alias
+ && STRCASECMP(NAME(s,(*ue_curr)->idx),NAME(s,ue->idx)) > 0){
+ ptr_if_no_alias = ue_curr;
+ }
+ ue_curr = &((*ue_curr)->next);
+ }
+ if (!found_alias && ptr_if_no_alias)
+ ue_curr = ptr_if_no_alias;
+ ue->next = *ue_curr;
+ *ue_curr = ue;
+ }
+ }
+
+#if 0
+ if(1){
+ printf("ORIGINAL:\n");
+ for(i=0; i < s->optc;i++)
+ printf("%2d: %s\n",i,NAME(s,i));
+ printf("SORTED:\n");
+ ue = byr_val;
+ while(ue) {
+ usg_elem *ue2;
+ printf("%2d: %s\n",ue->idx,NAME(s,ue->idx));
+ for(ue2=ue->alias; ue2; ue2=ue2->next)
+ printf(" +---> %2d: %s\n", ue2->idx, NAME(s,ue2->idx));
+ ue = ue->next;
+ }
+ }
+#endif
+
+ /* Now build each row of output. */
+
+ /* first pass calculate how much room we need. */
+ for (ue=byr_val; ue; ue=ue->next) {
+ usg_elem *ap;
+ int len=0;
+ int nshort=0,nlong=0;
+
+
+#define CALC_LEN(i) do {\
+ if(FLAGS(s,i) & IS_LONG) \
+ len += (nlong++||nshort) ? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\
+ else\
+ len += (nshort++||nlong)? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\
+ }while(0)
+
+ if(!(FLAGS(s,ue->idx) & IS_LONG))
+ CALC_LEN(ue->idx);
+
+ /* do short aliases first.*/
+ for(ap=ue->alias; ap; ap=ap->next){
+ if(FLAGS(s,ap->idx) & IS_LONG)
+ continue;
+ CALC_LEN(ap->idx);
+ }
+
+ if(FLAGS(s,ue->idx) & IS_LONG)
+ CALC_LEN(ue->idx);
+
+ /* repeat the above loop, this time for long aliases. */
+ for(ap=ue->alias; ap; ap=ap->next){
+ if( !(FLAGS(s,ap->idx) & IS_LONG))
+ continue;
+ CALC_LEN(ap->idx);
+ }
+
+ if(len > maxlen[0])
+ maxlen[0] = len;
+
+ /* It's much easier to calculate length for description column!*/
+ len = strlen(DESC(s,ue->idx));
+ if(len > maxlen[1])
+ maxlen[1] = len;
+ }
+
+ /* Determine how much room we have, and how much we will allocate to each col.
+ * Do not address pathological cases. Output will just be ugly. */
+ columns = get_cols() - 1;
+ if(maxlen[0] + maxlen[1] + indent*2 > columns ) {
+ /* col 0 gets whatever it wants. we'll wrap the desc col. */
+ maxlen[1] = columns - (maxlen[0] + indent*2);
+ if(maxlen[1]< 14) /* 14 is arbitrary lower limit on desc width.*/
+ maxlen[1]= INT_MAX;
+ }
+ desccol = maxlen[0] + indent*2;
+
+#define PRINT_SPACES(fp,n)\
+ do{\
+ int _n;\
+ _n=(n);\
+ while(_n-- > 0)\
+ fputc(' ',(fp));\
+ }while(0)
+
+
+ /* Second pass (same as above loop), this time we print. */
+ /* Sloppy hack: We iterate twice. The first time we print short and long options.
+ The second time we print those lines that have ONLY long options. */
+ while(print_run++ < 2) {
+ for (ue=byr_val; ue; ue=ue->next) {
+ usg_elem *ap;
+ int nwords=0,nchars=0,has_short=0;
+
+/* TODO: get has_short schtick to work */
+ has_short = !(FLAGS(s,ue->idx)&IS_LONG);
+ for(ap=ue->alias; ap; ap=ap->next){
+ if(!(FLAGS(s,ap->idx) & IS_LONG)){
+ has_short=1;
+ break;
+ }
+ }
+ if( (print_run == 1 && !has_short) ||
+ (print_run == 2 && has_short))
+ continue;
+
+ PRINT_SPACES(fp,indent);nchars+=indent;
+
+ /* Print, adding a ", " between aliases. */
+ #define PRINT_IT(i) do{\
+ if(nwords++)\
+ nchars+=fprintf(fp,", ");\
+ nchars+=fprintf(fp,"%s",s->options[i].opt_fmt);\
+ }while(0)
+
+ if(!(FLAGS(s,ue->idx) & IS_LONG))
+ PRINT_IT(ue->idx);
+
+ /* print short aliases first.*/
+ for(ap=ue->alias; ap; ap=ap->next){
+ if(!(FLAGS(s,ap->idx) & IS_LONG))
+ PRINT_IT(ap->idx);
+ }
+
+
+ if(FLAGS(s,ue->idx) & IS_LONG)
+ PRINT_IT(ue->idx);
+
+ /* repeat the above loop, this time for long aliases. */
+ for(ap=ue->alias; ap; ap=ap->next){
+ if( FLAGS(s,ap->idx) & IS_LONG)
+ PRINT_IT(ap->idx);
+ }
+
+ /* pad to desccol */
+ PRINT_SPACES(fp, desccol - nchars);
+
+ /* Print description, wrapped to maxlen[1] columns.*/
+ if(1){
+ const char * pstart;
+ pstart = DESC(s,ue->idx);
+ while(1){
+ int n=0;
+ const char * lastws=NULL,*p;
+ p=pstart;
+
+ while(*p && n < maxlen[1] && *p != '\n'){
+ if(isspace(*p) || *p=='-')
+ lastws = p;
+ n++;
+ p++;
+ }
+
+ if(!*p){ /* hit end of desc. done. */
+ fprintf(fp,"%s\n",pstart);
+ break;
+ }
+ else if(*p == '\n'){ /* print everything up to here then wrap.*/
+ fprintf(fp,"%.*s\n",n,pstart);
+ PRINT_SPACES(fp,desccol);
+ pstart = p+1;
+ continue;
+ }
+ else{ /* we hit the edge of the screen. wrap at space if possible.*/
+ if( lastws){
+ fprintf(fp,"%.*s\n",lastws-pstart,pstart);
+ pstart = lastws+1;
+ }else{
+ fprintf(fp,"%.*s\n",n,pstart);
+ pstart = p+1;
+ }
+ PRINT_SPACES(fp,desccol);
+ continue;
+ }
+ }
+ }
+ }
+ }/* end while */
+ free(store);
+ return 0;
+}
+#endif /* no scanopt_usage */
+
+
+static int
+scanopt_err(s,opt_offset,is_short,err)
+ struct _scanopt_t * s;
+ int opt_offset;
+ int is_short;
+ int err;
+{
+ const char *optname="";
+ char optchar[2];
+ const optspec_t * opt=NULL;
+
+ if ( opt_offset >= 0)
+ opt = s->options + opt_offset;
+
+ if ( !s->no_err_msg ) {
+
+ if( s->index > 0 && s->index < s->argc){
+ if (is_short ) {
+ optchar[0] = s->argv[s->index][s->subscript];
+ optchar[1] = '\0';
+ optname = optchar;
+ }else {
+ optname = s->argv[s->index];
+ }
+ }
+
+ fprintf(stderr,"%s: ", s->argv[0]);
+ switch (err) {
+ case SCANOPT_ERR_ARG_NOT_ALLOWED:
+ fprintf(stderr,"option `%s' doesn't allow an argument\n",optname);
+ break;
+ case SCANOPT_ERR_ARG_NOT_FOUND:
+ fprintf(stderr,"option `%s' requires an an argument\n",optname);
+ break;
+ case SCANOPT_ERR_OPT_AMBIGUOUS:
+ fprintf(stderr,"option `%s' is ambiguous\n",optname);
+ break;
+ case SCANOPT_ERR_OPT_UNRECOGNIZED:
+ fprintf(stderr,"Unrecognized option -- `%s'\n",optname);
+ break;
+ default:
+ fprintf(stderr,"Unknown error=(%d)\n",err);
+ break;
+ }
+ }
+ return err;
+}
+
+
+/* Internal. Match str against the regex ^--([^=]+)(=(.*))?
+ * return 1 if *looks* like a long option.
+ * 'str' is the only input argument, the rest of the arguments are output only.
+ * optname will point to str + 2
+ *
+ */
+static int
+matchlongopt(str, optname ,optlen, arg, arglen)
+ char* str;
+ char** optname;
+ int* optlen;
+ char** arg;
+ int* arglen;
+{
+ char * p;
+
+ *optname = *arg = (char*)0;
+ *optlen = *arglen = 0;
+
+ /* Match regex /--./ */
+ p = str;
+ if( p[0]!='-' || p[1]!='-' || !p[2])
+ return 0;
+
+ p += 2;
+ *optname = (char*)p;
+
+ /* find the end of optname */
+ while(*p && *p != '=')
+ ++p;
+
+ *optlen = p - *optname;
+
+ if (!*p)
+ /* an option with no '=...' part. */
+ return 1;
+
+
+ /* We saw an '=' char. The rest of p is the arg.*/
+ p++;
+ *arg = p;
+ while(*p)
+ ++p;
+ *arglen = p - *arg;
+
+ return 1;
+}
+
+
+/* Internal. Look up long or short option by name.
+ * Long options must match a non-ambiguous prefix, or exact match.
+ * Short options must be exact.
+ * Return boolean true if found and no error.
+ * Error stored in err_code or zero if no error. */
+static int
+find_opt (s, lookup_long, optstart, len, err_code, opt_offset)
+ struct _scanopt_t * s;
+ int lookup_long;
+ char * optstart;
+ int len;
+ int *err_code;
+ int* opt_offset;
+{
+ int nmatch=0,lastr_val=0,i;
+ *err_code = 0;
+ *opt_offset = -1;
+
+ if (!optstart)
+ return 0;
+
+ for(i=0; i < s->optc; i++) {
+ char* optname;
+ optname = (char*)(s->options[i].opt_fmt + (lookup_long?2:1));
+
+ if (lookup_long && (s->aux[i].flags & IS_LONG)) {
+ if (len > s->aux[i].namelen)
+ continue;
+
+ if (strncmp(optname, optstart, len) == 0) {
+ nmatch++;
+ *opt_offset = i;
+
+ /* exact match overrides all.*/
+ if(len == s->aux[i].namelen){
+ nmatch=1;
+ break;
+ }
+
+ /* ambiguity is ok between aliases. */
+ if(lastr_val && lastr_val == s->options[i].r_val)
+ nmatch--;
+ lastr_val = s->options[i].r_val;
+ }
+ }
+ else if ( !lookup_long && !(s->aux[i].flags&IS_LONG)) {
+ if (optname[0] == optstart[0]){
+ nmatch++;
+ *opt_offset = i;
+ }
+ }
+ }
+
+ if ( nmatch == 0 ) {
+ *err_code = SCANOPT_ERR_OPT_UNRECOGNIZED;
+ *opt_offset = -1;
+ }
+ else if ( nmatch > 1) {
+ *err_code = SCANOPT_ERR_OPT_AMBIGUOUS;
+ *opt_offset = -1;
+ }
+
+ return *err_code ? 0 : 1;
+}
+
+
+int
+scanopt (svoid, arg, optindex)
+ scanopt_t * svoid;
+ char ** arg;
+ int * optindex;
+{
+ char * optname=NULL, * optarg=NULL, *pstart;
+ int namelen=0, arglen=0;
+ int errcode=0, has_next;
+ const optspec_t * optp;
+ struct _scanopt_t* s;
+ struct _aux * auxp;
+ int is_short;
+ int opt_offset=-1;
+
+ s = (struct _scanopt_t*)svoid;
+
+ /* Normalize return-parameters. */
+ SAFE_ASSIGN(arg,NULL);
+ SAFE_ASSIGN(optindex , s->index);
+
+ if ( s->index >= s->argc )
+ return 0;
+
+ /* pstart always points to the start of our current scan. */
+ pstart = s->argv[s->index] + s->subscript;
+ if ( !pstart )
+ return 0;
+
+ if ( s->subscript == 0 ) {
+
+ /* test for exact match of "--" */
+ if ( pstart[0]=='-' && pstart[1]=='-' && !pstart[2]) {
+ SAFE_ASSIGN(optindex,s->index+1);
+ INC_INDEX(s,1);
+ return 0;
+ }
+
+ /* Match an opt. */
+ if(matchlongopt(pstart,&optname,&namelen,&optarg,&arglen)) {
+
+ /* it LOOKS like an opt, but is it one?! */
+ if( !find_opt(s, 1, optname, namelen, &errcode,&opt_offset)){
+ scanopt_err(s,opt_offset,0,errcode);
+ return errcode;
+ }
+ /* We handle this below. */
+ is_short=0;
+
+ /* Check for short opt. */
+ }else if ( pstart[0] == '-' && pstart[1]) {
+ /* Pass through to below. */
+ is_short=1;
+ s->subscript++;
+ pstart++;
+ }
+
+ else {
+ /* It's not an option. We're done. */
+ return 0;
+ }
+ }
+
+ /* We have to re-check the subscript status because it
+ * may have changed above. */
+
+ if(s->subscript != 0){
+
+ /* we are somewhere in a run of short opts,
+ * e.g., at the 'z' in `tar -xzf` */
+
+ optname = pstart;
+ namelen = 1;
+
+ if(!find_opt(s, 0, pstart, namelen, &errcode,&opt_offset)) {
+ return scanopt_err(s,opt_offset,1,errcode);
+ }
+
+ optarg = pstart+1;
+ arglen = 0;
+ while(optarg[arglen])
+ arglen++;
+
+ if (arglen==0)
+ optarg=NULL;
+ }
+
+ /* At this point, we have a long or short option matched at opt_offset into
+ * the s->options array (and corresponding aux array).
+ * A trailing argument is in {optarg,arglen}, if any.
+ */
+
+ /* Look ahead in argv[] to see if there is something
+ * that we can use as an argument (if needed). */
+ has_next = s->index+1 < s->argc
+ && strcmp("--",s->argv[s->index+1]) != 0;
+
+ optp = s->options + opt_offset;
+ auxp = s->aux + opt_offset;
+
+ /* case: no args allowed */
+ if ( auxp->flags & ARG_NONE) {
+ if ( optarg){
+ scanopt_err(s,opt_offset,is_short,errcode=SCANOPT_ERR_ARG_NOT_ALLOWED);
+ INC_INDEX(s,1);
+ return errcode;
+ }
+ INC_INDEX(s,1);
+ return optp->r_val;
+ }
+
+ /* case: required */
+ if (auxp->flags & ARG_REQ) {
+ if ( !optarg && !has_next)
+ return scanopt_err(s,opt_offset,is_short,SCANOPT_ERR_ARG_NOT_FOUND);
+
+ if (!optarg) {
+ /* Let the next argv element become the argument. */
+ SAFE_ASSIGN(arg,s->argv[s->index+1]);
+ INC_INDEX(s,2);
+ }else{
+ SAFE_ASSIGN(arg,(char*)optarg);
+ INC_INDEX(s,1);
+ }
+ return optp->r_val;
+ }
+
+ /* case: optional */
+ if (auxp->flags & ARG_OPT){
+ SAFE_ASSIGN(arg,optarg);
+ INC_INDEX(s,1);
+ return optp->r_val;
+ }
+
+
+ /* Should not reach here. */
+ return 0;
+}
+
+
+int
+scanopt_destroy(svoid)
+ scanopt_t* svoid;
+{
+ struct _scanopt_t* s;
+ s = (struct _scanopt_t*)svoid;
+ if ( s ) {
+ if (s->aux)
+ free (s->aux);
+ free(s);
+ }
+ return 0;
+}
+
+
+/* vim:set tabstop=8 softtabstop=4 shiftwidth=4: */
diff --git a/scanopt.h b/scanopt.h
new file mode 100644
index 0000000..b6a0638
--- /dev/null
+++ b/scanopt.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2001, John W. Millaway <john43@astro.temple.edu>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SCANOPT_H
+#define SCANOPT_H
+
+#include "flexdef.h"
+
+
+#ifndef NO_SCANOPT_USAGE
+/* Used by scanopt_usage for pretty-printing. */
+#ifdef HAVE_NCURSES_H
+#include <ncurses.h>
+#endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef PROTO
+#define PROTO(args) args
+#endif
+
+/* Error codes. */
+enum scanopt_err_t {
+ SCANOPT_ERR_OPT_UNRECOGNIZED = -1, /* Unrecognized option. */
+ SCANOPT_ERR_OPT_AMBIGUOUS = -2, /* It matched more than one option name. */
+ SCANOPT_ERR_ARG_NOT_FOUND= -3, /* The required arg was not found.*/
+ SCANOPT_ERR_ARG_NOT_ALLOWED = -4 /* Option does not take an argument. */
+};
+
+
+/* flags passed to scanopt_init */
+enum scanopt_flag_t {
+ SCANOPT_NO_ERR_MSG = 0x01 /* Suppress printing to stderr. */
+};
+
+/* Specification for a single option. */
+struct optspec_t
+{
+ const char * opt_fmt; /* e.g., "--foo=FILE", "-f FILE", "-n [NUM]" */
+ int r_val; /* Value to be returned by scanopt_ex(). */
+ const char* desc; /* Brief description of this option, or NULL. */
+};
+typedef struct optspec_t optspec_t;
+
+
+/* Used internally by scanopt() to maintain state. */
+/* Never modify these value directly. */
+typedef void * scanopt_t;
+
+
+/* Initializes scanner and checks option list for errors.
+ * Parameters:
+ * options - Array of options.
+ * argc - Same as passed to main().
+ * argv - Same as passed to main(). First element is skipped.
+ * flags - Control behavior.
+ * Return: A malloc'd pointer .
+ */
+scanopt_t* scanopt_init PROTO(( const optspec_t* options,
+ int argc, char** argv, int flags ));
+
+/* Frees memory used by scanner.
+ * Always returns 0. */
+int scanopt_destroy PROTO((scanopt_t* scanner));
+
+#ifndef NO_SCANOPT_USAGE
+/* Prints a usage message based on contents of optlist.
+ * Parameters:
+ * scanner - The scanner, already initialized with scanopt_init().
+ * fp - The file stream to write to.
+ * usage - Text to be prepended to option list. May be NULL.
+ * Return: Always returns 0 (zero).
+ */
+int scanopt_usage PROTO(( scanopt_t* scanner, FILE* fp, const char* usage));
+#endif
+
+/* Scans command-line options in argv[].
+ * Parameters:
+ * scanner - The scanner, already initialized with scanopt_init().
+ * optarg - Return argument, may be NULL.
+ * On success, it points to start of an argument.
+ * optindex - Return argument, may be NULL.
+ * On success or failure, it is the index of this option.
+ * If return is zero, then optindex is the NEXT valid option index.
+ *
+ * Return: > 0 on success. Return value is from optspec_t->rval.
+ * == 0 if at end of options.
+ * < 0 on error (return value is an error code).
+ *
+ */
+int scanopt PROTO(( scanopt_t * scanner, char ** optarg, int * optindex));
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+
+/* vim:set tabstop=8 softtabstop=4 shiftwidth=4: */