view tools/options.c @ 0:5242703e91d3 tip

Initial checkin for AIM92 aimR8.2 (last updated May 1997).
author tomwalters
date Fri, 20 May 2011 15:19:45 +0100
parents
children
line wrap: on
line source

#include <stdio.h>
#include <math.h>
#include "options.h"
#include "strmatch.h"
#include "units.h"


/*
Defaults for i/o parameters.
These are declared externally in options.h so they may be overridden by
option assignment.
*/

int LINE_LENGTH = 256 ; /* max length of line for ascii data.               */
int FIELDWIDTH  = 0   ; /* field width for printf.                          */
			/* (FIELDWIDTH=0 sets no extra fieldwidth.          */
			/* Positive integer fieldwidth sets right-justified */
			/* columns, negative integer fieldwidth sets left-  */
			/* justified columns).                              */
int PRECISION   = 3   ; /* precision (num decimal places) for printf,       */
			/* (PRECISION=0 sets integer output).               */



/*
Return a file pointer to a file `name' opened to read.
If `name' is `-' then return stdin.
If file `name' is not found return null pointer.
*/

FILE *fropen( name )
char *name ;
{
    FILE  *fopen() ;

    if ( isstr( name, "-" ) ) return stdin ;
    else  return ( fopen( name, "r" ) ) ;
}


/*
Generic options handler and file opener.
Return a file pointer to an opened file, either stdin (if no args remained on
command line after scanning) or the first name left on the command line.
*/

FILE *openopts( option, argc, argv )
Options *option ;
int      argc   ;
char    *argv[] ;
{
    FILE *fp ;
    int   i ;

    if ( ( i = getopts( option, argc, argv ) ) == 0 )
	return stdin ;
    if ( ( fp = fropen( argv[argc-i] ) ) == (FILE *)0 ) {
	fprintf(stderr, "%s: can't open %s\n", argv[0], argv[argc-i] ) ;
	exit( 1 ) ;
    }
    return ( fp ) ;
}


/*
Options handler and two file opener.
If one file is on command line after scanning options, then the first file
fp1 is assumed to be the stdin. Otherwise two files are expected.
Return 0 if incorrect number of files, otherwise return 1.
(Note: fp1 and fp2 are pts to file ptrs, so pass them as &fp1, &fp2 if
originally declared as file ptrs).
*/

open2opts( option, argc, argv, fp1, fp2 )
Options *option ;
int      argc   ;
char    *argv[] ;
FILE    **fp1   ;
FILE    **fp2   ;
{
    switch ( getopts( option, argc, argv ) ) {

	case 1 :
		*fp1 = stdin;
		if ( ( *fp2 = fropen( argv[argc-1] ) ) == (FILE *)0 ) {
		    fprintf( stderr,"%s: can't open %s\n", argv[0], argv[argc-1] ) ;
		    exit( 1 ) ;
		}
		break ;
	case 2 :
		if ( ( *fp1 = fropen( argv[argc-2] ) ) == (FILE *)0 ) {
		    fprintf( stderr,"%s: can't open %s\n", argv[0], argv[argc-2] ) ;
		    exit( 1 ) ;
		}
		if ( ( *fp2 = fropen( argv[argc-1] ) ) == (FILE *)0 ) {
		    fprintf( stderr,"%s: can't open %s\n", argv[0], argv[argc-1] ) ;
		    exit( 1 ) ;
		}
		break ;
	default :
		return ( 0 ) ;
    }
    return ( 1 ) ;
}



/*
  Build an options table.
  First assign all the option values with the defaults given in the table.
  Then scan the command line for arguments to overwrite corresponding vals.

  Argument syntax is one of the following three types (or combinations).
  Each syntax must contain either a leading '-' char or an embedded '=' char,
  to distinguish arguments from possible filenames.


  1)    -<name>                 FLAG_SYNTAX
  2)    -<name>[=]<value>       ARG_SYNTAX
  3)   [-]<name>=<value>        EQ_SYNTAX

  FLAG_SYNTAX takes no value. (The returned value is the empty string).

  ARG_SYNTAX takes a value after an optional '=' char.
  If the '=' char is not found, then the <value> is whatever follows the
  longest matching <name> string. Otherwise the <value> is whatever follows
  the '=' char. (The value is not allowed to be empty).

  EQ_SYNTAX takes a value which is whatever follows the '=' char.
  (The value is not allowed to be empty).

  It is assumed that all args remaining on the command line after an arg with
  invalid syntax must be filenames. These remain unscanned.
  Return argc, the number of args remaining unscanned on the command line,
  (argc=0 if no args left).
  To scan the remainder of the command line, and (eg) print the filenames:-

    if ( ( i = getopts( option, argc, argv, ) ) == 0 )
	printf("stdin\n" );
    else
	for ( ; i>0 ; i--)
	    printf("[%s]\n", argv[argc-i] );

*/


int getopts( option, argc, argv )
Options *option ;
int      argc   ;
char    *argv[] ;
{
    char *prog, *val ;
    int   i, n, index[64], span[64] ;

    /* Assign option vals using the defaults */

    for (i=0 ; option[i].name != (char *)0 ; i++)
	*(option[i].val) = option[i].dflt ;

    /* Overwrite option vals with corresponding vals found on command line */

    prog = *argv ;
    while ( --argc > 0 && ++argv ) {

	/* find list of options all with longest matching name span      */
	/* returning if none, with argc args remaining                   */
	if ( ( n = whichopt( option, *argv, index, span ) ) == 0 )
	    return ( argc ) ;

	/* check arg syntax, returning if invalid with argc args remaining */
	for ( i=0 ; i<n && ( val = checksyntax( *argv, span[i], option[index[i]].type ) ) == (char *)0  ; i++ )
	    ;
	if ( val == (char *)0 )
	    return ( argc ) ;

	/* if whichopt was ambiguous, yet an arg is valid, then its an     */
	/* ambiguous option, (and not a possible filename).                */
	if ( n > 1 ) {
	    fprintf(stderr,"%s: ambiguous option [%s]\n", prog, *argv ) ;
	    exit ( 1 ) ;
	}

	/* do operation */
	operate( option, *index, val ) ;
	side_effect( option, *index ) ;

    }
    return argc ;
}


/*
Find the default value of the first option whose name unambiguously matches
`s' (possibly abbreviated).
Return a ptr to the default value of the matching option.
Otherwise return the null ptr if a matching name is not found or is ambiguous.
*/

char *optdflt( option, s )
Options *option ;
char    *s      ;
{
    int  i ;

    if ( ( i = optindex( option, s ) ) < 0 )
	return ( (char *)0 ) ;
    return ( option[i].dflt ) ;
}

/*
Find the option whose name unambiguously matches `s' (possibly abbreviated).
Return the option list index of the matching option.
Otherwise return -1 if a matching name is not found, or -2 if the name is
ambiguous.
*/

int optindex( option, s )
Options *option ;
char    *s      ;
{
    int   i, n, index[64], span[64] ;

    if ( ( n = whichopt( option, s, index, span ) ) == 0 )
	return (-1) ;   /* option not found */
    if ( n > 1 )
	return (-2) ;   /* option name ambiguous */
    return ( *index ) ;
}


/*
Find the option whose name has the longest matching span with the head of `s'
(the argument string, possibly abbreviated or with trailing value parts).
Pass the option list index of the matching option back via arg `index'.
Pass the length of the matching span back via arg `span'.
Return the number of options found which have an equal longest matching span.
(Any `-' at the head of `s' is skipped before matching).
If there is an exact match (ie the longest matching span equals the length of
the option name) then return an unambiguous match.
*/

int whichopt( option, s, index, span )
Options *option ;
char    *s      ;
int     *index, *span ;
{
    int  i, j, n = 0, jmax = 0 ;

    if ( *s == '-' ) s++ ;  /* Skip leading hyphen  */

    for ( i = 0 ; option[i].name != (char *)0 ; i++ ) {

	if ( ( j = streqspn( s, option[i].name ) ) > jmax ) {
	    jmax = j ;                  /* a new longest matching span */
	    n = 0 ;
	    index[n]  = i ;
	    span[n++] = j ;
	}
	else if ( j > 0 && j == jmax ) {    /* ambiguous name with same matching span */
		index[n]  = i ;
		span[n++] = j ;
	}
    }

    for ( i = 0 ; i < n ; i++ )         /* check for an exact match */
	if ( span[i] == strlen( option[index[i]].name ) ) {
	    index[0] = index[i] ;
	    span[0]  = span[i] ;
	    return 1 ;
	}

    return n ;
}


/*
Check arg syntax and return a pointer to the value part, (or a pointer to the
terminating null if the value part is empty).
Return a NULL pointer if the syntax is invalid. It is assumed then that the
current arg (and all remaining) is a filename.
Print a warning if the syntax is an error rather than a possible filename,
(the filename will not be found in this case).

The check is arranged so that different option syntax types can be OR'd.
*/

char *checksyntax( s, span, type )
char *s ;
int   span, type ;
{
    int   i   ;
    char *v, *val = (char *)0 ;

    if ( bitset( type, FLAG_SYNTAX ) ) {        /*  -<name>             */
	if ( *s == '-' && *(v = s+1 + span) == '\0' )
	    val = v ;
    }

    if ( bitset( type, ARG_SYNTAX ) ) {         /*  -<name>[=]<value>   */
	if ( *s == '-' ) {
	    if ( *(v = s+1 + span) == '=' ) v++ ;
	    if ( *v != '\0' ) val = v ;
	}
    }

    if ( bitset( type, EQ_SYNTAX ) ) {          /* [-]<name>=<value>    */
	if ( *s == '-' ) s++ ;
	if ( *(v = s + span) == '=' && *++v != '\0' )
	    val = v ;
    }

    return ( val ) ;
}


/*
Return 1 if string s a possible option, (ie. a string which is not a <number>,
and contains either an embedded '=' or a leading '-').
Otherwise return 0.
*/

int isopt( s )
char  *s ;
{
    if ( isnumber( s ) ) return 0 ;
    if ( *s == '-' )     return 1 ;
    if ( strchr( s, '=' ) != (char *)0 ) return 1 ;
    return 0 ;
}


operate( option, i,  val )
Options *option ;
int      i ;
char    *val ;
{
    if ( bitset( option[i].type, LATCH ) )            /* LATCH  */
	*(option[i].val) = val ;

    else if ( bitset( option[i].type, AND ) )         /* AND    */
	*(option[i].val) = offstr ;

    else if ( bitset( option[i].type, OR  ) )         /* OR     */
	*(option[i].val) = onstr ;

    else if ( bitset( option[i].type, TOGGLE ) ) {    /* TOGGLE */
	if ( isempty( val ) ) {
	    if ( ison( *(option[i].val) ) )
		*(option[i].val) = offstr ;
	    else
		*(option[i].val) = onstr ;
	}
	else *(option[i].val) = val ;
    }
}


/*
Side-effects of options type specifiers.
*/

side_effect( option, j )
Options *option ;
int      j ;
{
    int   i ;
    char *exstr ;

    if ( bitset( option[j].type, EXCLUDE ) ) {  /* EXCLUDE */
	if ( ison( *(option[j].val) ) )
	    exstr = offstr ;
	else
	    exstr = onstr ;
	for ( i=0; option[i].name != (char *)0 ; i++)
	    if ( i != j && bitset( option[i].type, EXCLUDE ) )
		    *(option[i].val) = exstr  ;
    }
}



/*
Separate an option value string `optval' into two string tokens at the first
occurrence of a separator character `sep'. (Given as a string of one char).
Return the tokens via the pointers `val1' and `val2' (which could be passed
as the addresses of string pointers).
Return BADVAL if the option value string is bad (meaning that either `optval' was
null or empty, or the separator char was found but the 2nd token was missing).
Return 1 if the option value string is good (meaning that either two tokens
were found, or only the 1st token with no separator char, in which case `val2'
will be a ptr to an empty string). (See routine strpsep()).
*/

int getvals( optval, val1, val2, sep )
char  *optval ;
char **val1, **val2 ;
char  *sep ;
{
    *val1 = optval ;
    if ( isnull( *val2 = strsep( optval, sep ) ) )
	return BADVAL ;
    return 1 ;
}


/*
Parse a range selector of the form:  a[-b]
Return 0 if bad selectors, otherwise return 1.
Items are numbered 1,2,...,max, (where max is 0 by convention).
When either limit is "min", then set the respective value to 1.
When either limit is "max", then set the respective value to 0.
When `b' is missing, then set b=a.

If (a==0) then seek eof and process the final item.
Else seek past (a-1) items, and then process the next (b-a+1) items
(or all items until eof if (b==0)).
Eg:
    seekstart( a-1, bytes, fp ) ;
    for ( i = 0 ; ( b == 0  ||  i < b-a+1 ) ; i++ )
	do process

*/

int selector( s, a, b )
char *s ;
int  *a, *b ;
{
    char *val1, *val2 ;

    if ( getvals( s, &val1, &val2, "-" ) == BADVAL )
	return ( 0 ) ;
    if      ( ismin( val1 ) ) *a = 1 ;           /* first object         */
    else if ( ismax( val1 ) ) *a = 0 ;           /* last object          */
    else if ( ( *a = atoi( val1 ) ) <= 0 ) {     /* intermediate object  */
	if ( *a == 0 ) fprintf( stderr,"warning: selected items are numbered 1,...,max\n" ) ;
	return ( 0 ) ;
    }
    if ( isempty( val2 ) )    *b = *a ;          /* single object        */
    else if ( ismin( val2 ) ) *b = 1 ;
    else if ( ismax( val2 ) ) *b = 0 ;
    else if ( ( *b = atoi( val2 ) ) <= 0 ) {
	return ( 0 ) ;
    }
    if ( *b > 0  && ( *a == 0 || *a > *b ) )
	return ( 0 ) ;

    return ( 1 ) ;
}


/*
Parse a time range selector of the form:  a[-b]
The range is returned as limits meaning: start==a  duration==b-a+1.
(Note: when range=max or range=max-max then a==b==(-1) and duration==1).
Return 0 if bad selectors, otherwise return 1.
Time is numbered 0,2,...,max, (where max is (-1) by convention).
When either limit is "min", then set the respective value to 0.
When either limit is "max", then set the respective value to (-1).
When `b' is missing, then set b=a.
Time is specified as <number>[<units>], where <units> = {p, ms, s}.
Empty units are interpreted as `p' (ie sample points).
Convert `a' and `b' to sample points using the given samplerate.

(Note: this is like selector() but using to_p() instead of atoi(), and
numbering from 0 instead of 1).

If (a<0) then seek eof and process the final item.
Else seek past (a) items, and then process the next (b-a+1) items
(or all items until eof if (b==-1)).
Eg:
    seekstart( a, bytes, fp ) ;
    for ( i = 0 ; ( b < 0  ||  i < b-a+1 ) ; i++ )
	do process

*/

int range( s, a, b, samplerate )
char *s ;
int  *a, *b ;
int   samplerate ;
{
    char *val1, *val2 ;

    if ( getvals( s, &val1, &val2, "-" ) == BADVAL )
	return ( 0 ) ;
    if      ( ismin( val1 ) ) *a = 0    ;       /* first object         */
    else if ( ismax( val1 ) ) *a = (-1) ;       /* last object          */
    else if ( ( *a = to_p( val1, samplerate ) ) < 0 ) {
	return ( 0 ) ;
    }
    if ( isempty( val2 ) )    *b = *a   ;        /* single object        */
    else if ( ismin( val2 ) ) *b = 0    ;
    else if ( ismax( val2 ) ) *b = (-1) ;
    else if ( ( *b = to_p( val2, samplerate ) ) < 0 ) {
	return ( 0 ) ;
    }
    if ( *b >= 0  && ( *a < 0 || *a > *b ) )
	return ( 0 ) ;

    return ( 1 ) ;
}


/*
Seek n items of size bytes from current position in stream.
(Unlike fseek, this works even when fp is stdin).
Return the number of items actually read.
Example:  seekstart( n, sizeof(short), fp );
Datatype "ASCII" is a special case for which 0 is returned by the databytes
routine, so bytes==0 is taken to mean seek n ascii lines. In general use:
    seekstart( n, databytes( typestr ), fp )
*/

seekstart( n, bytes, fp )
int    n, bytes ;
FILE  *fp ;
{
    int   i    ;
    char *buf  ;
    char *line ;

    if ( bytes == 0 ) { /* special case meaning ascii lines */
	line = (char *)malloc( LINE_LENGTH ) ;
	for ( i = 0 ; i < n && fgets( line, LINE_LENGTH, fp ) != (char *)0 ; i++ )
	    ;
	free( line ) ;
	return ( i ) ;
    }

    if ( fp != stdin ) {
	if ( fseek( fp, (long)( n * bytes ), 1 ) == 0 )
	    return n ;          /* if improper, look for any input */
    }

    if ( ( buf = (char *)malloc( bytes ) ) == (char *)0 ) {
	fprintf( stderr, "seekstart: cannot allocate %d bytes\n", bytes ) ;
	exit( 1 ) ;
    }

    for ( i = 0 ; i < n && fread( buf, bytes, 1, fp ) ; i++ )
	;
    free( buf ) ;

    return ( i ) ;
}


/*
Seek n items of of given type from current position in stream.
This seekstart version takes a `type' arg instead of a size in `bytes'.
The `type' is an index to the datatype list in options.h, obtained for
example using: typeindex( typestr ).
*/

seektype( n, type, fp )
int    n, type ;
FILE  *fp ;
{
    return ( seekstart( n, typebytes( type ), fp ) ) ;
}


/*
Read the next n items of `type' from the given stream.
The `type' is an index to the datatype list in options.h, obtained for
example using: typeindex( typestr ).
Assign the item as a float in address `y', and return 1.
Return 0 if eof or error.
This can replace fread, eg:
	fread( &p, sizeof(short), 1, fp )               ( with short p )
becomes:
	readitem( &y, typeindex( "short" ), 1, fp )     ( with float y )
*/

readitem( y, type, n, fp )
float *y    ;
int    type ;
int    n    ;
FILE  *fp   ;
{
    char c ; short s ; int i ; float f ; double d ;
    int  j ;
    static char *line ;
    static int   first = 1 ;

    switch ( type ) {
	case   0  : for ( j = 0 ; j < n ; j++ ) {
			if ( fread( &c, sizeof(char),   1, fp ) == 0 ) return 0 ;
			*y++ = (float)c ;
		    }
		    break ;
	case   1  : for ( j = 0 ; j < n ; j++ ) {
			if ( fread( &s, sizeof(short),  1, fp ) == 0 ) return 0 ;
			*y++ = (float)s ;
		    }
		    break ;
	case   2  : for ( j = 0 ; j < n ; j++ ) {
			if ( fread( &i, sizeof(int),    1, fp ) == 0 ) return 0 ;
			*y++ = (float)i ;
		    }
		    break ;
	case   3  : for ( j = 0 ; j < n ; j++ ) {
			if ( fread( &f, sizeof(float),  1, fp ) == 0 ) return 0 ;
			*y++ = (float)f ;
		    }
		    break ;
	case   4  : for ( j = 0 ; j < n ; j++ ) {
			if ( fread( &d, sizeof(double), 1, fp ) == 0 ) return 0 ;
			*y++ = (float)d ;
		    }
		    break ;
	case   5  :
	case   6  : if ( first ) {
			line = (char *)malloc( LINE_LENGTH ) ;
			first = 0 ;
		    }
		    for ( j = 0 ; j < n ; j++ ) {
			if ( fgets( line, LINE_LENGTH, fp ) == (char *)0 )      return 0 ;
			*y++ = atof( line ) ;
		    }
		    break ;
    }
    return 1 ;
}


/*
Write the given n items of `type' onto the given stream.
The `type' is an index to the datatype list in options.h, (obtained for
example using: typeindex( typestr ) ).
*/

writeitem( y, type, n, fp )
float *y    ;
int    type ;
int    n    ;
FILE  *fp   ;
{
    char c ; short s ; int i ; float f ; double d ;
    int  j ;

    switch ( type ) {
	case   0  : for ( j = 0 ; j < n ; j++ ) {
			c = (char)   *y++ ;
			fwrite( &c, sizeof(char),   1, fp ) ;
		    }
		    break ;
	case   1  : for ( j = 0 ; j < n ; j++ ) {
			s = (short)  *y++ ;
			fwrite( &s, sizeof(short),  1, fp ) ;
		    }
		    break ;
	case   2  : for ( j = 0 ; j < n ; j++ ) {
			i = (int)    *y++ ;
			fwrite( &i, sizeof(int),    1, fp ) ;
		    }
		    break ;
	case   3  : for ( j = 0 ; j < n ; j++ ) {
			f = (float)  *y++ ;
			fwrite( &f, sizeof(float),  1, fp ) ;
		    }
		    break ;
	case   4  : for ( j = 0 ; j < n ; j++ ) {
			d = (double) *y++ ;
			fwrite( &d, sizeof(double), 1, fp ) ;
		    }
		    break ;
	case   5  :
	case   6  : for ( j = 0 ; j < n ; j++ )
			fprintf( fp, "%*.*f\n", FIELDWIDTH, PRECISION, *y++ ) ;
		    break ;
    }
}


/*
Read the next n items of `type' from the given stream within inclusive range
limits `a' and `b', (found using, eg: range( rangestr, &a, &b, samplerate ) ).
This is a version of readitem which incorporates the intial seek and handles
range=max correctly.
The `type' is an index to the datatype list in options.h, obtained for
example using: typeindex( typestr ).
Assign the item as a float in address `y', and return the number of items
read. Return 0 if eof or error.
*/

nextitem( y, type, n, fp, a, b )
float *y    ;
int    type ;
int    n    ;
FILE  *fp   ;
int    a, b ;
{
    static int count = 0 ;      /* total items read over all calls */
    int    num ;                /* number items read this call     */
    float  x   ;

    if ( count == 0 ) {

	if ( a == (-1) ) {      /* range=max or range=max-max */
	    if ( n == 1 && readitem( &x, type, 1, fp ) ) {
		*y = x ;
		while ( readitem( &x, type, 1, fp ) )
		    *y = x ;
		num = 1 ;
	    }
	    else num = 0 ;
	}

	else {
	    if ( seektype( a, type, fp ) < a )
		num = 0 ;
	    else {
		if ( b >= 0 && n > b-a+1 )  /* n restricted by upper limit */
		    n = b-a+1 ;
		for ( num = 0 ; num < n && readitem( &x, type, 1, fp ) ; num++ )
		    y[num] = x ;
	    }
	}

	count += num ;
    }

    else {
	if ( b >= 0 && count >= b-a+1 )
	    num = 0 ;
	else {
	    if ( b >= 0 && n > b-a+1 - count )  /* n restricted by upper limit */
		n = b-a+1 - count ;
	    for ( num = 0 ; num < n && readitem( &x, type, 1, fp ) ; num++ )
		y[num] = x ;
	}

	count += num ;
    }

    return num ;
}




/*
Return the type index (to the datatype list in options.h) of the given type
string. String matching allows for abbreviations.
If the given type string is in error then return an error code (less than 0):
    -1  type string not found in datatype list.
    -2  type string is ambiguous.
*/

int typeindex( typestr )
char   *typestr  ;
{
    return ( listindex( datatype, typestr ) ) ;
}


/*
Return a number of bytes for a given type index (to the datatype list in
options.h). Datatype "ASCII" is a special case for which 0 is returned.
*/

int typebytes( type )
int type ;
{
    int  bytes ;

    switch ( type ) {
	case   0  : bytes = sizeof( char   ) ;  break ;
	case   1  : bytes = sizeof( short  ) ;  break ;
	case   2  : bytes = sizeof( int    ) ;  break ;
	case   3  : bytes = sizeof( float  ) ;  break ;
	case   4  : bytes = sizeof( double ) ;  break ;
	case   5  :                                     /* ASCII */
	case   6  : bytes = 0 ;                 break ; /* ascii */
    }
    return ( bytes ) ;
}



/*
Return a number of bytes for a given type string.
The string can be a number (of bytes), or a token from the `datatype' list,
(see options.h) with which it is matched, allowing for abbreviations.
Datatype "ASCII" or "ascii" is a special case for which 0 is returned.
If the given type string is in error then return an error code (less than 0):
    -1  type string not found in datatype list.
    -2  type string is ambiguous.
    -3  type string is a negative number.
*/

int databytes( typestr )
char   *typestr  ;
{
    int  type, bytes ;

    if ( isnumber( typestr ) ) {        /* case of bytes number string */
	if ( ( bytes = atoi( typestr  ) ) < 0 )
	    return ( -3 ) ;
	else return bytes ;
    }

    if ( ( type = typeindex( typestr ) ) < 0 )
	return type ;

    return ( typebytes( type ) ) ;
}


/*
Check for over or underflow when the  given float is scaled and cast into the
given data type (an index to the datatype list in options.h).
Return a more appropriate scale factor, or a scale factor of 1 if no over
or underflow. The returned scale factor is the minimum scale factor over
a succession of calls to check_overflow.
*/

float check_overflow( p, scale, type )
float p, scale ;
int   type ;
{
    float  f, p1 = p * scale  ;
    static float first = 1 ;
    static float max, newscale ;

    if ( type >= 3 ) return ( 1. ) ;

    if ( first ) {

	switch ( type ) {
	    case   0  : max = pow( 2., 8.*sizeof(char)-1  ) - 1 ;  break ;
	    case   1  : max = pow( 2., 8.*sizeof(short)-1 ) - 1 ;  break ;
	    case   2  : max = pow( 2., 8.*sizeof(int)-1   ) - 1 ;  break ;
	}

	newscale = 1. ;
	first = 0 ;
    }

    if      ( p1 > max    ) f =    max / p   ;
    else if ( p1 < (-max) ) f = -( max / p ) ;
    else                    f = 1.           ;

    if ( f < newscale )
	newscale = f ;

    return ( newscale ) ;
}



/*
Some particular string tests.
*/


/*
Test for "on" or any <number> string other than "0".
*/

int ison( s )
char *s ;
{
    return ( strcmp( "on", s ) == 0 || ( isnumber( s ) && strcmp( "0", s ) != 0 ) ) ;
}

/*
Test for "off" or "0".
*/

int isoff( s )
char *s ;
{
    return ( strcmp( "off", s ) == 0 || strcmp( "0", s ) == 0 ) ;
}

/*
Test for "min".
*/

int ismin( s )
char *s ;
{
    return ( strcmp( "min", s ) == 0 ) ;
}

/*
Test for "max".
*/

int ismax( s )
char *s ;
{
    return ( strcmp( "max", s ) == 0 ) ;
}




/*************************************************************************/

/*
  Print help for user
  help=on or -help gets standard help for all except SILENT options.
  help=all gets standard help for all options.
  help=syntax gets help with syntax instead of comment for all except SILENT.
  help=<name> gets help for the named option (which can be abbreviated).
*/


/*
  types of helpopts (see defines in options.h)

helpopts   standard usage, exit when done
helpopts1  standard usage, supplied function for exit or additional help
helpopts2  supplied usage and function for exit or additional help
helpopts3  supplied usage, exit when done
*/


gethelp( helpstr, prog, applic, usage, option, tail )
char    *helpstr ;
char    *prog    ;
char    *applic  ;
char    *usage   ;
Options *option  ;
int    (*tail)() ;      /* function for exit or additional help */
{
    int  i, index[64], span[64] ;

    if ( isempty( helpstr ) || ison( helpstr ) || isstr( helpstr, "all" ) || isstr( helpstr, "syntax" ) )
	help( prog, applic, usage, option, isstr( helpstr, "all" ), isstr( helpstr, "syntax" ) ) ;
    else {
	i = whichopt( option, helpstr, index, span ) ;
	if ( i == 0 )
	    fprintf(stderr,"%s: unknown option [%s]\n", prog, helpstr ) ;
	else if ( i > 1 )
	    fprintf(stderr,"%s: ambiguous option [%s]\n", prog, helpstr ) ;
	else
	    helpshot( option[*index] ) ; /* single-line help i'th option */
    }
    (*tail)() ;
}



help( prog, applic, usage, option, all, syntax )
char    *prog, *applic, *usage ;
Options *option ;
int      all, syntax ;
{
    int  i ;
    char *which_syntax();

    if ( usage == (char *)0 )   /* a standard usage */
	printf( "%s:  %s\nUsage: %s  [arguments]  [file]\n", prog, applic, prog );
    else
	printf( "%s:  %s\nUsage: %s\n", prog, applic, usage );

    if ( num_printed_opts( option, all ) > 0 ) {
	printf("name        default     comment\n");
	printf("----------  ----------  --------.... \n");
    }
    for (i=0 ; option[i].name != (char *)0 ; i++) {
	if ( !bitset( option[i].type, SILENT ) || all ) {
	    if ( syntax )
		syntaxline( option[i] ) ;
	    else
		helpline( option[i] ) ;
	}
    }
}


helpline( option )
Options option ;
{
    printf("%-10s  ", option.name );
    printf("%-10s  ", option.dflt );
    printf("%s\n"   , option.help );
}

syntaxline( option )
Options option ;
{
    char *which_syntax();

    printf("%-10s  ", option.name );
    printf("%-10s  ", option.dflt );
    printf("%s\n"   , which_syntax( option.type ) );
}


helpshot( option )
Options option ;
{
    char *which_syntax();

    printf("name:    %s\n", option.name );
    printf("default: %s\n", option.dflt );
    printf("syntax:  %s\n", which_syntax( option.type ) );
    printf("comment: %s\n", option.help );
}


/*
Return the number of options which will get printed by help
*/

int num_printed_opts( option, all )
Options *option ;
int      all    ;
{
    int  i, j=0 ;

    for (i=0 ; option[i].name != (char *)0 ; i++)
	if ( !bitset( option[i].type, SILENT ) )
	    j++ ;
    if ( all ) return ( i ) ;   /* num all options        */
    else       return ( j ) ;   /* num non-SILENT options */
}


char *which_syntax( type )
int  type ;
{
    if ( bitsset( type, ALL_SYNTAX    ) ) return (char *)( All_Syntax    ) ;
    if ( bitsset( type, VAL_SYNTAX    ) ) return (char *)( Val_Syntax    ) ;
    if ( bitsset( type, TOGGLE_SYNTAX ) ) return (char *)( Toggle_Syntax ) ;
    if ( bitsset( type, ONOFF_SYNTAX  ) ) return (char *)( Onoff_Syntax  ) ;
    if ( bitsset( type, EQ_SYNTAX     ) ) return (char *)( Eq_Syntax     ) ;
    if ( bitsset( type, ARG_SYNTAX    ) ) return (char *)( Arg_Syntax    ) ;
    if ( bitsset( type, FLAG_SYNTAX   ) ) return (char *)( Flag_Syntax   ) ;
}