tomwalters@0: tomwalters@0: #include tomwalters@0: #include tomwalters@0: #include "options.h" tomwalters@0: #include "strmatch.h" tomwalters@0: #include "units.h" tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Defaults for i/o parameters. tomwalters@0: These are declared externally in options.h so they may be overridden by tomwalters@0: option assignment. tomwalters@0: */ tomwalters@0: tomwalters@0: int LINE_LENGTH = 256 ; /* max length of line for ascii data. */ tomwalters@0: int FIELDWIDTH = 0 ; /* field width for printf. */ tomwalters@0: /* (FIELDWIDTH=0 sets no extra fieldwidth. */ tomwalters@0: /* Positive integer fieldwidth sets right-justified */ tomwalters@0: /* columns, negative integer fieldwidth sets left- */ tomwalters@0: /* justified columns). */ tomwalters@0: int PRECISION = 3 ; /* precision (num decimal places) for printf, */ tomwalters@0: /* (PRECISION=0 sets integer output). */ tomwalters@0: tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Return a file pointer to a file `name' opened to read. tomwalters@0: If `name' is `-' then return stdin. tomwalters@0: If file `name' is not found return null pointer. tomwalters@0: */ tomwalters@0: tomwalters@0: FILE *fropen( name ) tomwalters@0: char *name ; tomwalters@0: { tomwalters@0: FILE *fopen() ; tomwalters@0: tomwalters@0: if ( isstr( name, "-" ) ) return stdin ; tomwalters@0: else return ( fopen( name, "r" ) ) ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Generic options handler and file opener. tomwalters@0: Return a file pointer to an opened file, either stdin (if no args remained on tomwalters@0: command line after scanning) or the first name left on the command line. tomwalters@0: */ tomwalters@0: tomwalters@0: FILE *openopts( option, argc, argv ) tomwalters@0: Options *option ; tomwalters@0: int argc ; tomwalters@0: char *argv[] ; tomwalters@0: { tomwalters@0: FILE *fp ; tomwalters@0: int i ; tomwalters@0: tomwalters@0: if ( ( i = getopts( option, argc, argv ) ) == 0 ) tomwalters@0: return stdin ; tomwalters@0: if ( ( fp = fropen( argv[argc-i] ) ) == (FILE *)0 ) { tomwalters@0: fprintf(stderr, "%s: can't open %s\n", argv[0], argv[argc-i] ) ; tomwalters@0: exit( 1 ) ; tomwalters@0: } tomwalters@0: return ( fp ) ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Options handler and two file opener. tomwalters@0: If one file is on command line after scanning options, then the first file tomwalters@0: fp1 is assumed to be the stdin. Otherwise two files are expected. tomwalters@0: Return 0 if incorrect number of files, otherwise return 1. tomwalters@0: (Note: fp1 and fp2 are pts to file ptrs, so pass them as &fp1, &fp2 if tomwalters@0: originally declared as file ptrs). tomwalters@0: */ tomwalters@0: tomwalters@0: open2opts( option, argc, argv, fp1, fp2 ) tomwalters@0: Options *option ; tomwalters@0: int argc ; tomwalters@0: char *argv[] ; tomwalters@0: FILE **fp1 ; tomwalters@0: FILE **fp2 ; tomwalters@0: { tomwalters@0: switch ( getopts( option, argc, argv ) ) { tomwalters@0: tomwalters@0: case 1 : tomwalters@0: *fp1 = stdin; tomwalters@0: if ( ( *fp2 = fropen( argv[argc-1] ) ) == (FILE *)0 ) { tomwalters@0: fprintf( stderr,"%s: can't open %s\n", argv[0], argv[argc-1] ) ; tomwalters@0: exit( 1 ) ; tomwalters@0: } tomwalters@0: break ; tomwalters@0: case 2 : tomwalters@0: if ( ( *fp1 = fropen( argv[argc-2] ) ) == (FILE *)0 ) { tomwalters@0: fprintf( stderr,"%s: can't open %s\n", argv[0], argv[argc-2] ) ; tomwalters@0: exit( 1 ) ; tomwalters@0: } tomwalters@0: if ( ( *fp2 = fropen( argv[argc-1] ) ) == (FILE *)0 ) { tomwalters@0: fprintf( stderr,"%s: can't open %s\n", argv[0], argv[argc-1] ) ; tomwalters@0: exit( 1 ) ; tomwalters@0: } tomwalters@0: break ; tomwalters@0: default : tomwalters@0: return ( 0 ) ; tomwalters@0: } tomwalters@0: return ( 1 ) ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Build an options table. tomwalters@0: First assign all the option values with the defaults given in the table. tomwalters@0: Then scan the command line for arguments to overwrite corresponding vals. tomwalters@0: tomwalters@0: Argument syntax is one of the following three types (or combinations). tomwalters@0: Each syntax must contain either a leading '-' char or an embedded '=' char, tomwalters@0: to distinguish arguments from possible filenames. tomwalters@0: tomwalters@0: tomwalters@0: 1) - FLAG_SYNTAX tomwalters@0: 2) -[=] ARG_SYNTAX tomwalters@0: 3) [-]= EQ_SYNTAX tomwalters@0: tomwalters@0: FLAG_SYNTAX takes no value. (The returned value is the empty string). tomwalters@0: tomwalters@0: ARG_SYNTAX takes a value after an optional '=' char. tomwalters@0: If the '=' char is not found, then the is whatever follows the tomwalters@0: longest matching string. Otherwise the is whatever follows tomwalters@0: the '=' char. (The value is not allowed to be empty). tomwalters@0: tomwalters@0: EQ_SYNTAX takes a value which is whatever follows the '=' char. tomwalters@0: (The value is not allowed to be empty). tomwalters@0: tomwalters@0: It is assumed that all args remaining on the command line after an arg with tomwalters@0: invalid syntax must be filenames. These remain unscanned. tomwalters@0: Return argc, the number of args remaining unscanned on the command line, tomwalters@0: (argc=0 if no args left). tomwalters@0: To scan the remainder of the command line, and (eg) print the filenames:- tomwalters@0: tomwalters@0: if ( ( i = getopts( option, argc, argv, ) ) == 0 ) tomwalters@0: printf("stdin\n" ); tomwalters@0: else tomwalters@0: for ( ; i>0 ; i--) tomwalters@0: printf("[%s]\n", argv[argc-i] ); tomwalters@0: tomwalters@0: */ tomwalters@0: tomwalters@0: tomwalters@0: int getopts( option, argc, argv ) tomwalters@0: Options *option ; tomwalters@0: int argc ; tomwalters@0: char *argv[] ; tomwalters@0: { tomwalters@0: char *prog, *val ; tomwalters@0: int i, n, index[64], span[64] ; tomwalters@0: tomwalters@0: /* Assign option vals using the defaults */ tomwalters@0: tomwalters@0: for (i=0 ; option[i].name != (char *)0 ; i++) tomwalters@0: *(option[i].val) = option[i].dflt ; tomwalters@0: tomwalters@0: /* Overwrite option vals with corresponding vals found on command line */ tomwalters@0: tomwalters@0: prog = *argv ; tomwalters@0: while ( --argc > 0 && ++argv ) { tomwalters@0: tomwalters@0: /* find list of options all with longest matching name span */ tomwalters@0: /* returning if none, with argc args remaining */ tomwalters@0: if ( ( n = whichopt( option, *argv, index, span ) ) == 0 ) tomwalters@0: return ( argc ) ; tomwalters@0: tomwalters@0: /* check arg syntax, returning if invalid with argc args remaining */ tomwalters@0: for ( i=0 ; i 1 ) { tomwalters@0: fprintf(stderr,"%s: ambiguous option [%s]\n", prog, *argv ) ; tomwalters@0: exit ( 1 ) ; tomwalters@0: } tomwalters@0: tomwalters@0: /* do operation */ tomwalters@0: operate( option, *index, val ) ; tomwalters@0: side_effect( option, *index ) ; tomwalters@0: tomwalters@0: } tomwalters@0: return argc ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Find the default value of the first option whose name unambiguously matches tomwalters@0: `s' (possibly abbreviated). tomwalters@0: Return a ptr to the default value of the matching option. tomwalters@0: Otherwise return the null ptr if a matching name is not found or is ambiguous. tomwalters@0: */ tomwalters@0: tomwalters@0: char *optdflt( option, s ) tomwalters@0: Options *option ; tomwalters@0: char *s ; tomwalters@0: { tomwalters@0: int i ; tomwalters@0: tomwalters@0: if ( ( i = optindex( option, s ) ) < 0 ) tomwalters@0: return ( (char *)0 ) ; tomwalters@0: return ( option[i].dflt ) ; tomwalters@0: } tomwalters@0: tomwalters@0: /* tomwalters@0: Find the option whose name unambiguously matches `s' (possibly abbreviated). tomwalters@0: Return the option list index of the matching option. tomwalters@0: Otherwise return -1 if a matching name is not found, or -2 if the name is tomwalters@0: ambiguous. tomwalters@0: */ tomwalters@0: tomwalters@0: int optindex( option, s ) tomwalters@0: Options *option ; tomwalters@0: char *s ; tomwalters@0: { tomwalters@0: int i, n, index[64], span[64] ; tomwalters@0: tomwalters@0: if ( ( n = whichopt( option, s, index, span ) ) == 0 ) tomwalters@0: return (-1) ; /* option not found */ tomwalters@0: if ( n > 1 ) tomwalters@0: return (-2) ; /* option name ambiguous */ tomwalters@0: return ( *index ) ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Find the option whose name has the longest matching span with the head of `s' tomwalters@0: (the argument string, possibly abbreviated or with trailing value parts). tomwalters@0: Pass the option list index of the matching option back via arg `index'. tomwalters@0: Pass the length of the matching span back via arg `span'. tomwalters@0: Return the number of options found which have an equal longest matching span. tomwalters@0: (Any `-' at the head of `s' is skipped before matching). tomwalters@0: If there is an exact match (ie the longest matching span equals the length of tomwalters@0: the option name) then return an unambiguous match. tomwalters@0: */ tomwalters@0: tomwalters@0: int whichopt( option, s, index, span ) tomwalters@0: Options *option ; tomwalters@0: char *s ; tomwalters@0: int *index, *span ; tomwalters@0: { tomwalters@0: int i, j, n = 0, jmax = 0 ; tomwalters@0: tomwalters@0: if ( *s == '-' ) s++ ; /* Skip leading hyphen */ tomwalters@0: tomwalters@0: for ( i = 0 ; option[i].name != (char *)0 ; i++ ) { tomwalters@0: tomwalters@0: if ( ( j = streqspn( s, option[i].name ) ) > jmax ) { tomwalters@0: jmax = j ; /* a new longest matching span */ tomwalters@0: n = 0 ; tomwalters@0: index[n] = i ; tomwalters@0: span[n++] = j ; tomwalters@0: } tomwalters@0: else if ( j > 0 && j == jmax ) { /* ambiguous name with same matching span */ tomwalters@0: index[n] = i ; tomwalters@0: span[n++] = j ; tomwalters@0: } tomwalters@0: } tomwalters@0: tomwalters@0: for ( i = 0 ; i < n ; i++ ) /* check for an exact match */ tomwalters@0: if ( span[i] == strlen( option[index[i]].name ) ) { tomwalters@0: index[0] = index[i] ; tomwalters@0: span[0] = span[i] ; tomwalters@0: return 1 ; tomwalters@0: } tomwalters@0: tomwalters@0: return n ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Check arg syntax and return a pointer to the value part, (or a pointer to the tomwalters@0: terminating null if the value part is empty). tomwalters@0: Return a NULL pointer if the syntax is invalid. It is assumed then that the tomwalters@0: current arg (and all remaining) is a filename. tomwalters@0: Print a warning if the syntax is an error rather than a possible filename, tomwalters@0: (the filename will not be found in this case). tomwalters@0: tomwalters@0: The check is arranged so that different option syntax types can be OR'd. tomwalters@0: */ tomwalters@0: tomwalters@0: char *checksyntax( s, span, type ) tomwalters@0: char *s ; tomwalters@0: int span, type ; tomwalters@0: { tomwalters@0: int i ; tomwalters@0: char *v, *val = (char *)0 ; tomwalters@0: tomwalters@0: if ( bitset( type, FLAG_SYNTAX ) ) { /* - */ tomwalters@0: if ( *s == '-' && *(v = s+1 + span) == '\0' ) tomwalters@0: val = v ; tomwalters@0: } tomwalters@0: tomwalters@0: if ( bitset( type, ARG_SYNTAX ) ) { /* -[=] */ tomwalters@0: if ( *s == '-' ) { tomwalters@0: if ( *(v = s+1 + span) == '=' ) v++ ; tomwalters@0: if ( *v != '\0' ) val = v ; tomwalters@0: } tomwalters@0: } tomwalters@0: tomwalters@0: if ( bitset( type, EQ_SYNTAX ) ) { /* [-]= */ tomwalters@0: if ( *s == '-' ) s++ ; tomwalters@0: if ( *(v = s + span) == '=' && *++v != '\0' ) tomwalters@0: val = v ; tomwalters@0: } tomwalters@0: tomwalters@0: return ( val ) ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Return 1 if string s a possible option, (ie. a string which is not a , tomwalters@0: and contains either an embedded '=' or a leading '-'). tomwalters@0: Otherwise return 0. tomwalters@0: */ tomwalters@0: tomwalters@0: int isopt( s ) tomwalters@0: char *s ; tomwalters@0: { tomwalters@0: if ( isnumber( s ) ) return 0 ; tomwalters@0: if ( *s == '-' ) return 1 ; tomwalters@0: if ( strchr( s, '=' ) != (char *)0 ) return 1 ; tomwalters@0: return 0 ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: operate( option, i, val ) tomwalters@0: Options *option ; tomwalters@0: int i ; tomwalters@0: char *val ; tomwalters@0: { tomwalters@0: if ( bitset( option[i].type, LATCH ) ) /* LATCH */ tomwalters@0: *(option[i].val) = val ; tomwalters@0: tomwalters@0: else if ( bitset( option[i].type, AND ) ) /* AND */ tomwalters@0: *(option[i].val) = offstr ; tomwalters@0: tomwalters@0: else if ( bitset( option[i].type, OR ) ) /* OR */ tomwalters@0: *(option[i].val) = onstr ; tomwalters@0: tomwalters@0: else if ( bitset( option[i].type, TOGGLE ) ) { /* TOGGLE */ tomwalters@0: if ( isempty( val ) ) { tomwalters@0: if ( ison( *(option[i].val) ) ) tomwalters@0: *(option[i].val) = offstr ; tomwalters@0: else tomwalters@0: *(option[i].val) = onstr ; tomwalters@0: } tomwalters@0: else *(option[i].val) = val ; tomwalters@0: } tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Side-effects of options type specifiers. tomwalters@0: */ tomwalters@0: tomwalters@0: side_effect( option, j ) tomwalters@0: Options *option ; tomwalters@0: int j ; tomwalters@0: { tomwalters@0: int i ; tomwalters@0: char *exstr ; tomwalters@0: tomwalters@0: if ( bitset( option[j].type, EXCLUDE ) ) { /* EXCLUDE */ tomwalters@0: if ( ison( *(option[j].val) ) ) tomwalters@0: exstr = offstr ; tomwalters@0: else tomwalters@0: exstr = onstr ; tomwalters@0: for ( i=0; option[i].name != (char *)0 ; i++) tomwalters@0: if ( i != j && bitset( option[i].type, EXCLUDE ) ) tomwalters@0: *(option[i].val) = exstr ; tomwalters@0: } tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Separate an option value string `optval' into two string tokens at the first tomwalters@0: occurrence of a separator character `sep'. (Given as a string of one char). tomwalters@0: Return the tokens via the pointers `val1' and `val2' (which could be passed tomwalters@0: as the addresses of string pointers). tomwalters@0: Return BADVAL if the option value string is bad (meaning that either `optval' was tomwalters@0: null or empty, or the separator char was found but the 2nd token was missing). tomwalters@0: Return 1 if the option value string is good (meaning that either two tokens tomwalters@0: were found, or only the 1st token with no separator char, in which case `val2' tomwalters@0: will be a ptr to an empty string). (See routine strpsep()). tomwalters@0: */ tomwalters@0: tomwalters@0: int getvals( optval, val1, val2, sep ) tomwalters@0: char *optval ; tomwalters@0: char **val1, **val2 ; tomwalters@0: char *sep ; tomwalters@0: { tomwalters@0: *val1 = optval ; tomwalters@0: if ( isnull( *val2 = strsep( optval, sep ) ) ) tomwalters@0: return BADVAL ; tomwalters@0: return 1 ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Parse a range selector of the form: a[-b] tomwalters@0: Return 0 if bad selectors, otherwise return 1. tomwalters@0: Items are numbered 1,2,...,max, (where max is 0 by convention). tomwalters@0: When either limit is "min", then set the respective value to 1. tomwalters@0: When either limit is "max", then set the respective value to 0. tomwalters@0: When `b' is missing, then set b=a. tomwalters@0: tomwalters@0: If (a==0) then seek eof and process the final item. tomwalters@0: Else seek past (a-1) items, and then process the next (b-a+1) items tomwalters@0: (or all items until eof if (b==0)). tomwalters@0: Eg: tomwalters@0: seekstart( a-1, bytes, fp ) ; tomwalters@0: for ( i = 0 ; ( b == 0 || i < b-a+1 ) ; i++ ) tomwalters@0: do process tomwalters@0: tomwalters@0: */ tomwalters@0: tomwalters@0: int selector( s, a, b ) tomwalters@0: char *s ; tomwalters@0: int *a, *b ; tomwalters@0: { tomwalters@0: char *val1, *val2 ; tomwalters@0: tomwalters@0: if ( getvals( s, &val1, &val2, "-" ) == BADVAL ) tomwalters@0: return ( 0 ) ; tomwalters@0: if ( ismin( val1 ) ) *a = 1 ; /* first object */ tomwalters@0: else if ( ismax( val1 ) ) *a = 0 ; /* last object */ tomwalters@0: else if ( ( *a = atoi( val1 ) ) <= 0 ) { /* intermediate object */ tomwalters@0: if ( *a == 0 ) fprintf( stderr,"warning: selected items are numbered 1,...,max\n" ) ; tomwalters@0: return ( 0 ) ; tomwalters@0: } tomwalters@0: if ( isempty( val2 ) ) *b = *a ; /* single object */ tomwalters@0: else if ( ismin( val2 ) ) *b = 1 ; tomwalters@0: else if ( ismax( val2 ) ) *b = 0 ; tomwalters@0: else if ( ( *b = atoi( val2 ) ) <= 0 ) { tomwalters@0: return ( 0 ) ; tomwalters@0: } tomwalters@0: if ( *b > 0 && ( *a == 0 || *a > *b ) ) tomwalters@0: return ( 0 ) ; tomwalters@0: tomwalters@0: return ( 1 ) ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Parse a time range selector of the form: a[-b] tomwalters@0: The range is returned as limits meaning: start==a duration==b-a+1. tomwalters@0: (Note: when range=max or range=max-max then a==b==(-1) and duration==1). tomwalters@0: Return 0 if bad selectors, otherwise return 1. tomwalters@0: Time is numbered 0,2,...,max, (where max is (-1) by convention). tomwalters@0: When either limit is "min", then set the respective value to 0. tomwalters@0: When either limit is "max", then set the respective value to (-1). tomwalters@0: When `b' is missing, then set b=a. tomwalters@0: Time is specified as [], where = {p, ms, s}. tomwalters@0: Empty units are interpreted as `p' (ie sample points). tomwalters@0: Convert `a' and `b' to sample points using the given samplerate. tomwalters@0: tomwalters@0: (Note: this is like selector() but using to_p() instead of atoi(), and tomwalters@0: numbering from 0 instead of 1). tomwalters@0: tomwalters@0: If (a<0) then seek eof and process the final item. tomwalters@0: Else seek past (a) items, and then process the next (b-a+1) items tomwalters@0: (or all items until eof if (b==-1)). tomwalters@0: Eg: tomwalters@0: seekstart( a, bytes, fp ) ; tomwalters@0: for ( i = 0 ; ( b < 0 || i < b-a+1 ) ; i++ ) tomwalters@0: do process tomwalters@0: tomwalters@0: */ tomwalters@0: tomwalters@0: int range( s, a, b, samplerate ) tomwalters@0: char *s ; tomwalters@0: int *a, *b ; tomwalters@0: int samplerate ; tomwalters@0: { tomwalters@0: char *val1, *val2 ; tomwalters@0: tomwalters@0: if ( getvals( s, &val1, &val2, "-" ) == BADVAL ) tomwalters@0: return ( 0 ) ; tomwalters@0: if ( ismin( val1 ) ) *a = 0 ; /* first object */ tomwalters@0: else if ( ismax( val1 ) ) *a = (-1) ; /* last object */ tomwalters@0: else if ( ( *a = to_p( val1, samplerate ) ) < 0 ) { tomwalters@0: return ( 0 ) ; tomwalters@0: } tomwalters@0: if ( isempty( val2 ) ) *b = *a ; /* single object */ tomwalters@0: else if ( ismin( val2 ) ) *b = 0 ; tomwalters@0: else if ( ismax( val2 ) ) *b = (-1) ; tomwalters@0: else if ( ( *b = to_p( val2, samplerate ) ) < 0 ) { tomwalters@0: return ( 0 ) ; tomwalters@0: } tomwalters@0: if ( *b >= 0 && ( *a < 0 || *a > *b ) ) tomwalters@0: return ( 0 ) ; tomwalters@0: tomwalters@0: return ( 1 ) ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Seek n items of size bytes from current position in stream. tomwalters@0: (Unlike fseek, this works even when fp is stdin). tomwalters@0: Return the number of items actually read. tomwalters@0: Example: seekstart( n, sizeof(short), fp ); tomwalters@0: Datatype "ASCII" is a special case for which 0 is returned by the databytes tomwalters@0: routine, so bytes==0 is taken to mean seek n ascii lines. In general use: tomwalters@0: seekstart( n, databytes( typestr ), fp ) tomwalters@0: */ tomwalters@0: tomwalters@0: seekstart( n, bytes, fp ) tomwalters@0: int n, bytes ; tomwalters@0: FILE *fp ; tomwalters@0: { tomwalters@0: int i ; tomwalters@0: char *buf ; tomwalters@0: char *line ; tomwalters@0: tomwalters@0: if ( bytes == 0 ) { /* special case meaning ascii lines */ tomwalters@0: line = (char *)malloc( LINE_LENGTH ) ; tomwalters@0: for ( i = 0 ; i < n && fgets( line, LINE_LENGTH, fp ) != (char *)0 ; i++ ) tomwalters@0: ; tomwalters@0: free( line ) ; tomwalters@0: return ( i ) ; tomwalters@0: } tomwalters@0: tomwalters@0: if ( fp != stdin ) { tomwalters@0: if ( fseek( fp, (long)( n * bytes ), 1 ) == 0 ) tomwalters@0: return n ; /* if improper, look for any input */ tomwalters@0: } tomwalters@0: tomwalters@0: if ( ( buf = (char *)malloc( bytes ) ) == (char *)0 ) { tomwalters@0: fprintf( stderr, "seekstart: cannot allocate %d bytes\n", bytes ) ; tomwalters@0: exit( 1 ) ; tomwalters@0: } tomwalters@0: tomwalters@0: for ( i = 0 ; i < n && fread( buf, bytes, 1, fp ) ; i++ ) tomwalters@0: ; tomwalters@0: free( buf ) ; tomwalters@0: tomwalters@0: return ( i ) ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Seek n items of of given type from current position in stream. tomwalters@0: This seekstart version takes a `type' arg instead of a size in `bytes'. tomwalters@0: The `type' is an index to the datatype list in options.h, obtained for tomwalters@0: example using: typeindex( typestr ). tomwalters@0: */ tomwalters@0: tomwalters@0: seektype( n, type, fp ) tomwalters@0: int n, type ; tomwalters@0: FILE *fp ; tomwalters@0: { tomwalters@0: return ( seekstart( n, typebytes( type ), fp ) ) ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Read the next n items of `type' from the given stream. tomwalters@0: The `type' is an index to the datatype list in options.h, obtained for tomwalters@0: example using: typeindex( typestr ). tomwalters@0: Assign the item as a float in address `y', and return 1. tomwalters@0: Return 0 if eof or error. tomwalters@0: This can replace fread, eg: tomwalters@0: fread( &p, sizeof(short), 1, fp ) ( with short p ) tomwalters@0: becomes: tomwalters@0: readitem( &y, typeindex( "short" ), 1, fp ) ( with float y ) tomwalters@0: */ tomwalters@0: tomwalters@0: readitem( y, type, n, fp ) tomwalters@0: float *y ; tomwalters@0: int type ; tomwalters@0: int n ; tomwalters@0: FILE *fp ; tomwalters@0: { tomwalters@0: char c ; short s ; int i ; float f ; double d ; tomwalters@0: int j ; tomwalters@0: static char *line ; tomwalters@0: static int first = 1 ; tomwalters@0: tomwalters@0: switch ( type ) { tomwalters@0: case 0 : for ( j = 0 ; j < n ; j++ ) { tomwalters@0: if ( fread( &c, sizeof(char), 1, fp ) == 0 ) return 0 ; tomwalters@0: *y++ = (float)c ; tomwalters@0: } tomwalters@0: break ; tomwalters@0: case 1 : for ( j = 0 ; j < n ; j++ ) { tomwalters@0: if ( fread( &s, sizeof(short), 1, fp ) == 0 ) return 0 ; tomwalters@0: *y++ = (float)s ; tomwalters@0: } tomwalters@0: break ; tomwalters@0: case 2 : for ( j = 0 ; j < n ; j++ ) { tomwalters@0: if ( fread( &i, sizeof(int), 1, fp ) == 0 ) return 0 ; tomwalters@0: *y++ = (float)i ; tomwalters@0: } tomwalters@0: break ; tomwalters@0: case 3 : for ( j = 0 ; j < n ; j++ ) { tomwalters@0: if ( fread( &f, sizeof(float), 1, fp ) == 0 ) return 0 ; tomwalters@0: *y++ = (float)f ; tomwalters@0: } tomwalters@0: break ; tomwalters@0: case 4 : for ( j = 0 ; j < n ; j++ ) { tomwalters@0: if ( fread( &d, sizeof(double), 1, fp ) == 0 ) return 0 ; tomwalters@0: *y++ = (float)d ; tomwalters@0: } tomwalters@0: break ; tomwalters@0: case 5 : tomwalters@0: case 6 : if ( first ) { tomwalters@0: line = (char *)malloc( LINE_LENGTH ) ; tomwalters@0: first = 0 ; tomwalters@0: } tomwalters@0: for ( j = 0 ; j < n ; j++ ) { tomwalters@0: if ( fgets( line, LINE_LENGTH, fp ) == (char *)0 ) return 0 ; tomwalters@0: *y++ = atof( line ) ; tomwalters@0: } tomwalters@0: break ; tomwalters@0: } tomwalters@0: return 1 ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Write the given n items of `type' onto the given stream. tomwalters@0: The `type' is an index to the datatype list in options.h, (obtained for tomwalters@0: example using: typeindex( typestr ) ). tomwalters@0: */ tomwalters@0: tomwalters@0: writeitem( y, type, n, fp ) tomwalters@0: float *y ; tomwalters@0: int type ; tomwalters@0: int n ; tomwalters@0: FILE *fp ; tomwalters@0: { tomwalters@0: char c ; short s ; int i ; float f ; double d ; tomwalters@0: int j ; tomwalters@0: tomwalters@0: switch ( type ) { tomwalters@0: case 0 : for ( j = 0 ; j < n ; j++ ) { tomwalters@0: c = (char) *y++ ; tomwalters@0: fwrite( &c, sizeof(char), 1, fp ) ; tomwalters@0: } tomwalters@0: break ; tomwalters@0: case 1 : for ( j = 0 ; j < n ; j++ ) { tomwalters@0: s = (short) *y++ ; tomwalters@0: fwrite( &s, sizeof(short), 1, fp ) ; tomwalters@0: } tomwalters@0: break ; tomwalters@0: case 2 : for ( j = 0 ; j < n ; j++ ) { tomwalters@0: i = (int) *y++ ; tomwalters@0: fwrite( &i, sizeof(int), 1, fp ) ; tomwalters@0: } tomwalters@0: break ; tomwalters@0: case 3 : for ( j = 0 ; j < n ; j++ ) { tomwalters@0: f = (float) *y++ ; tomwalters@0: fwrite( &f, sizeof(float), 1, fp ) ; tomwalters@0: } tomwalters@0: break ; tomwalters@0: case 4 : for ( j = 0 ; j < n ; j++ ) { tomwalters@0: d = (double) *y++ ; tomwalters@0: fwrite( &d, sizeof(double), 1, fp ) ; tomwalters@0: } tomwalters@0: break ; tomwalters@0: case 5 : tomwalters@0: case 6 : for ( j = 0 ; j < n ; j++ ) tomwalters@0: fprintf( fp, "%*.*f\n", FIELDWIDTH, PRECISION, *y++ ) ; tomwalters@0: break ; tomwalters@0: } tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Read the next n items of `type' from the given stream within inclusive range tomwalters@0: limits `a' and `b', (found using, eg: range( rangestr, &a, &b, samplerate ) ). tomwalters@0: This is a version of readitem which incorporates the intial seek and handles tomwalters@0: range=max correctly. tomwalters@0: The `type' is an index to the datatype list in options.h, obtained for tomwalters@0: example using: typeindex( typestr ). tomwalters@0: Assign the item as a float in address `y', and return the number of items tomwalters@0: read. Return 0 if eof or error. tomwalters@0: */ tomwalters@0: tomwalters@0: nextitem( y, type, n, fp, a, b ) tomwalters@0: float *y ; tomwalters@0: int type ; tomwalters@0: int n ; tomwalters@0: FILE *fp ; tomwalters@0: int a, b ; tomwalters@0: { tomwalters@0: static int count = 0 ; /* total items read over all calls */ tomwalters@0: int num ; /* number items read this call */ tomwalters@0: float x ; tomwalters@0: tomwalters@0: if ( count == 0 ) { tomwalters@0: tomwalters@0: if ( a == (-1) ) { /* range=max or range=max-max */ tomwalters@0: if ( n == 1 && readitem( &x, type, 1, fp ) ) { tomwalters@0: *y = x ; tomwalters@0: while ( readitem( &x, type, 1, fp ) ) tomwalters@0: *y = x ; tomwalters@0: num = 1 ; tomwalters@0: } tomwalters@0: else num = 0 ; tomwalters@0: } tomwalters@0: tomwalters@0: else { tomwalters@0: if ( seektype( a, type, fp ) < a ) tomwalters@0: num = 0 ; tomwalters@0: else { tomwalters@0: if ( b >= 0 && n > b-a+1 ) /* n restricted by upper limit */ tomwalters@0: n = b-a+1 ; tomwalters@0: for ( num = 0 ; num < n && readitem( &x, type, 1, fp ) ; num++ ) tomwalters@0: y[num] = x ; tomwalters@0: } tomwalters@0: } tomwalters@0: tomwalters@0: count += num ; tomwalters@0: } tomwalters@0: tomwalters@0: else { tomwalters@0: if ( b >= 0 && count >= b-a+1 ) tomwalters@0: num = 0 ; tomwalters@0: else { tomwalters@0: if ( b >= 0 && n > b-a+1 - count ) /* n restricted by upper limit */ tomwalters@0: n = b-a+1 - count ; tomwalters@0: for ( num = 0 ; num < n && readitem( &x, type, 1, fp ) ; num++ ) tomwalters@0: y[num] = x ; tomwalters@0: } tomwalters@0: tomwalters@0: count += num ; tomwalters@0: } tomwalters@0: tomwalters@0: return num ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Return the type index (to the datatype list in options.h) of the given type tomwalters@0: string. String matching allows for abbreviations. tomwalters@0: If the given type string is in error then return an error code (less than 0): tomwalters@0: -1 type string not found in datatype list. tomwalters@0: -2 type string is ambiguous. tomwalters@0: */ tomwalters@0: tomwalters@0: int typeindex( typestr ) tomwalters@0: char *typestr ; tomwalters@0: { tomwalters@0: return ( listindex( datatype, typestr ) ) ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Return a number of bytes for a given type index (to the datatype list in tomwalters@0: options.h). Datatype "ASCII" is a special case for which 0 is returned. tomwalters@0: */ tomwalters@0: tomwalters@0: int typebytes( type ) tomwalters@0: int type ; tomwalters@0: { tomwalters@0: int bytes ; tomwalters@0: tomwalters@0: switch ( type ) { tomwalters@0: case 0 : bytes = sizeof( char ) ; break ; tomwalters@0: case 1 : bytes = sizeof( short ) ; break ; tomwalters@0: case 2 : bytes = sizeof( int ) ; break ; tomwalters@0: case 3 : bytes = sizeof( float ) ; break ; tomwalters@0: case 4 : bytes = sizeof( double ) ; break ; tomwalters@0: case 5 : /* ASCII */ tomwalters@0: case 6 : bytes = 0 ; break ; /* ascii */ tomwalters@0: } tomwalters@0: return ( bytes ) ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Return a number of bytes for a given type string. tomwalters@0: The string can be a number (of bytes), or a token from the `datatype' list, tomwalters@0: (see options.h) with which it is matched, allowing for abbreviations. tomwalters@0: Datatype "ASCII" or "ascii" is a special case for which 0 is returned. tomwalters@0: If the given type string is in error then return an error code (less than 0): tomwalters@0: -1 type string not found in datatype list. tomwalters@0: -2 type string is ambiguous. tomwalters@0: -3 type string is a negative number. tomwalters@0: */ tomwalters@0: tomwalters@0: int databytes( typestr ) tomwalters@0: char *typestr ; tomwalters@0: { tomwalters@0: int type, bytes ; tomwalters@0: tomwalters@0: if ( isnumber( typestr ) ) { /* case of bytes number string */ tomwalters@0: if ( ( bytes = atoi( typestr ) ) < 0 ) tomwalters@0: return ( -3 ) ; tomwalters@0: else return bytes ; tomwalters@0: } tomwalters@0: tomwalters@0: if ( ( type = typeindex( typestr ) ) < 0 ) tomwalters@0: return type ; tomwalters@0: tomwalters@0: return ( typebytes( type ) ) ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Check for over or underflow when the given float is scaled and cast into the tomwalters@0: given data type (an index to the datatype list in options.h). tomwalters@0: Return a more appropriate scale factor, or a scale factor of 1 if no over tomwalters@0: or underflow. The returned scale factor is the minimum scale factor over tomwalters@0: a succession of calls to check_overflow. tomwalters@0: */ tomwalters@0: tomwalters@0: float check_overflow( p, scale, type ) tomwalters@0: float p, scale ; tomwalters@0: int type ; tomwalters@0: { tomwalters@0: float f, p1 = p * scale ; tomwalters@0: static float first = 1 ; tomwalters@0: static float max, newscale ; tomwalters@0: tomwalters@0: if ( type >= 3 ) return ( 1. ) ; tomwalters@0: tomwalters@0: if ( first ) { tomwalters@0: tomwalters@0: switch ( type ) { tomwalters@0: case 0 : max = pow( 2., 8.*sizeof(char)-1 ) - 1 ; break ; tomwalters@0: case 1 : max = pow( 2., 8.*sizeof(short)-1 ) - 1 ; break ; tomwalters@0: case 2 : max = pow( 2., 8.*sizeof(int)-1 ) - 1 ; break ; tomwalters@0: } tomwalters@0: tomwalters@0: newscale = 1. ; tomwalters@0: first = 0 ; tomwalters@0: } tomwalters@0: tomwalters@0: if ( p1 > max ) f = max / p ; tomwalters@0: else if ( p1 < (-max) ) f = -( max / p ) ; tomwalters@0: else f = 1. ; tomwalters@0: tomwalters@0: if ( f < newscale ) tomwalters@0: newscale = f ; tomwalters@0: tomwalters@0: return ( newscale ) ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Some particular string tests. tomwalters@0: */ tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Test for "on" or any string other than "0". tomwalters@0: */ tomwalters@0: tomwalters@0: int ison( s ) tomwalters@0: char *s ; tomwalters@0: { tomwalters@0: return ( strcmp( "on", s ) == 0 || ( isnumber( s ) && strcmp( "0", s ) != 0 ) ) ; tomwalters@0: } tomwalters@0: tomwalters@0: /* tomwalters@0: Test for "off" or "0". tomwalters@0: */ tomwalters@0: tomwalters@0: int isoff( s ) tomwalters@0: char *s ; tomwalters@0: { tomwalters@0: return ( strcmp( "off", s ) == 0 || strcmp( "0", s ) == 0 ) ; tomwalters@0: } tomwalters@0: tomwalters@0: /* tomwalters@0: Test for "min". tomwalters@0: */ tomwalters@0: tomwalters@0: int ismin( s ) tomwalters@0: char *s ; tomwalters@0: { tomwalters@0: return ( strcmp( "min", s ) == 0 ) ; tomwalters@0: } tomwalters@0: tomwalters@0: /* tomwalters@0: Test for "max". tomwalters@0: */ tomwalters@0: tomwalters@0: int ismax( s ) tomwalters@0: char *s ; tomwalters@0: { tomwalters@0: return ( strcmp( "max", s ) == 0 ) ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: tomwalters@0: tomwalters@0: /*************************************************************************/ tomwalters@0: tomwalters@0: /* tomwalters@0: Print help for user tomwalters@0: help=on or -help gets standard help for all except SILENT options. tomwalters@0: help=all gets standard help for all options. tomwalters@0: help=syntax gets help with syntax instead of comment for all except SILENT. tomwalters@0: help= gets help for the named option (which can be abbreviated). tomwalters@0: */ tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: types of helpopts (see defines in options.h) tomwalters@0: tomwalters@0: helpopts standard usage, exit when done tomwalters@0: helpopts1 standard usage, supplied function for exit or additional help tomwalters@0: helpopts2 supplied usage and function for exit or additional help tomwalters@0: helpopts3 supplied usage, exit when done tomwalters@0: */ tomwalters@0: tomwalters@0: tomwalters@0: gethelp( helpstr, prog, applic, usage, option, tail ) tomwalters@0: char *helpstr ; tomwalters@0: char *prog ; tomwalters@0: char *applic ; tomwalters@0: char *usage ; tomwalters@0: Options *option ; tomwalters@0: int (*tail)() ; /* function for exit or additional help */ tomwalters@0: { tomwalters@0: int i, index[64], span[64] ; tomwalters@0: tomwalters@0: if ( isempty( helpstr ) || ison( helpstr ) || isstr( helpstr, "all" ) || isstr( helpstr, "syntax" ) ) tomwalters@0: help( prog, applic, usage, option, isstr( helpstr, "all" ), isstr( helpstr, "syntax" ) ) ; tomwalters@0: else { tomwalters@0: i = whichopt( option, helpstr, index, span ) ; tomwalters@0: if ( i == 0 ) tomwalters@0: fprintf(stderr,"%s: unknown option [%s]\n", prog, helpstr ) ; tomwalters@0: else if ( i > 1 ) tomwalters@0: fprintf(stderr,"%s: ambiguous option [%s]\n", prog, helpstr ) ; tomwalters@0: else tomwalters@0: helpshot( option[*index] ) ; /* single-line help i'th option */ tomwalters@0: } tomwalters@0: (*tail)() ; tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: tomwalters@0: help( prog, applic, usage, option, all, syntax ) tomwalters@0: char *prog, *applic, *usage ; tomwalters@0: Options *option ; tomwalters@0: int all, syntax ; tomwalters@0: { tomwalters@0: int i ; tomwalters@0: char *which_syntax(); tomwalters@0: tomwalters@0: if ( usage == (char *)0 ) /* a standard usage */ tomwalters@0: printf( "%s: %s\nUsage: %s [arguments] [file]\n", prog, applic, prog ); tomwalters@0: else tomwalters@0: printf( "%s: %s\nUsage: %s\n", prog, applic, usage ); tomwalters@0: tomwalters@0: if ( num_printed_opts( option, all ) > 0 ) { tomwalters@0: printf("name default comment\n"); tomwalters@0: printf("---------- ---------- --------.... \n"); tomwalters@0: } tomwalters@0: for (i=0 ; option[i].name != (char *)0 ; i++) { tomwalters@0: if ( !bitset( option[i].type, SILENT ) || all ) { tomwalters@0: if ( syntax ) tomwalters@0: syntaxline( option[i] ) ; tomwalters@0: else tomwalters@0: helpline( option[i] ) ; tomwalters@0: } tomwalters@0: } tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: helpline( option ) tomwalters@0: Options option ; tomwalters@0: { tomwalters@0: printf("%-10s ", option.name ); tomwalters@0: printf("%-10s ", option.dflt ); tomwalters@0: printf("%s\n" , option.help ); tomwalters@0: } tomwalters@0: tomwalters@0: syntaxline( option ) tomwalters@0: Options option ; tomwalters@0: { tomwalters@0: char *which_syntax(); tomwalters@0: tomwalters@0: printf("%-10s ", option.name ); tomwalters@0: printf("%-10s ", option.dflt ); tomwalters@0: printf("%s\n" , which_syntax( option.type ) ); tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: helpshot( option ) tomwalters@0: Options option ; tomwalters@0: { tomwalters@0: char *which_syntax(); tomwalters@0: tomwalters@0: printf("name: %s\n", option.name ); tomwalters@0: printf("default: %s\n", option.dflt ); tomwalters@0: printf("syntax: %s\n", which_syntax( option.type ) ); tomwalters@0: printf("comment: %s\n", option.help ); tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: /* tomwalters@0: Return the number of options which will get printed by help tomwalters@0: */ tomwalters@0: tomwalters@0: int num_printed_opts( option, all ) tomwalters@0: Options *option ; tomwalters@0: int all ; tomwalters@0: { tomwalters@0: int i, j=0 ; tomwalters@0: tomwalters@0: for (i=0 ; option[i].name != (char *)0 ; i++) tomwalters@0: if ( !bitset( option[i].type, SILENT ) ) tomwalters@0: j++ ; tomwalters@0: if ( all ) return ( i ) ; /* num all options */ tomwalters@0: else return ( j ) ; /* num non-SILENT options */ tomwalters@0: } tomwalters@0: tomwalters@0: tomwalters@0: char *which_syntax( type ) tomwalters@0: int type ; tomwalters@0: { tomwalters@0: if ( bitsset( type, ALL_SYNTAX ) ) return (char *)( All_Syntax ) ; tomwalters@0: if ( bitsset( type, VAL_SYNTAX ) ) return (char *)( Val_Syntax ) ; tomwalters@0: if ( bitsset( type, TOGGLE_SYNTAX ) ) return (char *)( Toggle_Syntax ) ; tomwalters@0: if ( bitsset( type, ONOFF_SYNTAX ) ) return (char *)( Onoff_Syntax ) ; tomwalters@0: if ( bitsset( type, EQ_SYNTAX ) ) return (char *)( Eq_Syntax ) ; tomwalters@0: if ( bitsset( type, ARG_SYNTAX ) ) return (char *)( Arg_Syntax ) ; tomwalters@0: if ( bitsset( type, FLAG_SYNTAX ) ) return (char *)( Flag_Syntax ) ; tomwalters@0: } tomwalters@0: tomwalters@0: