1 /*---------------------------------------------------------------------------*
2 
3   Copyright 2010-2012 Nintendo.  All rights reserved.
4 
5   These coded instructions, statements, and computer programs contain
6   proprietary information of Nintendo of America Inc. and/or Nintendo
7   Company Ltd., and are protected by Federal copyright law.  They may
8   not be disclosed to third parties or copied or duplicated in any form,
9   in whole or in part, without the prior written consent of Nintendo.
10 
11  *---------------------------------------------------------------------------*/
12 using System;
13 using System.IO;
14 using System.Diagnostics;
15 using System.Xml;
16 using System.Text;
17 using System.Collections.Generic;
18 
19 namespace CafeX
20 {
21     /// <summary>
22     /// This class encapsulates the environment variables required by Cafe
23     /// </summary>
24     internal class EnvVar
25     {
26         private string var_name;
27         private string var_value;
28         private static Dictionary<string, EnvVar> dictionaryOfVariables = new Dictionary<string, EnvVar>();
29         private static Dictionary<string, string> dictionaryOfSavedValues = new Dictionary<string, string>();
30 
BackUpCurrentEnvironment()31         public static void BackUpCurrentEnvironment()
32         {
33             #if DEBUG
34                 Log.WriteLine("BackUpCurrentEnvironment started.");
35             #endif
36             dictionaryOfSavedValues.Clear();
37 
38             foreach (string variableName in dictionaryOfVariables.Keys)
39             {
40                 dictionaryOfSavedValues.Add(variableName, dictionaryOfVariables[variableName].value);
41             }
42             #if DEBUG
43                 Log.WriteLine("BackUpCurrentEnvironment completed.");
44             #endif
45         }
46 
RestoreSavedEnvironment()47         public static void RestoreSavedEnvironment()
48         {
49             #if DEBUG
50                 Log.WriteLine("RestoreSavedEnvironment started.");
51             #endif
52             if (dictionaryOfSavedValues.Count == 0)
53             {
54                 #if DEBUG
55                     Log.WriteLine("RestoreSavedEnvironment ignored. No backup available.");
56                 #endif
57 
58                 // No backup had been made, so there is no way to restore. Just return.
59                 return;
60             }
61 
62             foreach (string variableName in dictionaryOfVariables.Keys)
63             {
64                 // Set the respective envVar instance in the dictionary of variables.
65                 if (dictionaryOfSavedValues.ContainsKey(variableName))
66                 {
67                     EnvVar temp = dictionaryOfVariables[variableName];
68                     temp.value = dictionaryOfSavedValues[variableName];
69                 }
70             }
71             #if DEBUG
72                 Log.WriteLine("RestoreSavedEnvironment completed successfully.");
73             #endif
74         }
75 
EnvVar(string name)76         public EnvVar(string name)
77         {
78             if (string.IsNullOrEmpty(name))
79             {
80                 throw new ArgumentNullException("name cannot be null.");
81             }
82 
83             var_name = name;
84             var_value = Environment.GetEnvironmentVariable(var_name);
85             // Store the environment variable, so it can be backed-up and restored at will
86             EnvVar.dictionaryOfVariables.Add(name, this);
87         }
88 
89         public string value
90         {
91             get
92             {
93                 var_value = Environment.GetEnvironmentVariable(var_name);
94                 return var_value;
95             }
96 
97             set
98             {
99                 var_value = value;
100                 Environment.SetEnvironmentVariable(var_name, var_value);
101             }
102         }
103 
AddToVar(string addition, char delimiter)104         public void AddToVar(string addition, char delimiter)
105         {
106             if (value == null)
107                 value = addition;
108             else
109                 value = value + delimiter + addition;
110         }
111 
RemoveFromVar(string removal, char delimiter)112         public void RemoveFromVar(string removal, char delimiter)
113         {
114             if (string.IsNullOrEmpty(value))
115             {
116 #if DEBUG
117                 Log.WriteLine("RemoveFromVar: removal is empty!");
118 #endif
119 
120                 return;
121             }
122             if (string.IsNullOrEmpty(delimiter.ToString()))
123             {
124 #if DEBUG
125                 Log.WriteLine("RemoveFromVar: delimiter is null!");
126 #endif
127 
128                 return;
129             }
130 
131             int position = value.IndexOf(removal);
132 
133             if (position >= 0)
134             {
135                 int len = removal.Length;
136                 if (position > 0)
137                 {
138                     // There is more text in value than only the one we want to remove.
139                     // ex: data1 data2 removal data3
140                     // ex: data1 removal.
141                     // In those scenarios, we need to remove the delimiter char before, since AddToVar does value = value + delimiter + addition;
142                     --position;
143                     ++len;
144                 }
145 
146                 value = value.Remove(position, len);
147             }
148             else
149             {
150 #if DEBUG
151                 Log.WriteLine("RemoveFromVar: \"" + removal + "\" does not exist in the arg list!");
152 #endif
153                 return;
154             }
155         }
156 
157 
158     }
159 
160     /// <summary>
161     /// This class provides enables CafeX to work in both Windows and Cygwin environments by converting paths to the Windows format.
162     /// </summary>
163     internal static class PathConverter
164     {
165         /// <summary>
166         /// Ensure the provide path is rooted.
167         /// </summary>
168         /// <param name="input">Path to a file.</param>
169         /// <returns>Rooted path.</returns>
Root(string input)170         internal static string Root(string input)
171         {
172             if (Path.IsPathRooted(input))
173             {
174                 return input;
175             }
176             else
177             {
178                 return Environment.CurrentDirectory + "\\" + input;
179             }
180         }
181 
182         /// <summary>
183         /// Convert a Cygwin path to a Windows path.
184         /// If the input is already a valid Windows path, it is returned unchanged.
185         /// </summary>
186         /// <param name="input">Cywin path.</param>
187         /// <returns>Windows equivalent of the provided Cygwin path.</returns>
Windowsify(string input)188         internal static string Windowsify(string input)
189         {
190 #if DEBUG
191             Log.WriteLine("Windowsify input = " + input);
192 #endif
193 			if (string.IsNullOrEmpty(input))
194 			{
195 				return input;
196 			}
197 
198             int cygdriverIndex = -1;
199 
200             cygdriverIndex = input.IndexOf(@"/cygdrive///");
201 
202             if ((cygdriverIndex == 0 || cygdriverIndex == 1 ) &&
203                 !string.IsNullOrEmpty(CafeX.Program.CYGWIN_PATH.value) )
204             {
205                 // Special treatment
206                 input = input.Replace( @"/cygdrive///", Path.Combine(CafeX.Program.CYGWIN_PATH.value, "cygdrive/") );
207                 // it should continue with normal conversion to fix the \\
208             }
209 
210             // Normal conversion -> path starts with /cygdriver/<stuff>  or "/cygdriver/<stuff>"
211             cygdriverIndex = input.IndexOf("/cygdrive/");
212             if (cygdriverIndex == 0 || cygdriverIndex == 1)
213             {
214                 char drive_letter = input.ToCharArray()[10 + cygdriverIndex];
215                 input = input.Remove(0, 11 + cygdriverIndex);
216                 input = input.Replace('/', '\\');
217                 input = drive_letter + ":" + input;
218             }
219             else
220             {
221                 // This should be a path that doesn't start with cygdrive,
222                 // so just make sure it has the proper \\ instead of /
223                 input = input.Replace('/', '\\');
224             }
225 
226 #if DEBUG
227             Log.WriteLine("Windowsify return = " + input);
228 #endif
229             return input;
230         }
231     }
232 
233     /// <summary>
234     /// This class enables external processes to be started and monitored.
235     /// </summary>
236     internal static class ProcessExecutor
237     {
238         /// <summary>
239         /// Creates a process, starts it, wait for its execution and return its exit code.
240         /// </summary>
241         /// <param name="filename">Path to the executable to start on a different process.</param>
242         /// <param name="arguments">Arguments to the executable.</param>
243         /// <returns>The process' exit code.</returns>
DoProcess(string filename, string arguments)244         internal static int DoProcess(string filename, string arguments)
245         {
246             #if DEBUG
247                 Log.WriteLine("ProcessorExecutor.DoProcess : " + filename + " " + arguments);
248             #endif
249             try
250             {
251                 Process p = new Process();
252                 ProcessStartInfo si = new ProcessStartInfo(filename);
253                 si.UseShellExecute = false;
254                 si.CreateNoWindow = true;
255                 si.Arguments = arguments;
256                 si.RedirectStandardError = true;
257                 si.RedirectStandardOutput = true;
258                 p.StartInfo = si;
259 
260                 p.Start();
261 
262                 p.WaitForExit();
263                 int ret = p.ExitCode;
264                 p.Close();
265                 p.Dispose();
266                 p = null;
267 
268                 return ret;
269             }
270             catch (Exception e)
271             {
272                 Console.WriteLine("CafeX Error: Exception in process executor");
273                 Console.WriteLine(filename + " " + arguments);
274                 Console.WriteLine(e.Message);
275                 return -1;
276             }
277         }
278 
279         /// <summary>
280         /// Creates and starts a new process, but return immediately the execution to the caller.
281         /// The child's stdout and errout is printed to this process' stdout.
282         /// </summary>
283         /// <param name="filename">Path to the executable to start on a different process.</param>
284         /// <param name="arguments">Arguments to the executable.</param>
285         /// <returns>0 if the process is created, -1 otherwise.</returns>
DoProcess_DontWait(string filename, string arguments)286         internal static int DoProcess_DontWait(string filename, string arguments)
287         {
288             #if DEBUG
289                 Log.WriteLine("ProcessorExecutor.DoProcess_DontWait : " + filename + " " + arguments);
290             #endif
291 
292             try
293             {
294                 Process p = new Process();
295                 ProcessStartInfo si = new ProcessStartInfo(filename);
296                 si.UseShellExecute = false;
297                 si.CreateNoWindow = true;
298                 si.Arguments = arguments;
299                 si.RedirectStandardError = true;
300                 si.RedirectStandardOutput = true;
301                 p.StartInfo = si;
302                 // Won't wait, but need to connect stdout and stderr to this parent process.
303                 p.OutputDataReceived += (sender, args) => Console.WriteLine(args.Data);
304                 p.ErrorDataReceived += (sender, args) => Console.WriteLine(args.Data);
305                 p.Start();
306                 p.BeginOutputReadLine();
307                 p.BeginErrorReadLine();
308                 return 0;
309             }
310             catch (Exception e)
311             {
312                 Console.WriteLine("CafeX Error: Exception in process executor");
313                 Console.WriteLine(filename + " " + arguments);
314                 Console.WriteLine(e.Message);
315                 return -1;
316             }
317         }
318 
319         /// <summary>
320         /// Creates a process, starts it, wait for its execution and return its exit code.
321         /// The child's process stdout is captured and returned to the caller in the ouput variable.
322         /// </summary>
323         /// <param name="filename">Path to the executable to start on a different process.</param>
324         /// <param name="arguments">Arguments to the executable.</param>
325         /// <param name="output">Returns the child's process captured output the the caller.</param>
326         /// <returns>Chid's process exit code.</returns>
DoProcess_GetOutput(string filename, string arguments, out string output)327         internal static int DoProcess_GetOutput(string filename, string arguments, out string output)
328         {
329             #if DEBUG
330                 Log.WriteLine("ProcessorExecutor.DoProcess_GetOutput : " + filename + " " + arguments);
331             #endif
332 
333             return DoProcess_GetOutput(filename, arguments, false, -1, out output);
334         }
335 
336         /// <summary>
337         /// Creates a process, starts it, wait for its execution and return its exit code.
338         /// The child's process stdout is captured and returned to the caller in the ouput variable.
339         /// </summary>
340         /// <param name="filename">Path to the executable to start on a different process.</param>
341         /// <param name="arguments">Arguments to the executable.</param>
342         /// <param name="timeout">How long (in ms) to wait for the process to end. Set to values less than 1 to wait indefinately.</param>
343         /// <param name="output">Returns the child's process captured output the the caller.</param>
344         /// <returns>Chid's process exit code.</returns>
DoProcess_GetOutput(string filename, string arguments, bool waitDataReceived, int timeout, out string output)345         internal static int DoProcess_GetOutput(string filename, string arguments, bool waitDataReceived, int timeout, out string output)
346         {
347             #if DEBUG
348                 Log.WriteLine("ProcessorExecutor.DoProcess_GetOutput : " + filename + " " + arguments);
349             #endif
350 
351             try
352             {
353                 int ret;
354                 bool isDataReceived = !(waitDataReceived); // If we want to wait for data received event (waitDataReceived=true), need to set isDataReceived = false.
355                 System.Threading.ManualResetEvent dataReceivedEvent = new System.Threading.ManualResetEvent(false);
356                 TimeSpan DATA_RECEIVE_FLUSH_TIMEOUT = new TimeSpan(0, 0, 2);
357                 Process p = new Process();
358                 ProcessStartInfo si = new ProcessStartInfo(filename);
359                 si.UseShellExecute = false;
360                 si.CreateNoWindow = true;
361                 si.Arguments = arguments;
362                 StringBuilder outputTmp = new StringBuilder();
363                 DataReceivedEventHandler callback = (sender, e) =>
364                 {
365                     if (e != null && e.Data != null)
366                     {
367                         outputTmp.AppendLine( e.Data );
368                         if (!isDataReceived)
369                         {
370                             isDataReceived = true;
371                             dataReceivedEvent.Set();
372                         }
373                     }
374                 };
375 
376                 si.RedirectStandardError = true;
377                 si.RedirectStandardOutput = true;
378                 p.StartInfo = si;
379                 p.OutputDataReceived += callback;
380                 p.ErrorDataReceived += callback;
381                 p.Start();
382                 p.BeginOutputReadLine();
383                 p.BeginErrorReadLine();
384                 bool exitedProperly = true;
385 
386                 if (timeout > 0)
387                 {
388                     exitedProperly = p.WaitForExit(timeout);
389                     if (!exitedProperly)
390                     {
391                         Console.WriteLine("ERROR: Process started for '" + filename + " " + arguments + "' did not finish in the expected timeout (" + timeout.ToString() + "ms).");
392                         Console.WriteLine("Dumping process std/out std/err until now:\n {0} \n", outputTmp.ToString());
393                     }
394                 }
395                 else
396                 {
397                     p.WaitForExit();
398                 }
399 
400                 if (exitedProperly)
401                 {
402                     ret = p.ExitCode;
403                 }
404                 else
405                 {
406                     ret = -1;
407                 }
408 
409                 // Some output should have been received either in stdout or stderr, so wait
410                 if (isDataReceived == false)
411                 {
412                     dataReceivedEvent.WaitOne(DATA_RECEIVE_FLUSH_TIMEOUT);
413                 }
414 
415                 p.CancelOutputRead();
416                 p.CancelErrorRead();
417                 p.StartInfo.RedirectStandardOutput = false;
418                 p.StartInfo.RedirectStandardError = false;
419                 p.OutputDataReceived -= callback;
420                 p.ErrorDataReceived -= callback;
421 
422                 p.Close();
423                 p.Dispose();
424                 p = null;
425 
426                 output = outputTmp.ToString();
427 
428                 return ret;
429             }
430             catch (Exception e)
431             {
432                 output = string.Empty;
433                 Console.WriteLine("CafeX Error: Exception in process executor");
434                 Console.WriteLine(filename + " " + arguments);
435                 Console.WriteLine(e.Message);
436                 return -1;
437             }
438         }
439 
440         /// <summary>
441         /// Creates a process, starts it, wait for its execution and return its exit code.
442         /// The child's process stdout and errout are set to a callback handler provided.
443         /// </summary>
444         /// <param name="filename">Path to the executable to start on a different process.</param>
445         /// <param name="arguments">Arguments to the executable.</param>
446         /// <param name="callback">An instance of DataReceivedEventHandler provided by the caller.</param>
447         /// <returns>Chid's process exit code.</returns>
StartProcess_RedirectOutput(string filename, string arguments, DataReceivedEventHandler callback, bool waitCompletion)448         internal static int StartProcess_RedirectOutput(string filename, string arguments, DataReceivedEventHandler callback, bool waitCompletion)
449         {
450             #if DEBUG
451             Log.WriteLine("ProcessorExecutor.StartProcess_RedirectOutput : " + filename + " " + arguments);
452             #endif
453 
454             try
455             {
456                 Process p = new Process();
457                 ProcessStartInfo si = new ProcessStartInfo(filename);
458                 si.UseShellExecute = false;
459                 si.CreateNoWindow = true;
460                 si.Arguments = arguments;
461                 si.RedirectStandardError = true;
462                 si.RedirectStandardOutput = true;
463                 p.StartInfo = si;
464                 p.OutputDataReceived += callback;
465                 p.ErrorDataReceived += callback;
466 
467                 p.Start();
468 
469                 p.BeginOutputReadLine();
470                 p.BeginErrorReadLine();
471 
472                 if (waitCompletion)
473                 {
474                     p.WaitForExit();
475                     int ret = p.ExitCode;
476                     p.CancelOutputRead();
477                     p.CancelErrorRead();
478                     p.StartInfo.RedirectStandardOutput = false;
479                     p.StartInfo.RedirectStandardError = false;
480                     p.OutputDataReceived -= callback;
481                     p.ErrorDataReceived -= callback;
482                     p.Close();
483                     p.Dispose();
484                     p = null;
485 
486                     return ret;
487 
488                 }
489                 else
490                 {
491                     return 0;
492                 }
493 
494             }
495             catch (Exception e)
496             {
497                 Console.WriteLine("CafeX Error: Exception in process executor");
498                 Console.WriteLine(filename + " " + arguments);
499                 Console.WriteLine(e.Message);
500                 return -1;
501             }
502         }
503 
StartProcess_RedirectOutput(string filename, string arguments)504         internal static int StartProcess_RedirectOutput(string filename, string arguments)
505         {
506 #if DEBUG
507             Log.WriteLine("ProcessorExecutor.StartProcess_RedirectOutput : " + filename + " " + arguments);
508 #endif
509 
510             try
511             {
512                 Process p = new Process();
513                 ProcessStartInfo si = new ProcessStartInfo(filename);
514                 si.UseShellExecute = false;
515                 si.CreateNoWindow = true;
516                 si.Arguments = arguments;
517                 si.RedirectStandardError = true;
518                 si.RedirectStandardOutput = true;
519                 si.RedirectStandardInput = true;
520                 p.StartInfo = si;
521 
522                 p.Start();
523 
524                 p.BeginOutputReadLine();
525 
526                 return 0;
527             }
528             catch (Exception e)
529             {
530                 Console.WriteLine("CafeX Error: Exception in process executor");
531                 Console.WriteLine(filename + " " + arguments);
532                 Console.WriteLine(e.Message);
533                 return -1;
534             }
535         }
536     }
537 
538     /// <summary>
539     /// This class allows string arrays to be combined into a single string with the delimiter of choice.
540     /// </summary>
541     internal static class StringCombiner
542     {
MakeDelimitedString(string[] arr, char delimiter)543         internal static string MakeDelimitedString(string[] arr, char delimiter)
544         {
545             string ret = string.Empty;
546             if (arr != null)
547             {
548                 for (int i = 0; i < arr.Length; ++i)
549                 {
550                     if (i == 0)
551                     {
552                         ret += arr[i];
553                     }
554                     else
555                     {
556                         ret += delimiter + arr[i];
557                     }
558                 }
559             }
560 
561             return ret;
562         }
563     }
564 
565     /// <summary>
566     /// This class is used to edit xml content files.
567     /// </summary>
568     internal static class XmlHandler
569     {
570         /// <summary>
571         /// Traverses the xml document from the provided root node until it finds the node that matches the provided name.
572         /// </summary>
573         /// <param name="root_name">Root node</param>
574         /// <param name="node_name">Name of the node being searched</param>
575         /// <returns>The node that matches the provided node name, null if it was not found.</returns>
GetXmlNodeRecursive(XmlNode root_name, string node_name)576         internal static XmlNode GetXmlNodeRecursive(XmlNode root_name, string node_name)
577         {
578             if (root_name.Name == node_name)
579                 return root_name;
580 
581             XmlNode ret = null;
582             foreach (XmlNode child in root_name.ChildNodes)
583             {
584                 ret = GetXmlNodeRecursive(child, node_name);
585                 if (ret != null)
586                     break;
587             }
588 
589             return ret;
590         }
591 
592         /// <summary>
593         /// Check if a node by the provided name exists inside an xml document.
594         /// </summary>
595         /// <param name="file_name">Full path to the xml document.</param>
596         /// <param name="node_name">Name of the node to search.</param>
597         /// <returns>True if the node was found, false otherwise.</returns>
NodeExists(string file_name, string node_name)598         internal static bool NodeExists(string file_name, string node_name)
599         {
600 #if DEBUG
601             Log.WriteLine(string.Format("NodeExists: File: {0}, Node: {1}", file_name, node_name));
602 #endif
603             XmlDocument document = new XmlDocument();
604             document.Load(file_name);
605             XmlNode node = GetXmlNodeRecursive(document.DocumentElement, node_name);
606             return (node == null) ? false : true;
607         }
608 
609         /// <summary>
610         /// Returns the value of a node from an existing xml document.
611         /// </summary>
612         /// <param name="file_name">Full path to the xml document.</param>
613         /// <param name="node_name">Name of the node to search.</param>
614         /// <returns>String representing the node's value, null if the node was not found.</returns>
GetNodeValue(string file_name, string node_name)615         internal static string GetNodeValue(string file_name, string node_name)
616         {
617             #if DEBUG
618                 Log.WriteLine("GetNodeValue called. file_name=" + file_name + ", node_name=" + node_name);
619             #endif
620             XmlDocument document = new XmlDocument();
621             document.Load(file_name);
622             XmlNode node = GetXmlNodeRecursive(document.DocumentElement, node_name);
623             if (node == null)
624             {
625                 Console.WriteLine("cafex xmlhandler getnodevalue failed: Node " + node_name + " does not exist in " + file_name);
626                 return null;
627             }
628             #if DEBUG
629                 Log.WriteLine("GetNodeValue return = " + node.InnerText);
630             #endif
631             return node.InnerText;
632         }
633 
634         /// <summary>
635         /// Updates the value of a node inside an xml document.
636         /// </summary>
637         /// <param name="file_name">Full path to the xml document.</param>
638         /// <param name="node_name">Name of the node to search.</param>
639         /// <param name="new_value">The new value for the node.</param>
UpdateNodeValue(string file_name, string node_name, string new_value)640         internal static void UpdateNodeValue(string file_name, string node_name, string new_value)
641         {
642             #if DEBUG
643                 Log.WriteLine("UpdateNodeValue called. file_name=" + file_name + ", node_name=" + node_name + ", new_value=" + new_value);
644             #endif
645             XmlDocument document = new XmlDocument();
646             document.Load(file_name);
647             XmlNode node = GetXmlNodeRecursive(document.DocumentElement, node_name);
648             if (node == null)
649             {
650                 Console.WriteLine("cafex xmlhandler updatenodevalue failed: Node " + node_name + " does not exist in " + file_name);
651                 return;
652             }
653 
654             // Only update if necessary.
655             if (node.InnerText != new_value)
656             {
657                 if (File.Exists(file_name))
658                 {
659                     // Strip the read-only attribute so we can overwrite it if it exists
660                     File.SetAttributes(file_name, File.GetAttributes(file_name) & ~FileAttributes.ReadOnly);
661                 }
662 
663                 // Before the update, check if the file is not locked for 3 times
664                 uint retries = Program.RETRY_ATTEMPS;
665                 while (FileUtil.IsFileLocked(file_name) && retries > 0)
666                 {
667                     System.Threading.Thread.Sleep(Program.RETRY_TIMESPAN);
668                     --retries;
669                 }
670 
671                 node.InnerText = new_value;
672                 #if DEBUG
673                     Log.WriteLine("updateNodeValue: file: " + file_name + ", node: " + node_name + ", newvalue: " + new_value);
674                 #endif
675                 XmlWriterSettings settings = new XmlWriterSettings();
676                 settings.NewLineChars = "\n";
677                 settings.Indent = true;
678                 settings.Encoding = new UTF8Encoding(false);
679                 using (XmlWriter writer = XmlWriter.Create(file_name, settings))
680                 {
681                     document.WriteTo(writer);
682                     writer.Flush();
683                     writer.Close();
684                 }
685 
686                 File.SetAttributes(file_name, FileAttributes.Normal);
687                 #if DEBUG
688                     Log.WriteLine("updateNodeValue completed on '" + file_name + "' and file in use = " + FileUtil.IsFileLocked(file_name));
689                 #endif
690             }
691         }
692 
693         /// <summary>
694         /// Add an xml node to an xml document.
695         /// </summary>
696         /// <param name="file_name">Full path to the xml document.</param>
697         /// <param name="node_name">Name for the new xml node.</param>
698         /// <param name="value">Node's value.</param>
699         /// <param name="attributes">Array of strings representing the node's attributes.</param>
AddNode(string file_name, string node_name, string value, string[] attributes)700         internal static void AddNode(string file_name, string node_name, string value, string[] attributes)
701         {
702 #if DEBUG
703             Log.WriteLine("AddNode called. file_name=" + file_name + ", node_name=" + node_name + ", value=" + value);
704             string attrString = null;
705             foreach (string attribute in attributes)
706             {
707                 attrString += attribute + " ";
708             }
709             Log.WriteLine("Attributes = " + attrString);
710 #endif
711             XmlDocument document = new XmlDocument();
712             document.Load(file_name);
713             XmlElement node = document.CreateElement(node_name);
714             node.InnerText = value;
715 
716             for (int i = 0; i < attributes.Length; i += 2)
717             {
718                 node.SetAttribute(attributes[i], attributes[i + 1]);
719             }
720 
721             document.DocumentElement.AppendChild(node);
722             #if DEBUG
723                 Log.WriteLine("AddNode: file: " + file_name + ", node: " + node_name + ", value: " + value);
724             #endif
725             XmlWriterSettings settings = new XmlWriterSettings();
726             settings.NewLineChars = "\n";
727             settings.Indent = true;
728             settings.Encoding = new UTF8Encoding(false);
729             // Before the update, check if the file is not locked for 3 times
730             uint retries = Program.RETRY_ATTEMPS;
731             while (FileUtil.IsFileLocked(file_name) && retries > 0)
732             {
733                 System.Threading.Thread.Sleep(Program.RETRY_TIMESPAN); // 1s
734                 --retries;
735             }
736 
737             using (XmlWriter writer = XmlWriter.Create(file_name, settings))
738             {
739                 document.WriteTo(writer);
740                 writer.Flush();
741                 writer.Close();
742             }
743 
744             File.SetAttributes(file_name, FileAttributes.Normal);
745             #if DEBUG
746                 Log.WriteLine("AddNode completed on '" + file_name + "' and file in use = " + FileUtil.IsFileLocked(file_name));
747             #endif
748         }
749 
750         /// <summary>
751         /// Remove a node from an xml document.
752         /// </summary>
753         /// <param name="file_name">Full path to the xml document.</param>
754         /// <param name="node_name">Name for the node to search.</param>
RemoveNode(string file_name, string node_name)755         internal static void RemoveNode(string file_name, string node_name)
756         {
757 #if DEBUG
758             Log.WriteLine("RemoveNode called. file_name=" + file_name + ", node_name=" + node_name);
759 #endif
760             XmlDocument document = new XmlDocument();
761             document.Load(file_name);
762             XmlNode node = GetXmlNodeRecursive(document.DocumentElement, node_name);
763             if (node == null)
764             {
765                 return;
766             }
767             #if DEBUG
768                 Log.WriteLine("RemoveNode: file: " + file_name + ", node: " + node_name);
769             #endif
770             document.DocumentElement.RemoveChild(node);
771 
772             XmlWriterSettings settings = new XmlWriterSettings();
773             settings.NewLineChars = "\n";
774             settings.Indent = true;
775             settings.Encoding = new UTF8Encoding(false);
776             // Before the update, check if the file is not locked for 3 times
777             uint retries = Program.RETRY_ATTEMPS;
778             while (FileUtil.IsFileLocked(file_name) && retries > 0)
779             {
780                 System.Threading.Thread.Sleep(Program.RETRY_TIMESPAN); // 1s
781                 --retries;
782             }
783 
784             using (XmlWriter writer = XmlWriter.Create(file_name, settings))
785             {
786                 document.WriteTo(writer);
787                 writer.Flush();
788                 writer.Close();
789             }
790 
791             File.SetAttributes(file_name, FileAttributes.Normal);
792             #if DEBUG
793                 Log.WriteLine("RemoveNode completed on '" + file_name + "' and file in use = " + FileUtil.IsFileLocked(file_name));
794             #endif
795         }
796 
UpdateNodeValueByPath(string file_name, string xpath, string new_value)797         internal static void UpdateNodeValueByPath(string file_name, string xpath, string new_value)
798         {
799 #if DEBUG
800             Log.WriteLine("UpdateNodeValueByPath called.  file_name=" + file_name + ", xpath=" + xpath + ", new_value=" + new_value);
801 #endif
802 
803             XmlDocument document = new XmlDocument();
804             document.Load(file_name);
805 
806             try
807             {
808                 XmlNode target = document.SelectSingleNode(xpath);
809 
810                 if (target == null)
811                 {
812                     Console.WriteLine("cafex xmlhandler UpdateNodeValueByPath failed: Node specified by path " + xpath + " does not exist in " + file_name);
813                     return;
814                 }
815 
816                 if (target.InnerText != new_value)
817                 {
818                     uint retries = Program.RETRY_ATTEMPS;
819                     while (FileUtil.IsFileLocked(file_name) && retries > 0)
820                     {
821                         System.Threading.Thread.Sleep(Program.RETRY_TIMESPAN);
822                         --retries;
823                     }
824 
825                     target.InnerText = new_value;
826 #if DEBUG
827                     Log.WriteLine("UpdateNodeValueByPath:  file=" + file_name + ", xpath=" + xpath + ", new_value=" + new_value);
828 #endif
829                     XmlWriterSettings settings = new XmlWriterSettings();
830                     settings.NewLineChars = "\n";
831                     settings.Indent = true;
832                     settings.Encoding = new UTF8Encoding(false);
833                     using (XmlWriter writer = XmlWriter.Create(file_name, settings))
834                     {
835                         document.WriteTo(writer);
836                         writer.Flush();
837                         writer.Close();
838                     }
839 
840                     File.SetAttributes(file_name, FileAttributes.Normal);
841 #if DEBUG
842                     Log.WriteLine("UpdateNodeValueByPath completed on '" + file_name + "' and file in use = " + FileUtil.IsFileLocked(file_name));
843 #endif
844                 }
845             }
846             catch (System.Xml.XPath.XPathException)
847             {
848                 Console.WriteLine("UpdateNodeValueByPath contained an invalid xpath parameter! xpath={0}\nNode will not be updated!",xpath);
849                 return;
850             }
851         }
852     }
853 
854     /// <summary>
855     /// This class is a wrapper on HEX conversion functions used by CafeX.
856     /// </summary>
857     internal static class HexHandler
858     {
859         /// <summary>
860         /// Transform the input string into its integer representation.
861         /// Strings that are invalid representations of integers will cause exceptions.
862         /// </summary>
863         /// <param name="input">String representing a number.</param>
864         /// <returns>The integer that represents the provided string.</returns>
GetNumberFromString(string input)865         internal static uint GetNumberFromString(string input)
866         {
867 #if DEBUG
868             Log.WriteLine("GetNumberFromString called. input=" + input);
869 #endif
870             if (input.StartsWith("0x"))
871             {
872                 return Convert.ToUInt32(input, 16);
873             }
874             else
875             {
876                 foreach (char c in input)
877                 {
878                     if ((c >= 'a' || c >= 'A') &&
879                         (c <= 'f' || c <= 'F'))
880                     {
881                         return Convert.ToUInt32(input, 16);
882                     }
883                 }
884 
885                 return Convert.ToUInt32(input);
886             }
887         }
888 
889         /// <summary>
890         /// Transform the input string into its long representation.
891         /// Strings that are invalid representations of longs will cause exceptions.
892         /// </summary>
893         /// <param name="input">String representing a number.</param>
894         /// <returns>The long that represents the provided string.</returns>
GetLongNumberFromString(string input)895         internal static long GetLongNumberFromString(string input)
896         {
897 #if DEBUG
898             Log.WriteLine("GetLongNumberFromString called. input=" + input);
899 #endif
900 
901             if (input.StartsWith("0x"))
902             {
903                 return Convert.ToInt64(input, 16);
904             }
905             else
906             {
907                 foreach (char c in input)
908                 {
909                     if ((c >= 'a' || c >= 'A') &&
910                         (c <= 'f' || c <= 'F'))
911                     {
912                         return Convert.ToInt64(input, 16);
913                     }
914                 }
915                 return Convert.ToInt64(input);
916             }
917         }
918     }
919 
920     /// <summary>
921     /// Utilities for file verification.
922     /// </summary>
923     internal static class FileUtil
924     {
925         private static string cafex_system_wide_mtx_name = "Global\\cafex_system_wide_mtx";
926         private static DateTime entryAssemblyTs;
927         private static bool bRunningFromCygwin = false;
928         private static string assemblyFileName;
929 
930 
931         /// <summary>
932         /// Initializes one-shot only utility variables
933         /// </summary>
GatherEnvironment()934         internal static void GatherEnvironment()
935         {
936             string cygcheck;
937             assemblyFileName = System.Reflection.Assembly.GetExecutingAssembly().Location;
938             entryAssemblyTs = File.GetLastWriteTimeUtc(assemblyFileName);
939             cygcheck = Environment.GetEnvironmentVariable("CYGCHECK");
940             if ( !(string.IsNullOrEmpty(cygcheck)) )
941             {
942                 if( cygcheck.StartsWith("cygwin" ) )
943                 {
944                     bRunningFromCygwin = true;
945                 }
946             }
947         }
948 
949 
950         /// <summary>
951         /// Determine whether we are running from within cygwin
952         /// </summary>
953         internal static bool RunningFromCygwin
954         {
955             get { return bRunningFromCygwin; }
956         }
957 
958         /// <summary>
959         /// Check if a file is locked for R/W and/or delete.
960         /// </summary>
961         /// <param name="filename">Full path to the file.</param>
962         /// <returns>True if the file is locked, false otherwise.</returns>
IsFileLocked(string filename)963         internal static bool IsFileLocked(string filename)
964         {
965             FileStream stream = null;
966             bool result = false;
967 
968             try
969             {
970                 FileInfo file = new FileInfo(filename);
971                 stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
972                 result = false;
973             }
974             catch (IOException)
975             {
976                 //the file is unavailable because it is:
977                 //a) still being written to
978                 //b) or being processed by another thread (maybe held by PCFS)
979                 //c) or does not exist (has already been processed)
980 #if DEBUG
981                 // Collect debug information to help troubleshoot who has this file handle
982                 try
983                 {
984                     Process handleTool = new Process();
985                     handleTool.StartInfo.FileName = "handle.exe";
986                     handleTool.StartInfo.Arguments = filename + " /accepteula";
987                     handleTool.StartInfo.UseShellExecute = false;
988                     handleTool.StartInfo.RedirectStandardOutput = true;
989                     handleTool.Start();
990                     handleTool.WaitForExit();
991                     string handleOutput = handleTool.StandardOutput.ReadToEnd();
992                     Log.WriteLine("############## FILE LOCKED WARNING REPORT ##############");
993                     Log.WriteLine("Filename = " + filename);
994                     Log.WriteLine("Handles opened:");
995                     Log.WriteLine(handleOutput);
996                     Log.WriteLine("############## FILE LOCKED END OF REPORT  ##############");
997 
998 
999                 }
1000                 catch (Exception)
1001                 {
1002                     Log.WriteLine("Unable to get handle information on file " + filename);
1003                 }
1004                 finally
1005 #endif
1006                 {
1007                     result = true;
1008                 }
1009             }
1010             finally
1011             {
1012                 if (stream != null)
1013                 {
1014                     stream.Close();
1015                 }
1016             }
1017 
1018             return result;
1019 
1020         }
1021 
1022 
1023 
1024         /// <summary>
1025         /// Expand an executable to the temp folder from a resource location
1026         /// </summary>
1027         /// <param name="payload">Executable bytes from resource</param>
1028         /// <param name="filename">File to be written to disk</param>
1029         /// <returns>Full path to temporary file written to disk.</returns>
ExpandExecutable(byte[] payload, string filename)1030         internal static string ExpandExecutable(byte[] payload, string filename)
1031         {
1032             System.Threading.Mutex mtx = null;
1033             string full_tmp_path;
1034 
1035             full_tmp_path = Environment.GetEnvironmentVariable("CAFE_TEMP");
1036 
1037             if( string.IsNullOrEmpty(full_tmp_path) )
1038             {
1039                 full_tmp_path = Environment.GetEnvironmentVariable("TEMP");
1040                 if( string.IsNullOrEmpty(full_tmp_path)  )
1041                 {
1042                     full_tmp_path = Environment.GetEnvironmentVariable("TMP");
1043                     if ( string.IsNullOrEmpty(full_tmp_path) )
1044                     {
1045                         full_tmp_path = Path.GetDirectoryName(assemblyFileName);
1046                     }
1047                 }
1048             }
1049 
1050 
1051             full_tmp_path = PathConverter.Windowsify(full_tmp_path) + "\\" + filename;
1052 
1053             // the kinds of things that can prevent this from happening are all fatal
1054 			// errors, don't try to catch anything
1055             mtx = new System.Threading.Mutex(false, cafex_system_wide_mtx_name);
1056 
1057             mtx.WaitOne();
1058 
1059             // from this point on, we must release the mutex no matter what happens!
1060 
1061             try
1062             {
1063                 DateTime tempExeTs;
1064                 bool doOverwrite = false;
1065                 try
1066                 {
1067                     tempExeTs = File.GetLastWriteTimeUtc(full_tmp_path);
1068 
1069                     // if the internal resource is newer than the file on disk
1070                     if (entryAssemblyTs >= tempExeTs)
1071                     {
1072                         doOverwrite = true;
1073                     }
1074                     // else, we will use the file that's there, as it is newer than we are
1075                 }
1076                 catch (FileNotFoundException)
1077                 {
1078                     // file's not there - we will overwrite it
1079                     doOverwrite = true;
1080                 }
1081 
1082                 if (doOverwrite)
1083                 {
1084 					try
1085 					{
1086                     	File.WriteAllBytes(full_tmp_path, payload);
1087 					}
1088 					catch( System.IO.DirectoryNotFoundException )
1089 					{
1090 						try
1091 						{
1092 							Directory.CreateDirectory( Path.GetDirectoryName(full_tmp_path) );
1093 						}
1094 						catch
1095 						{
1096 						}
1097 						File.WriteAllBytes(full_tmp_path, payload);
1098 					}
1099 
1100                 }
1101             }
1102             finally
1103             {
1104                 mtx.ReleaseMutex();
1105             }
1106 
1107             return full_tmp_path;
1108 
1109         }
1110 
1111         /// <summary>
1112         /// Check if the provide string is a valid file name.
1113         /// </summary>
1114         /// <param name="filename">String to check.</param>
1115         /// <returns>True if it is a valid file name, false otherwise.</returns>
IsValidFile(string filename, bool checkExistence)1116         internal static bool IsValidFile(string filename, bool checkExistence)
1117         {
1118 #if DEBUG
1119             Log.WriteLine("IsValidFile called. filename=" + filename + ", checkExistence=" + checkExistence.ToString());
1120 #endif
1121             if (string.IsNullOrEmpty(filename))
1122             {
1123                 return false;
1124             }
1125 
1126             // Convert to windows, if it was a cygwin path
1127             filename = PathConverter.Windowsify(filename);
1128             bool returnVal = false;
1129             try
1130             {
1131                 FileInfo file = new FileInfo(filename);
1132                 // A path without a filename resolves to a valid FileInfo instance
1133                 // so we need to check if there is a file at the end of the path.
1134                 if (Path.GetFileName(file.FullName).Length == 0)
1135                 {
1136                     // There is no filename, only a valid path!
1137                     return false;
1138                 }
1139 
1140                 if (!checkExistence)
1141                 {
1142                     returnVal = true;
1143                 }
1144                 else
1145                 {
1146                     returnVal = file.Exists;
1147                 }
1148             }
1149             catch (ArgumentException)
1150             {
1151             }
1152             catch (System.IO.PathTooLongException)
1153             {
1154             }
1155             catch (NotSupportedException)
1156             {
1157             }
1158 
1159 #if DEBUG
1160             Log.WriteLine("IsValidFile return " + returnVal.ToString());
1161 #endif
1162             return returnVal;
1163         }
1164 
AddQuotes(ref string fileNameOrListWithSpaces)1165         internal static void AddQuotes(ref string fileNameOrListWithSpaces)
1166         {
1167 #if DEBUG
1168             Log.WriteLine("AddQuotes called. " + "fileNameOrListWithSpaces=" + fileNameOrListWithSpaces);
1169 #endif
1170             if (string.IsNullOrEmpty(fileNameOrListWithSpaces))
1171             {
1172                 return;
1173             }
1174 
1175             if (fileNameOrListWithSpaces.StartsWith("\"") && fileNameOrListWithSpaces.EndsWith("\""))
1176             {
1177                 return;
1178             }
1179 
1180             fileNameOrListWithSpaces = "\"" + fileNameOrListWithSpaces + "\"";
1181         }
1182 
1183 
1184         /// <summary>
1185         /// Copy a directory from one location to another with optional recursion.
1186         /// </summary>
1187         /// <param name="sourceDirName">Source folder name.</param>
1188         /// <param name="destDirName">Destination folder name.</param>
1189         /// <param name="copySubDirs">Flag to copy all of the sub-folders recurively.</param>
DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)1190         internal static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)
1191         {
1192 #if DEBUG
1193             Console.WriteLine("FileUtil::DirectoryCopy: Copying '{0}' => '{1}'{2}", sourceDirName, destDirName, copySubDirs ? " recursively" : "");
1194 #endif
1195             // Get the subdirectories for the specified directory.
1196             DirectoryInfo dir = new DirectoryInfo(sourceDirName);
1197             DirectoryInfo[] dirs = dir.GetDirectories();
1198 
1199             if (!dir.Exists)
1200             {
1201                 throw new DirectoryNotFoundException("Source directory does not exist or could not be found: " + sourceDirName);
1202             }
1203 
1204             // If the destination directory doesn't exist, create it.
1205             if (!Directory.Exists(destDirName))
1206             {
1207                 Directory.CreateDirectory(destDirName);
1208             }
1209 
1210             // Get the files in the directory and copy them to the new location.
1211             FileInfo[] files = dir.GetFiles();
1212             foreach (FileInfo file in files)
1213             {
1214                 string temppath = Path.Combine(destDirName, file.Name);
1215                 file.CopyTo(temppath, false);
1216             }
1217 
1218             // If copying subdirectories, copy them and their contents to new location.
1219             if (copySubDirs)
1220             {
1221                 foreach (DirectoryInfo subdir in dirs)
1222                 {
1223                     string temppath = Path.Combine(destDirName, subdir.Name);
1224                     DirectoryCopy(subdir.FullName, temppath, copySubDirs);
1225                 }
1226             }
1227         }
1228 
1229         /// <summary>
1230         /// Deletes the content of a directory with optional recursion
1231         /// </summary>
1232         /// <param name="dirName">Directory folder name.</param>
1233         /// <param name="deleteSubDirs">Flag to remove subdirectories recursively.</param>
DirectoryDelete(string dirName, bool deleteSubDirs)1234         internal static void DirectoryDelete(string dirName, bool deleteSubDirs)
1235         {
1236 #if DEBUG
1237             Console.WriteLine("FileUtil::DirectoryDelete: Deleting '{0}'{1}", dirName, deleteSubDirs ? " recursively" : "");
1238 #endif
1239             // Delete all of the files in the directory first
1240             foreach (string srcEntry in Directory.GetFiles(dirName))
1241             {
1242                 FileDelete(srcEntry);
1243             }
1244 
1245             if (deleteSubDirs)
1246             {
1247                 foreach (string srcEntry in Directory.GetDirectories(dirName))
1248                 {
1249                     DirectoryDelete(srcEntry, deleteSubDirs);
1250                 }
1251 
1252 #if DEBUG
1253                 Console.WriteLine("FileUtil::DirectoryDelete: Deleting '{0}'", dirName);
1254 #endif
1255                 Directory.Delete(dirName);
1256             }
1257         }
1258 
FileDelete(string fileName)1259         internal static void FileDelete(string fileName)
1260         {
1261 #if DEBUG
1262             Console.WriteLine("FileUtil::FileDelete: Deleting '{0}'", fileName);
1263 #endif
1264             if (File.Exists(fileName))
1265             {
1266                 // Strip any readonly, hidden and system attributes
1267                 FileInfo fileInfo = new FileInfo(fileName);
1268                 fileInfo.Attributes = FileAttributes.Normal;
1269 
1270                 // Now delete the file
1271                 File.Delete(fileName);
1272             }
1273         }
1274     }
1275 
1276     /// <summary>
1277     /// Log to the stdout and debug.  This is only used in debug builds.
1278     /// </summary>
1279     internal static class Log
1280     {
1281         internal static StreamWriter sw = null;
1282 
OpenLogFile(string logFilePath)1283         internal static bool OpenLogFile(string logFilePath)
1284         {
1285             try
1286             {
1287                 if (sw == null)
1288                 {
1289                     string path = Environment.ExpandEnvironmentVariables(logFilePath);
1290                     path = PathConverter.Windowsify(path);
1291                     string directory = path.Substring(0, path.LastIndexOf('\\'));
1292                     if (!Directory.Exists(directory))
1293                     {
1294                         Directory.CreateDirectory(directory);
1295                     }
1296                     sw = new StreamWriter(path);
1297                 }
1298             }
1299             catch (Exception ex)
1300             {
1301                 Console.WriteLine("Failed to open the log file. InnerException: {0}", ex.Message);
1302                 sw = null;
1303             }
1304             return (sw != null);
1305         }
WriteLine(string message)1306         internal static void WriteLine(string message)
1307         {
1308             message = string.Format("[ {0} - DEBUG: {1} ] {2}", System.Diagnostics.Process.GetCurrentProcess().Id, DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-ffff"), message);
1309             Console.WriteLine(message);
1310             Debug.WriteLine(message);
1311             if (sw != null)
1312             {
1313                 sw.WriteLine(message);
1314             }
1315         }
1316 
WriteLine(string format, object arg0)1317         internal static void WriteLine(string format, object arg0)
1318         {
1319             WriteLine(string.Format(format, arg0));
1320         }
1321 
WriteLine(string format, object arg0, object arg1)1322         internal static void WriteLine(string format, object arg0, object arg1)
1323         {
1324             WriteLine(string.Format(format, arg0, arg1));
1325         }
1326 
WriteLine(string format, object arg0, object arg1, object arg2)1327         internal static void WriteLine(string format, object arg0, object arg1, object arg2)
1328         {
1329             WriteLine(string.Format(format, arg0, arg1, arg2));
1330         }
1331 
WriteLineRaw(string message)1332         internal static void WriteLineRaw(string message)
1333         {
1334             Console.WriteLine(message);
1335             Debug.WriteLine(message);
1336             if (sw != null)
1337             {
1338                 sw.WriteLine(message);
1339             }
1340         }
1341 
WriteLineRaw(string format, object arg0)1342         internal static void WriteLineRaw(string format, object arg0)
1343         {
1344             WriteLineRaw(string.Format(format, arg0));
1345         }
1346 
WriteLineRaw(string format, object arg0, object arg1)1347         internal static void WriteLineRaw(string format, object arg0, object arg1)
1348         {
1349             WriteLineRaw(string.Format(format, arg0, arg1));
1350         }
1351 
WriteLineRaw(string format, object arg0, object arg1, object arg2)1352         internal static void WriteLineRaw(string format, object arg0, object arg1, object arg2)
1353         {
1354             WriteLineRaw(string.Format(format, arg0, arg1, arg2));
1355         }
1356 
CloseLogFile()1357         internal static void CloseLogFile()
1358         {
1359             if (sw != null)
1360             {
1361                 sw.Close();
1362             }
1363         }
1364     }
1365 
1366     /// <summary>
1367     /// This class provides tools for argument validation.
1368     /// </summary>
1369     internal static class ArgumentChecks
1370     {
IsArgumentValidSetting(string[] args, int index)1371         internal static bool IsArgumentValidSetting(string[] args, int index)
1372         {
1373 #if DEBUG
1374             Log.WriteLine("IsArgumentValidSetting called. Checking index=" + index.ToString());
1375             string argString = null;
1376             for (int counter = 0; counter < args.Length; ++counter)
1377             {
1378                 argString += "{" + counter.ToString() + "}" + args[counter] + " ";
1379             }
1380             Log.WriteLine("arguments=" + argString);
1381 #endif
1382             bool returnVal = false;
1383             // If the argument doesn't exist, it is not valid.
1384             if (!ArgumentExists(args, index))
1385             {
1386                 returnVal = false;
1387             }
1388             else
1389             {
1390                 // If the argument starts with '-' it is another option, not a setting.
1391                 if (!args[index].StartsWith("-"))
1392                 {
1393                     returnVal = true;
1394                 }
1395                 else
1396                 {
1397                     returnVal = false;
1398                 }
1399             }
1400 
1401 #if DEBUG
1402             Log.WriteLine("IsArgumentValidSetting return " + returnVal.ToString());
1403 #endif
1404             return returnVal;
1405         }
1406 
1407         /// <summary>
1408         /// Check if the provided index exists in the argument list.
1409         /// </summary>
1410         /// <param name="args">Array of string representing an argument list.</param>
1411         /// <param name="index">The index of the argument that will be checked.</param>
1412         /// <returns>True if the argument at the provide index exists, false otherwise.</returns>
ArgumentExists(string[] args, int index)1413         internal static bool ArgumentExists(string[] args, int index)
1414         {
1415             if (args == null || args.Length == 0 ||
1416                  index < 0 || index > (args.Length - 1))
1417             {
1418                 return false;
1419             }
1420             else
1421             {
1422                 return true;
1423             }
1424         }
1425     }
1426 
1427     internal class PerfTimer
1428     {
1429         private System.Diagnostics.Stopwatch _clock;
1430         private string _name;
1431 
1432         public string Name
1433         {
1434             get { return _name;}
1435         }
1436 
PerfTimer(string name)1437         public PerfTimer(string name)
1438         {
1439             _name = name;
1440             _clock = new Stopwatch();
1441         }
1442 
Start()1443         public void Start()
1444         {
1445             Log.WriteLine("[TIMER]: Starting PerfTimer " + _name);
1446             _clock.Start();
1447         }
1448 
Report()1449         public void Report()
1450         {
1451             Log.WriteLine("[TIMER]: PerfTimer " + _name + " current time is " + _clock.ElapsedMilliseconds + " ms.");
1452         }
1453 
Stop()1454         public void Stop()
1455         {
1456             _clock.Stop();
1457             Log.WriteLine("[TIMER]: PerfTimer " + _name + " stopped.");
1458             this.Report();
1459         }
1460 
Reset()1461         public void Reset()
1462         {
1463             _clock.Reset();
1464             Log.WriteLine("[TIMER]: PerfTimer " + _name + " reset.");
1465         }
1466     }
1467 }