using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
namespace RobvanderWoude
{
class RxGrep
{
public static int skipmatches = 0;
public static int takematches = 0;
static int Main( string[] args )
{
try
{
#region Command Line Parsing
// Check for /? on the command line
foreach ( string arg in args )
{
if ( arg == "/?" )
{
return WriteError( );
}
}
string filename = string.Empty;
string pattern = string.Empty;
string[] options = null;
RegexOptions regexoptions = RegexOptions.None;
bool isredirected = ConsoleEx.InputRedirected;
int redirectnum = ( isredirected ? 1 : 0 );
string input = string.Empty;
// Check and interpret command line arguments
switch ( args.Length + redirectnum )
{
case 2:
if ( isredirected )
{
pattern = args[0];
}
else
{
filename = args[0];
pattern = args[1];
}
break;
case 3:
case 4:
case 5:
if ( isredirected )
{
pattern = args[0];
options = args.Slice( 1, args.Length );
}
else
{
filename = args[0];
pattern = args[1];
options = args.Slice( 2, args.Length );
}
break;
default:
return WriteError( );
}
if ( options != null )
{
foreach ( string option in options )
{
// Right now, /I is the only valid command line switch
switch ( option.ToUpper( ).Substring( 0, 2 ) )
{
case "/I":
regexoptions |= RegexOptions.IgnoreCase;
break;
case "/S":
try
{
skipmatches = Convert.ToInt32( option.Substring( 3 ) );
}
catch ( Exception e )
{
Console.Error.WriteLine( "Error: {0}", e.Message );
return WriteError( "Invalid command line switch: " + option );
}
break;
case "/T":
try
{
takematches = Convert.ToInt32( option.Substring( 3 ) );
}
catch ( Exception e )
{
Console.Error.WriteLine( "Error: {0}", e.Message );
return WriteError( "Invalid command line switch: " + option );
}
break;
default:
return WriteError( "Invalid command line " + ( option.Substring( 0, 1 ) == "/" ? "switch" : "argument" ) + ": " + option );
}
}
}
if ( isredirected )
{
// Read the redirected Standard Input
input = Console.In.ReadToEnd( );
}
else
{
// Check if the file name is valid
if ( filename.IndexOf( "/" ) > -1 )
{
return WriteError( );
}
if ( filename.IndexOfAny( "?*".ToCharArray( ) ) > -1 )
{
return WriteError( "Wildcards not allowed" );
}
// Check if the file exists
if ( File.Exists( filename ) )
{
// Read the file content
using ( StreamReader file = new StreamReader( filename ) )
{
input = file.ReadToEnd( );
}
}
else
{
return WriteError( "File not found: \"" + filename + "\"" );
}
}
#endregion Command Line Parsing
// Now that the command line parsing is done, let's get some action
if ( DisplayMatches( input, pattern, regexoptions ) == 0 )
{
return WriteError( "No match found" );
}
else
{
return 0;
}
}
catch ( Exception e )
{
return WriteError( e.Message );
}
}
// The main functionality: display all matching substrings
public static int DisplayMatches( string haystack, string needle, RegexOptions options )
{
int counter = 0;
int displayed = 0;
// Get all matches
MatchCollection matches = Regex.Matches( haystack, needle, options );
if ( matches.Count > skipmatches )
{
foreach ( Match match in matches )
{
if ( counter >= skipmatches && ( displayed < takematches || takematches == 0 ) )
{
Console.WriteLine( match.Value );
displayed += 1;
}
counter += 1;
}
}
return displayed;
}
#region Redirection Detection
// Code to detect redirection by Hans Passant on StackOverflow.com
// http://stackoverflow.com/questions/3453220/how-to-detect-if-console-in-stdin-has-been-redirected
public static class ConsoleEx
{
public static bool OutputRedirected
{
get
{
return FileType.Char != GetFileType( GetStdHandle( StdHandle.Stdout ) );
}
}
public static bool InputRedirected
{
get
{
return FileType.Char != GetFileType( GetStdHandle( StdHandle.Stdin ) );
}
}
public static bool ErrorRedirected
{
get
{
return FileType.Char != GetFileType( GetStdHandle( StdHandle.Stderr ) );
}
}
// P/Invoke:
private enum FileType { Unknown, Disk, Char, Pipe };
private enum StdHandle { Stdin = -10, Stdout = -11, Stderr = -12 };
[DllImport( "kernel32.dll" )]
private static extern FileType GetFileType( IntPtr hdl );
[DllImport( "kernel32.dll" )]
private static extern IntPtr GetStdHandle( StdHandle std );
}
#endregion Redirection Detection
#region Error Handling
public static int WriteError( Exception e = null )
{
return WriteError( e == null ? null : e.Message );
}
public static int WriteError( string errorMessage )
{
Console.OpenStandardError( );
if ( string.IsNullOrEmpty( errorMessage ) == false )
{
Console.Error.WriteLine( );
Console.ForegroundColor = ConsoleColor.Red;
Console.Error.Write( "ERROR: " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.WriteLine( errorMessage );
Console.ResetColor( );
}
/*
RxGrep, Version 2.00
Multi-line FindStr/Grep like tool
Usage: RXGREP filename pattern [ /I ] [ /S:nn ] [ /T:nn ]
or: some_command | RXGREP pattern [ /I ] [ /S:nn ] [ /T:nn ]
Where: filename is the file to be filtered
some_command is the command whose standard output is to be filtered
pattern is the search pattern (regular expression)
/I makes the search case insensitive
/S:nn Skip the first nn matches
/T:nn Take only nn matches
Example: ROBOCOPY D:\sourcedir E:\targetdir /NP /MIR |
RXGREP "\s+\d+\s+D:\\sourcedir\\[^\n\r]*\r\n([^\n\r\\]+\r\n)+"
(to be read as a single command line) will return something like:
125 D:\sourcedir\subdir\
New File 342 brandnewfile.ext
Newer 4.06m updatedfile.ext
*EXTRA File 2.40m deletedfile.ext
Check for redirection by Hans Passant on StackOverflow.com
/questions/3453220/how-to-detect-if-console-in-stdin-has-been-redirected
Array Slice extension by Sam Allen http://www.dotnetperls.com/array-slice
Written by Rob van der Woude
http://www.robvanderwoude.com
*/
Console.Error.WriteLine( );
Console.Error.WriteLine( "RxGrep, Version 2.00" );
Console.Error.WriteLine( "Multi-line FindStr/Grep like tool" );
Console.Error.WriteLine( );
Console.Error.Write( "Usage: " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.WriteLine( "RXGREP filename pattern [ /I ] [ /S:nn ] [ /T:nn ]" );
Console.ResetColor( );
Console.Error.Write( "or: " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.WriteLine( "some_command | RXGREP pattern [ /I ] [ /S:nn ] [ /T:nn ]" );
Console.ResetColor( );
Console.Error.WriteLine( );
Console.Error.Write( "Where: " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "filename" );
Console.ResetColor( );
Console.Error.WriteLine( " is the file to be filtered" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " some_command" );
Console.ResetColor( );
Console.Error.WriteLine( " is the command whose standard output is to be filtered" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " pattern" );
Console.ResetColor( );
Console.Error.WriteLine( " is the search pattern (regular expression)" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " /I" );
Console.ResetColor( );
Console.Error.Write( " makes the search case " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "I" );
Console.ResetColor( );
Console.Error.WriteLine( "nsensitive" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " /S:nn S" );
Console.ResetColor( );
Console.Error.Write( "kip the first " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "nn" );
Console.ResetColor( );
Console.Error.WriteLine( " matches" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( " /T:nn T" );
Console.ResetColor( );
Console.Error.Write( "ake only " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.Write( "nn" );
Console.ResetColor( );
Console.Error.WriteLine( " matches" );
Console.Error.WriteLine( );
Console.Error.Write( "Example: " );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.WriteLine( @"ROBOCOPY D:\sourcedir E:\targetdir /NP /MIR |" );
Console.Error.WriteLine( @" RXGREP ""\s+\d+\s+D:\\sourcedir\\[^\n\r]*\r\n([^\n\r\\]+\r\n)+""" );
Console.ResetColor( );
Console.Error.WriteLine( " (to be read as a single command line) will return something like:" );
Console.ForegroundColor = ConsoleColor.White;
Console.Error.WriteLine( @" 125 D:\sourcedir\subdir\" );
Console.Error.WriteLine( " New File 342 brandnewfile.ext" );
Console.Error.WriteLine( " Newer 4.06m updatedfile.ext" );
Console.Error.WriteLine( " *EXTRA File 2.40m deletedfile.ext" );
Console.ResetColor( );
Console.Error.WriteLine( );
Console.Error.Write( "Check for redirection by Hans Passant on " );
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.Error.WriteLine( "StackOverflow.com" );
Console.Error.WriteLine( "/questions/3453220/how-to-detect-if-console-in-stdin-has-been-redirected" );
Console.ResetColor( );
Console.Error.Write( "Array Slice extension by Sam Allen " );
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.Error.WriteLine( "http://www.dotnetperls.com/array-slice" );
Console.ResetColor( );
Console.Error.WriteLine( );
Console.Error.WriteLine( "Written by Rob van der Woude" );
Console.Error.Write( "http://www.robvanderwoude.com" );
Console.OpenStandardOutput( );
return 1;
}
#endregion Error Handling
}
#region Extensions
// Array Slice
// http://www.dotnetperls.com/array-slice
public static class Extensions
{
///
/// Get the array slice between the two indexes.
/// ... Inclusive for start index, exclusive for end index.
///
public static T[] Slice( this T[] source, int start, int end )
{
// Handles negative ends.
if ( end < 0 )
{
end = source.Length + end;
}
int len = end - start;
// Return new array.
T[] res = new T[len];
for ( int i = 0; i < len; i++ )
{
res[i] = source[i + start];
}
return res;
}
}
#endregion Extensions
}
//csharp/7349