(view source code of secondchoice.cs as plain text)
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading;
using System.Timers;
namespace RobvanderWoude
{
class SecondChoice
{
static string progver = "1.02";
#region Global Variables
static bool cancel = false;
static int rc = 0;
static string choices = "YN";
static string defaultchoice = String.Empty;
#endregion Global Variables
static int Main( string[] args )
{
#region Capture Control+Break
// Code to gracefully exit on Ctrl+Break by Alois Kraus
// http://www.codeproject.com/Articles/16164/Managed-Application-Shutdown
//
// Note that this will NOT prevent passing on the Ctrl+Break to the hosting process, the
// only purpose for this code is to make this program exit wit errorlevel 0 on Ctrl+Break
Console.CancelKeyPress += delegate( object sender, ConsoleCancelEventArgs e )
{
if ( e.SpecialKey == ConsoleSpecialKey.ControlBreak )
{
e.Cancel = true;
new Thread( delegate( )
{
Environment.Exit( 0 );
} ).Start( );
}
};
#endregion Capture Control+Break
#region Local variables
bool casesensitive = false;
bool hidechoices = false;
bool usedefault = false;
bool usetimeout = false;
bool waitingforchoices = false;
bool waitingfordefault = false;
bool waitingformessage = false;
bool waitingfortimeout = false;
string message = String.Empty;
int timeout = -1;
#endregion Local variables
#region Command Line Parsing
foreach ( string arg in args )
{
if ( arg == "/?" )
{
return ShowHelp( );
}
}
if ( args.Length > 10 )
{
return ShowHelp( "Too many command line arguments" );
}
foreach ( string arg in args )
{
switch ( arg.ToUpper( ) )
{
// Check for command line switches separates by spaces
case "/C":
if ( choices.ToUpper( ) != "YN" || waitingforchoices )
{
return ShowHelp( "Duplicate command line switch /C" );
}
if ( waitingfordefault )
{
return ShowHelp( "/D is used, but no default choice is specified" );
}
if ( waitingformessage )
{
return ShowHelp( "/M is used, but no message is specified" );
}
if ( waitingfortimeout )
{
return ShowHelp( "/T is used, but no timeout is specified" );
}
waitingforchoices = true;
break;
case "/CS":
if ( casesensitive )
{
return ShowHelp( "Duplicate command line switch /CS" );
}
casesensitive = true;
break;
case "/D":
if ( usedefault || waitingfordefault )
{
return ShowHelp( "Duplicate command line switch /D" );
}
if ( waitingforchoices )
{
return ShowHelp( "/C is used, but no choices are specified" );
}
if ( waitingformessage )
{
return ShowHelp( "/M is used, but no message is specified" );
}
if ( waitingfortimeout )
{
return ShowHelp( "/T is used, but no timeout is specified" );
}
usedefault = true;
waitingfordefault = true;
break;
case "/M":
if ( waitingformessage )
{
return ShowHelp( "Duplicate command line switch /D" );
}
if ( waitingforchoices )
{
return ShowHelp( "/C is used, but no choices are specified" );
}
if ( waitingfordefault )
{
return ShowHelp( "/D is used, but no default choice is specified" );
}
if ( waitingfortimeout )
{
return ShowHelp( "/T is used, but no timeout is specified" );
}
waitingformessage = true;
break;
case "/N":
if ( hidechoices )
{
return ShowHelp( "Duplicate command line switch /N" );
}
hidechoices = true;
break;
case "/T":
if ( usetimeout || waitingfortimeout )
{
return ShowHelp( "Duplicate command line switch /T" );
}
if ( waitingforchoices )
{
return ShowHelp( "/C is used, but no choices are specified" );
}
if ( waitingfordefault )
{
return ShowHelp( "/D is used, but no default choice is specified" );
}
if ( waitingformessage )
{
return ShowHelp( "/M is used, but no message is specified" );
}
usetimeout = true;
waitingfortimeout = true;
break;
default:
// Check for values belonging to a previous switch
if ( waitingforchoices )
{
choices = arg;
waitingforchoices = false;
}
else if ( waitingfordefault )
{
defaultchoice = arg;
waitingfordefault = false;
}
else if ( waitingformessage )
{
message = arg;
waitingformessage = false;
}
else if ( waitingfortimeout )
{
try
{
timeout = Convert.ToInt32( arg );
waitingfortimeout = false;
}
catch ( Exception )
{
return ShowHelp( "Invalid timeout value \"{0}\"", arg );
}
}
else
{
// Check for arguments with values immediately appended, with colon or equal sign as separator
if ( arg.Length > 3 && arg[0] == '/' && "CDMNTcdmnt".IndexOf( arg[1] ) != -1 && ( arg[2] == ':' || arg[2] == '=' ) )
{
switch ( arg.ToUpper( ).Substring( 0, 2 ) )
{
case "/C":
if ( choices.ToUpper( ) != "YN" || waitingforchoices )
{
return ShowHelp( "Duplicate command line switch /C" );
}
choices = arg.Substring( 3 );
break;
case "/D":
if ( usedefault || waitingfordefault )
{
return ShowHelp( "Duplicate command line switch /D" );
}
usedefault = true;
defaultchoice = arg.Substring( 3 );
break;
case "/M":
if ( waitingformessage )
{
return ShowHelp( "Duplicate command line switch /D" );
}
message = arg.Substring( 3 );
break;
case "/T":
if ( usetimeout || waitingfortimeout )
{
return ShowHelp( "Duplicate command line switch /T" );
}
try
{
usetimeout = true;
timeout = Convert.ToInt32( arg.Substring( 3 ) );
}
catch ( Exception )
{
return ShowHelp( "Invalid timeout value \"{0}\"", arg );
}
break;
}
}
else
{
return ShowHelp( "Invalid command line argument \"{0}\"", arg );
}
}
break;
}
}
#endregion Command Line Parsing
#region Command Line Validation
// Timeout requires a default value
if ( usetimeout && !usedefault )
{
return ShowHelp( "If timeout is specified, default value must be specified too" );
}
// If a default is specified, it must be a single character, found in the list of choices
if ( usedefault && ( defaultchoice.Length != 1 || choices.IndexOfAny( defaultchoice.ToCharArray( ) ) == -1 ) )
{
return ShowHelp( "Default specified by /D must be a single character\n\tfrom the list of choices specified by /C" );
}
// Less than 1 choice is useless
if ( choices.Length < 2 )
{
return ShowHelp( "Specify at least 2 choices" );
}
// Check for duplicates in list of choices
Regex regex;
string pattern = @"([\w ]).*\1";
if ( casesensitive )
{
regex = new Regex( pattern );
}
else
{
regex = new Regex( pattern, RegexOptions.IgnoreCase );
}
if ( regex.IsMatch( choices ) )
{
return ShowHelp( "All choices specified by /C must be unique" );
}
if ( !casesensitive )
{
if ( usedefault )
{
// Show the list of choices in lower case, with the default choice in upper case
choices = choices.ToLower( ).Replace( defaultchoice.ToLower( ), defaultchoice.ToUpper( ) );
}
else
{
// Show the list of choices in upper case
choices = choices.ToUpper( );
}
}
if ( usetimeout && timeout > 0 )
{
// Check if specified timeout is in accepted range
if ( timeout < 0 || timeout > 86400 )
{
return ShowHelp( "Timeout must be in range 0..86400 seconds (0..24h)" );
}
System.Timers.Timer timer = new System.Timers.Timer( );
timer.Elapsed += new ElapsedEventHandler( timer_Elapsed );
timer.Interval = timeout * 1000;
timer.Start( );
}
#endregion Command Line Validation
#region Show Prompt
string prompt = String.Empty;
if ( hidechoices )
{
if ( !String.IsNullOrWhiteSpace( message ) )
{
prompt = String.Format( "{0}: ", message );
}
}
else
{
prompt = String.Format( "{0} [{1}]: ", message, choices ).TrimStart( );
}
#endregion Show Prompt
#region Handle Response
Console.Write( prompt );
// If timeout is zero, return default immediately
if ( usetimeout && timeout == 0 )
{
Console.WriteLine( defaultchoice );
rc = choices.IndexOf( defaultchoice[0] ) + 1;
return rc;
}
// Tell console to capture Ctrl+C as input
Console.TreatControlCAsInput = true;
// Wait for response
ConsoleKeyInfo response;
while ( !cancel )
{
response = Console.ReadKey( true );
string key = response.KeyChar.ToString( );
bool ctrlkey = ( ( ( response.Modifiers & ConsoleModifiers.Control ) != 0 ) && ( ( response.Modifiers & ConsoleModifiers.Alt ) == 0 ) && ( ( response.Modifiers & ConsoleModifiers.Shift ) == 0 ) );
bool shiftkey = ( ( ( response.Modifiers & ConsoleModifiers.Shift ) != 0 ) && ( ( response.Modifiers & ConsoleModifiers.Alt ) == 0 ) && ( ( response.Modifiers & ConsoleModifiers.Control ) == 0 ) );
bool nomodifiers = ( response.Modifiers == 0 );
bool escapekey = ( ( response.Key == ConsoleKey.Escape ) && nomodifiers );
bool enterkey = ( ( response.Key == ConsoleKey.Enter ) && nomodifiers );
// Ctrl+C
if ( ctrlkey && ( key == ( (char) 3 ).ToString( ) ) )
{
rc = 0;
cancel = true;
break;
}
// Escape
if ( escapekey )
{
rc = 0;
cancel = true;
break;
}
// Case sensitive?
if ( shiftkey || !casesensitive )
{
key = key.ToUpper( );
}
// Default on Enter
if ( enterkey && usedefault )
{
rc = choices.IndexOf( defaultchoice[0] ) + 1;
if ( rc > 0 )
{
Console.WriteLine( defaultchoice );
break;
}
}
// Check if valid key pressed
rc = choices.IndexOf( key[0] ) + 1;
if ( rc > 0 )
{
Console.WriteLine( key );
break;
}
}
#endregion Handle Response
return rc;
}
static int ShowHelp( params string[] errmsg )
{
#region Help Text
/*
SecondChoice.exe, Version 1.02
Alternative to Windows' native CHOICE command.
Like CHOICE, SecondChoice allows users to select one item from
a list of choices and returns the index of the selected choice.
Unlike CHOICE, SecondChoice also returns the default value on
pressing the Enter key (besides after timeout expiration).
And unlike CHOICE, SecondChoice accepts a space in the list of
choices if that list is in doublequotes.
Usage: SECONDCHOICE [ options ]
Options: /C choices Specifies the list of Choices to be created.
Default list is "YN".
/CS Enables Case-Sensitive choices to be selected.
By default, the utility is case-insensitive.
/D default Specifies the Default choice after the timeout
expires or after pressing Enter.
The default character must be in the list of
choices specified by /C option.
/M message Specifies the Message to be displayed before
the prompt. If not specified, the utility
displays only a prompt.
/N Hides the list of choices in the prompt.
The message before the prompt is displayed
and the choices are still enabled.
/T timeout The number of seconds to pause before a default
choice is made. Acceptable values are from 0 to
86400 (24 hours). If 0 is specified, there will
be no pause and the default choice is selected.
Notes: The ERRORLEVEL environment variable is set to the index of the
key that was selected from the set of choices. The first choice
listed returns a value of 1, the second a value of 2, and so on.
Invalid choices are ignored.
If SecondChoice detects an error, it returns ERRORLEVEL 255, if the
user presses ESC, CTRL+C or CTRL+BREAK, the ERRORLEVEL will be 0.
Pressing CTRL+BREAK will also be passed on to the hosting process.
Besides spaces between switches and values, colons or equal signs
are accepted too, e.g. /C choices or /C:choices or /C=choices are
all accepted.
SecondChoice also accepts a space as a valid choice; make sure you
place the list of choices in doublequotes if you want to use a space
in the list of choices, e.g. /C " ABC" or /C:" ABC" or /C=" ABC".
Examples: SECONDCHOICE /C YNC /M "Press Y for Yes, N for No or C for Cancel."
SECONDCHOICE /T=10 /C=YNC /D=C /M="Y=yes, N=no, C or timeout=cancel"
SECONDCHOICE /C YN /N /D Y /M "Press Enter or Y for Yes, or N for No"
SECONDCHOICE /C:AB /M:"Select A for option 1 and B for option 2."
SECONDCHOICE /C 12 /N /M "Select 1 for option 1 and 2 for option 2."
Credits: Code to gracefully exit on Ctrl+Break by Alois Kraus
http://codeproject.com/Articles/16164/Managed-Application-Shutdown
Written by Rob van der Woude
http://www.robvanderwoude.com
*/
if ( errmsg.Length > 0 )
{
List<string> errargs = new List<string>( errmsg );
errargs.RemoveAt( 0 );
Console.Error.WriteLine( );
Console.ForegroundColor = ConsoleColor.Red;
Console.Error.Write( "ERROR:\t" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.WriteLine( errmsg[0], errargs.ToArray( ) );
Console.ResetColor( );
}
Console.Error.WriteLine( );
Console.Error.WriteLine( "SecondChoice.exe, Version {0}", progver );
Console.Error.WriteLine( "Alternative to Windows' native CHOICE command." );
Console.Error.WriteLine( "Like CHOICE, SecondChoice allows users to select one item from" );
Console.Error.WriteLine( "a list of choices and returns the index of the selected choice." );
Console.Error.WriteLine( "Unlike CHOICE, SecondChoice also returns the default value on" );
Console.Error.WriteLine( "pressing the Enter key (besides after timeout expiration)." );
Console.Error.WriteLine( "And unlike CHOICE, SecondChoice accepts a space in the list of" );
Console.Error.WriteLine( "choices if that list is in doublequotes." );
Console.Error.WriteLine( );
Console.Error.Write( "Usage: " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.WriteLine( "SECONDCHOICE [ options ]" );
Console.ResetColor( );
Console.Error.WriteLine( );
Console.Error.Write( "Options: " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "/C choices" );
Console.ResetColor( );
Console.Error.Write( " Specifies the list of " );
Console.ResetColor( );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "C" );
Console.ResetColor( );
Console.Error.WriteLine( "hoices to be created." );
Console.Error.WriteLine( " Default list is \"YN\"." );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " /CS" );
Console.ResetColor( );
Console.Error.Write( " Enables " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "C" );
Console.ResetColor( );
Console.Error.Write( "ase-" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "S" );
Console.ResetColor( );
Console.Error.WriteLine( "ensitive choices to be selected." );
Console.Error.WriteLine( " By default, the utility is case-insensitive." );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " /D default" );
Console.ResetColor( );
Console.Error.Write( " Specifies the " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "D" );
Console.ResetColor( );
Console.Error.WriteLine( "efault choice after the timeout" );
Console.Error.WriteLine( " expires or after pressing Enter." );
Console.Error.WriteLine( " The default character must be in the list of" );
Console.Error.Write( " choices specified by " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "/C" );
Console.ResetColor( );
Console.Error.WriteLine( " option." );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " /M message" );
Console.ResetColor( );
Console.Error.Write( " Specifies the " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "M" );
Console.ResetColor( );
Console.Error.WriteLine( "essage to be displayed before" );
Console.Error.WriteLine( " the prompt. If not specified, the utility" );
Console.Error.WriteLine( " displays only a prompt." );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " /N" );
Console.ResetColor( );
Console.Error.WriteLine( " Hides the list of choices in the prompt." );
Console.Error.WriteLine( " The message before the prompt is displayed" );
Console.Error.WriteLine( " and the choices are still enabled." );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " /T timeout" );
Console.ResetColor( );
Console.Error.WriteLine( " The number of seconds to pause before a default" );
Console.Error.WriteLine( " choice is made. Acceptable values are from 0 to" );
Console.Error.WriteLine( " 86400 (24 hours). If 0 is specified, there will" );
Console.Error.WriteLine( " be no pause and the default choice is selected." );
Console.Error.WriteLine( );
Console.Error.WriteLine( "Notes: The ERRORLEVEL environment variable is set to the index of the" );
Console.Error.WriteLine( " key that was selected from the set of choices. The first choice" );
Console.Error.WriteLine( " listed returns a value of 1, the second a value of 2, and so on." );
Console.Error.WriteLine( " Invalid choices are ignored." );
Console.Error.WriteLine( " If SecondChoice detects an error, it returns ERRORLEVEL 255, if the" );
Console.Error.WriteLine( " user presses ESC, CTRL+C or CTRL+BREAK, the ERRORLEVEL will be 0." );
Console.Error.WriteLine( " Pressing CTRL+BREAK will also be passed on to the hosting process." );
Console.Error.WriteLine( " Besides spaces between switches and values, colons or equal signs" );
Console.Error.Write( " are accepted too, e.g. " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "/C choices" );
Console.ResetColor( );
Console.Error.Write( " or " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "/C:choices" );
Console.ResetColor( );
Console.Error.Write( " or " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "/C=choices" );
Console.ResetColor( );
Console.Error.WriteLine( " are" );
Console.Error.WriteLine( " all accepted." );
Console.Error.WriteLine( " SecondChoice also accepts a space as a valid choice; make sure you" );
Console.Error.WriteLine( " place the list of choices in doublequotes if you want to use a space" );
Console.Error.Write( " in the list of choices, e.g. " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "/C \" ABC\"" );
Console.ResetColor( );
Console.Error.Write( " or " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "/C:\" ABC\"" );
Console.ResetColor( );
Console.Error.Write( " or " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "/C=\" ABC\"" );
Console.ResetColor( );
Console.Error.WriteLine( "." );
Console.Error.WriteLine( );
Console.Error.Write( "Examples: " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.WriteLine( "SECONDCHOICE /C YNC /M \"Press Y for Yes, N for No or C for Cancel.\"" );
Console.Error.WriteLine( " SECONDCHOICE /T=10 /C=YNC /D=C /M=\"Y=yes, N=no, C or timeout=cancel\"" );
Console.Error.WriteLine( " SECONDCHOICE /C YN /N /D Y /M \"Press Enter or Y for Yes, or N for No\"" );
Console.Error.WriteLine( " SECONDCHOICE /C:AB /M:\"Select A for option 1 and B for option 2.\"" );
Console.Error.WriteLine( " SECONDCHOICE /C 12 /N /M \"Select 1 for option 1 and 2 for option 2.\"" );
Console.ResetColor( );
Console.Error.WriteLine( );
Console.Error.WriteLine( "Credits: Code to gracefully exit on Ctrl+Break by Alois Kraus" );
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.Error.WriteLine( " http://codeproject.com/Articles/16164/Managed-Application-Shutdown" );
Console.ResetColor( );
Console.Error.WriteLine( );
Console.Error.WriteLine( "Written by Rob van der Woude" );
Console.Error.WriteLine( "http://www.robvanderwoude.com" );
#endregion Help Text
return 255;
}
#region Event Handlers
public static void timer_Elapsed( object sender, System.EventArgs e )
{
Console.WriteLine( defaultchoice );
rc = choices.IndexOf( defaultchoice ) + 1;
Environment.Exit( rc );
}
#endregion Event Handlers
}
}
page last modified: 2024-04-16; loaded in 0.0112 seconds