/*++ HELP.C PrintHelp function split from options.c, 6/9/1997 by DavidCHR --*/ #include "private.h" #include 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