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.Collections.Generic;
  3. using System.IO;
  4. using System.Text.RegularExpressions;
  5.  
  6.  
  7. namespace RobvanderWoude
  8. {
  9. 	class RxGrep
  10. 	{
  11. 		static readonly string progver = "3.01";
  12.  
  13.  
  14. 		#region Global Variables
  15.  
  16. 		static bool dedup = false;
  17. 		static bool returngroup = false;
  18. 		static bool singlelinemode = false;
  19. 		static int skipmatches = 0;
  20. 		static int takematches = 0;
  21. 		static int groupnum = 0;
  22. 		static int bytes = -1;
  23. 		static List<string> dedupedmatches = new List<string>( );
  24.  
  25. 		#endregion Global Variables
  26.  
  27.  
  28. 		static int Main( string[] args )
  29. 		{
  30. 			#region Initialize Variables
  31.  
  32. 			bool caseset = false;
  33. 			bool quiet = false;
  34. 			bool skipset = false;
  35. 			bool takeset = false;
  36. 			string filename = string.Empty;
  37. 			string pattern;
  38. 			string input = string.Empty;
  39. 			int matchcount;
  40. 			int redirectnum = ( Console.IsInputRedirected ? 1 : 0 );
  41. 			RegexOptions regexoptions = RegexOptions.None;
  42.  
  43. 			#endregion Initialize Variables
  44.  
  45.  
  46. 			#region Command Line Parsing
  47.  
  48. 			if ( args.Length + redirectnum < 2 )
  49. 			{
  50. 				return ShowHelp( );
  51. 			}
  52.  
  53. 			List<string> arguments = new List<string>( args );
  54. 			if ( arguments.Contains( "/?" ) )
  55. 			{
  56. 				return ShowHelp( );
  57. 			}
  58.  
  59. 			if ( !Console.IsInputRedirected )
  60. 			{
  61. 				filename = arguments[0];
  62. 				arguments.RemoveAt( 0 );
  63. 			}
  64. 			pattern = arguments[0];
  65. 			arguments.RemoveAt( 0 );
  66.  
  67. 			foreach ( string option in arguments )
  68. 			{
  69. 				switch ( option.ToUpper( ).Substring( 0, 2 ) )
  70. 				{
  71. 					case "/D":
  72. 						if ( dedup )
  73. 						{
  74. 							return ShowHelp( "Duplicate switch /D" );
  75. 						}
  76. 						dedup = true;
  77. 						break;
  78. 					case "/F":
  79. 						if ( bytes != -1 )
  80. 						{
  81. 							return ShowHelp( "Duplicate switch /F" );
  82. 						}
  83. 						try
  84. 						{
  85. 							bytes = Convert.ToInt32( option.Substring( 3 ) );
  86. 						}
  87. 						catch ( Exception e )
  88. 						{
  89. 							Console.Error.WriteLine( "Error: {0}", e.Message );
  90. 							return ShowHelp( string.Format( "Invalid command line switch \"{0}\"", option ) );
  91. 						}
  92. 						break;
  93. 					case "/G":
  94. 						if ( returngroup )
  95. 						{
  96. 							return ShowHelp( "Duplicate switch /G" );
  97. 						}
  98. 						returngroup = true;
  99. 						try
  100. 						{
  101. 							groupnum = Convert.ToInt32( option.Substring( 3 ) );
  102. 							if ( groupnum < 0 )
  103. 							{
  104. 								return ShowHelp( "Invalid group number {0} specified", groupnum.ToString( ) );
  105. 							}
  106. 						}
  107. 						catch ( Exception e )
  108. 						{
  109. 							Console.Error.WriteLine( "Error: {0}", e.Message );
  110. 							return ShowHelp( string.Format( "Invalid command line switch \"{0}\"", option ) );
  111. 						}
  112. 						break;
  113. 					case "/I":
  114. 						if ( caseset )
  115. 						{
  116. 							return ShowHelp( "Duplicate switch /L" );
  117. 						}
  118. 						regexoptions |= RegexOptions.IgnoreCase;
  119. 						caseset = true;
  120. 						break;
  121. 					case "/L":
  122. 						if ( singlelinemode )
  123. 						{
  124. 							return ShowHelp( "Duplicate switch /I" );
  125. 						}
  126. 						singlelinemode = true;
  127. 						break;
  128. 					case "/Q":
  129. 						if ( quiet )
  130. 						{
  131. 							return ShowHelp( "Duplicate switch /Q" );
  132. 						}
  133. 						quiet = true;
  134. 						break;
  135. 					case "/S":
  136. 						if ( skipset )
  137. 						{
  138. 							return ShowHelp( "Duplicate switch /S" );
  139. 						}
  140. 						try
  141. 						{
  142. 							skipmatches = Convert.ToInt32( option.Substring( 3 ) );
  143. 						}
  144. 						catch ( Exception e )
  145. 						{
  146. 							Console.Error.WriteLine( "Error: {0}", e.Message );
  147. 							return ShowHelp( string.Format( "Invalid command line switch \"{0}\"", option ) );
  148. 						}
  149. 						break;
  150. 					case "/T":
  151. 						if ( takeset )
  152. 						{
  153. 							return ShowHelp( "Duplicate switch /T" );
  154. 						}
  155. 						try
  156. 						{
  157. 							takematches = Convert.ToInt32( option.Substring( 3 ) );
  158. 						}
  159. 						catch ( Exception e )
  160. 						{
  161. 							Console.Error.WriteLine( "Error: {0}", e.Message );
  162. 							return ShowHelp( string.Format( "Invalid command line switch \"{0}\"", option ) );
  163. 						}
  164. 						break;
  165. 					default:
  166. 						return ShowHelp( string.Format( "Invalid command line {0}: \"{1}\"", ( option[0] == '/' ? "switch" : "argument" ), option ) );
  167. 				}
  168. 			}
  169.  
  170. 			#endregion Command Line Parsing
  171.  
  172.  
  173. 			#region Command Line Arguments Validation
  174.  
  175. 			if ( Console.IsInputRedirected )
  176. 			{
  177. 				// Read the redirected Standard Input
  178. 				input = Console.In.ReadToEnd( );
  179. 				if ( bytes != -1 )
  180. 				{
  181. 					input = input.Substring( 0, Math.Min( bytes, input.Length ) );
  182. 				}
  183. 			}
  184. 			else
  185. 			{
  186. 				// Check if the file name is valid
  187. 				if ( filename.IndexOf( "/" ) > -1 )
  188. 				{
  189. 					return ShowHelp( );
  190. 				}
  191. 				if ( filename.IndexOfAny( "?*".ToCharArray( ) ) > -1 )
  192. 				{
  193. 					return ShowHelp( "Wildcards not allowed" );
  194. 				}
  195. 				// Check if the file exists
  196. 				if ( File.Exists( filename ) )
  197. 				{
  198. 					// Read the file content
  199. 					using ( StreamReader file = new StreamReader( filename ) )
  200. 					{
  201. 						if ( bytes == -1 )
  202. 						{
  203. 							input = file.ReadToEnd( );
  204. 						}
  205. 						else
  206. 						{
  207. 							char[] buffer = new char[bytes];
  208. 							file.Read( buffer, 0, bytes );
  209. 							input = string.Join( string.Empty, buffer );
  210. 						}
  211. 					}
  212. 				}
  213. 				else
  214. 				{
  215. 					return ShowHelp( string.Format( "File not found: \"{0}\"", filename ) );
  216. 				}
  217. 			}
  218.  
  219. 			if ( dedup && ( skipset || takeset ) )
  220. 			{
  221. 				return ShowHelp( "Switch /D cannot be combined with switches /S or /T" );
  222. 			}
  223.  
  224. 			#endregion Command Line Arguments Validation
  225.  
  226.  
  227. 			// Now that the command line parsing is finally done, let's get some action
  228. 			if ( singlelinemode )
  229. 			{
  230. 				matchcount = 0;
  231. 				foreach ( string line in input.Split( new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries ) )
  232. 				{
  233. 					matchcount += DisplayMatches( line, pattern, regexoptions );
  234. 				}
  235.             }
  236. 			else
  237. 			{
  238. 				matchcount = DisplayMatches( input, pattern, regexoptions );
  239. 			}
  240. 			return matchcount;
  241. 		}
  242.  
  243.  
  244. 		// The main functionality: display all matching substrings
  245. 		public static int DisplayMatches( string haystack, string needle, RegexOptions options )
  246. 		{
  247. 			int counter = 0;
  248. 			int displayed = 0;
  249. 			// Get all matches
  250. 			MatchCollection matches = Regex.Matches( haystack, needle, options );
  251. 			if ( dedup )
  252. 			{
  253. 				//List<string> dedupedmatches = new List<string>( );
  254. 				foreach ( Match match in matches )
  255. 				{
  256. 					if ( returngroup )
  257. 					{
  258. 						if ( match.Groups.Count >= groupnum )
  259. 						{
  260. 							Group group = match.Groups[groupnum];
  261. 							if ( !dedupedmatches.Contains( group.Value ) )
  262. 							{
  263. 								Console.WriteLine( group.Value );
  264. 								dedupedmatches.Add( group.Value );
  265. 								displayed += 1;
  266. 							}
  267. 						}
  268. 					}
  269. 					else if ( !dedupedmatches.Contains( match.Value ) )
  270. 					{
  271. 						Console.WriteLine( match.Value );
  272. 						dedupedmatches.Add( match.Value );
  273. 						displayed += 1;
  274. 					}
  275. 				}
  276. 			}
  277. 			else
  278. 			{
  279. 				if ( matches.Count > skipmatches )
  280. 				{
  281. 					foreach ( Match match in matches )
  282. 					{
  283. 						if ( counter >= skipmatches && ( displayed < takematches || takematches == 0 ) )
  284. 						{
  285. 							if ( returngroup && match.Groups.Count >= groupnum )
  286. 							{
  287. 								Group group = match.Groups[groupnum];
  288. 								Console.WriteLine( group.Value );
  289. 								displayed += 1;
  290. 							}
  291. 							else
  292. 							{
  293. 								Console.WriteLine( match.Value );
  294. 								displayed += 1;
  295. 							}
  296. 						}
  297. 						counter += 1;
  298. 					}
  299. 				}
  300. 			}
  301. 			return displayed;
  302. 		}
  303.  
  304.  
  305. 		#region Error Handling
  306.  
  307. 		public static int ShowHelp( params string[] errmsg )
  308. 		{
  309. 			#region Error Message
  310.  
  311. 			if ( errmsg.Length > 0 )
  312.  
  313. 			{
  314. 				List<string> errargs = new List<string>( errmsg );
  315. 				errargs.RemoveAt( 0 );
  316. 				Console.Error.WriteLine( );
  317. 				Console.ForegroundColor = ConsoleColor.Red;
  318. 				Console.Error.Write( "ERROR:\t" );
  319. 				Console.ForegroundColor = ConsoleColor.White;
  320. 				Console.Error.WriteLine( errmsg[0], errargs.ToArray( ) );
  321. 				Console.ResetColor( );
  322. 			}
  323.  
  324. 			#endregion Error Message
  325.  
  326.  
  327. 			#region Help Text
  328.  
  329. 			/*
  330. 			RxGrep,  Version 3.00
  331. 			Multi-line FindStr/Grep like tool
  332.  
  333. 			Usage:   RXGREP  filename  pattern  [ options ]
  334. 			or:      command  |  RXGREP  pattern  [ options ]
  335.  
  336. 			Where:   filename   is the file to be filtered
  337. 			         command    is the command whose standard output is to be filtered
  338. 			         pattern    is the search pattern (regular expression)
  339.  
  340. 			Options: /D         do not show Duplicate matches
  341. 			         /F:nn      search only the First nn bytes
  342. 			         /G:nn      return match Group #nn
  343. 			         /I         case Insensitive search
  344. 			         /L         single Line mode: ^ and $ match begin and end of each line
  345. 			         /Q         Quiet mode: no message if no match is found
  346. 			         /S:nn      Skip the first nn matches
  347. 			         /T:nn      Take only nn matches
  348.  
  349. 			Example: ROBOCOPY D:\sourcedir E:\targetdir /NP /MIR |
  350. 			         RXGREP "\s+\d+\s+D:\\sourcedir\\[^\n\r]*\r\n([^\n\r\\]+\r\n)+"
  351. 			         (to be read as a single command line) will return something like:
  352. 			                         125    D:\sourcedir\subdir\
  353. 			            New File                 342        brandnewfile.ext
  354. 			            Newer                  4.06m        updatedfile.ext
  355. 			          *EXTRA File              2.40m        deletedfile.ext
  356.  
  357. 			Notes:   If /F:nn is used and a file is specified, only the first nn bytes
  358. 			         of that file will be read; if the input is redirected, it is read
  359. 			         entirely, and then chopped to nn bytes before being searched.
  360. 			         Switches /D and /G cannot be combined with /S or /T.
  361. 			         Return code ("errorlevel") will equal the number of matches found,
  362. 			         or -1 in case of (command line) errors.
  363.  
  364. 			Written by Rob van der Woude
  365. 			https://www.robvanderwoude.com
  366. 			 */
  367.  
  368. 			#endregion Help Text
  369.  
  370.  
  371. 			#region Display Help Text
  372.  
  373. 			Console.Error.WriteLine( );
  374. 			Console.Error.WriteLine( "RxGrep,  Version {0}", progver );
  375. 			Console.Error.WriteLine( "Multi-line FindStr/Grep like tool" );
  376. 			Console.Error.WriteLine( );
  377.  
  378. 			Console.Error.Write( "Usage:   " );
  379. 			Console.ForegroundColor = ConsoleColor.White;
  380. 			Console.Error.WriteLine( "RXGREP  filename  pattern  [ options ]" );
  381. 			Console.ResetColor( );
  382.  
  383. 			Console.Error.Write( "or:      " );
  384. 			Console.ForegroundColor = ConsoleColor.White;
  385. 			Console.Error.WriteLine( "command | RXGREP  pattern  [ options ]" );
  386. 			Console.ResetColor( );
  387.  
  388. 			Console.Error.WriteLine( );
  389.  
  390. 			Console.Error.Write( "Where:   " );
  391. 			Console.ForegroundColor = ConsoleColor.White;
  392. 			Console.Error.Write( "filename" );
  393. 			Console.ResetColor( );
  394. 			Console.Error.WriteLine( "   is the file to be filtered" );
  395.  
  396. 			Console.ForegroundColor = ConsoleColor.White;
  397. 			Console.Error.Write( "         command" );
  398. 			Console.ResetColor( );
  399. 			Console.Error.WriteLine( "    is the command whose standard output is to be filtered" );
  400.  
  401. 			Console.ForegroundColor = ConsoleColor.White;
  402. 			Console.Error.Write( "         pattern" );
  403. 			Console.ResetColor( );
  404. 			Console.Error.WriteLine( "    is the search pattern (regular expression)" );
  405.  
  406. 			Console.Error.WriteLine( );
  407.  
  408. 			Console.Error.Write( "Options: " );
  409. 			Console.ForegroundColor = ConsoleColor.White;
  410. 			Console.Error.Write( "/D" );
  411. 			Console.ResetColor( );
  412. 			Console.Error.Write( "         do not show " );
  413. 			Console.ForegroundColor = ConsoleColor.White;
  414. 			Console.Error.Write( "D" );
  415. 			Console.ResetColor( );
  416. 			Console.Error.WriteLine( "uplicate matches" );
  417.  
  418. 			Console.ForegroundColor = ConsoleColor.White;
  419. 			Console.Error.Write( "         /F:nn" );
  420. 			Console.ResetColor( );
  421. 			Console.Error.Write( "      search only the " );
  422. 			Console.ForegroundColor = ConsoleColor.White;
  423. 			Console.Error.Write( "F" );
  424. 			Console.ResetColor( );
  425. 			Console.Error.Write( "irst " );
  426. 			Console.ForegroundColor = ConsoleColor.White;
  427. 			Console.Error.Write( "nn" );
  428. 			Console.ResetColor( );
  429. 			Console.Error.WriteLine( " bytes" );
  430.  
  431. 			Console.ForegroundColor = ConsoleColor.White;
  432. 			Console.Error.Write( "         /G:nn" );
  433. 			Console.ResetColor( );
  434. 			Console.Error.Write( "      return match " );
  435. 			Console.ForegroundColor = ConsoleColor .White;
  436. 			Console.Error.Write( "G" );
  437. 			Console.ResetColor( );
  438. 			Console.Error.Write( "roup " );
  439. 			Console.ForegroundColor = ConsoleColor.White;
  440. 			Console.Error.WriteLine( "#nn" );
  441. 			Console.ResetColor( );
  442.  
  443. 			Console.ForegroundColor = ConsoleColor.White;
  444. 			Console.Error.Write( "         /I" );
  445. 			Console.ResetColor( );
  446. 			Console.Error.Write( "         case " );
  447. 			Console.ForegroundColor = ConsoleColor.White;
  448. 			Console.Error.Write( "I" );
  449. 			Console.ResetColor( );
  450. 			Console.Error.WriteLine( "nsensitive search" );
  451.  
  452. 			Console.ForegroundColor = ConsoleColor.White;
  453. 			Console.Error.Write( "         /L" );
  454. 			Console.ResetColor( );
  455. 			Console.Error.Write( "         single " );
  456. 			Console.ForegroundColor = ConsoleColor.White;
  457. 			Console.Error.Write( "L" );
  458. 			Console.ResetColor( );
  459. 			Console.Error.WriteLine( "ine mode: ^ and $ match begin and end of each line" );
  460.  
  461. 			Console.ForegroundColor = ConsoleColor.White;
  462. 			Console.Error.Write( "         /Q         Q" );
  463. 			Console.ResetColor( );
  464. 			Console.Error.WriteLine( "uiet mode: no message if no match is found" );
  465.  
  466. 			Console.ForegroundColor = ConsoleColor.White;
  467. 			Console.Error.Write( "         /S:nn      S" );
  468. 			Console.ResetColor( );
  469. 			Console.Error.Write( "kip the first " );
  470. 			Console.ForegroundColor = ConsoleColor.White;
  471. 			Console.Error.Write( "nn" );
  472. 			Console.ResetColor( );
  473. 			Console.Error.WriteLine( " matches" );
  474.  
  475. 			Console.ForegroundColor = ConsoleColor.White;
  476. 			Console.Error.Write( "         /T:nn      T" );
  477. 			Console.ResetColor( );
  478. 			Console.Error.Write( "ake only " );
  479. 			Console.ForegroundColor = ConsoleColor.White;
  480. 			Console.Error.Write( "nn" );
  481. 			Console.ResetColor( );
  482. 			Console.Error.WriteLine( " matches" );
  483.  
  484. 			Console.Error.WriteLine( );
  485.  
  486. 			Console.Error.Write( "Example: " );
  487. 			Console.ForegroundColor = ConsoleColor.White;
  488. 			Console.Error.WriteLine( @"ROBOCOPY D:\sourcedir E:\targetdir /NP /MIR |" );
  489.  
  490. 			Console.Error.WriteLine( @"         RXGREP ""\s+\d+\s+D:\\sourcedir\\[^\n\r]*\r\n([^\n\r\\]+\r\n)+""" );
  491. 			Console.ResetColor( );
  492.  
  493. 			Console.Error.WriteLine( "         (to be read as a single command line) will return something like:" );
  494.  
  495. 			Console.ForegroundColor = ConsoleColor.White;
  496. 			Console.Error.WriteLine( @"                         125    D:\sourcedir\subdir\" );
  497.  
  498. 			Console.Error.WriteLine( "            New File                 342        brandnewfile.ext" );
  499.  
  500. 			Console.Error.WriteLine( "            Newer                  4.06m        updatedfile.ext" );
  501.  
  502. 			Console.Error.WriteLine( "          *EXTRA File              2.40m        deletedfile.ext" );
  503. 			Console.ResetColor( );
  504.  
  505. 			Console.Error.WriteLine( );
  506.  
  507. 			Console.Error.Write( "Notes:   If " );
  508. 			Console.ForegroundColor = ConsoleColor.White;
  509. 			Console.Error.Write( "/F:nn" );
  510. 			Console.ResetColor( );
  511. 			Console.Error.Write( " is used and a file is specified, only the first " );
  512. 			Console.ForegroundColor = ConsoleColor.White;
  513. 			Console.Error.Write( "nn" );
  514. 			Console.ResetColor( );
  515. 			Console.Error.WriteLine( " bytes" );
  516.  
  517. 			Console.Error.WriteLine( "         of that file will be read; if the input is redirected, it is read" );
  518.  
  519. 			Console.Error.Write( "         entirely, and then chopped to " );
  520. 			Console.ForegroundColor = ConsoleColor.White;
  521. 			Console.Error.Write( "nn" );
  522. 			Console.ResetColor( );
  523. 			Console.Error.WriteLine( " bytes before being searched." );
  524.  
  525. 			Console.Error.Write( "         Switches " );
  526. 			Console.ForegroundColor = ConsoleColor.White;
  527. 			Console.Error.Write( "/D" );
  528. 			Console.ResetColor( );
  529. 			Console.Error.Write( " and " );
  530. 			Console.ForegroundColor = ConsoleColor.White;
  531. 			Console.Error.Write( "/G" );
  532. 			Console.ResetColor( );
  533. 			Console.Error.Write( " cannot be combined with " );
  534. 			Console.ForegroundColor = ConsoleColor.White;
  535. 			Console.Error.Write( "/S" );
  536. 			Console.ResetColor( );
  537. 			Console.Error.Write( " or " );
  538. 			Console.ForegroundColor = ConsoleColor.White;
  539. 			Console.Error.Write( "/T" );
  540. 			Console.ResetColor( );
  541. 			Console.Error.WriteLine( "." );
  542.  
  543. 			Console.Error.WriteLine( "         Return code (\"errorlevel\") will equal the number of matches found," );
  544.  
  545. 			Console.Error.WriteLine( "         or -1 in case of (command line) errors." );
  546.  
  547. 			Console.Error.WriteLine( );
  548.  
  549. 			Console.Error.WriteLine( "Written by Rob van der Woude" );
  550.  
  551. 			Console.Error.WriteLine( "https://www.robvanderwoude.com" );
  552.  
  553. 			#endregion Display Help Text
  554.  
  555.  
  556. 			return -1;
  557. 		}
  558.  
  559. 		#endregion Error Handling
  560. 	}
  561. }

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