419 lines
10 KiB
C
419 lines
10 KiB
C
/*++
|
|
|
|
HELP.C
|
|
|
|
PrintHelp function
|
|
|
|
split from options.c, 6/9/1997 by DavidCHR
|
|
|
|
--*/
|
|
|
|
#include "private.h"
|
|
#include <malloc.h>
|
|
|
|
PCHAR SlashVector = "[- /]"; /* These should all be the same */
|
|
PCHAR BoolVector = "[- +]"; /* size-- for formatting reasons */
|
|
PCHAR ColonVector = " : "; /* Separator */
|
|
|
|
#ifdef DEBUG_OPTIONS
|
|
VOID OptionHelpDebugPrint( PCHAR fmt, ... );
|
|
#define HELPDEBUG OptionHelpDebugPrint
|
|
#else
|
|
#define HELPDEBUG
|
|
#endif
|
|
|
|
|
|
VOID
|
|
FillBufferWithRepeatedString( IN PCHAR repeated_string,
|
|
IN PCHAR buffer,
|
|
IN ULONG bufferLength /* without null */ ){
|
|
|
|
ULONG stringi, bufferj = 0;
|
|
ULONG size;
|
|
|
|
size = strlen( repeated_string );
|
|
|
|
if ( size == 0 ) {
|
|
|
|
memset( buffer, ' ', bufferLength );
|
|
|
|
} else {
|
|
|
|
for ( stringi = 0 ; stringi < bufferLength ; stringi++, bufferj++ ) {
|
|
|
|
buffer[ bufferj ] = repeated_string[ bufferj % size ];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buffer[ bufferLength ] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
/* PrintUsageEntry:
|
|
|
|
formats a single line of text and sends it out.
|
|
This is where all the output goes, so we can be assured that it all ends
|
|
up formatted the same. It uses the following globals so that clients
|
|
can adjust the values if needed. */
|
|
|
|
ULONG OptMaxHeaderLength = 5;
|
|
ULONG OptMaxCommandLength = 13;
|
|
ULONG OptMaxSeparatorLength = 3;
|
|
ULONG OptMaxDescriptionLength = 58;
|
|
|
|
VOID
|
|
PrintUsageEntry( FILE *out, // output file stream
|
|
PCHAR aHeader, // usually SlashVector, BoolVector or NULL
|
|
PCHAR aCommand, // command name or NULL
|
|
PCHAR aSeparator, // between command and description
|
|
PCHAR Description, // NULL-terminated string vector
|
|
BOOL fRepeatSeparator ) {
|
|
|
|
PCHAR output_line; // sick. see below
|
|
PCHAR Separator;
|
|
PCHAR Header;
|
|
PCHAR Command;
|
|
|
|
HELPDEBUG( "PrintUsageEntry( aHeader = \"%s\"\n"
|
|
" aCommand = \"%s\"\n"
|
|
" aSeparator = \"%s\"\n"
|
|
" Description = \"%s\"\n"
|
|
" fRepeat = %d )...\n",
|
|
|
|
aHeader, aCommand, aSeparator, Description,
|
|
fRepeatSeparator );
|
|
|
|
ASSERT( aSeparator != NULL );
|
|
|
|
if ( fRepeatSeparator ) {
|
|
|
|
#define EXPAND_TO_SEPARATOR( arg ) { \
|
|
PCHAR local_arg; \
|
|
arg = aSeparator; \
|
|
ASSERT( arg != NULL ); \
|
|
if ( strlen( arg ) < OptMax##arg##Length ) { \
|
|
arg = (PCHAR) alloca( ( OptMax##arg##Length+1 ) * sizeof( CHAR ) ); \
|
|
if ( arg ) { \
|
|
HELPDEBUG( "filling " #arg " with \"%s\"...", aSeparator ); \
|
|
FillBufferWithRepeatedString( aSeparator, arg, \
|
|
OptMax##arg##Length ); \
|
|
HELPDEBUG( "results in \"%s\".\n", arg ); \
|
|
} else { \
|
|
arg = a##arg; \
|
|
} \
|
|
} else { \
|
|
arg = a##arg; \
|
|
} \
|
|
}
|
|
|
|
/* BEWARE:
|
|
|
|
if you are using emacs, this next statement may not automatically
|
|
format correctly. Set it manually and the other lines will fix
|
|
themselves.
|
|
|
|
This is a bug in emacs's macro-handling code. :-) */
|
|
|
|
EXPAND_TO_SEPARATOR( Separator ); // separator may need expanding anyway
|
|
|
|
if ( !aHeader) {
|
|
EXPAND_TO_SEPARATOR( Header );
|
|
} else {
|
|
Header = aHeader;
|
|
}
|
|
if ( !aCommand ) {
|
|
EXPAND_TO_SEPARATOR( Command );
|
|
} else {
|
|
Command = aCommand;
|
|
}
|
|
|
|
} else {
|
|
|
|
Separator = aSeparator;
|
|
Header = aHeader;
|
|
Command = aCommand;
|
|
|
|
ASSERT( Separator != NULL );
|
|
ASSERT( Header != NULL );
|
|
ASSERT( Command != NULL );
|
|
|
|
}
|
|
|
|
/* before we try to do all this sick string manipulation, try to
|
|
allocate the buffer. If this fails, well... it'll save us the
|
|
trouble. :-) */
|
|
|
|
output_line = (PCHAR) alloca( ( OptMaxHeaderLength +
|
|
OptMaxCommandLength +
|
|
OptMaxSeparatorLength +
|
|
OptMaxDescriptionLength +
|
|
2 /* NULL-termination */ ) *
|
|
sizeof( CHAR ) );
|
|
if ( output_line ) {
|
|
|
|
PCHAR index;
|
|
CHAR outputFormatter[ 10 ] = { 0 }; // "%50hs" and the like
|
|
|
|
#ifdef WINNT // ugh. Why can't we support this function? I can't find it...
|
|
#define snprintf _snprintf
|
|
#endif
|
|
|
|
#define FORMAT_FORMAT( arg ) { \
|
|
snprintf( outputFormatter, sizeof( outputFormatter), \
|
|
"%%%ds", OptMax##arg##Length ); \
|
|
HELPDEBUG( #arg ": formatter = \"%s\"\n ", outputFormatter ); \
|
|
HELPDEBUG( "input value = \"%s\"\n", arg ); \
|
|
snprintf( index, OptMax##arg##Length, \
|
|
outputFormatter, arg ); \
|
|
index[ OptMax##arg##Length ] = '\0'; \
|
|
HELPDEBUG( "output = \"%s\"\n", index ); \
|
|
index += OptMax##arg##Length; \
|
|
}
|
|
|
|
index = output_line;
|
|
|
|
FORMAT_FORMAT( Header );
|
|
FORMAT_FORMAT( Command );
|
|
FORMAT_FORMAT( Separator );
|
|
|
|
// the description does not want to be right-justified.
|
|
|
|
snprintf( index, OptMaxDescriptionLength, "%s", Description );
|
|
index[OptMaxDescriptionLength] = '\0';
|
|
|
|
#undef FORMAT_FORMAT
|
|
|
|
fprintf( out, "%s\n", output_line );
|
|
|
|
} else {
|
|
|
|
fprintf( stderr,
|
|
"ERROR: cannot format for %s %s %s -- "
|
|
"STACK SPACE EXHAUSTED\n",
|
|
Header, Command, Description );
|
|
|
|
fprintf( out, "%s%s%s%s\n", Header, Command,
|
|
aSeparator, Description );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
PrintUsage( FILE *out,
|
|
ULONG flags,
|
|
optionStruct *options,
|
|
PCHAR prefix /* can be NULL */) {
|
|
|
|
ULONG i;
|
|
BOOL PrintAnything = TRUE;
|
|
PCHAR Syntax = NULL;
|
|
PCHAR CommandName = NULL;
|
|
PCHAR Description = NULL;
|
|
PCHAR Separator = NULL;
|
|
|
|
fprintf(out, "Command line options:\n\n");
|
|
|
|
|
|
for (i = 0 ; !ARRAY_TERMINATED( options+i ); i++ ) {
|
|
|
|
Description = options[i].helpMsg;
|
|
|
|
HELPDEBUG("option %d has flags 0x%x\n", i, options[i].flags );
|
|
|
|
if ( options[i].flags & OPT_HIDDEN ) {
|
|
continue;
|
|
}
|
|
|
|
if ( options[i].flags & OPT_NOSWITCH ) {
|
|
Syntax = "";
|
|
} else {
|
|
Syntax = SlashVector;
|
|
}
|
|
|
|
if ( options[i].flags & OPT_NOCOMMAND ) {
|
|
CommandName = "";
|
|
} else {
|
|
CommandName = options[i].cmd;
|
|
}
|
|
|
|
if ( options[i].flags & OPT_NOSEPARATOR ) {
|
|
Separator = "";
|
|
} else {
|
|
Separator = ColonVector;
|
|
}
|
|
|
|
switch (options[i].flags & OPT_MUTEX_MASK) {
|
|
|
|
case OPT_ENUMERATED:
|
|
|
|
{
|
|
|
|
// special case.
|
|
|
|
CHAR HeaderBuffer[ 22 ]; // formatting = 21 chars wide + null
|
|
|
|
HELPDEBUG("[OPT_ENUM]");
|
|
|
|
PrintAnything = FALSE;
|
|
|
|
sprintf( HeaderBuffer, "%5hs%13hs%3hs", SlashVector,
|
|
CommandName, Separator );
|
|
|
|
fprintf( out, "%hs%hs\n", HeaderBuffer, Description );
|
|
|
|
fprintf( out, "%hs is one of: \n", HeaderBuffer );
|
|
|
|
PrintEnumValues( out, HeaderBuffer,
|
|
( optEnumStruct * ) options[i].optData );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case OPT_PAUSE:
|
|
|
|
HELPDEBUG("[OPT_PAUSE]");
|
|
|
|
PrintAnything = FALSE;
|
|
|
|
if ( !Description ) {
|
|
Description = "Press [ENTER] to continue";
|
|
}
|
|
|
|
fprintf( stderr, "%hs\n", Description );
|
|
|
|
getchar();
|
|
|
|
break;
|
|
|
|
case OPT_DUMMY:
|
|
|
|
PrintUsageEntry( out,
|
|
( options[i].flags & OPT_NOSWITCH ) ?
|
|
"" : NULL,
|
|
( options[i].flags & OPT_NOCOMMAND ) ?
|
|
"" : NULL,
|
|
( options[i].flags & OPT_NOSEPARATOR ) ?
|
|
"" : "-" ,
|
|
Description, TRUE );
|
|
|
|
break;
|
|
|
|
case OPT_CONTINUE:
|
|
|
|
PrintUsageEntry( out, "", "", "", Description, FALSE );
|
|
|
|
break;
|
|
|
|
|
|
case OPT_HELP:
|
|
|
|
if ( !Description ) {
|
|
Description = "Prints this message.";
|
|
}
|
|
|
|
PrintUsageEntry( out, Syntax, CommandName,
|
|
ColonVector, Description, FALSE );
|
|
|
|
break;
|
|
|
|
case OPT_SUBOPTION:
|
|
|
|
if ( !Description ) {
|
|
Description = "[ undocumented suboption ]";
|
|
}
|
|
|
|
PrintUsageEntry( out, Syntax, CommandName,
|
|
ColonVector, Description, FALSE );
|
|
|
|
break;
|
|
|
|
case OPT_BOOL:
|
|
|
|
PrintUsageEntry( out,
|
|
( ( options[i].flags & OPT_NOSWITCH ) ?
|
|
Syntax : BoolVector ), CommandName,
|
|
ColonVector, Description, FALSE );
|
|
|
|
break;
|
|
|
|
case OPT_STOP_PARSING:
|
|
|
|
if ( !Description ) {
|
|
Description = "Terminates optionlist.";
|
|
}
|
|
goto defaulted;
|
|
|
|
case OPT_FUNC2:
|
|
|
|
if ( !Description ) {
|
|
OPT_FUNC_PARAMETER_DATA optFuncData = { 0 };
|
|
|
|
optFuncData.argv = &( options[i].cmd );
|
|
|
|
HELPDEBUG("Jumping to OPT_FUNC2 0x%x...",
|
|
((POPTU) &options[i].data)->raw_data );
|
|
|
|
( (POPTU)&options[i].data)->func2( TRUE, &optFuncData );
|
|
|
|
break;
|
|
}
|
|
|
|
/* fallthrough-- if this one has no description,
|
|
they both will, so the next if will not be taken. */
|
|
|
|
case OPT_FUNC:
|
|
|
|
if ( !Description ) {
|
|
|
|
HELPDEBUG("Jumping to OPTFUNC 0x%x...",
|
|
((POPTU) &options[i].data)->raw_data );
|
|
|
|
( (POPTU) &options[i].data )->func( 0, NULL );
|
|
break;
|
|
}
|
|
|
|
// fallthrough
|
|
|
|
#ifdef WINNT
|
|
case OPT_WSTRING:
|
|
case OPT_USTRING:
|
|
#endif
|
|
case OPT_STRING:
|
|
case OPT_INT:
|
|
case OPT_FLOAT:
|
|
|
|
// fallthrough
|
|
|
|
default: // this is the default means.
|
|
defaulted:
|
|
|
|
#if (HIGHEST_OPTION_SUPPORTED != OPT_STOP_PARSING )
|
|
#error "new options? update this switch statement or bad things will happen."
|
|
#endif
|
|
|
|
PrintUsageEntry( out, Syntax, CommandName,
|
|
ColonVector, Description, FALSE );
|
|
|
|
}
|
|
|
|
if ( options[i].flags & OPT_ENVIRONMENT ) {
|
|
|
|
CHAR buffer[ MAX_PATH ];
|
|
|
|
sprintf( buffer, " (or set environment variable \"%hs\")",
|
|
options[i].optData );
|
|
|
|
PrintUsageEntry( out, "", CommandName, ColonVector,
|
|
buffer, FALSE );
|
|
}
|
|
|
|
} // for-loop
|
|
} // function
|
|
|