Rob van der Woude's Scripting Pages
Powered by GeSHi

Source code for which.cs

(view source code of which.cs as plain text)

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Text;
  6. using System.Text.RegularExpressions;
  7. using System.Windows.Forms;
  8.  
  9.  
  10. namespace RobvanderWoude
  11. {
  12. 	class Which
  13. 	{
  14. 		public static string progver = "1.48";
  15.  
  16. 		public static char switchchar = '/';
  17.  
  18. 		[STAThread]
  19. 		static int Main( string[] args )
  20. 		{
  21. 			#region Initialize Variables
  22.  
  23. 			string[] path = String.Format( "{0};{1}", Environment.CurrentDirectory, Environment.GetEnvironmentVariable( "PATH" ) ).Split( ";".ToCharArray( ), StringSplitOptions.RemoveEmptyEntries );
  24. 			string[] pathext = ( ";" + Environment.GetEnvironmentVariable( "PATHEXT" ).ToLower( ) ).Split( ';' ); // unlike PATH, do NOT remove empty entries, we REQUIRE the first one to be empty
  25. 			string prog = string.Empty;
  26. 			string result = String.Empty;
  27. 			bool all = false;
  28. 			bool copy = false;
  29. 			bool extonly = false;
  30. 			bool filever = false;
  31. 			bool openexp = false;
  32. 			bool prodver = false;
  33. 			bool set_all = false;
  34. 			bool set_copy = false;
  35. 			bool set_exp = false;
  36. 			bool set_ext = false;
  37. 			bool set_fver = false;
  38. 			bool set_prog = false;
  39. 			bool set_pver = false;
  40. 			bool found = false;
  41.  
  42. 			#endregion Initialize Variables
  43.  
  44. 			#region Command Line Parsing
  45.  
  46. 			if ( args.Length == 0 )
  47. 			{
  48. 				return WriteError( );
  49. 			}
  50.  
  51. 			int scd = 0;
  52. 			int scu = 0;
  53.  
  54. 			foreach ( string arg in args )
  55. 			{
  56. 				if ( arg[0] == '-' )
  57. 				{
  58. 					scu += 1;
  59. 				}
  60. 				if ( arg[0] == '/' )
  61. 				{
  62. 					scd += 1;
  63. 				}
  64. 				if ( arg == "/?" )
  65. 				{
  66. 					return WriteError( );
  67. 				}
  68. 				if ( arg == "-?" || arg == "-h" || arg == "--help" )
  69. 				{
  70. 					switchchar = '-';
  71. 					return WriteError( );
  72. 				}
  73. 			}
  74.  
  75. 			if ( scu > scd )
  76. 			{
  77. 				switchchar = '-';
  78. 			}
  79.  
  80. 			foreach ( string arg in args )
  81. 			{
  82. 				if ( arg[0] == '/' || arg[0] == '-' )
  83. 				{
  84. 					if ( arg.Length != 2 )
  85. 					{
  86. 						return WriteError( "Invalid command line switch {0}", ( switchchar == '/' ? arg.ToUpper( ) : arg.ToLower( ) ) );
  87. 					}
  88. 					switch ( arg[1].ToString( ).ToUpper( ) )
  89. 					{
  90. 						case "A":
  91. 							if ( set_all )
  92. 							{
  93. 								return WriteError( "Duplicate command line switch {0}", SwitchString( arg[1] ) );
  94. 							}
  95. 							if ( set_exp )
  96. 							{
  97. 								return WriteError( "Command line switches {0} and {1} are mutually exclusive", SwitchString( arg[1] ), SwitchString( "E" ) );
  98. 							}
  99. 							all = true;
  100. 							set_all = true;
  101. 							break;
  102. 						case "C":
  103. 							if ( set_copy )
  104. 							{
  105. 								return WriteError( "Duplicate command line switch {0}", SwitchString( arg[1] ) );
  106. 							}
  107. 							copy = true;
  108. 							set_copy = true;
  109. 							break;
  110. 						case "E":
  111. 							if ( set_exp )
  112. 							{
  113. 								return WriteError( "Duplicate command line switch {0}", SwitchString( arg[1] ) );
  114. 							}
  115. 							if ( set_all )
  116. 							{
  117. 								return WriteError( "Command line switches {0} and {1} are mutually exclusive", SwitchString( "A" ), SwitchString( arg[1] ) );
  118. 							}
  119. 							openexp = true;
  120. 							set_exp = true;
  121. 							break;
  122. 						case "F":
  123. 							if ( set_fver )
  124. 							{
  125. 								return WriteError( "Duplicate command line switch {0}", SwitchString( arg[1] ) );
  126. 							}
  127. 							if ( set_pver )
  128. 							{
  129. 								return WriteError( "Command line switches {0} and {1} are mutually exclusive", SwitchString( arg[1] ), SwitchString( "P" ) );
  130. 							}
  131. 							filever = true;
  132. 							set_fver = true;
  133. 							break;
  134. 						case "P":
  135. 							if ( set_pver )
  136. 							{
  137. 								return WriteError( "Duplicate command line switch {0}", SwitchString( "P" ) );
  138. 							}
  139. 							if ( set_fver )
  140. 							{
  141. 								return WriteError( "Command line switches {0} and {1} are mutually exclusive", SwitchString( "F" ), SwitchString( arg[1] ) );
  142. 							}
  143. 							prodver = true;
  144. 							set_pver = true;
  145. 							break;
  146. 						case "X":
  147. 							if ( set_ext )
  148. 							{
  149. 								return WriteError( "Duplicate command line switch {0}", SwitchString( arg[1] ) );
  150. 							}
  151. 							extonly = true;
  152. 							set_ext = true;
  153. 							break;
  154. 						case "?":
  155. 							return WriteError( );
  156. 						default:
  157. 							return WriteError( "Invalid command line switch {0}", ( switchchar == '/' ? arg.ToUpper( ) : arg.ToLower( ) ) );
  158. 					}
  159. 				}
  160. 				else
  161. 				{
  162. 					if ( set_prog )
  163. 					{
  164. 						return WriteError( "Invalid or duplicate command line argument: \"{0}\"", arg );
  165. 					}
  166. 					else
  167. 					{
  168. 						char[] forbidden = { '\\', '?', '*', ':', ';', '/' };
  169. 						if ( arg.IndexOfAny( forbidden ) == -1 )
  170. 						{
  171. 							prog = arg;
  172. 							set_prog = true;
  173. 						}
  174. 						else
  175. 						{
  176. 							return WriteError( "Invalid characters in specified program name: \"{0}\"", arg );
  177. 						}
  178. 					}
  179. 				}
  180. 			}
  181.  
  182. 			#endregion Command Line Parsing
  183.  
  184. 			try
  185. 			{
  186. 				if ( !extonly )
  187. 				{
  188. 					#region DOSKEY macros
  189.  
  190. 					// Try DOSKEY macros first
  191. 					Process doskey = new Process( );
  192. 					doskey.StartInfo.Arguments = "/macros";
  193. 					doskey.StartInfo.CreateNoWindow = false;
  194. 					doskey.StartInfo.FileName = Environment.GetFolderPath( Environment.SpecialFolder.System ) + "\\doskey.exe";
  195. 					doskey.StartInfo.LoadUserProfile = false;
  196. 					doskey.StartInfo.RedirectStandardError = false;
  197. 					doskey.StartInfo.RedirectStandardInput = false;
  198. 					doskey.StartInfo.RedirectStandardOutput = true;
  199. 					doskey.StartInfo.UseShellExecute = false;
  200. 					doskey.Start( );
  201. 					doskey.WaitForExit( 1000 );
  202. 					do
  203. 					{
  204. 						string line = doskey.StandardOutput.ReadLine( );
  205. 						if ( !found || all )
  206. 						{
  207. 							if ( !String.IsNullOrEmpty( line ) )
  208. 							{
  209. 								if ( line.IndexOf( '=' ) > 0 )
  210. 								{
  211. 									string pattern = "^" + prog.ToUpper( ).Replace( ".", "\\." ) + "=";
  212. 									if ( Regex.IsMatch( line.ToUpper( ), pattern ) )
  213. 									{
  214. 										Console.ForegroundColor = ConsoleColor.White;
  215. 										Console.Write( "[{0}]::", doskey.StartInfo.FileName.ToUpper( ) );
  216. 										Console.ResetColor( );
  217. 										Console.WriteLine( line );
  218. 										result += String.Format( "[{0}]::{1}\n", doskey.StartInfo.FileName.ToUpper( ), line );
  219. 										found = true;
  220. 									}
  221. 								}
  222. 							}
  223. 						}
  224. 					} while ( doskey.StandardOutput.Peek( ) != -1 );
  225. 					doskey.Close( );
  226.  
  227. 					#endregion DOSKEY macros
  228.  
  229. 					#region Internal commands
  230.  
  231. 					// Next try internal commands
  232. 					if ( !found || all )
  233. 					{
  234. 						if ( prog.IndexOf( '.' ) == -1 )
  235. 						{
  236. 							if ( ListInternalCommands( ).Contains( prog.ToUpper( ) ) )
  237. 							{
  238. 								Console.ForegroundColor = ConsoleColor.White;
  239. 								Console.Write( "[{0}]::", Environment.GetEnvironmentVariable( "COMSPEC" ).ToUpper( ) );
  240. 								Console.ResetColor( );
  241. 								Console.WriteLine( prog.ToUpper( ) );
  242. 								result += String.Format( "[{0}]::{1}\n", Environment.GetEnvironmentVariable( "COMSPEC" ).ToUpper( ), prog.ToUpper( ) );
  243. 								found = true;
  244. 							}
  245. 						}
  246. 					}
  247.  
  248. 					#endregion Internal commands
  249. 				}
  250.  
  251. 				#region External commands
  252.  
  253. 				// Finally try external commands
  254. 				if ( !found || all )
  255. 				{
  256. 					foreach ( string folder in path )
  257. 					{
  258. 						if ( !found || all )
  259. 						{
  260. 							string dir = ( folder + @"\" ).Replace( @"\\", @"\" );
  261. 							foreach ( string ext in pathext )
  262. 							{
  263. 								if ( !found || all )
  264. 								{
  265. 									// The EXTERNAL program FILE to be searched MUST have an extension, either
  266. 									// specified on the command line or one of the extensions listed in PATHEXT.
  267. 									if ( ( prog + ext ).IndexOf( '.' ) > -1 )
  268. 									{
  269. 										if ( File.Exists( dir + prog + ext ) )
  270. 										{
  271. 											string ver = String.Empty;
  272. 											if ( filever )
  273. 											{
  274. 												string fileversion = FileVersionInfo.GetVersionInfo( dir + prog + ext ).FileVersion;
  275. 												if ( String.IsNullOrEmpty( fileversion ) )
  276. 												{
  277. 													ver = String.Empty;
  278. 												}
  279. 												else
  280. 												{
  281. 													ver = String.Format( " (file version {0})", fileversion );
  282. 												}
  283. 											}
  284. 											else if ( prodver )
  285. 											{
  286. 												string productversion = FileVersionInfo.GetVersionInfo( dir + prog + ext ).ProductVersion;
  287. 												if ( String.IsNullOrEmpty( productversion ) )
  288. 												{
  289. 													ver = String.Empty;
  290. 												}
  291. 												else
  292. 												{
  293. 													ver = String.Format( " (product version {0})", productversion );
  294. 												}
  295. 											}
  296. 											Console.WriteLine( dir + prog + ext + ver );
  297. 											result += String.Format( "{0}{1}{2}{3}\n", dir, prog, ext, ver );
  298. 											found = true;
  299. 										}
  300. 									}
  301. 								}
  302. 							}
  303. 						}
  304. 					}
  305. 				}
  306.  
  307. 				#endregion External commands
  308.  
  309. 				if ( found )
  310. 				{
  311. 					#region Copy to clipboard
  312.  
  313. 					if ( copy )
  314. 					{
  315. 						if ( !all )
  316. 						{
  317. 							result = result.TrimEnd( "\n".ToCharArray( ) );
  318. 						}
  319. 						Clipboard.SetText( result );
  320. 					}
  321.  
  322. 					#endregion Copy to clipboard
  323.  
  324. 					#region Open In Explorer
  325.  
  326. 					if ( openexp )
  327. 					{
  328. 						string file = result.TrimEnd( "\n".ToCharArray( ) );
  329. 						string sel = String.Format( "/Select, {0}", file );
  330. 						ProcessStartInfo expl = new ProcessStartInfo( "Explorer.exe", sel );
  331. 						System.Diagnostics.Process.Start( expl );
  332. 					}
  333.  
  334. 					#endregion Open In Explorer
  335.  
  336. 					return 0;
  337. 				}
  338. 				else
  339. 				{
  340. 					return 1;
  341. 				}
  342. 			}
  343. 			catch ( Exception e )
  344. 			{
  345. 				return WriteError( e.Message );
  346. 			}
  347. 		}
  348.  
  349.  
  350. 		public static List<string> ListInternalCommands( )
  351. 		{
  352. 			string comspec = Environment.GetEnvironmentVariable( "COMSPEC" );
  353. 			StreamReader file = new StreamReader( comspec, Encoding.ASCII );
  354. 			string content = file.ReadToEnd( );
  355. 			file.Close( );
  356.  
  357. 			bool include = false;
  358. 			List<string> intcmds = new List<string>( );
  359. 			string excludestr = "ABOVENORMAL,AFFINITY,ANSI,APPICON,ASCII,AZ,BAT,BELOWNORMAL,BOTH,CMD,CMDCMDLINE,CMDEXTVERSION,COM,COMSPEC,CONFIG,COPYCMD,COPYRIGHT,CRLF,CSVFS,CTRL,CURRENT,DEFINED,DIRCMD,DISABLEDELAYEDEXPANSION,DISABLEEXTENSIONS,DLL,DO,DOC,DOS,DWORD,ENABLEDELAYEDEXPANSION,ENABLEEXTENSIONS,ELSE,ENTER,EOF,EQU,ERROR,ERRORLEVEL,EXE,EXIST,EXISTS,EXPAND,FALSE,FAT,FH,GEQ,GTR,GUI,HIGH,HIGHESTNUMANODENUMBER,HH,HKEY,HSM,IDI,IDLE,IN,INFO,IS,JS,KERNEL,LEQ,LIST,LNK,LOCAL,LOW,LSS,MACHINE,MAX,MIN,MM,MSC,MUI,NEQ,NODE,NORMAL,NOT,NT,NTDLL,NTFS,NY,NYA,OFF,ON,OTHER,PATHEXT,PROCESSING,RANDOM,REALTIME,REFS,REG,REGEDT,SCRIPT,SEPARATE,SHARED,STACK,SYS,SZ,TEMP,TWO,UNC,UNCC,UNKNOWN,US,USER,VAR,VBS,VERSION,VS,WAIT,WA,WC,WD,WINDOWS,WKERNEL,WORD,WP,WS,WV,XCOPY,XP";
  360. 			string[] excludearr = excludestr.Split( ",".ToCharArray( ) );
  361. 			List<string> exclude = new List<string>( excludearr ); // Optimized for .NET Framework 2.0; in .NET Framework 3.5+ we might have used List<string> exclude = excludestr.Split( ",".ToCharArray( ) ).ToList<string>( );
  362.  
  363. 			string pattern = @"([A-Z]\0){2,}";
  364. 			Regex regex = new Regex( pattern );
  365. 			if ( regex.IsMatch( content ) )
  366. 			{
  367. 				foreach ( Match match in regex.Matches( content ) )
  368. 				{
  369. 					string intcmd = match.ToString( ).Replace( "\0", String.Empty );
  370. 					if ( intcmd == "CD" ) // The start of the commands list, as found in Windows 7 SP1, EN-GB
  371. 					{
  372. 						include = true;
  373. 					}
  374. 					if ( intcmd == "ERRORLEVEL" ) // The end of the commands list, as found in Windows 7 SP1, EN-GB
  375. 					{
  376. 						include = false;
  377. 					}
  378. 					if ( include && !exclude.Contains( intcmd ) && !intcmds.Contains( intcmd ) )
  379. 					{
  380. 						intcmds.Add( intcmd );
  381. 					}
  382. 				}
  383. 				intcmds.Sort( );
  384. 			}
  385. 			if ( intcmds.Count == 0 )
  386. 			{
  387. 				// Return a default list if we could not find the internal commands in %COMSPEC%
  388. 				string defaultinternalcommands = @"ASSOC,BREAK,CALL,CD,CHDIR,CLS,COLOR,COPY,DATE,DEL,DIR,DPATH,ECHO,ENDLOCAL,ERASE,EXIT,FOR,FTYPE,GOTO,IF,KEYS,MD,MKDIR,MKLINK,MOVE,PATH,PAUSE,POPD,PROMPT,PUSHD,RD,REM,REN,RENAME,RMDIR,SET,SETLOCAL,SHIFT,START,TIME,TITLE,TYPE,VER,VERIFY,VOL";
  389. 				string[] defaultintcmdarr = defaultinternalcommands.Split( ",".ToCharArray( ), StringSplitOptions.RemoveEmptyEntries );
  390. 				intcmds = new List<string>( defaultintcmdarr );
  391. 			}
  392. 			return intcmds;
  393. 		}
  394.  
  395.  
  396. 		public static string SwitchString( char sw )
  397. 		{
  398. 			return SwitchString( sw.ToString( ) );
  399. 		}
  400.  
  401.  
  402. 		public static string SwitchString( string sw )
  403. 		{
  404. 			if ( switchchar == '-' )
  405. 			{
  406. 				return String.Format( "{0}{1}", switchchar, sw.ToLower( ) );
  407. 			}
  408. 			else
  409. 			{
  410. 				return String.Format( "{0}{1}", switchchar, sw.ToUpper( ) );
  411. 			}
  412. 		}
  413.  
  414.  
  415. 		public static int WriteError( params string[] errmsg )
  416. 		{
  417. 			/*
  418. 			Which,  Version 1.47
  419. 			Port of the UNIX command to Windows
  420.  
  421. 			Usage:   WHICH  progname  [ /A | /E ]  [ /C ]  [ /F | /P ]  [ /X ]
  422.  
  423. 			Where:   progname   the program name or internal command to be searched for
  424. 			         /A         returns All matches (default: stop at first match)
  425. 			         /C         Copies result to clipboard
  426. 			         /E         opens Explorer with result selected, if it is a file
  427. 			         /F         returns name and File version for external commands
  428. 			         /P         returns name and Product version for external commands
  429. 			         /X         returns eXternal commands only, no DOSKEY macros or
  430. 			                    internal commands   (default: all types)
  431.  
  432. 			Note:    By default, this program first compares the specified name against
  433. 			         a list of active DOSKEY macros, next against a list of CMD's
  434. 			         internal commands, and finally it tries to find a matching program
  435. 			         file in the current directory and the PATH, using the extensions
  436. 			         listed in PATHEXT.
  437.  
  438. 			Written by Rob van der Woude
  439. 			http://www.robvanderwoude.com
  440. 			*/
  441.  
  442. 			if ( errmsg.Length > 0 )
  443. 			{
  444. 				List<string> errargs = new List<string>( errmsg );
  445. 				errargs.RemoveAt( 0 );
  446. 				Console.Error.WriteLine( );
  447. 				Console.ForegroundColor = ConsoleColor.Red;
  448. 				Console.Error.Write( "ERROR: " );
  449. 				Console.ForegroundColor = ConsoleColor.White;
  450. 				Console.Error.WriteLine( errmsg[0], errargs.ToArray( ) );
  451. 				Console.ResetColor( );
  452. 			}
  453.  
  454. 			Console.Error.WriteLine( );
  455. 			Console.Error.WriteLine( "Which,  Version {0}", progver );
  456. 			Console.Error.WriteLine( "Port of the UNIX command to Windows" );
  457. 			Console.Error.WriteLine( );
  458.  
  459. 			Console.Error.Write( "Usage:   " );
  460. 			Console.ForegroundColor = ConsoleColor.White;
  461. 			if ( switchchar == '/' )
  462. 			{
  463. 				Console.Error.WriteLine( "WHICH  progname  [ /A | /E ]  [ /C ]  [ /F | /P ]  [ /X ]" );
  464. 			}
  465. 			else
  466. 			{
  467. 				Console.Error.WriteLine( "which  progname  [ -a | -e ]  [ -c ]  [ -f | -p ]  [ -x ]" );
  468. 			}
  469. 			Console.ResetColor( );
  470.  
  471. 			Console.Error.WriteLine( );
  472.  
  473. 			Console.Error.Write( "Where:   " );
  474. 			Console.ForegroundColor = ConsoleColor.White;
  475. 			Console.Error.Write( "progname" );
  476. 			Console.ResetColor( );
  477. 			Console.Error.WriteLine( "   the program name or internal command to be searched for" );
  478.  
  479. 			Console.ForegroundColor = ConsoleColor.White;
  480. 			Console.Error.Write( "         {0}", SwitchString( "A" ) );
  481. 			Console.ResetColor( );
  482. 			Console.Error.Write( "         returns " );
  483. 			Console.ForegroundColor = ConsoleColor.White;
  484. 			Console.Error.Write( "A" );
  485. 			Console.ResetColor( );
  486. 			Console.Error.WriteLine( "ll matches (default: stop at first match)" );
  487.  
  488. 			Console.ForegroundColor = ConsoleColor.White;
  489. 			Console.Error.Write( "         {0}         C", SwitchString( "C" ) );
  490. 			Console.ResetColor( );
  491. 			Console.Error.WriteLine( "opies result to clipboard" );
  492.  
  493. 			Console.ForegroundColor = ConsoleColor.White;
  494. 			Console.Error.Write( "         {0}", SwitchString( "E" ) );
  495. 			Console.ResetColor( );
  496. 			Console.Error.Write( "         opens " );
  497. 			Console.ForegroundColor = ConsoleColor.White;
  498. 			Console.Error.Write( "E" );
  499. 			Console.ResetColor( );
  500. 			Console.Error.Write( "xplorer with result selected, " );
  501. 			Console.ForegroundColor = ConsoleColor.White;
  502. 			Console.Error.Write( "if" );
  503. 			Console.ResetColor( );
  504. 			Console.Error.WriteLine( " it is a file" );
  505.  
  506. 			Console.ForegroundColor = ConsoleColor.White;
  507. 			Console.Error.Write( "         {0}", SwitchString( "F" ) );
  508. 			Console.ResetColor( );
  509. 			Console.Error.Write( "         returns name and " );
  510. 			Console.ForegroundColor = ConsoleColor.White;
  511. 			Console.Error.Write( "F" );
  512. 			Console.ResetColor( );
  513. 			Console.Error.WriteLine( "ile version for external commands" );
  514.  
  515. 			Console.ForegroundColor = ConsoleColor.White;
  516. 			Console.Error.Write( "         {0}", SwitchString( "P" ) );
  517. 			Console.ResetColor( );
  518. 			Console.Error.Write( "         returns name and " );
  519. 			Console.ForegroundColor = ConsoleColor.White;
  520. 			Console.Error.Write( "P" );
  521. 			Console.ResetColor( );
  522. 			Console.Error.WriteLine( "roduct version for external commands" );
  523.  
  524. 			Console.ForegroundColor = ConsoleColor.White;
  525. 			Console.Error.Write( "         {0}", SwitchString( "X" ) );
  526. 			Console.ResetColor( );
  527. 			Console.Error.Write( "         returns e" );
  528. 			Console.ForegroundColor = ConsoleColor.White;
  529. 			Console.Error.Write( "X" );
  530. 			Console.ResetColor( );
  531. 			Console.Error.WriteLine( "ternal commands only, no DOSKEY macros or" );
  532.  
  533. 			Console.Error.WriteLine( "                    internal commands   (default: all types)" );
  534.  
  535. 			Console.Error.WriteLine( );
  536. 			Console.Error.WriteLine( "Note:    By default, this program first compares the specified name against" );
  537. 			Console.Error.WriteLine( "         a list of active DOSKEY macros, next against a list of CMD's" );
  538. 			Console.Error.WriteLine( "         internal commands, and finally it tries to find a matching program" );
  539. 			Console.Error.WriteLine( "         file in the current directory and the PATH, using the extensions" );
  540. 			Console.Error.WriteLine( "         listed in PATHEXT." );
  541. 			Console.Error.WriteLine( );
  542. 			Console.Error.WriteLine( "Written by Rob van der Woude" );
  543. 			Console.Error.WriteLine( "http://www.robvanderwoude.com" );
  544. 			return 1;
  545. 		}
  546. 	}
  547. }
  548.  

page last modified: 2024-04-16; loaded in 0.0396 seconds