// Initialize our dictionary class variable with [element, EID] for all elements in all dynamic streams in depot. // AcUtilsException caught and logged in %LOCALAPPDATA%\AcTools\Logs\EvilTwins-YYYY-MM-DD.log on stat command failure. // Exception caught and logged in same for a range of exceptions. private static async Task <bool> initMapAsync(AcDepot depot) { bool ret = false; // assume failure try { IEnumerable <AcStream> filter = from n in depot.Streams where n.IsDynamic && !n.Hidden select n; List <Task <AcResult> > tasks = new List <Task <AcResult> >(filter.Count()); foreach (AcStream stream in filter) { tasks.Add(AcCommand.runAsync($@"stat -a -s ""{stream}"" -fx")); // -a for all elements in stream } AcResult[] arr = await Task.WhenAll(tasks); // finish running stat commands in parallel if (arr != null && arr.All(n => n.RetVal == 0)) // true if all initialized successfully { HashSet <Tuple <string, int> > hset = new HashSet <Tuple <string, int> >(_tcompare); foreach (AcResult r in arr) { // if empty the stream has an ACL that is preventing us from reading it or some other error occurred if (r == null || String.IsNullOrEmpty(r.CmdResult)) { continue; } XElement xml = XElement.Parse(r.CmdResult); foreach (XElement e in xml.Elements("element")) { string path = (string)e.Attribute("location"); int eid = (int)e.Attribute("id"); hset.Add(new Tuple <string, int>(path, eid)); } } lock (_locker) { _map.Add(depot, hset); } ret = true; // operation succeeded } } catch (AcUtilsException ecx) { AcDebug.Log($"AcUtilsException caught and logged in Program.initMapAsync{Environment.NewLine}{ecx.Message}"); } catch (Exception ecx) { AcDebug.Log($"Exception caught and logged in Program.initMapAsync{Environment.NewLine}{ecx.Message}"); } return(ret); }
// Run the stat command for all dynamic streams in depot and initialize our dictionary class variable // with the xlinked elements found that have a type listed in ElementTypes from XLinked.exe.config. // Returns true if the operation succeeded, false otherwise. AcUtilsException caught and logged in // %LOCALAPPDATA%\AcTools\Logs\XLinked-YYYY-MM-DD.log on stat command failure. Exception caught and // logged in same for a range of exceptions. private static async Task <bool> initAsync(AcDepot depot) { bool ret = false; // assume failure try { int num = depot.Streams.Count(); List <Task <AcResult> > tasks = new List <Task <AcResult> >(num); foreach (AcStream stream in depot.Streams) { // -k: Display the element type (that is, data type) of this version // -v: Display the target of an element link or symbolic link tasks.Add(AcCommand.runAsync($@"stat -s ""{stream}"" -a -fkvx")); } HashSet <XElement> hset = new HashSet <XElement>(_comparer); while (tasks.Count > 0) { Task <AcResult> r = await Task.WhenAny(tasks); tasks.Remove(r); if (r == null || r.Result.RetVal != 0) { return(false); } XElement xml = XElement.Parse(r.Result.CmdResult); foreach (XElement e in xml.Elements("element") // attribute xlinked="true" exists only when status includes (xlinked), // otherwise it isn't there (i.e. there never is an xlinked="false" in the XML) .Where(n => (string)n.Attribute("xlinked") != null && _etypes.Any(t => t == n.acxType("elemType")))) { hset.Add(e); } } lock (_locker) { _map.Add(depot, hset); } ret = true; } catch (AcUtilsException exc) { AcDebug.Log($"AcUtilsException caught and logged in Program.initAsync{Environment.NewLine}{exc.Message}"); } catch (Exception ecx) { AcDebug.Log($"Exception caught and logged in Program.initAsync{Environment.NewLine}{ecx.Message}"); } return(ret); }
// Report evil twins if found. Assumes the initMapAsync method has been called. Exception caught // and logged in %LOCALAPPDATA%\AcTools\Logs\EvilTwins-YYYY-MM-DD.log on operation failure. private static async Task <bool> reportAsync() { bool ret = false; // assume failure try { foreach (KeyValuePair <AcDepot, HashSet <Tuple <string, int> > > pair in _map.OrderBy(n => n.Key)) // order by AcDepot { AcDepot depot = pair.Key; // depot HashSet <Tuple <string, int> > hset = pair.Value; // [element, EID] from all dynamic streams in depot // from our hashset create a collection of elements mapped to their EID's Lookup <string, Tuple <string, int> > col = (Lookup <string, Tuple <string, int> >)hset.ToLookup(n => n.Item1); // where more than one EID exists for the element, order by element foreach (var ii in col.Where(n => n.Count() > 1).OrderBy(n => n.Key)) { string element = ii.Key; if (_excludeList != null && _excludeList.Contains(element)) { continue; // ignore if in TwinsExcludeFile } log(element); IEnumerable <AcStream> filter = from n in depot.Streams where n.IsDynamic && !n.Hidden select n; List <Task <XElement> > tasks = new List <Task <XElement> >(filter.Count()); foreach (AcStream stream in filter) { tasks.Add(getElementInfoAsync(stream, element)); } XElement[] arr = await Task.WhenAll(tasks); // finish running stat commands in parallel if (arr != null && arr.All(n => n != null)) // true if all ran successfully { foreach (Tuple <string, int> jj in ii.OrderBy(n => n.Item2)) // order twins by EID { int eid = jj.Item2; log($"\tEID: {eid} on {DateTime.Now.ToString("ddd MMM d h:mm tt")}"); // C# language short-circuit: the test for id equals eid isn't evaluated if status equals "(no such elem)", // otherwise an exception would be thrown since the id attribute doesn't exist in this case foreach (XElement e in arr.Where(n => (string)n.Attribute("status") != "(no such elem)" && (int)n.Attribute("id") == eid).OrderBy(n => n.Annotation <AcStream>())) { log($"\t\t{e.Annotation<AcStream>()} {(string)e.Attribute("status")}"); string namedVersion = (string)e.Attribute("namedVersion"); // virtual stream name and version number string temp = (string)e.Attribute("Real"); string[] real = temp.Split('\\'); // workspace stream and version numbers AcStream wkspace = depot.getStream(int.Parse(real[0])); // workspace stream ElementType elemType = e.acxType("elemType"); string twin; if ((long?)e.Attribute("size") != null) { twin = $"\t\t\tSize: {(long)e.Attribute("size")}, ModTime: {e.acxTime("modTime")} {{{elemType}}}" + $"{Environment.NewLine}\t\t\tReal: {wkspace}\\{real[1]}, Virtual: {namedVersion}"; } else // a folder or link { twin = $"\t\t\tReal: {wkspace}\\{real[1]}, Virtual: {namedVersion} {{{elemType}}}"; } log(twin); } log(""); } } } } ret = true; // operation succeeded } catch (Exception ecx) { AcDebug.Log($"Exception caught and logged in Program.reportAsync{Environment.NewLine}{ecx.Message}"); } return(ret); }