void VisitFolderInternal(IProgressStatus monitor, string path, string domain, bool recursive) { // Avoid folders including each other if (!visitedFolders.Add(path) || ScanContext.IgnorePath(path)) { return; } OnVisitFolder(monitor, path, domain, recursive); }
protected virtual void OnVisitFolder(IProgressStatus monitor, string path, string domain, bool recursive) { if (!FileSystem.DirectoryExists(path)) { return; } var files = FileSystem.GetFiles(path).ToArray(); // First of all scan .addins files, since they can contain exclude paths. // Only extract the information, don't follow directory inclusions yet List <AddinsEntry> addinsFileEntries = new List <AddinsEntry>(); foreach (string file in files) { if (Path.GetExtension(file).EndsWith(".addins", StringComparison.Ordinal)) { addinsFileEntries.AddRange(ParseAddinsFile(monitor, file, domain)); } } // Now look for .addin files. Addin files must be processed before // assemblies, because they may add files to the ignore list (i.e., assemblies // included in .addin files won't be scanned twice). foreach (string file in files) { if (!ScanContext.IgnorePath(file) && (file.EndsWith(".addin.xml", StringComparison.Ordinal) || file.EndsWith(".addin", StringComparison.Ordinal))) { OnVisitAddinManifestFile(monitor, file); } } // Now scan assemblies. They can also add files to the ignore list. foreach (string file in files) { if (!ScanContext.IgnorePath(file)) { string ext = Path.GetExtension(file).ToLower(); if (ext == ".dll" || ext == ".exe") { OnVisitAssemblyFile(monitor, file); } } } // Follow .addins file inclusions foreach (var entry in addinsFileEntries) { string dir = entry.Folder; if (!Path.IsPathRooted(dir)) { dir = Path.GetFullPath(Path.Combine(path, entry.Folder)); } VisitFolderInternal(monitor, dir, entry.Domain, entry.Recursive); } // Scan subfolders if (recursive) { foreach (string sd in FileSystem.GetDirectories(path)) { VisitFolderInternal(monitor, sd, domain, true); } } }
List <AddinsEntry> ParseAddinsFile(IProgressStatus monitor, string file, string domain) { List <AddinsEntry> entries = new List <AddinsEntry>(); XmlTextReader r = null; string basePath = Path.GetDirectoryName(file); try { r = new XmlTextReader(FileSystem.OpenTextFile(file)); r.MoveToContent(); if (r.IsEmptyElement) { return(entries); } r.ReadStartElement(); r.MoveToContent(); while (r.NodeType != XmlNodeType.EndElement) { if (r.NodeType == XmlNodeType.Element && r.LocalName == "Directory") { bool.TryParse(r.GetAttribute("include-subdirs"), out var subs); string sdom; string share = r.GetAttribute("shared"); if (share == "true") { sdom = AddinDatabase.GlobalDomain; } else if (share == "false") { sdom = null; } else { sdom = domain; // Inherit the domain } string path = r.ReadElementString().Trim(); if (path.Length > 0) { path = Util.NormalizePath(path); entries.Add(new AddinsEntry { Folder = path, Domain = sdom, Recursive = subs }); } } else if (r.NodeType == XmlNodeType.Element && r.LocalName == "GacAssembly") { string aname = r.ReadElementString().Trim(); if (aname.Length > 0) { aname = Util.NormalizePath(aname); aname = Util.GetGacPath(aname); if (aname != null) { // Gac assemblies always use the global domain entries.Add(new AddinsEntry { Folder = aname, Domain = AddinDatabase.GlobalDomain }); } } } else if (r.NodeType == XmlNodeType.Element && r.LocalName == "Exclude") { string path = r.ReadElementString().Trim(); if (path.Length > 0) { path = Util.NormalizePath(path); if (!Path.IsPathRooted(path)) { path = Path.Combine(basePath, path); } ScanContext.AddPathToIgnore(Path.GetFullPath(path)); } } else { r.Skip(); } r.MoveToContent(); } } catch (Exception ex) { if (monitor != null) { monitor.ReportError("Could not process addins file: " + file, ex); } } finally { if (r != null) { r.Close(); } } return(entries); }