Rob van der Woude's Scripting Pages
Powered by GeSHi

Source code for truename.cs

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

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Text.RegularExpressions;
  6.  
  7.  
  8. namespace RobvanderWoude
  9. {
  10. 	class TrueName
  11. 	{
  12. 		#region Global Variables
  13.  
  14. 		static string progver = "1.02";
  15. 		static SortedList<string, string> subst = new SortedList<string, string>( );
  16. 		static SortedList<string, string> mapped = new SortedList<string, string>( );
  17.  
  18. 		#endregion Global Variables
  19.  
  20.  
  21. 		static int Main( string[] args )
  22. 		{
  23. 			#region Parse Command Line
  24.  
  25. 			bool verbose = false;
  26.  
  27. 			if ( args.Length < 1 || args.Length > 2 )
  28. 			{
  29. 				return ShowHelp( );
  30. 			}
  31.  
  32. 			if ( args.Length == 2 )
  33. 			{
  34. 				if ( args[1].ToUpper( ) == "/V" )
  35. 				{
  36. 					verbose = true;
  37. 				}
  38. 				else if ( args[1] == "/?" )
  39. 				{
  40. 					return ShowHelp( );
  41. 				}
  42. 				else
  43. 				{
  44. 					//return ShowHelp( "Invalid command line argument \"{0}\"", args[1] );
  45. 					return ShowHelp( "Invalid command line argument \"" + args[1] + "\"" );
  46. 				}
  47. 			}
  48.  
  49. 			string inputpath = args[0];
  50.  
  51. 			#endregion Parse Command Line
  52.  
  53. 			#region Skip UNCs
  54.  
  55. 			if ( inputpath.StartsWith( @"\\" ) )
  56. 			{
  57. 				if ( verbose )
  58. 				{
  59. 					Console.WriteLine( "\"{0}\" => \"{1}\"", inputpath, inputpath );
  60. 				}
  61. 				else
  62. 				{
  63. 					Console.WriteLine( inputpath );
  64. 				}
  65. 				return 0;
  66. 			}
  67.  
  68. 			#endregion Skip UNCs
  69.  
  70. 			#region Abort On Invalid First Character
  71.  
  72. 			if ( "/?*<>:;".IndexOf( inputpath[0] ) > -1 )
  73. 			{
  74. 				return ShowHelp( );
  75. 			}
  76.  
  77. 			#endregion Abort On Invalid First Character
  78.  
  79. 			// Convert relative path to full path first
  80. 			string fullpath = inputpath;
  81. 			try
  82. 			{
  83. 				if ( File.Exists( inputpath ) || Directory.Exists( inputpath ) || Directory.Exists( Directory.GetParent( inputpath ).FullName ) )
  84. 				{
  85. 					fullpath = Path.GetFullPath( inputpath );
  86. 				}
  87. 				else
  88. 				{
  89. 					return ShowHelp( String.Format( "File or folder not found \"{0}\"", inputpath ) );
  90. 				}
  91. 			}
  92. 			catch ( Exception e )
  93. 			{
  94. 				return ShowHelp( e );
  95. 			}
  96.  
  97. 			// List all SUBSTituted drives
  98. 			ListSUBST( );
  99.  
  100. 			// List all mapped network drives
  101. 			ListMapped( );
  102.  
  103. 			string outputpath = fullpath;
  104. 			// Use a loop to detect combinations of SUBSTituted and mapped drives too
  105. 			do
  106. 			{
  107. 				fullpath = outputpath;
  108. 				string driveletter = Path.GetPathRoot( outputpath )[0].ToString( ).ToUpper( );
  109. 				if ( mapped.ContainsKey( driveletter ) )
  110. 				{
  111. 					outputpath = Regex.Replace( fullpath, "^" + driveletter + ":", mapped[driveletter], RegexOptions.IgnoreCase );
  112. 				}
  113. 				else if ( subst.ContainsKey( driveletter ) )
  114. 				{
  115. 					outputpath = Regex.Replace( fullpath, "^" + driveletter + ":", subst[driveletter], RegexOptions.IgnoreCase );
  116. 				}
  117. 				else
  118. 				{
  119. 					outputpath = fullpath;
  120. 				}
  121. 			}
  122. 			while ( outputpath != fullpath && !outputpath.StartsWith( @"\\" ) );
  123.  
  124. 			// Check for reparse points
  125. 			outputpath = ReparsePoints( outputpath );
  126.  
  127. 			// Display the result
  128. 			if ( verbose )
  129. 			{
  130. 				Console.WriteLine( "\"{0}\" => \"{1}\"", inputpath, outputpath );
  131. 			}
  132. 			else
  133. 			{
  134. 				Console.WriteLine( outputpath );
  135. 			}
  136.  
  137. 			return 0;
  138. 		}
  139.  
  140.  
  141. 		static string GetReparseTarget( string folder )
  142. 		{
  143. 			// This function uses CMD.EXE's internal DIR command with its
  144. 			// /ADL switch to find the true location of folder reparse points.
  145. 			string parentfolder = Directory.GetParent( folder ).FullName;
  146. 			string testsubfolder = Path.GetFileName( folder );
  147. 			ProcessStartInfo psi = new ProcessStartInfo( );
  148. 			psi.FileName = "CMD.EXE";
  149. 			psi.UseShellExecute = false;
  150. 			psi.CreateNoWindow = true;
  151. 			psi.RedirectStandardOutput = true;
  152. 			Process process = new Process( );
  153. 			psi.Arguments = String.Format( "/C DIR /ADL \"{0}\"", parentfolder );
  154. 			process.StartInfo = psi;
  155. 			process.Start( );
  156. 			process.WaitForExit( );
  157. 			string result = process.StandardOutput.ReadToEnd( );
  158. 			string[] lines = result.Split( Environment.NewLine.ToCharArray( ), StringSplitOptions.RemoveEmptyEntries );
  159. 			foreach ( string line in lines )
  160. 			{
  161. 				string pattern = String.Format( @"\s+{0}\s+\[([^\]]+)\]", testsubfolder );
  162. 				Regex regex = new Regex( pattern, RegexOptions.IgnoreCase );
  163. 				if ( regex.IsMatch( line ) )
  164. 				{
  165. 					MatchCollection matches = regex.Matches( line );
  166. 					return matches[0].Groups[1].ToString( );
  167. 				}
  168. 			}
  169. 			return folder;
  170. 		}
  171.  
  172.  
  173. 		static bool IsReparsePoint( string folder )
  174. 		{
  175. 			bool isreparsepoint = false;
  176. 			DirectoryInfo dirinfo = new DirectoryInfo( folder );
  177. 			isreparsepoint = ( ( dirinfo.Attributes & FileAttributes.ReparsePoint ) != 0 );
  178. 			return isreparsepoint;
  179. 		}
  180.  
  181.  
  182. 		static void ListMapped( )
  183. 		{
  184. 			// List all mapped network drives
  185. 			string pattern = @"^OK\s+([A-Z]):\s+(\\\\[^\\]+\\.+)\s{2,}[^\s]+"; // This regex will fail if the UNC path contains multiple consecutive spaces
  186. 			Regex regex = new Regex( pattern, RegexOptions.IgnoreCase );
  187. 			ProcessStartInfo psi = new ProcessStartInfo( "NET.EXE", "Use" );
  188. 			psi.CreateNoWindow = true;
  189. 			psi.UseShellExecute = false;
  190. 			psi.RedirectStandardOutput = true;
  191. 			Process process = new Process( );
  192. 			process.StartInfo = psi;
  193. 			process.Start( );
  194. 			process.WaitForExit( );
  195. 			foreach ( string line in process.StandardOutput.ReadToEnd( ).Split( Environment.NewLine.ToCharArray( ) ) )
  196. 			{
  197. 				if ( regex.IsMatch( line ) )
  198. 				{
  199. 					MatchCollection matches = regex.Matches( line );
  200. 					if ( matches[0].Groups.Count == 3 )
  201. 					{
  202. 						string drive = matches[0].Groups[1].ToString( ).ToUpper( );
  203. 						string path = matches[0].Groups[2].ToString( ).Trim( );
  204. 						mapped[drive] = path;
  205. 					}
  206. 				}
  207. 			}
  208. 		}
  209.  
  210.  
  211. 		static void ListSUBST( )
  212. 		{
  213. 			// List all SUBSTituted drives
  214. 			ProcessStartInfo psi = new ProcessStartInfo( "SUBST.EXE" );
  215. 			psi.CreateNoWindow = true;
  216. 			psi.UseShellExecute = false;
  217. 			psi.RedirectStandardOutput = true;
  218. 			Process process = new Process( );
  219. 			process.StartInfo = psi;
  220. 			process.Start( );
  221. 			process.WaitForExit( );
  222. 			foreach ( string line in process.StandardOutput.ReadToEnd( ).Split( Environment.NewLine.ToCharArray( ) ) )
  223. 			{
  224. 				if ( !String.IsNullOrWhiteSpace( line ) )
  225. 				{
  226. 					string drive = line.Trim( )[0].ToString( ).ToUpper( );
  227. 					string path = line.Split( " \t".ToCharArray( ) )[2];
  228. 					path = Regex.Replace( path, @"^UNC\\", @"\\" );
  229. 					subst[drive] = path;
  230. 				}
  231. 			}
  232. 		}
  233.  
  234.  
  235. 		static string ReparsePoints( string path )
  236. 		{
  237. 			// This function iterates throught the folder and its parent folder(s) until it finds a reparse point for the specified path;
  238. 			// if found, it replaces the specified path with the true path, and starts all over again to check for nested reparse points.
  239. 			string folder = Path.GetFullPath( path );
  240. 			string file = String.Empty;
  241. 			if ( File.Exists( path ) )
  242. 			{
  243. 				// Check folder reparse points only
  244. 				folder = Directory.GetParent( folder ).ToString( );
  245. 				file = Path.GetFileName( path );
  246. 			}
  247.  
  248. 			string testfolder = folder;
  249. 			string remainder = String.Empty;
  250. 			while ( testfolder != Path.GetPathRoot( testfolder ) )
  251. 			{
  252. 				if ( IsReparsePoint( testfolder ) )
  253. 				{
  254. 					testfolder = Path.Combine( GetReparseTarget( testfolder ), remainder );
  255. 					remainder = String.Empty;
  256. 				}
  257. 				remainder = Path.Combine( Path.GetFileName( testfolder ), remainder );
  258. 				string dummy = Path.GetPathRoot( testfolder );
  259. 				testfolder = Directory.GetParent( testfolder ).FullName;
  260. 			}
  261. 			folder = Path.Combine( testfolder, remainder );
  262. 			path = Path.Combine( folder, file );
  263. 			return path;
  264. 		}
  265.  
  266.  
  267. 		static int ShowHelp( Exception e )
  268. 		{
  269. 			if ( e.GetType( ) == typeof( IOException ) || e.GetType( ) == typeof( AccessViolationException ) )
  270. 			{
  271. 				ShowHelp( e.Message );
  272. 				return -2;
  273. 			}
  274. 			else
  275. 			{
  276. 				return ShowHelp( e.Message );
  277. 			}
  278. 		}
  279.  
  280.  
  281. 		static int ShowHelp( string errmsg = "" )
  282. 		{
  283. 			#region Error Message
  284.  
  285. 			if ( errmsg.Length > 0 )
  286. 			{
  287. 				Console.Error.WriteLine( );
  288. 				Console.ForegroundColor = ConsoleColor.Red;
  289. 				Console.Error.Write( "ERROR:\t" );
  290. 				Console.ForegroundColor = ConsoleColor.White;
  291. 				Console.Error.WriteLine( errmsg );
  292. 				Console.ResetColor( );
  293. 			}
  294.  
  295. 			#endregion Error Message
  296.  
  297. 			#region Help Text
  298.  
  299. 			/*
  300. 			TrueName,  Version 1.02
  301. 			Displays the true path of a file or directory, like COMMAND.COM's internal
  302. 			TRUENAME command; this program handles SUBSTituted drives as well as mapped
  303. 			network drives and directory junctions, and even some combinations.
  304.  
  305. 			Usage:  TRUENAME  "path"  [ /V ]
  306.  
  307. 			Where:  "path"    is the drive, or path to the folder or file to be checked
  308. 			        /V        displays Verbose output, i.e. "specified path" => "true path"
  309. 			                  (default: display true path only, without doublequotes)
  310.  
  311. 			Notes:  When specifying a file for "path", it doesn't necessarily have to
  312. 			        exist, as long as its parent folder does.
  313. 			        If "path" is an UNC path, it will be returned without testing.
  314. 			        File reparse points are ignored by this program.
  315. 			        This program uses DIR's /ADL switch to find the reparse target.
  316. 			        As always, enclose paths and file names in doublequotes if necessary.
  317. 			        This program's return code ("errorlevel") will be -1 in case of
  318. 			        (command line) errors, -2 if the specified folder is invalid or if
  319. 			        access is denied, or 0 otherwise.
  320.  
  321. 			Written by Rob van der Woude
  322. 			http://www.robvanderwoude.com
  323. 			*/
  324.  
  325. 			Console.Error.WriteLine( );
  326.  
  327. 			Console.Error.WriteLine( "TrueName,  Version {0}", progver );
  328.  
  329. 			Console.Error.WriteLine( "Displays the true path of a file or directory, like COMMAND.COM's internal" );
  330.  
  331. 			Console.Error.WriteLine( "TRUENAME command; this program handles SUBSTituted drives as well as mapped" );
  332.  
  333. 			Console.Error.WriteLine( "network drives and directory junctions, and even some combinations." );
  334.  
  335. 			Console.Error.WriteLine( );
  336.  
  337. 			Console.Error.Write( "Usage:  " );
  338. 			Console.ForegroundColor = ConsoleColor.White;
  339. 			Console.Error.WriteLine( "TRUENAME  \"path\"  [ /V ]" );
  340. 			Console.ResetColor( );
  341.  
  342. 			Console.Error.WriteLine( );
  343.  
  344. 			Console.Error.Write( "Where:  " );
  345. 			Console.ForegroundColor = ConsoleColor.White;
  346. 			Console.Error.Write( "\"path\"" );
  347. 			Console.ResetColor( );
  348. 			Console.Error.WriteLine( "    is the drive, or path to the folder or file to be checked" );
  349.  
  350. 			Console.ForegroundColor = ConsoleColor.White;
  351. 			Console.Error.Write( "        /V" );
  352. 			Console.ResetColor( );
  353. 			Console.Error.Write( "        displays " );
  354. 			Console.ForegroundColor = ConsoleColor.White;
  355. 			Console.Error.Write( "V" );
  356. 			Console.ResetColor( );
  357. 			Console.Error.Write( "erbose output, i.e. " );
  358. 			Console.ForegroundColor = ConsoleColor.DarkGray;
  359. 			Console.Error.WriteLine( "\"specified path\" => \"true path\"" );
  360. 			Console.ResetColor( );
  361.  
  362. 			Console.Error.WriteLine( "                  (default: display true path only, without doublequotes)" );
  363.  
  364. 			Console.Error.WriteLine( );
  365.  
  366. 			Console.Error.Write( "Notes:  When specifying a file for " );
  367. 			Console.ForegroundColor = ConsoleColor.White;
  368. 			Console.Error.Write( "\"path\"" );
  369. 			Console.ResetColor( );
  370. 			Console.Error.WriteLine( ", it doesn't necessarily have to" );
  371.  
  372. 			Console.Error.WriteLine( "        exist, as long as its parent folder does." );
  373.  
  374. 			Console.Error.Write( "        If " );
  375. 			Console.ForegroundColor = ConsoleColor.White;
  376. 			Console.Error.Write( "\"path\"" );
  377. 			Console.ResetColor( );
  378. 			Console.Error.WriteLine( " is an UNC path, it will be returned without testing." );
  379.  
  380. 			Console.Error.WriteLine( "        File reparse points are ignored by this program." );
  381.  
  382. 			Console.Error.Write( "        This program uses " );
  383. 			Console.ForegroundColor = ConsoleColor.DarkGray;
  384. 			Console.Error.Write( "DIR" );
  385. 			Console.ResetColor( );
  386. 			Console.Error.Write( "'s " );
  387. 			Console.ForegroundColor = ConsoleColor.DarkGray; ;
  388. 			Console.Error.Write( "/ADL" );
  389. 			Console.ResetColor( );
  390. 			Console.Error.WriteLine( " switch to find the reparse target." );
  391.  
  392. 			Console.Error.WriteLine( "        As always, enclose paths and file names in doublequotes if necessary." );
  393.  
  394. 			Console.Error.WriteLine( "        This program's return code (\"errorlevel\") will be -1 in case of" );
  395.  
  396. 			Console.Error.WriteLine( "        (command line) errors, -2 if the specified folder is invalid or if" );
  397.  
  398. 			Console.Error.WriteLine( "        access is denied, or 0 otherwise." );
  399.  
  400. 			Console.Error.WriteLine( );
  401.  
  402. 			Console.Error.WriteLine( "Written by Rob van der Woude" );
  403.  
  404. 			Console.Error.WriteLine( "http://www.robvanderwoude.com" );
  405.  
  406. 			#endregion Help Text
  407.  
  408. 			return -1;
  409. 		}
  410. 	}
  411. }
  412.  

page last modified: 2024-02-26; loaded in 0.0266 seconds