Powered by GeSHi

Source code for tail.cs

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

  1. using System;
  2. using System.IO;
  3. using System.Runtime.InteropServices;
  4. using System.Text;
  5.  
  6. namespace RobvanderWoude
  7. {
  8. 	class Tail
  9. 	{
  10. 		static int Main( string[] args )
  11. 		{
  12. 			try
  13. 			{
  14. 				// Default number of lines is 1
  15. 				int numlines = 1;
  16.  
  17. 				#region Command Line Parsing
  18.  
  19. 				string filename = string.Empty;
  20. 				bool redirected;
  21. 				bool set_l = false;
  22. 				bool set_input = false;
  23.  
  24. 				foreach ( string arg in args )
  25. 				{
  26. 					if ( arg[0] == '/' )
  27. 					{
  28. 						if ( arg.Length > 3 )
  29. 						{
  30. 							if ( arg.ToUpper( )[1] == 'L' )
  31. 							{
  32. 								if ( arg[2] != ':' )
  33. 								{
  34. 									return WriteError( "Invalid argument: " + arg );
  35. 								}
  36. 								try
  37. 								{
  38. 									if ( set_l )
  39. 									{
  40. 										return WriteError( "Duplicate /L argument" );
  41. 									}
  42. 									else
  43. 									{
  44. 										numlines = Convert.ToInt32( arg.Substring( 3 ) );
  45. 										if ( numlines < 1 )
  46. 										{
  47. 											return WriteError( "Number of lines must be 1 or greater" );
  48. 										}
  49. 										set_l = true;
  50. 									}
  51. 								}
  52. 								catch ( FormatException )
  53. 								{
  54. 									return WriteError( "Invalid number of lines: " + arg );
  55. 								}
  56. 							}
  57. 						}
  58. 						else
  59. 						{
  60. 							if ( arg == "/?" )
  61. 							{
  62. 								return WriteError( );
  63. 							}
  64. 							else
  65. 							{
  66. 								return WriteError( "Invalid argument: " + arg );
  67. 							}
  68. 						}
  69. 					}
  70. 					else
  71. 					{
  72. 						if ( set_input )
  73. 						{
  74. 							return WriteError( "Duplicate file argument" );
  75. 						}
  76. 						else
  77. 						{
  78. 							if ( File.Exists( arg ) )
  79. 							{
  80. 								filename = arg;
  81. 							}
  82. 							else
  83. 							{
  84. 								return WriteError( "Invalid filename: " + arg );
  85. 							}
  86. 							set_input = true;
  87. 						}
  88. 					}
  89. 				}
  90.  
  91. 				if ( ConsoleEx.InputRedirected )
  92. 				{
  93. 					if ( set_input )
  94. 					{
  95. 						return WriteError( "Use either file name or redirection, not both" );
  96. 					}
  97. 					else
  98. 					{
  99. 						set_input = true;
  100. 						redirected = true;
  101. 					}
  102. 				}
  103. 				else
  104. 				{
  105. 					if ( args.Length == 0 )
  106. 					{
  107. 						return WriteError( );
  108. 					}
  109. 					redirected = false;
  110. 				}
  111.  
  112. 				#endregion Command Line Parsing
  113.  
  114.  
  115. 				int index = 0;
  116. 				string[] output = new string[numlines];
  117.  
  118. 				if ( redirected )
  119. 				{
  120. 					// Read standard input and store the lines in a list
  121. 					while ( Console.In.Peek( ) > -1 )
  122. 					{
  123. 						output.SetValue( Console.In.ReadLine( ), index );
  124. 						index = ( index + 1 ) % numlines;
  125. 					}
  126. 				}
  127. 				else
  128. 				{
  129. 					// Read the file and store the lines in an array
  130. 					Encoding enc = GetEncoding( filename );
  131. 					using ( FileStream fsi = File.Open( filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ) )
  132. 					using ( BufferedStream bsi = new BufferedStream( fsi ) )
  133. 					using ( StreamReader sri = new StreamReader( bsi, enc ) )
  134. 					{
  135. 						while ( sri.Peek( ) > -1 )
  136. 						{
  137. 							output.SetValue( sri.ReadLine( ), index );
  138. 							index = ( index + 1 ) % numlines;
  139. 						}
  140. 					}
  141. 				}
  142.  
  143. 				// Display the lines in the correct order
  144. 				for ( int i = index; i < output.Length; i++ )
  145. 				{
  146. 					Console.WriteLine( output[i] );
  147. 				}
  148. 				if ( index > 0 )
  149. 				{
  150. 					for ( int i = 0; i < index; i++ )
  151. 					{
  152. 						Console.WriteLine( output[i] );
  153. 					}
  154. 				}
  155. 				return 0;
  156. 			}
  157. 			catch ( Exception e )
  158. 			{
  159. 				return WriteError( e.Message );
  160. 			}
  161. 		}
  162.  
  163.  
  164. 		#region Redirection Detection
  165.  
  166. 		// Code to detect redirection by Hans Passant on StackOverflow.com/a/3453272
  167. 		public static class ConsoleEx
  168. 		{
  169. 			public static bool OutputRedirected
  170. 			{
  171. 				get
  172. 				{
  173. 					return FileType.Char != GetFileType( GetStdHandle( StdHandle.Stdout ) );
  174. 				}
  175. 			}
  176.  
  177. 			public static bool InputRedirected
  178. 			{
  179. 				get
  180. 				{
  181. 					return FileType.Char != GetFileType( GetStdHandle( StdHandle.Stdin ) );
  182. 				}
  183. 			}
  184.  
  185. 			public static bool ErrorRedirected
  186. 			{
  187. 				get
  188. 				{
  189. 					return FileType.Char != GetFileType( GetStdHandle( StdHandle.Stderr ) );
  190. 				}
  191. 			}
  192.  
  193. 			// P/Invoke:
  194. 			private enum FileType { Unknown, Disk, Char, Pipe };
  195. 			private enum StdHandle { Stdin = -10, Stdout = -11, Stderr = -12 };
  196.  
  197. 			[DllImport( "kernel32.dll" )]
  198. 			private static extern FileType GetFileType( IntPtr hdl );
  199.  
  200. 			[DllImport( "kernel32.dll" )]
  201. 			private static extern IntPtr GetStdHandle( StdHandle std );
  202. 		}
  203.  
  204. 		#endregion Redirection Detection
  205.  
  206.  
  207. 		#region File Encoding Detection
  208.  
  209. 		// Code by Jason Pierce on http://stackoverflow.com/a/19283954
  210. 		/// <summary>
  211. 		/// Determines a text file's encoding by analyzing its byte order mark (BOM).
  212. 		/// Defaults to the system default when detection of the text file's endianness fails.
  213. 		/// </summary>
  214. 		/// <param name="filename">The text file to analyze.</param>
  215. 		/// <returns>The detected encoding.</returns>
  216. 		public static Encoding GetEncoding( string filename )
  217. 		{
  218. 			// Read the BOM
  219. 			var bom = new byte[4];
  220. 			using ( var file = new FileStream( filename, FileMode.Open ) ) file.Read( bom, 0, 4 );
  221. 			// Analyze the BOM
  222. 			if ( bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76 ) return Encoding.UTF7;
  223. 			if ( bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf ) return Encoding.UTF8;
  224. 			if ( bom[0] == 0xff && bom[1] == 0xfe ) return Encoding.Unicode; //UTF-16LE
  225. 			if ( bom[0] == 0xfe && bom[1] == 0xff ) return Encoding.BigEndianUnicode; //UTF-16BE
  226. 			if ( bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff ) return Encoding.UTF32;
  227. 			return Encoding.Default;
  228. 		}
  229.  
  230. 		#endregion File Encoding Detection
  231.  
  232.  
  233. 		#region Error Handling
  234.  
  235. 		public static int WriteError( Exception e = null )
  236. 		{
  237. 			return WriteError( e == null ? null : e.Message );
  238. 		}
  239.  
  240. 		public static int WriteError( string errorMessage )
  241. 		{
  242. 			if ( string.IsNullOrEmpty( errorMessage ) == false )
  243. 			{
  244. 				Console.Error.WriteLine( );
  245. 				Console.ForegroundColor = ConsoleColor.Red;
  246. 				Console.Error.Write( "ERROR: " );
  247. 				Console.ForegroundColor = ConsoleColor.White;
  248. 				Console.Error.WriteLine( errorMessage );
  249. 				Console.ResetColor( );
  250. 			}
  251.  
  252. 			/*
  253. 			Tail,  Version 1.01
  254. 			Return the specified number of lines from the end of the specified file
  255.  
  256. 			Usage:   TAIL  filename  [ option ]
  257. 			   or:   TAIL  [ option ]  <  filename
  258.  
  259. 			Where:   filename   is the file to be read
  260. 			         /L:n       read the last n Lines (default: 1 line)
  261.  
  262. 			Examples:
  263. 			TAIL  filename                 read the last line (default)
  264. 			TAIL  filename  /L:5           read the last 5 lines
  265. 			TAIL  /L:5  <  filename        read the last 5 lines
  266.  
  267. 			Check for redirection by Hans Passant on StackOverflow.com/a/3453272
  268. 			Check file encoding by Jason Pierce on StackOverflow.com/a/19283954
  269.  
  270. 			Written by Rob van der Woude
  271. 			http://www.robvanderwoude.com
  272. 			*/
  273.  
  274. 			Console.Error.WriteLine( );
  275. 			Console.Error.WriteLine( "Tail,  Version 1.01" );
  276. 			Console.Error.WriteLine( "Return the specified number of lines from the end of the specified file" );
  277. 			Console.Error.WriteLine( );
  278. 			Console.Error.Write( "Usage:   " );
  279. 			Console.ForegroundColor = ConsoleColor.White;
  280. 			Console.Error.WriteLine( "TAIL  filename  [ option ]" );
  281. 			Console.ResetColor( );
  282. 			Console.Error.Write( "   or:   " );
  283. 			Console.ForegroundColor = ConsoleColor.White;
  284. 			Console.Error.WriteLine( "TAIL  [ option ]  <  filename" );
  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 read" );
  292. 			Console.ForegroundColor = ConsoleColor.White;
  293. 			Console.Error.Write( "         /L:n" );
  294. 			Console.ResetColor( );
  295. 			Console.Error.Write( "       read the last " );
  296. 			Console.ForegroundColor = ConsoleColor.White;
  297. 			Console.Error.Write( "n L" );
  298. 			Console.ResetColor( );
  299. 			Console.Error.WriteLine( "ines (default: 1 line)" );
  300. 			Console.Error.WriteLine( );
  301. 			Console.Error.WriteLine( "Examples:" );
  302. 			Console.Error.WriteLine( "TAIL  filename                 read the last line (default)" );
  303. 			Console.Error.WriteLine( "TAIL  filename  /L:5           read the last 5 lines" );
  304. 			Console.Error.WriteLine( "TAIL  /L:5  <  filename        read the last 5 lines" );
  305. 			Console.Error.WriteLine( );
  306. 			Console.Error.Write( "Check for redirection by Hans Passant on " );
  307. 			Console.ForegroundColor = ConsoleColor.DarkGray;
  308. 			Console.Error.WriteLine( "StackOverflow.com/a/3453272" );
  309. 			Console.ResetColor( );
  310. 			Console.Error.Write( "Check file encoding by Jason Pierce on " );
  311. 			Console.ForegroundColor = ConsoleColor.DarkGray;
  312. 			Console.Error.WriteLine( "StackOverflow.com/a/19283954" );
  313. 			Console.ResetColor( );
  314. 			Console.Error.WriteLine( );
  315. 			Console.Error.WriteLine( "Written by Rob van der Woude" );
  316. 			Console.Error.WriteLine( "http://www.robvanderwoude.com" );
  317. 			return 1;
  318. 		}
  319.  
  320. 		#endregion Error Handling
  321. 	}
  322. }
  323.