diff options
author | Will Estes <wlestes@users.sourceforge.net> | 2001-10-17 14:29:52 +0000 |
---|---|---|
committer | Will Estes <wlestes@users.sourceforge.net> | 2001-10-17 14:29:52 +0000 |
commit | 16fe6f376abdc0e84ea99f010b007aa018b6bf9f (patch) | |
tree | bf1473e55e2a54d10ac08d583f86b1e11a795277 | |
parent | 6e841250e0f2d884f1d8f360ce56003f15d93bd1 (diff) |
merge latest batch of millaway's changes
-rw-r--r-- | Makefile.am | 12 | ||||
-rw-r--r-- | buf.c | 107 | ||||
-rw-r--r-- | flex.skl | 40 | ||||
-rw-r--r-- | flex.texi | 4 | ||||
-rw-r--r-- | flexdef.h | 31 | ||||
-rw-r--r-- | main.c | 736 | ||||
-rw-r--r-- | misc.c | 17 | ||||
-rw-r--r-- | options.c | 78 | ||||
-rw-r--r-- | options.h | 58 | ||||
-rw-r--r-- | parse.y | 4 | ||||
-rw-r--r-- | scan.l | 1 | ||||
-rw-r--r-- | scanopt.c | 803 | ||||
-rw-r--r-- | scanopt.h | 128 |
13 files changed, 1694 insertions, 325 deletions
diff --git a/Makefile.am b/Makefile.am index 79795cc..83484cc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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. @@ -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: */ @@ -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 @@ -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 @@ -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" @@ -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 */ @@ -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); + +} @@ -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 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 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: */ @@ -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' @@ -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 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. + */ + +#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; +} + + +/* 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: */ |