]> granicus.if.org Git - flex/commitdiff
merge latest batch of millaway's changes
authorWill Estes <wlestes@users.sourceforge.net>
Wed, 17 Oct 2001 14:29:52 +0000 (14:29 +0000)
committerWill Estes <wlestes@users.sourceforge.net>
Wed, 17 Oct 2001 14:29:52 +0000 (14:29 +0000)
13 files changed:
Makefile.am
buf.c [new file with mode: 0644]
flex.skl
flex.texi
flexdef.h
main.c
misc.c
options.c [new file with mode: 0644]
options.h [new file with mode: 0644]
parse.y
scan.l
scanopt.c [new file with mode: 0644]
scanopt.h [new file with mode: 0644]

index 79795ccaf447680494d3800b498f406a2bd42e76..83484ccc9a75f49074775c86f9db0dc49570803e 100644 (file)
@@ -57,7 +57,10 @@ flex_SOURCES = \
        skel.c \
        sym.c \
        tblcmp.c \
-       yylex.c
+       yylex.c \
+       options.c \
+       scanopt.c \
+       buf.c
 
 libfl_a_SOURCES = \
        libmain.c \
@@ -66,7 +69,9 @@ libfl_a_SOURCES = \
 noinst_HEADERS = \
        flexdef.h \
        parse.h \
-       version.h
+       version.h \
+       options.h \
+       scanopt.h
 
 include_HEADERS = \
        FlexLexer.h
@@ -111,6 +116,9 @@ 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
+buf.o: buf.c flexdef.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/buf.c b/buf.c
new file mode 100644 (file)
index 0000000..ce2c8aa
--- /dev/null
+++ b/buf.c
@@ -0,0 +1,107 @@
+#include "flexdef.h"
+
+/* global buffers. */
+struct Buf userdef_buf; /* for user #definitions triggered by cmd-line. */
+
+
+/* functions for growable buffer. */
+
+/* Appends n characters in str to buf. */
+struct Buf* buf_strnappend (buf,str,n)
+    struct Buf* buf;
+    const char* str;
+    int n;
+{
+    buf_append(buf, str, n+1);
+
+    /* "undo" the '\0' character that buf_append() already copied. */
+    buf->nelts--;
+
+    return buf;
+}
+
+/* Appends characters in str to buf. */
+struct Buf* buf_strappend (buf,str)
+    struct Buf* buf;
+    const char* str;
+{
+    return buf_strnappend(buf, str, strlen(str));
+}
+
+/* appends "#define str def\n" */
+struct Buf* buf_strdefine (buf,str,def)
+    struct Buf* buf;
+    const char* str;
+    const char* def;
+{
+    buf_strappend(buf, "#define ");
+    buf_strappend(buf, " ");
+    buf_strappend(buf, str);
+    buf_strappend(buf, " ");
+    buf_strappend(buf, def);
+    buf_strappend(buf, "\n");
+    return buf;
+}
+
+/* create buf with 0 elements, each of size elem_size. */
+void buf_init(buf, elem_size)
+    struct Buf * buf;
+    size_t elem_size;
+{
+    buf->elts      = (void*)0;
+    buf->nelts     = 0;
+    buf->elt_size  = elem_size;
+    buf->nmax      = 0;
+}
+
+/* frees memory */
+void buf_destroy(buf)
+    struct Buf * buf;
+{
+    if(buf && buf->elts)
+        flex_free(buf->elts);
+    buf->elts = (void*)0;
+}
+
+
+/* appends ptr[] to buf, grow if necessary.
+ * n_elem is number of elements in ptr[], NOT bytes.
+ * returns buf.
+ * We grow by mod(512) boundaries.
+ */
+
+struct Buf* buf_append(buf,ptr,n_elem)
+    struct Buf * buf;
+    const void * ptr;
+    int n_elem;
+{
+    int n_alloc=0;
+
+    if (!ptr || n_elem==0)
+        return buf;
+
+    /* May need to alloc more. */
+    if (n_elem + buf->nelts > buf->nmax) {
+
+        /* exact amount needed... */
+        n_alloc = (n_elem + buf->nelts) * buf->elt_size;
+
+        /* ...plus some extra */
+        if (((n_alloc*buf->elt_size)%512) != 0 && buf->elt_size < 512)
+            n_alloc += (512 - ((n_alloc*buf->elt_size)%512)) / buf->elt_size;
+
+        if (!buf->elts)
+            buf->elts = allocate_array( n_alloc , buf->elt_size);
+        else
+            buf->elts = reallocate_array(buf->elts, n_alloc, buf->elt_size);
+
+        buf->nmax = n_alloc;
+    }
+
+    memcpy((char*)buf->elts + buf->nelts*buf->elt_size, ptr, n_elem*buf->elt_size);
+    buf->nelts += n_elem;
+
+    return buf;
+}
+
+/* vim:set tabstop=8 softtabstop=4 shiftwidth=4: */
index a49f3ba43752e941d9fd450f6f6f426616e7aad8..d9fc54f5a0553ab31b148d5cbcead613e8031d0c 100644 (file)
--- a/flex.skl
+++ b/flex.skl
@@ -256,9 +256,11 @@ struct yy_buffer_state
        };
 
 %- Standard (non-C++) definition
+%c
 #ifndef YY_REENTRANT
 static YY_BUFFER_STATE yy_current_buffer = 0;
 #endif
+%e
 %*
 
 /* We provide macros for accessing buffer states in case in the
@@ -271,6 +273,7 @@ static YY_BUFFER_STATE yy_current_buffer = 0;
 %- Standard (non-C++) definition
 
 #ifndef YY_REENTRANT
+%c
 /* yy_hold_char holds the character lost when yytext is formed. */
 static char yy_hold_char;
 
@@ -288,7 +291,7 @@ static int yy_start = 0;    /* start state number */
  * instead of setting up a fresh yyin.  A bit of a hack ...
  */
 static int yy_did_buffer_switch_on_eof;
-
+%e
 #endif /* end !YY_REENTRANT */
 
 void yyrestart YY_PROTO(( FILE *input_file YY_LAST_ARG ));
@@ -306,9 +309,11 @@ YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str YY_LAST_ARG ));
 YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len YY_LAST_ARG ));
 %*
 
+%c
 static void *yy_flex_alloc YY_PROTO(( yy_size_t YY_LAST_ARG ));
 static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t YY_LAST_ARG ));
 static void yy_flex_free YY_PROTO(( void * YY_LAST_ARG ));
+%e
 
 #define yy_new_buffer yy_create_buffer
 
@@ -333,10 +338,12 @@ static void yy_flex_free YY_PROTO(( void * YY_LAST_ARG ));
 %% yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here
 
 %- Standard (non-C++) definition
+%c
 static yy_state_type yy_get_previous_state YY_PROTO(( YY_ONLY_ARG ));
 static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state  YY_LAST_ARG));
 static int yy_get_next_buffer YY_PROTO(( YY_ONLY_ARG ));
 static void yy_fatal_error YY_PROTO(( yyconst char msg[] ));
+%e
 %*
 
 /* Done after the current pattern has been matched and before the
@@ -344,16 +351,17 @@ static void yy_fatal_error YY_PROTO(( yyconst char msg[] ));
  */
 #define YY_DO_BEFORE_ACTION \
        yytext_ptr = yy_bp; \
-%% code to fiddle yytext and yyleng for yymore() goes here
+%% code to fiddle yytext and yyleng for yymore() goes here \
        YY_G(yy_hold_char) = *yy_cp; \
        *yy_cp = '\0'; \
-%% code to copy yytext_ptr to yytext[] goes here, if %array
+%% code to copy yytext_ptr to yytext[] goes here, if %array \
        YY_G(yy_c_buf_p) = yy_cp;
 
 %*
 
+%c
 %% data tables for the DFA and the user's section 1 definitions go here
-
+%e
 
 #ifndef YY_EXTRA_TYPE
 #define YY_EXTRA_TYPE void *
@@ -406,7 +414,9 @@ struct yy_globals_t
 
     };
 
+%c
 static int yy_init_globals YY_PROTO(( void * ));
+%e
 
 /* This must go here because YYSTYPE and YYLSTYPE are included
  * from bison output in section 1.*/
@@ -489,9 +499,11 @@ extern int yywrap YY_PROTO(( YY_ONLY_ARG ));
 #endif
 
 %-
+%c
 #ifndef YY_NO_UNPUT
 static void yyunput YY_PROTO(( int c, char *buf_ptr  YY_LAST_ARG));
 #endif
+%e
 %*
 
 #ifndef yytext_ptr
@@ -504,19 +516,23 @@ static int yy_flex_strlen YY_PROTO(( yyconst char * YY_LAST_ARG));
 
 #ifndef YY_NO_INPUT
 %- Standard (non-C++) definition
+%c
 #ifdef __cplusplus
 static int yyinput YY_PROTO(( YY_ONLY_ARG ));
 #else
 static int input YY_PROTO(( YY_ONLY_ARG ));
 #endif
+%e
 %*
 #endif
 
 #if YY_STACK_USED
 #ifndef YY_REENTRANT
+%c
 static int yy_start_stack_ptr = 0;
 static int yy_start_stack_depth = 0;
 static int *yy_start_stack = 0;
+%e
 #endif
 #ifndef YY_NO_PUSH_STATE
 static void yy_push_state YY_PROTO(( int new_state YY_LAST_ARG));
@@ -572,8 +588,8 @@ YY_MALLOC_DECL
  */
 #ifndef YY_INPUT
 #define YY_INPUT(buf,result,max_size) \
-%% fread()/read() definition of YY_INPUT goes here unless we're doing C++
-%+ C++ definition
+%% fread()/read() definition of YY_INPUT goes here unless we're doing C++ \
+%+ C++ definition \
        if ( (result = LexerInput( (char *) buf, max_size )) < 0 ) \
                YY_FATAL_ERROR( "input in flex scanner failed" );
 %*
@@ -634,6 +650,9 @@ YY_MALLOC_DECL
 #  endif
 #endif
 
+
+extern int yylex YY_PROTO( YY_LEX_ARGS );
+
 #define YY_DECL int yylex YY_LEX_ARGS
 %+ C++ definition
 #define YY_DECL int yyFlexLexer::yylex()
@@ -654,6 +673,7 @@ YY_MALLOC_DECL
 
 %% YY_RULE_SETUP definition goes here
 
+%c
 YY_DECL
        {
        register yy_state_type yy_current_state;
@@ -859,7 +879,7 @@ do_action:  /* This label is used only to access EOF actions. */
        } /* end of action switch */
                } /* end of scanning one token */
        } /* end of yylex */
-
+%e
 %+
 yyFlexLexer::yyFlexLexer( FLEX_STD istream* arg_yyin, FLEX_STD ostream* arg_yyout )
        {
@@ -943,6 +963,7 @@ void yyFlexLexer::LexerOutput( const char* buf, int size )
        {
        (void) yyout->write( buf, size );
        }
+%e
 %*
 
 /* yy_get_next_buffer - try to read in a new buffer
@@ -954,6 +975,7 @@ void yyFlexLexer::LexerOutput( const char* buf, int size )
  */
 
 %-
+%c
 #ifdef YY_USE_PROTOS
 static int yy_get_next_buffer(YY_ONLY_ARG)
 #else
@@ -1090,11 +1112,12 @@ int yyFlexLexer::yy_get_next_buffer()
 
        return ret_val;
        }
-
+%e
 
 /* yy_get_previous_state - get the state just before the EOB char was reached */
 
 %-
+%c
 #ifdef YY_USE_PROTOS
 static yy_state_type yy_get_previous_state(YY_ONLY_ARG)
 #else
@@ -2074,3 +2097,4 @@ int main()
        return 0;
        }
 #endif
+%e
index 569e45e4aa26cb91b5e98eb9a5f764123daaa13c..2984cf32260111d28338a10dd67a69983bf9e176 100644 (file)
--- a/flex.texi
+++ b/flex.texi
@@ -1913,7 +1913,7 @@ Only users who wish to squeeze every last cycle out of their scanners
 need worry about this option.  (@pxref{performance}).
 
 @item -c
-is a do-nothing, deprecated option included for POSIX compliance.
+is a do-nothing option included for POSIX compliance.
 
 @item -d
 makes the generated scanner run in @dfn{debug} mode.  Whenever a pattern
@@ -1963,7 +1963,7 @@ cannot be used with the @samp{-+}, @samp{-f}, @samp{-F}, @samp{-Cf}, or
 @code{YY_FLEX_LEX_COMPAT} being @code{#define}'d in the generated scanner.
 
 @item -n
-is another do-nothing, deprecated option included only for
+is another do-nothing option included only for
 POSIX compliance.
 
 @item -p
index 0ccc5d919ae2e655ff461e3d9c9dd3e4c902c140..e046288e2f3f2e500066f1bab225c617ca4b310b 100644 (file)
--- a/flexdef.h
+++ b/flexdef.h
 /*  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"
 
@@ -390,6 +394,7 @@ extern int yymore_really_used, reject_really_used;
  * backing_up_file - file to summarize backing-up states to
  * infilename - name of input file
  * outfilename - name of output file
+ * headerfilename - name of the .h file to generate
  * did_outfilename - whether outfilename was explicitly set
  * prefix - the prefix used for externally visible names ("yy" by default)
  * yyclass - yyFlexLexer subclass to use for YY_DECL
@@ -413,7 +418,7 @@ extern int datapos, dataline, linenum, out_linenum;
 extern FILE *skelfile, *yyin, *backing_up_file;
 extern const char *skel[];
 extern int skel_ind;
-extern char *infilename, *outfilename;
+extern char *infilename, *outfilename, *headerfilename;
 extern int did_outfilename;
 extern char *prefix, *yyclass;
 extern int do_stdinit, use_stdout;
@@ -1022,3 +1027,27 @@ extern void stack1 PROTO((int, int, int, int));
 /* from file yylex.c */
 
 extern int yylex PROTO((void));
+
+/* A growable array. See buf.c. */
+struct Buf {
+    void * elts;     /* elements. */
+    int nelts;       /* number of elements. */
+    size_t elt_size; /* in bytes. */
+    int nmax;        /* max capacity of elements. */
+};
+
+extern void buf_init PROTO((struct Buf* buf, size_t elem_size));
+extern void buf_destroy PROTO((struct Buf* buf));
+extern struct Buf* buf_append PROTO((struct Buf* buf, const void* ptr, int n_elem));
+extern struct Buf* buf_strappend PROTO((struct Buf*, const char* str));
+extern struct Buf* buf_strnappend PROTO((struct Buf*, const char* str, int nchars));
+extern struct Buf* buf_strdefine PROTO((struct Buf* buf, const char* str, const char* def));
+
+/* buffer for #define's generated by user-options on cmd line. */
+extern struct Buf userdef_buf;
+
+/* For blocking out code from the header file. */
+#define OUT_BEGIN_CODE() out_str("#ifndef %sIN_HEADER\n",prefix)
+#define OUT_END_CODE() out_str("#endif /* !%sIN_HEADER */\n",prefix);
+
+#endif /* not defined FLEXDEF_H */
diff --git a/main.c b/main.c
index ea1785f91608fd510161232c6126de0e6f7cafd0..bf7a1718f5bf42fcf7981164cf3532506092e88c 100644 (file)
--- 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;
 
@@ -48,8 +49,7 @@ static char flex_version[] = FLEX_VERSION;
 void flexinit PROTO((int, char**));
 void readin PROTO((void));
 void set_up_initial_allocations PROTO((void));
-
-
+static char * basename2 PROTO((char* path, int should_strip_ext));
 
 /* these globals are all defined and commented in flexdef.h */
 int printstats, syntaxerror, eofseen, ddebug, trace, nowarn, spprdflt;
@@ -64,7 +64,7 @@ FILE *skelfile = NULL;
 int skel_ind = 0;
 char *action_array;
 int action_size, defs1_offset, prolog_offset, action_offset, action_index;
-char *infilename = NULL, *outfilename = NULL;
+char *infilename = NULL, *outfilename = NULL, *headerfilename = NULL;
 int did_outfilename;
 char *prefix, *yyclass;
 int do_stdinit, use_stdout;
@@ -244,10 +244,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. */
 
@@ -294,7 +294,7 @@ void check_options()
                        outfilename = outfile_path;
                        }
 
-               prev_stdout = freopen( outfilename, "w", stdout );
+               prev_stdout = freopen( outfilename, "w+", stdout );
 
                if ( prev_stdout == NULL )
                        lerrsf( _( "could not create %s" ), outfilename );
@@ -362,7 +362,7 @@ void check_options()
 
                        if ( do_yylineno )
                                GEN_PREFIX( "lineno" );
-            
+
             if ( do_yylineno && reentrant)
                 outn ( "#define YY_USE_LINENO 1");
                        }
@@ -376,6 +376,10 @@ void check_options()
        if ( did_outfilename )
                line_directive_out( stdout, 0 );
 
+        /* Dump the user defined preproc directives. */
+        if (userdef_buf.elts)
+            outn( (char*)(userdef_buf.elts) );
+
        skelout();
        }
 
@@ -390,9 +394,13 @@ void flexend( exit_status )
 int exit_status;
 
        {
-       int tblsiz;
+       static int called_before = -1; /* prevent infinite recursion. */
+       int tblsiz;     
        int unlink();
 
+       if( ++called_before )
+               exit( exit_status );
+
        if ( skelfile != NULL )
                {
                if ( ferror( skelfile ) )
@@ -404,6 +412,45 @@ int exit_status;
                                skelname );
                }
 
+       if ( headerfilename && exit_status == 0 && outfile_created && !ferror(stdout))
+               {
+                       /* Copy the file we just wrote to a header file. */
+                       #define COPY_SZ 512
+                       FILE *header_out;
+                       char copybuf[COPY_SZ];
+                       int ncopy;
+
+                       /* rewind the outfile file. */
+                       fflush(stdout);
+                       fseek(stdout, 0L, SEEK_SET);
+
+                       header_out = fopen(headerfilename, "w");
+                       if ( header_out == NULL)
+                               lerrsf( _( "could not create %s"), headerfilename );
+
+                       fprintf(header_out,
+                                       "#ifndef %sHEADER_H\n"
+                                       "#define %sHEADER_H 1\n"
+                                       "#define %sIN_HEADER 1\n",
+                                       prefix,prefix,prefix);
+                       fflush(header_out);
+
+                       while((ncopy=fread(copybuf, 1, COPY_SZ, stdout)) > 0) 
+                               if ( fwrite(copybuf, 1, ncopy, header_out) <= 0)
+                                       break;
+
+                       fflush(header_out);
+                       fprintf(header_out,
+                                       "\n"
+                                       "#undef %sIN_HEADER\n"
+                                       "#endif /* %sHEADER_H */\n",
+                                       prefix, prefix);
+
+                       if ( ferror( header_out ) )
+                               lerrsf( _( "error creating header file %s" ), headerfilename);
+                       fclose(header_out);
+               }
+
        if ( exit_status != 0 && outfile_created )
                {
                if ( ferror( stdout ) )
@@ -419,6 +466,7 @@ int exit_status;
                                outfilename );
                }
 
+
        if ( backing_up_report && backing_up_file )
                {
                if ( num_backing_up == 0 )
@@ -461,12 +509,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 +677,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;
@@ -654,248 +703,288 @@ char **argv;
        defs1_offset = prolog_offset = action_offset = action_index = 0;
        action_array[0] = '\0';
 
+        /* Initialize any buffers. */
+        buf_init(&userdef_buf, sizeof(char));
+
     /* Enable C++ if program name ends with '+'. */
-       program_name = argv[0];
+       program_name = basename2(argv[0],0);
 
        if ( program_name[0] != '\0' &&
             program_name[strlen( program_name ) - 1] == '+' )
                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;
-                               }
-                       }
-
-               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" ) );
+        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);
+        }
 
-                                       prefix = arg + i + 1;
-                                       goto get_next_arg;
+        while((rv=scanopt(sopt, &arg, &optind)) != 0){
 
-                               case 'p':
-                                       ++performance_report;
-                                       break;
+            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;
+            }
 
-                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_DONOTHING:
+                    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:
+                    buf_strdefine(&userdef_buf, "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;
-                    
-                    /* 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 );
+                    reentrant_bison_pure = true;
+                    break;
 
-                               case 'w':
-                                       nowarn = true;
-                                       break;
-
-                               case '7':
-                                       csize = 128;
-                                       break;
-
-                               case '8':
-                                       csize = CSIZE;
-                                       break;
+            case OPT_REENTRANT:
+                    reentrant = true;
 
-                               default:
-                                       fprintf( stderr,
-               _( "%s: unknown flag '%c'.  For usage, try\n\t%s --help\n" ),
-                                               program_name, (int) arg[i],
-                                               program_name );
-                                       exit( 1 );
-                               }
+                    /* 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:
+                    buf_strdefine(&userdef_buf,"YY_ALWAYS_INTERACTIVE", "1");
+                    break;
+
+            case OPT_NEVER_INTERACTIVE:
+                    buf_strdefine(&userdef_buf, "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:
+                    headerfilename = arg;
+                    break;
+
+            case OPT_META_ECS:
+                    usemecs = true;
+
+
+            case OPT_PREPROCDEFINE: 
+                    {
+                    /* arg is "symbol" or "symbol=definition". */
+                        char *def;
+
+                        for(def=arg; *def != '\0' && *def!='='; ++def)
+                            ;
+
+                        buf_strappend(&userdef_buf,"#define ");
+                        if (*def=='\0'){
+                            buf_strappend(&userdef_buf,arg);
+                            buf_strappend(&userdef_buf, " 1\n");
+                        }else{
+                            buf_strnappend(&userdef_buf, arg,def-arg);
+                            buf_strappend(&userdef_buf, " ");
+                            buf_strappend(&userdef_buf, def+1);
+                            buf_strappend(&userdef_buf, "\n");
+                        }
+                    }
+                    break;
+
+            case OPT_READ:
+                    use_read = true;
+                    break;
+
+            case OPT_STACK:
+                    buf_strdefine(&userdef_buf,"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);
 
-               /* 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;
+       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 +1129,8 @@ _( "Variable trailing context rules entail a large performance penalty\n" ) );
                }
 
        else
-               { 
+               {
+               OUT_BEGIN_CODE();
             /* In reentrant scanner, stdinit is handled in flex.skl. */
                if ( do_stdinit )
                        {
@@ -1065,12 +1155,13 @@ _( "Variable trailing context rules entail a large performance penalty\n" ) );
             outn( "#endif" );
                        }
 
-               else 
+               else
             {
             outn( "#ifndef YY_REENTRANT" );
                        outn( yy_nostdinit );
             outn( "#endif" );
             }
+               OUT_END_CODE();
                }
 
        if ( fullspd )
@@ -1087,7 +1178,9 @@ _( "Variable trailing context rules entail a large performance penalty\n" ) );
        if ( do_yylineno && ! C_plus_plus && ! reentrant )
                {
                outn( "extern int yylineno;" );
+               OUT_BEGIN_CODE();
                outn( "int yylineno = 1;" );
+               OUT_END_CODE();
                }
 
        if ( C_plus_plus )
@@ -1102,7 +1195,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 );
                        }
@@ -1199,72 +1292,95 @@ void set_up_initial_allocations()
        }
 
 
+/* extracts basename from path, optionally stripping the extension "\.*"
+ * (same concept as /bin/sh `basename`, but different handling of extension). */
+static char * basename2(path, strip_ext)
+    char * path;
+    int strip_ext; /* boolean */
+{
+    char *b, *e=0;
+    b = path;
+    for (b=path; *path; path++)
+        if (*path== '/')
+            b = path+1;
+        else if (*path=='.')
+            e = path;
+
+    if (strip_ext && e && e > b)
+        *e = '\0';
+    return b;
+}
+
 void usage()
        {
-       FILE *f = stdout;
-
-       fprintf( f,
-_( "%s [-bcdfhilnpstvwBFILTV78+? -R[b] -C[aefFmr] -ooutput -Pprefix -Sskeleton]\n" ),
-               program_name );
-       fprintf( f, _( "\t[--help --version] [file ...]\n" ) );
-
-       fprintf( f, _( "\t-b  generate backing-up information to %s\n" ),
-               backing_name );
-       fprintf( f, _( "\t-c  do-nothing POSIX option\n" ) );
-       fprintf( f, _( "\t-d  turn on debug mode in generated scanner\n" ) );
-       fprintf( f, _( "\t-f  generate fast, large scanner\n" ) );
-       fprintf( f, _( "\t-h  produce this help message\n" ) );
-       fprintf( f, _( "\t-i  generate case-insensitive scanner\n" ) );
-       fprintf( f, _( "\t-l  maximal compatibility with original lex\n" ) );
-       fprintf( f, _( "\t-n  do-nothing POSIX option\n" ) );
-       fprintf( f, _( "\t-p  generate performance report to stderr\n" ) );
-       fprintf( f,
-               _( "\t-s  suppress default rule to ECHO unmatched text\n" ) );
-
-       if ( ! did_outfilename )
-               {
-               sprintf( outfile_path, outfile_template,
-                       prefix, C_plus_plus ? "cc" : "c" );
-               outfilename = outfile_path;
-               }
+    FILE *f = stdout;
+    if ( ! did_outfilename )
+        {
+        sprintf( outfile_path, outfile_template,
+                prefix, C_plus_plus ? "cc" : "c" );
+        outfilename = outfile_path;
+        }
 
-       fprintf( f,
-               _( "\t-t  write generated scanner on stdout instead of %s\n" ),
-               outfilename );
-
-       fprintf( f,
-               _( "\t-v  write summary of scanner statistics to stdout\n" ) );
-       fprintf( f, _( "\t-w  do not generate warnings\n" ) );
-       fprintf( f, _( "\t-B  generate batch scanner (opposite of -I)\n" ) );
-       fprintf( f,
-               _( "\t-F  use alternative fast scanner representation\n" ) );
-       fprintf( f,
-               _( "\t-I  generate interactive scanner (opposite of -B)\n" ) );
-       fprintf( f, _( "\t-L  suppress #line directives in scanner\n" ) );
-       fprintf( f, _( "\t-R  generate a reentrant C scanner\n" ) );
-       fprintf( f,
-_( "\t\t-Rb  reentrant scanner is to be called by a bison pure parser.\n" ) );
-       fprintf( f, _( "\t-T  %s should run in trace mode\n" ), program_name );
-       fprintf( f, _( "\t-V  report %s version\n" ), program_name );
-       fprintf( f, _( "\t-7  generate 7-bit scanner\n" ) );
-       fprintf( f, _( "\t-8  generate 8-bit scanner\n" ) );
-       fprintf( f, _( "\t-+  generate C++ scanner class\n" ) );
-       fprintf( f, _( "\t-?  produce this help message\n" ) );
-       fprintf( f,
-_( "\t-C  specify degree of table compression (default is -Cem):\n" ) );
-       fprintf( f,
-_( "\t\t-Ca  trade off larger tables for better memory alignment\n" ) );
-       fprintf( f, _( "\t\t-Ce  construct equivalence classes\n" ) );
-       fprintf( f,
-_( "\t\t-Cf  do not compress scanner tables; use -f representation\n" ) );
-       fprintf( f,
-_( "\t\t-CF  do not compress scanner tables; use -F representation\n" ) );
-       fprintf( f, _( "\t\t-Cm  construct meta-equivalence classes\n" ) );
-       fprintf( f,
-       _( "\t\t-Cr  use read() instead of stdio for scanner input\n" ) );
-       fprintf( f, _( "\t-o  specify output filename\n" ) );
-       fprintf( f, _( "\t-P  specify scanner prefix other than \"yy\"\n" ) );
-       fprintf( f, _( "\t-S  specify skeleton file\n" ) );
-       fprintf( f, _( "\t--help     produce this help message\n" ) );
-       fprintf( f, _( "\t--version  report %s version\n" ), program_name );
-       }
+    fprintf(f,_( "%s [OPTIONS...] [file...]\n"), program_name);
+    fprintf(f,
+_(
+"Table Compression: (default is -Cem)\n"
+"  -Ca, --align      trade off larger tables for better memory alignment\n"
+"  -Ce, --ecs        construct equivalence classes\n"
+"  -Cf               do not compress tables; use -f representation\n"
+"  -CF               do not compress tables; use -F representation\n"
+"  -Cm, --meta-ecs   construct meta-equivalence classes\n"
+"  -Cr, --read       use read() instead of stdio for scanner input\n"
+"  -f, --full        generate fast, large scanner. Same as -Cfr\n"
+"  -F, --fast        use alternate table representation. Same as -CFr\n"
+  
+"\n"
+"Debugging:\n"
+"  -d, --debug             enable debug mode in scanner\n"
+"  -b, --backup            write backing-up information to %s\n"
+"  -p, --perf-report       write performance report to stderr\n"
+"  -s, --nodefault         suppress default rule to ECHO unmatched text\n"
+"  -T, --trace             %s should run in trace mode\n"
+"  -w, --nowarn            do not generate warnings\n"
+"  -v, --verbose           write summary of scanner statistics to stdout\n"
+
+"\n"
+"Files:\n"
+"  -o, --outfile=FILE      specify output filename\n"
+"  -S, --skel=FILE         specify skeleton file\n"
+"  -t, --stdout            write scanner on stdout instead of %s\n"
+"      --yyclass=NAME      name of C++ class\n"
+    
+"\n"
+"Scanner behavior:\n"
+"  -7, --7bit              generate 7-bit scanner\n"
+"  -8, --8bit              generate 8-bit scanner\n"
+"  -B, --batch             generate batch scanner (opposite of -I)\n"
+"  -i, --case-insensitive  ignore case in patterns\n"
+"  -l, --lex-compat        maximal compatibility with original lex\n"
+"  -I, --interactive       generate interactive scanner (opposite of -B)\n"
+"      --yylineno          track line count in yylineno\n"
+
+"\n"
+"Generated code:\n"
+"  -+, --c++               generate C++ scanner class\n"
+"  -Dmacro[=defn]          #define macro defn  (default defn is '1')\n"
+"  -L, --noline            suppress #line directives in scanner\n"
+"  -P, --prefix=STRING     use STRING as prefix instead of \"yy\"\n"
+"  -R, --reentrant         generate a reentrant C scanner\n"
+"  -Rb, --reentrant-bison  reentrant scanner for bison pure parser.\n"
+
+"\n"
+"Functions:\n"
+"  --yywrap                call yywrap on EOF\n"
+
+"\n"
+"Miscellaneous:\n"
+"  -c                      do-nothing POSIX option\n"
+"  -n                      do-nothing POSIX option\n"
+"  -?\n"
+"  -h, --help              produce this help message\n"
+"  -V, --version           report %s version\n"
+), backing_name, program_name, outfile_path, program_name);
+
+}
diff --git a/misc.c b/misc.c
index 7f5295027d7f38dfe6cee758e89e0a88edee3e21..dc5b6ef64a690da49c7fc5ef4e839c02868cbe8b 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -34,7 +34,7 @@
 
 #include "flexdef.h"
 
-
+/* Append "#define defname value\n" to the running buffer. */
 void action_define( defname, value )
 char *defname;
 int value;
@@ -53,6 +53,7 @@ int value;
        }
 
 
+/* Append "new_text" to the running buffer. */
 void add_action( new_text )
 char *new_text;
        {
@@ -793,6 +794,12 @@ void skelout()
                { /* copy from skel array */
                if ( buf[0] == '%' )
                        { /* control line */
+                       /* print the control line as a comment. */
+                       if (buf[strlen(buf)-1]=='\\')
+                               out_str("/* %s */\\\n", buf);
+                       else
+                               out_str("/* %s */\n", buf);
+                               
                        switch ( buf[1] )
                                {
                                case '%':
@@ -810,6 +817,14 @@ void skelout()
                                        do_copy = 1;
                                        break;
 
+                               case 'c':
+                                       OUT_BEGIN_CODE();
+                                       break;
+
+                               case 'e':
+                                       OUT_END_CODE();
+                                       break;
+
                                default:
                                        flexfatal(
                                        _( "bad line in skeleton file" ) );
diff --git a/options.c b/options.c
new file mode 100644 (file)
index 0000000..26a9039
--- /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_DONOTHING,0},/* For POSIX lex compatibility. */
+{"-n",                  OPT_DONOTHING,0},/* For POSIX lex compatibility. */
+{"--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},
+{"--nodefault",         OPT_NODEFAULT,0},/* Suppress default rule to ECHO unmatched text. */
+{"-s",                  OPT_NODEFAULT,0},
+{"--noline",            OPT_NOLINE,0},/* Suppress #line directives in scanner. */
+{"-L",                  OPT_NOLINE,0},/* Suppress #line directives in scanner. */
+{"--nowarn",            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},
+{"-Dmacro",             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 (file)
index 0000000..5ee1dc6
--- /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_DONOTHING,
+    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/parse.y b/parse.y
index 3a556912c4c31ed6f46a6028f982b13c78d12dab..26b646b1ed1d3e28bc06a056f27b3c57482a4101 100644 (file)
--- a/parse.y
+++ b/parse.y
@@ -1,7 +1,7 @@
 /* parse.y - parser for flex input */
 
 %token CHAR NUMBER SECTEND SCDECL XSCDECL NAME PREVCCL EOF_OP
-%token OPTION_OP OPT_OUTFILE OPT_PREFIX OPT_YYCLASS
+%token OPTION_OP OPT_OUTFILE OPT_PREFIX OPT_YYCLASS OPT_HEADER
 
 %token CCE_ALNUM CCE_ALPHA CCE_BLANK CCE_CNTRL CCE_DIGIT CCE_GRAPH
 %token CCE_LOWER CCE_PRINT CCE_PUNCT CCE_SPACE CCE_UPPER CCE_XDIGIT
@@ -196,6 +196,8 @@ option              :  OPT_OUTFILE '=' NAME
                        { prefix = copy_string( nmstr ); }
                |  OPT_YYCLASS '=' NAME
                        { yyclass = copy_string( nmstr ); }
+               |  OPT_HEADER '=' NAME
+                       { headerfilename = copy_string( nmstr ); }
                ;
 
 sect2          :  sect2 scon initforrule flexrule '\n'
diff --git a/scan.l b/scan.l
index 22b2e4852b833a1b6a968ebb4666d6366349b765..ac58aaff09238aa613b2956dfcb7cfaeee70c9d9 100644 (file)
--- a/scan.l
+++ b/scan.l
@@ -301,6 +301,7 @@ LEXOPT              [aceknopr]
        outfile         return OPT_OUTFILE;
        prefix          return OPT_PREFIX;
        yyclass         return OPT_YYCLASS;
+       header      return OPT_HEADER;
 
        \"[^"\n]*\"     {
                        strcpy( nmstr, yytext + 1 );
diff --git a/scanopt.c b/scanopt.c
new file mode 100644 (file)
index 0000000..0adf512
--- /dev/null
+++ b/scanopt.c
@@ -0,0 +1,803 @@
+/*
+ * 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.
+ */
+\f
+#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+1; *p; p++) {
+            /* detect required arg */
+            if (*p == '=' || isspace(*p) || !(aux->flags & IS_LONG)) {
+                if (aux->namelen==0)
+                    aux->namelen = p - pname;
+                    aux->flags |= ARG_REQ;
+                    aux->flags &= ~ARG_NONE;
+            }
+            /* detect optional arg. This overrides required arg. */
+            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;
+}
+\f
+
+/* 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;
+}
+\f
+
+/* 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;
+}
+\f
+
+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 (file)
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.
+ */
+\f
+#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;
+
+\f
+/* 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: */