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.45";
  15.  
  16. 		public static char switchchar = '/';
  17.  
  18. 		static int Main( string[] args )
  19. 		{
  20. 			#region Initialize Variables
  21.  
  22. 			string[] path = String.Format( "{0};{1}", Environment.CurrentDirectory, Environment.GetEnvironmentVariable( "PATH" ) ).Split( ";".ToCharArray( ), StringSplitOptions.RemoveEmptyEntries );
  23. 			string[] pathext = ( ";" + Environment.GetEnvironmentVariable( "PATHEXT" ).ToLower( ) ).Split( ';' ); // unlike PATH, do NOT remove empty entries, we REQUIRE the first one to be empty
  24. 			string prog = string.Empty;
  25. 			string result = String.Empty;
  26. 			bool all = false;
  27. 			bool copy = false;
  28. 			bool extonly = false;
  29. 			bool filever = false;
  30. 			bool openexp = false;
  31. 			bool prodver = false;
  32. 			bool set_all = false;
  33. 			bool set_copy = false;
  34. 			bool set_exp = false;
  35. 			bool set_ext = false;
  36. 			bool set_fver = false;
  37. 			bool set_prog = false;
  38. 			bool set_pver = false;
  39. 			bool found = false;
  40.  
  41. 			#endregion Initialize Variables
  42.  
  43. 			#region Command Line Parsing
  44.  
  45. 			if ( args.Length == 0 )
  46. 			{
  47. 				return WriteError( );
  48. 			}
  49.  
  50. 			int scd = 0;
  51. 			int scu = 0;
  52.  
  53. 			foreach ( string arg in args )
  54. 			{
  55. 				if ( arg[0] == '-' )
  56. 				{
  57. 					scu += 1;
  58. 				}
  59. 				if ( arg[0] == '/' )
  60. 				{
  61. 					scd += 1;
  62. 				}
  63. 				if ( arg == "/?" )
  64. 				{
  65. 					return WriteError( );
  66. 				}
  67. 				if ( arg == "-?" || arg == "-h" || arg == "--help" )
  68. 				{
  69. 					switchchar = '-';
  70. 					return WriteError( );
  71. 				}
  72. 			}
  73.  
  74. 			if ( scu > scd )
  75. 			{
  76. 				switchchar = '-';
  77. 			}
  78.  
  79. 			foreach ( string arg in args )
  80. 			{
  81. 				if ( arg[0] == '/' || arg[0] == '-' )
  82. 				{
  83. 					if ( arg.Length != 2 )
  84. 					{
  85. 						return WriteError( "Invalid command line switch {0}", ( switchchar == '/' ? arg.ToUpper( ) : arg.ToLower( ) ) );
  86. 					}
  87. 					switch ( arg[1].ToString( ).ToUpper( ) )
  88. 					{
  89. 						case "A":
  90. 							if ( set_all )
  91. 							{
  92. 								return WriteError( "Duplicate command line switch {0}", ( switchchar == '/' ? "/A" : "-a" ) );
  93. 							}
  94. 							if ( set_exp )
  95. 							{
  96. 								return WriteError( "Command line switches {0} and {1} are mutually exclusive", ( switchchar == '/' ? "/A" : "-a" ), ( switchchar == '/' ? "/E" : "-e" ) );
  97. 							}
  98. 							all = true;
  99. 							set_all = true;
  100. 							break;
  101. 						case "C":
  102. 							if ( set_copy )
  103. 							{
  104. 								return WriteError( "Duplicate command line switch {0}", ( switchchar == '/' ? "/C" : "-c" ) );
  105. 							}
  106. 							copy = true;
  107. 							set_copy = true;
  108. 							break;
  109. 						case "E":
  110. 							if ( set_exp )
  111. 							{
  112. 								return WriteError( "Duplicate command line switch {0}", ( switchchar == '/' ? "/E" : "-e" ) );
  113. 							}
  114. 							if ( set_all )
  115. 							{
  116. 								return WriteError( "Command line switches {0} and {1} are mutually exclusive", ( switchchar == '/' ? "/A" : "-a" ), ( switchchar == '/' ? "/E" : "-e" ) );
  117. 							}
  118. 							openexp = true;
  119. 							set_exp = true;
  120. 							break;
  121. 						case "F":
  122. 							if ( set_fver )
  123. 							{
  124. 								return WriteError( "Duplicate command line switch {0}", ( switchchar == '/' ? "/F" : "-f" ) );
  125. 							}
  126. 							if ( set_pver )
  127. 							{
  128. 								return WriteError( "Command line switches {0} and {1} are mutually exclusive", ( switchchar == '/' ? "/F" : "-f" ), ( switchchar == '/' ? "/P" : "-p" ) );
  129. 							}
  130. 							filever = true;
  131. 							set_fver = true;
  132. 							break;
  133. 						case "P":
  134. 							if ( set_pver )
  135. 							{
  136. 								return WriteError( "Duplicate command line switch {0}", ( switchchar == '/' ? "/P" : "-p" ) );
  137. 							}
  138. 							if ( set_fver )
  139. 							{
  140. 								return WriteError( "Command line switches {0} and {1} are mutually exclusive", ( switchchar == '/' ? "/F" : "-f" ), ( switchchar == '/' ? "/P" : "-p" ) );
  141. 							}
  142. 							prodver = true;
  143. 							set_pver = true;
  144. 							break;
  145. 						case "X":
  146. 							if ( set_ext )
  147. 							{
  148. 								return WriteError( "Duplicate command line switch {0}", ( switchchar == '/' ? "/X" : "-x" ) );
  149. 							}
  150. 							extonly = true;
  151. 							set_ext = true;
  152. 							break;
  153. 						case "?":
  154. 							return WriteError( );
  155. 						default:
  156. 							return WriteError( "Invalid command line switch {0}", ( switchchar == '/' ? arg.ToUpper( ) : arg.ToLower( ) ) );
  157. 					}
  158. 				}
  159. 				else
  160. 				{
  161. 					if ( set_prog )
  162. 					{
  163. 						return WriteError( "Invalid or duplicate command line argument: \"{0}\"", arg );
  164. 					}
  165. 					else
  166. 					{
  167. 						char[] forbidden = { '\\', '?', '*', ':', ';', '/' };
  168. 						if ( arg.IndexOfAny( forbidden ) == -1 )
  169. 						{
  170. 							prog = arg;
  171. 							set_prog = true;
  172. 						}
  173. 						else
  174. 						{
  175. 							return WriteError( "Invalid characters in specified program name: \"{0}\"", arg );
  176. 						}
  177. 					}
  178. 				}
  179. 			}
  180.  
  181. 			#endregion Command Line Parsing
  182.  
  183. 			try
  184. 			{
  185. 				if ( !extonly )
  186. 				{
  187. 					#region DOSKEY macros
  188.  
  189. 					// Try DOSKEY macros first
  190. 					Process doskey = new Process( );
  191. 					doskey.StartInfo.Arguments = "/macros";
  192. 					doskey.StartInfo.CreateNoWindow = false;
  193. 					doskey.StartInfo.FileName = Environment.GetFolderPath( Environment.SpecialFolder.System ) + "\\doskey.exe";
  194. 					doskey.StartInfo.LoadUserProfile = false;
  195. 					doskey.StartInfo.RedirectStandardError = false;
  196. 					doskey.StartInfo.RedirectStandardInput = false;
  197. 					doskey.StartInfo.RedirectStandardOutput = true;
  198. 					doskey.StartInfo.UseShellExecute = false;
  199. 					doskey.Start( );
  200. 					doskey.WaitForExit( 1000 );
  201. 					do
  202. 					{
  203. 						string line = doskey.StandardOutput.ReadLine( );
  204. 						if ( !found || all )
  205. 						{
  206. 							if ( !String.IsNullOrEmpty( line ) )
  207. 							{
  208. 								if ( line.IndexOf( '=' ) > 0 )
  209. 								{
  210. 									string pattern = "^" + prog.ToUpper( ).Replace( ".", "\\." ) + "=";
  211. 									if ( Regex.IsMatch( line.ToUpper( ), pattern ) )
  212. 									{
  213. 										Console.ForegroundColor = ConsoleColor.White;
  214. 										Console.Write( "[{0}]::", doskey.StartInfo.FileName.ToUpper( ) );
  215. 										Console.ResetColor( );
  216. 										Console.WriteLine( line );
  217. 										result += String.Format( "[{0}]::{1}\n", doskey.StartInfo.FileName.ToUpper( ), line );
  218. 										found = true;
  219. 									}
  220. 								}
  221. 							}
  222. 						}
  223. 					} while ( doskey.StandardOutput.Peek( ) != -1 );
  224. 					doskey.Close( );
  225.  
  226. 					#endregion DOSKEY macros
  227.  
  228. 					#region Internal commands
  229.  
  230. 					// Next try internal commands
  231. 					if ( !found || all )
  232. 					{
  233. 						if ( prog.IndexOf( '.' ) == -1 )
  234. 						{
  235. 							if ( ListInternalCommands( ).Contains( prog.ToUpper( ) ) )
  236. 							{
  237. 								Console.ForegroundColor = ConsoleColor.White;
  238. 								Console.Write( "[{0}]::", Environment.GetEnvironmentVariable( "COMSPEC" ).ToUpper( ) );
  239. 								Console.ResetColor( );
  240. 								Console.WriteLine( prog.ToUpper( ) );
  241. 								result += String.Format( "[{0}]::{1}\n", Environment.GetEnvironmentVariable( "COMSPEC" ).ToUpper( ), prog.ToUpper( ) );
  242. 								found = true;
  243. 							}
  244. 						}
  245. 					}
  246.  
  247. 					#endregion Internal commands
  248. 				}
  249.  
  250. 				#region External commands
  251.  
  252. 				// Finally try external commands
  253. 				if ( !found || all )
  254. 				{
  255. 					foreach ( string folder in path )
  256. 					{
  257. 						if ( !found || all )
  258. 						{
  259. 							string dir = ( folder + @"\" ).Replace( @"\\", @"\" );
  260. 							foreach ( string ext in pathext )
  261. 							{
  262. 								if ( !found || all )
  263. 								{
  264. 									// The EXTERNAL program FILE to be searched MUST have an extension, either
  265. 									// specified on the command line or one of the extensions listed in PATHEXT.
  266. 									if ( ( prog + ext ).IndexOf( '.' ) > -1 )
  267. 									{
  268. 										if ( File.Exists( dir + prog + ext ) )
  269. 										{
  270. 											string ver = String.Empty;
  271. 											if ( filever )
  272. 											{
  273. 												ver = String.Format( " (file version {0})", FileVersionInfo.GetVersionInfo( dir + prog + ext ).FileVersion );
  274. 											}
  275. 											else if ( prodver )
  276. 											{
  277. 												ver = String.Format( " (product version {0})", FileVersionInfo.GetVersionInfo( dir + prog + ext ).ProductVersion );
  278. 											}
  279. 											Console.WriteLine( dir + prog + ext + ver );
  280. 											result += String.Format( "{0}{1}{2}{3}\n", dir, prog, ext, ver );
  281. 											found = true;
  282. 										}
  283. 									}
  284. 								}
  285. 							}
  286. 						}
  287. 					}
  288. 				}
  289.  
  290. 				#endregion External commands
  291.  
  292. 				if ( found )
  293. 				{
  294. 					#region Copy to clipboard
  295.  
  296. 					if ( copy )
  297. 					{
  298. 						if ( !all )
  299. 						{
  300. 							result = result.TrimEnd( "\n".ToCharArray( ) );
  301. 						}
  302. 						Clipboard.SetText( result );
  303. 					}
  304.  
  305. 					#endregion Copy to clipboard
  306.  
  307. 					#region Open In Explorer
  308.  
  309. 					if ( openexp )
  310. 					{
  311. 						string file = result.TrimEnd( "\n".ToCharArray( ) );
  312. 						string sel = String.Format( "/Select, {0}", file );
  313. 						ProcessStartInfo expl = new ProcessStartInfo( "Explorer.exe", sel );
  314. 						System.Diagnostics.Process.Start( expl );
  315. 					}
  316.  
  317. 					#endregion Open In Explorer
  318.  
  319. 					return 0;
  320. 				}
  321. 				else
  322. 				{
  323. 					return 1;
  324. 				}
  325. 			}
  326. 			catch ( Exception e )
  327. 			{
  328. 				return WriteError( e.Message );
  329. 			}
  330. 		}
  331.  
  332.  
  333. 		public static List<string> ListInternalCommands( )
  334. 		{
  335. 			string comspec = Environment.GetEnvironmentVariable( "COMSPEC" );
  336. 			StreamReader file = new StreamReader( comspec, Encoding.ASCII );
  337. 			string content = file.ReadToEnd( );
  338. 			file.Close( );
  339.  
  340. 			bool include = false;
  341. 			List<string> intcmds = new List<string>( );
  342. 			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";
  343. 			string[] excludearr = excludestr.Split( ",".ToCharArray( ) );
  344. 			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>( );
  345.  
  346. 			string pattern = @"([A-Z]\0){2,}";
  347. 			Regex regex = new Regex( pattern );
  348. 			if ( regex.IsMatch( content ) )
  349. 			{
  350. 				foreach ( Match match in regex.Matches( content ) )
  351. 				{
  352. 					string intcmd = match.ToString( ).Replace( "\0", String.Empty );
  353. 					if ( intcmd == "CD" ) // The start of the commands list, as found in Windows 7 SP1, EN-GB
  354. 					{
  355. 						include = true;
  356. 					}
  357. 					if ( intcmd == "ERRORLEVEL" ) // The end of the commands list, as found in Windows 7 SP1, EN-GB
  358. 					{
  359. 						include = false;
  360. 					}
  361. 					if ( include && !exclude.Contains( intcmd ) && !intcmds.Contains( intcmd ) )
  362. 					{
  363. 						intcmds.Add( intcmd );
  364. 					}
  365. 				}
  366. 				intcmds.Sort( );
  367. 			}
  368. 			if ( intcmds.Count == 0 )
  369. 			{
  370. 				// Return a default list if we could not find the internal commands in %COMSPEC%
  371. 				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";
  372. 				string[] defaultintcmdarr = defaultinternalcommands.Split( ",".ToCharArray( ), StringSplitOptions.RemoveEmptyEntries );
  373. 				intcmds = new List<string>( defaultintcmdarr );
  374. 			}
  375. 			return intcmds;
  376. 		}
  377.  
  378.  
  379. 		public static int WriteError( params string[] errmsg )
  380. 		{
  381. 			/*
  382. 			Which,  Version 1.44
  383. 			Port of the UNIX command to Windows
  384.  
  385. 			Usage:   WHICH  progname  [ /A | /E ]  [ /C ]  [ /F | /P ]  [ /X ]
  386.  
  387. 			Where:   progname   the program name or internal command to be searched for
  388. 			         /A         returns All matches (default: stop at first match)
  389. 			         /C         Copies result to clipboard
  390. 			         /E         opens Explorer with result selected, if it is a file
  391. 			         /F         returns name and File version for external commands
  392. 			         /P         returns name and Product version for external commands
  393. 			         /X         returns eXternal commands only, no DOSKEY macros or
  394. 			                    internal commands   (default: all types)
  395.  
  396. 			Note:    By default, this program first compares the specified name against
  397. 			         a list of active DOSKEY macros, next against a list of CMD's
  398. 			         internal commands, and finally it tries to find a matching program
  399. 			         file in the current directory and the PATH, using the extensions
  400. 			         listed in PATHEXT.
  401.  
  402. 			Written by Rob van der Woude
  403. 			http://www.robvanderwoude.com
  404. 			*/
  405.  
  406. 			if ( errmsg.Length > 0 )
  407. 			{
  408. 				List<string> errargs = new List<string>( errmsg );
  409. 				errargs.RemoveAt( 0 );
  410. 				Console.Error.WriteLine( );
  411. 				Console.ForegroundColor = ConsoleColor.Red;
  412. 				Console.Error.Write( "ERROR: " );
  413. 				Console.ForegroundColor = ConsoleColor.White;
  414. 				Console.Error.WriteLine( errmsg[0], errargs.ToArray( ) );
  415. 				Console.ResetColor( );
  416. 			}
  417.  
  418. 			Console.Error.WriteLine( );
  419. 			Console.Error.WriteLine( "Which,  Version {0}", progver );
  420. 			Console.Error.WriteLine( "Port of the UNIX command to Windows" );
  421. 			Console.Error.WriteLine( );
  422.  
  423. 			Console.Error.Write( "Usage:   " );
  424. 			Console.ForegroundColor = ConsoleColor.White;
  425. 			if ( switchchar == '/' )
  426. 			{
  427. 				Console.Error.WriteLine( "WHICH  progname  [ /A | /E ]  [ /C ]  [ /F | /P ]  [ /X ]" );
  428. 			}
  429. 			else
  430. 			{
  431. 				Console.Error.WriteLine( "which  progname  [ -a | -e ]  [ -c ]  [ -f | -p ]  [ -x ]" );
  432. 			}
  433. 			Console.ResetColor( );
  434.  
  435. 			Console.Error.WriteLine( );
  436.  
  437. 			Console.Error.Write( "Where:   " );
  438. 			Console.ForegroundColor = ConsoleColor.White;
  439. 			Console.Error.Write( "progname" );
  440. 			Console.ResetColor( );
  441. 			Console.Error.WriteLine( "   the program name or internal command to be searched for" );
  442.  
  443. 			Console.ForegroundColor = ConsoleColor.White;
  444. 			Console.Error.Write( "         {0}", ( switchchar == '/' ? "/A" : "-a" ) );
  445. 			Console.ResetColor( );
  446. 			Console.Error.Write( "         returns " );
  447. 			Console.ForegroundColor = ConsoleColor.White;
  448. 			Console.Error.Write( "A" );
  449. 			Console.ResetColor( );
  450. 			Console.Error.WriteLine( "ll matches (default: stop at first match)" );
  451.  
  452. 			Console.ForegroundColor = ConsoleColor.White;
  453. 			Console.Error.Write( "         {0}         C", ( switchchar == '/' ? "/C" : "-c" ) );
  454. 			Console.ResetColor( );
  455. 			Console.Error.WriteLine( "opies result to clipboard" );
  456.  
  457. 			Console.ForegroundColor = ConsoleColor.White;
  458. 			Console.Error.Write( "         {0}", ( switchchar == '/' ? "/E" : "-e" ) );
  459. 			Console.ResetColor( );
  460. 			Console.Error.Write( "         opens " );
  461. 			Console.ForegroundColor = ConsoleColor.White;
  462. 			Console.Error.Write( "E" );
  463. 			Console.ResetColor( );
  464. 			Console.Error.Write( "xplorer with result selected, " );
  465. 			Console.ForegroundColor = ConsoleColor.White;
  466. 			Console.Error.Write( "if" );
  467. 			Console.ResetColor( );
  468. 			Console.Error.WriteLine( " it is a file" );
  469.  
  470. 			Console.ForegroundColor = ConsoleColor.White;
  471. 			Console.Error.Write( "         {0}", ( switchchar == '/' ? "/F" : "-f" ) );
  472. 			Console.ResetColor( );
  473. 			Console.Error.Write( "         returns name and " );
  474. 			Console.ForegroundColor = ConsoleColor.White;
  475. 			Console.Error.Write( "F" );
  476. 			Console.ResetColor( );
  477. 			Console.Error.WriteLine( "ile version for external commands" );
  478.  
  479. 			Console.ForegroundColor = ConsoleColor.White;
  480. 			Console.Error.Write( "         {0}", ( switchchar == '/' ? "/P" : "-p" ) );
  481. 			Console.ResetColor( );
  482. 			Console.Error.Write( "         returns name and " );
  483. 			Console.ForegroundColor = ConsoleColor.White;
  484. 			Console.Error.Write( "P" );
  485. 			Console.ResetColor( );
  486. 			Console.Error.WriteLine( "roduct version for external commands" );
  487.  
  488. 			Console.ForegroundColor = ConsoleColor.White;
  489. 			Console.Error.Write( "         {0}", ( switchchar == '/' ? "/X" : "-x" ) );
  490. 			Console.ResetColor( );
  491. 			Console.Error.Write( "         returns e" );
  492. 			Console.ForegroundColor = ConsoleColor.White;
  493. 			Console.Error.Write( "X" );
  494. 			Console.ResetColor( );
  495. 			Console.Error.WriteLine( "ternal commands only, no DOSKEY macros or" );
  496.  
  497. 			Console.Error.WriteLine( "                    internal commands   (default: all types)" );
  498.  
  499. 			Console.Error.WriteLine( );
  500. 			Console.Error.WriteLine( "Note:    By default, this program first compares the specified name against" );
  501. 			Console.Error.WriteLine( "         a list of active DOSKEY macros, next against a list of CMD's" );
  502. 			Console.Error.WriteLine( "         internal commands, and finally it tries to find a matching program" );
  503. 			Console.Error.WriteLine( "         file in the current directory and the PATH, using the extensions" );
  504. 			Console.Error.WriteLine( "         listed in PATHEXT." );
  505. 			Console.Error.WriteLine( );
  506. 			Console.Error.WriteLine( "Written by Rob van der Woude" );
  507. 			Console.Error.WriteLine( "http://www.robvanderwoude.com" );
  508. 			return 1;
  509. 		}
  510. 	}
  511. }
  512.