Rob van der Woude's Scripting Pages
Powered by GeSHi

Source code for rxgrep.cs

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

  1. using System;
  2. using System.IO;
  3. using System.Runtime.InteropServices;
  4. using System.Text.RegularExpressions;
  5.  
  6.  
  7. namespace RobvanderWoude
  8. {
  9. 	class RxGrep
  10. 	{
  11. 		static string progver = "2.02";
  12.  
  13. 		static int skipmatches = 0;
  14. 		static int takematches = 0;
  15. 		static int bytes = -1;
  16.  
  17.  
  18. 		static int Main( string[] args )
  19. 		{
  20. 			try
  21. 			{
  22. 				#region Command Line Parsing
  23.  
  24. 				// Check for /? on the command line
  25. 				foreach ( string arg in args )
  26. 				{
  27. 					if ( arg == "/?" )
  28. 					{
  29. 						return WriteError( );
  30. 					}
  31. 				}
  32.  
  33. 				string filename = string.Empty;
  34. 				string pattern = string.Empty;
  35. 				string[] options = null;
  36. 				RegexOptions regexoptions = RegexOptions.None;
  37. 				bool isredirected = ConsoleEx.InputRedirected;
  38. 				bool caseset = false;
  39. 				bool skipset = false;
  40. 				bool takeset = false;
  41. 				int redirectnum = ( isredirected ? 1 : 0 );
  42. 				string input = string.Empty;
  43.  
  44. 				// Check and interpret command line arguments
  45. 				switch ( args.Length + redirectnum )
  46. 				{
  47. 					case 2:
  48. 						if ( isredirected )
  49. 						{
  50. 							pattern = args[0];
  51. 						}
  52. 						else
  53. 						{
  54. 							filename = args[0];
  55. 							pattern = args[1];
  56. 						}
  57. 						break;
  58. 					case 3:
  59. 					case 4:
  60. 					case 5:
  61. 					case 6:
  62. 						if ( isredirected )
  63. 						{
  64. 							pattern = args[0];
  65. 							options = args.Slice( 1, args.Length );
  66. 						}
  67. 						else
  68. 						{
  69. 							filename = args[0];
  70. 							pattern = args[1];
  71. 							options = args.Slice( 2, args.Length );
  72. 						}
  73. 						break;
  74. 					default:
  75. 						return WriteError( );
  76. 				}
  77.  
  78. 				if ( options != null )
  79. 				{
  80. 					foreach ( string option in options )
  81. 					{
  82. 						// Right now, /I is the only valid command line switch
  83. 						switch ( option.ToUpper( ).Substring( 0, 2 ) )
  84. 						{
  85. 							case "/F":
  86. 								if ( bytes != -1 )
  87. 								{
  88. 									return WriteError( "Duplicate switch /F" );
  89. 								}
  90. 								try
  91. 								{
  92. 									bytes = Convert.ToInt32( option.Substring( 3 ) );
  93. 								}
  94. 								catch ( Exception e )
  95. 								{
  96. 									Console.Error.WriteLine( "Error: {0}", e.Message );
  97. 									return WriteError( String.Format( "Invalid command line switch \"{0}\"", option ) );
  98. 								}
  99. 								break;
  100. 							case "/I":
  101. 								if ( caseset )
  102. 								{
  103. 									return WriteError( "Duplicate switch /I" );
  104. 								}
  105. 								regexoptions |= RegexOptions.IgnoreCase;
  106. 								caseset = true;
  107. 								break;
  108. 							case "/S":
  109. 								if ( skipset )
  110. 								{
  111. 									return WriteError( "Duplicate switch /S" );
  112. 								}
  113. 								try
  114. 								{
  115. 									skipmatches = Convert.ToInt32( option.Substring( 3 ) );
  116. 								}
  117. 								catch ( Exception e )
  118. 								{
  119. 									Console.Error.WriteLine( "Error: {0}", e.Message );
  120. 									return WriteError( String.Format( "Invalid command line switch \"{0}\"", option ) );
  121. 								}
  122. 								break;
  123. 							case "/T":
  124. 								if ( takeset )
  125. 								{
  126. 									return WriteError( "Duplicate switch /T" );
  127. 								}
  128. 								try
  129. 								{
  130. 									takematches = Convert.ToInt32( option.Substring( 3 ) );
  131. 								}
  132. 								catch ( Exception e )
  133. 								{
  134. 									Console.Error.WriteLine( "Error: {0}", e.Message );
  135. 									return WriteError( String.Format( "Invalid command line switch \"{0}\"", option ) );
  136. 								}
  137. 								break;
  138. 							default:
  139. 								return WriteError( String.Format( "Invalid command line {0}: \"{1}\"", ( option.Substring( 0, 1 ) == "/" ? "switch" : "argument" ), option ) );
  140. 						}
  141. 					}
  142. 				}
  143.  
  144. 				if ( isredirected )
  145. 				{
  146. 					// Read the redirected Standard Input
  147. 					input = Console.In.ReadToEnd( );
  148. 					if ( bytes != -1 )
  149. 					{
  150. 						input = input.Substring( 0, Math.Min( bytes, input.Length ) );
  151. 					}
  152. 				}
  153. 				else
  154. 				{
  155. 					// Check if the file name is valid
  156. 					if ( filename.IndexOf( "/" ) > -1 )
  157. 					{
  158. 						return WriteError( );
  159. 					}
  160. 					if ( filename.IndexOfAny( "?*".ToCharArray( ) ) > -1 )
  161. 					{
  162. 						return WriteError( "Wildcards not allowed" );
  163. 					}
  164. 					// Check if the file exists
  165. 					if ( File.Exists( filename ) )
  166. 					{
  167. 						// Read the file content
  168. 						using ( StreamReader file = new StreamReader( filename ) )
  169. 						{
  170. 							if ( bytes == -1 )
  171. 							{
  172. 								input = file.ReadToEnd( );
  173. 							}
  174. 							else
  175. 							{
  176. 								char[] buffer = new char[bytes];
  177. 								file.Read( buffer, 0, bytes );
  178. 								input = String.Join( String.Empty, buffer );
  179. 							}
  180. 						}
  181. 					}
  182. 					else
  183. 					{
  184. 						return WriteError( String.Format( "File not found: \"{0}\"", filename ) );
  185. 					}
  186. 				}
  187.  
  188. 				#endregion Command Line Parsing
  189.  
  190.  
  191. 				// Now that the command line parsing is done, let's get some action
  192. 				if ( DisplayMatches( input, pattern, regexoptions ) == 0 )
  193. 				{
  194. 					return WriteError( "No match found" );
  195. 				}
  196. 				else
  197. 				{
  198. 					return 0;
  199. 				}
  200. 			}
  201. 			catch ( Exception e )
  202. 			{
  203. 				return WriteError( e.Message );
  204. 			}
  205. 		}
  206.  
  207.  
  208. 		// The main functionality: display all matching substrings
  209. 		public static int DisplayMatches( string haystack, string needle, RegexOptions options )
  210. 		{
  211. 			int counter = 0;
  212. 			int displayed = 0;
  213. 			// Get all matches
  214. 			MatchCollection matches = Regex.Matches( haystack, needle, options );
  215. 			if ( matches.Count > skipmatches )
  216. 			{
  217. 				foreach ( Match match in matches )
  218. 				{
  219. 					if ( counter >= skipmatches && ( displayed < takematches || takematches == 0 ) )
  220. 					{
  221. 						Console.WriteLine( match.Value );
  222. 						displayed += 1;
  223. 					}
  224. 					counter += 1;
  225. 				}
  226. 			}
  227. 			return displayed;
  228. 		}
  229.  
  230.  
  231. 		#region Redirection Detection
  232.  
  233. 		// Code to detect redirection by Hans Passant on StackOverflow.com
  234. 		// http://stackoverflow.com/questions/3453220/how-to-detect-if-console-in-stdin-has-been-redirected
  235. 		public static class ConsoleEx
  236. 		{
  237. 			public static bool OutputRedirected
  238. 			{
  239. 				get
  240. 				{
  241. 					return FileType.Char != GetFileType( GetStdHandle( StdHandle.Stdout ) );
  242. 				}
  243. 			}
  244.  
  245. 			public static bool InputRedirected
  246. 			{
  247. 				get
  248. 				{
  249. 					return FileType.Char != GetFileType( GetStdHandle( StdHandle.Stdin ) );
  250. 				}
  251. 			}
  252.  
  253. 			public static bool ErrorRedirected
  254. 			{
  255. 				get
  256. 				{
  257. 					return FileType.Char != GetFileType( GetStdHandle( StdHandle.Stderr ) );
  258. 				}
  259. 			}
  260.  
  261. 			// P/Invoke:
  262. 			private enum FileType { Unknown, Disk, Char, Pipe };
  263. 			private enum StdHandle { Stdin = -10, Stdout = -11, Stderr = -12 };
  264.  
  265. 			[DllImport( "kernel32.dll" )]
  266. 			private static extern FileType GetFileType( IntPtr hdl );
  267.  
  268. 			[DllImport( "kernel32.dll" )]
  269. 			private static extern IntPtr GetStdHandle( StdHandle std );
  270. 		}
  271.  
  272. 		#endregion Redirection Detection
  273.  
  274.  
  275. 		#region Error Handling
  276.  
  277. 		public static int WriteError( Exception e = null )
  278. 		{
  279. 			return WriteError( e == null ? null : e.Message );
  280. 		}
  281.  
  282. 		public static int WriteError( string errorMessage )
  283. 		{
  284. 			if ( string.IsNullOrEmpty( errorMessage ) == false )
  285. 			{
  286. 				Console.Error.WriteLine( );
  287. 				Console.ForegroundColor = ConsoleColor.Red;
  288. 				Console.Error.Write( "ERROR: " );
  289. 				Console.ForegroundColor = ConsoleColor.White;
  290. 				Console.Error.WriteLine( errorMessage );
  291. 				Console.ResetColor( );
  292. 			}
  293.  
  294. 			/*
  295. 			RxGrep,  Version 2.02
  296. 			Multi-line FindStr/Grep like tool
  297.  
  298. 			Usage:   RXGREP  filename  pattern  [ /F:size ]  [ /I ]  [ /S:nn ]  [ /T:nn ]
  299. 			or:      command  |  RXGREP  pattern  [ /F:size ]  [ /I ]  [ /S:nn ]  [ /T:nn ]
  300.  
  301. 			Where:   filename   is the file to be filtered
  302. 					 command    is the command whose standard output is to be filtered
  303. 					 pattern    is the search pattern (regular expression)
  304. 			         /F:bytes   searches only the first number of bytes
  305. 					 /I         makes the search case Insensitive
  306. 					 /S:nn      Skip the first nn matches
  307. 					 /T:nn      Take only nn matches
  308.  
  309. 			Example: ROBOCOPY D:\sourcedir E:\targetdir /NP /MIR |
  310. 					 RXGREP "\s+\d+\s+D:\\sourcedir\\[^\n\r]*\r\n([^\n\r\\]+\r\n)+"
  311. 					 (to be read as a single command line) will return something like:
  312. 									 125    D:\sourcedir\subdir\
  313. 						New File                 342        brandnewfile.ext
  314. 						Newer                  4.06m        updatedfile.ext
  315. 					  *EXTRA File              2.40m        deletedfile.ext
  316.  
  317. 			Note:    If /F:bytes is used and a file is specified, only the first bytes
  318. 			         of that file will be read; if the input is redirected, it is read
  319. 			         entirely, and will then be chopped to the specified number of bytes
  320. 			         before being searched.
  321.  
  322. 			Credits: Check for redirection by Hans Passant on StackOverflow.com
  323. 			         http://stackoverflow.com/a/3453272
  324. 			         Array Slice extension by Sam Allen
  325. 			         http://www.dotnetperls.com/array-slice
  326.  
  327. 			Written by Rob van der Woude
  328. 			http://www.robvanderwoude.com
  329. 			 */
  330.  
  331. 			Console.Error.WriteLine( );
  332. 			Console.Error.WriteLine( "RxGrep,  Version {0}", progver );
  333. 			Console.Error.WriteLine( "Multi-line FindStr/Grep like tool" );
  334. 			Console.Error.WriteLine( );
  335.  
  336. 			Console.Error.Write( "Usage:   " );
  337. 			Console.ForegroundColor = ConsoleColor.White;
  338. 			Console.Error.WriteLine( "RXGREP  filename  pattern  [ /F:size ]  [ /I ]  [ /S:nn ]  [ /T:nn ]" );
  339. 			Console.ResetColor( );
  340.  
  341. 			Console.Error.Write( "or:      " );
  342. 			Console.ForegroundColor = ConsoleColor.White;
  343. 			Console.Error.WriteLine( "command  |  RXGREP  pattern  [ /F:size ]  [ /I ]  [ /S:nn ]  [ /T:nn ]" );
  344. 			Console.ResetColor( );
  345.  
  346. 			Console.Error.WriteLine( );
  347.  
  348. 			Console.Error.Write( "Where:   " );
  349. 			Console.ForegroundColor = ConsoleColor.White;
  350. 			Console.Error.Write( "filename" );
  351. 			Console.ResetColor( );
  352. 			Console.Error.WriteLine( "   is the file to be filtered" );
  353.  
  354. 			Console.ForegroundColor = ConsoleColor.White;
  355. 			Console.Error.Write( "         command" );
  356. 			Console.ResetColor( );
  357. 			Console.Error.WriteLine( "    is the command whose standard output is to be filtered" );
  358.  
  359. 			Console.ForegroundColor = ConsoleColor.White;
  360. 			Console.Error.Write( "         pattern" );
  361. 			Console.ResetColor( );
  362. 			Console.Error.WriteLine( "    is the search pattern (regular expression)" );
  363.  
  364. 			Console.ForegroundColor = ConsoleColor.White;
  365. 			Console.Error.Write( "         /F:bytes" );
  366. 			Console.ResetColor( );
  367. 			Console.Error.Write( "   searches only the first number of " );
  368. 			Console.ForegroundColor = ConsoleColor.White;
  369. 			Console.Error.WriteLine( "bytes" );
  370. 			Console.ResetColor( );
  371.  
  372. 			Console.ForegroundColor = ConsoleColor.White;
  373. 			Console.Error.Write( "         /I" );
  374. 			Console.ResetColor( );
  375. 			Console.Error.Write( "         makes the search case " );
  376. 			Console.ForegroundColor = ConsoleColor.White;
  377. 			Console.Error.Write( "I" );
  378. 			Console.ResetColor( );
  379. 			Console.Error.WriteLine( "nsensitive" );
  380.  
  381. 			Console.ForegroundColor = ConsoleColor.White;
  382. 			Console.Error.Write( "         /S:nn      S" );
  383. 			Console.ResetColor( );
  384. 			Console.Error.Write( "kip the first " );
  385. 			Console.ForegroundColor = ConsoleColor.White;
  386. 			Console.Error.Write( "nn" );
  387. 			Console.ResetColor( );
  388. 			Console.Error.WriteLine( " matches" );
  389.  
  390. 			Console.ForegroundColor = ConsoleColor.White;
  391. 			Console.Error.Write( "         /T:nn      T" );
  392. 			Console.ResetColor( );
  393. 			Console.Error.Write( "ake only " );
  394. 			Console.ForegroundColor = ConsoleColor.White;
  395. 			Console.Error.Write( "nn" );
  396. 			Console.ResetColor( );
  397. 			Console.Error.WriteLine( " matches" );
  398.  
  399. 			Console.Error.WriteLine( );
  400.  
  401. 			Console.Error.Write( "Example: " );
  402. 			Console.ForegroundColor = ConsoleColor.White;
  403. 			Console.Error.WriteLine( @"ROBOCOPY D:\sourcedir E:\targetdir /NP /MIR |" );
  404.  
  405. 			Console.Error.WriteLine( @"         RXGREP ""\s+\d+\s+D:\\sourcedir\\[^\n\r]*\r\n([^\n\r\\]+\r\n)+""" );
  406. 			Console.ResetColor( );
  407.  
  408. 			Console.Error.WriteLine( "         (to be read as a single command line) will return something like:" );
  409.  
  410. 			Console.ForegroundColor = ConsoleColor.White;
  411. 			Console.Error.WriteLine( @"                         125    D:\sourcedir\subdir\" );
  412.  
  413. 			Console.Error.WriteLine( "            New File                 342        brandnewfile.ext" );
  414.  
  415. 			Console.Error.WriteLine( "            Newer                  4.06m        updatedfile.ext" );
  416.  
  417. 			Console.Error.WriteLine( "          *EXTRA File              2.40m        deletedfile.ext" );
  418. 			Console.ResetColor( );
  419.  
  420. 			Console.Error.WriteLine( );
  421.  
  422. 			Console.Error.Write( "Note:    If " );
  423. 			Console.ForegroundColor = ConsoleColor.White;
  424. 			Console.Error.Write( "/F:bytes" );
  425. 			Console.ResetColor( );
  426. 			Console.Error.Write( " is used and a file is specified, only the first " );
  427. 			Console.ForegroundColor = ConsoleColor.White;
  428. 			Console.Error.WriteLine( "bytes" );
  429. 			Console.ResetColor( );
  430.  
  431. 			Console.Error.WriteLine( "         of that file will be read; if the input is redirected, it is read" );
  432.  
  433. 			Console.Error.WriteLine( "         entirely, and will then be chopped to the specified number of bytes" );
  434.  
  435. 			Console.Error.WriteLine( "         before being searched." );
  436.  
  437. 			Console.Error.WriteLine( );
  438.  
  439. 			Console.Error.WriteLine( "Credits: Check for redirection by Hans Passant on StackOverflow.com" );
  440.  
  441. 			Console.ForegroundColor = ConsoleColor.DarkGray;
  442. 			Console.Error.WriteLine( "         http://stackoverflow.com/a/3453272" );
  443. 			Console.ResetColor( );
  444.  
  445. 			Console.Error.WriteLine( "         Array Slice extension by Sam Allen" );
  446.  
  447. 			Console.ForegroundColor = ConsoleColor.DarkGray;
  448. 			Console.Error.WriteLine( "         http://www.dotnetperls.com/array-slice" );
  449. 			Console.ResetColor( );
  450.  
  451. 			Console.Error.WriteLine( );
  452.  
  453. 			Console.Error.WriteLine( "Written by Rob van der Woude" );
  454.  
  455. 			Console.Error.WriteLine( "http://www.robvanderwoude.com" );
  456.  
  457. 			return 1;
  458. 		}
  459.  
  460. 		#endregion Error Handling
  461. 	}
  462.  
  463. 	#region Extensions
  464.  
  465. 	// Array Slice
  466. 	// http://www.dotnetperls.com/array-slice
  467. 	public static class Extensions
  468. 	{
  469. 		/// <summary>
  470. 		/// Get the array slice between the two indexes.
  471. 		/// ... Inclusive for start index, exclusive for end index.
  472. 		/// </summary>
  473. 		public static T[] Slice<T>( this T[] source, int start, int end )
  474. 		{
  475. 			// Handles negative ends.
  476. 			if ( end < 0 )
  477. 			{
  478. 				end = source.Length + end;
  479. 			}
  480. 			int len = end - start;
  481.  
  482. 			// Return new array.
  483. 			T[] res = new T[len];
  484. 			for ( int i = 0; i < len; i++ )
  485. 			{
  486. 				res[i] = source[i + start];
  487. 			}
  488. 			return res;
  489. 		}
  490. 	}
  491.  
  492. 	#endregion Extensions
  493. }
  494.  

page last uploaded: 2015-12-04, 16:51