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