/// <summary> /// Takes a collection of directories and adds all parent directories within the GameData structure. /// </summary> /// <param name="directories">The collection of directory path strings to examine</param> public HashSet <string> AddParentDirectories(HashSet <string> directories) { if (directories == null || directories.Count == 0) { return(new HashSet <string>()); } var gameDir = KSPPathUtils.NormalizePath(ksp.GameDir()); return(directories .Where(dir => !string.IsNullOrWhiteSpace(dir)) // normalize all paths before deduplicate .Select(KSPPathUtils.NormalizePath) // remove any duplicate paths .Distinct() .SelectMany(dir => { var results = new HashSet <string>(); // adding in the DirectorySeparatorChar fixes attempts on Windows // to parse "X:" which resolves to Environment.CurrentDirectory var dirInfo = new DirectoryInfo(dir + Path.DirectorySeparatorChar); // if this is a parentless directory (Windows) // or if the Root equals the current directory (Mono) if (dirInfo.Parent == null || dirInfo.Root == dirInfo) { return results; } if (!dir.StartsWith(gameDir, StringComparison.CurrentCultureIgnoreCase)) { dir = KSPPathUtils.ToAbsolute(dir, gameDir); } // remove the system paths, leaving the path under the instance directory var relativeHead = KSPPathUtils.ToRelative(dir, gameDir); var pathArray = relativeHead.Split('/'); var builtPath = string.Empty; foreach (var path in pathArray) { builtPath += path + '/'; results.Add(KSPPathUtils.ToAbsolute(builtPath, gameDir)); } return results; }) .Where(dir => !IsReservedDirectory(dir)) .ToHashSet()); }
/// <summary> /// Given a path relative to this KSP's GameDir, returns the /// absolute path on the system. /// </summary> public string ToAbsoluteGameDir(string path) { return(KSPPathUtils.ToAbsolute(path, GameDir())); }
/// <summary> /// Uninstall the module provided. For internal use only. /// Use UninstallList for user queries, it also does dependency handling. /// This does *NOT* save the registry. /// </summary> private void Uninstall(string modName) { using (var transaction = CkanTransaction.CreateTransactionScope()) { InstalledModule mod = registry_manager.registry.InstalledModule(modName); if (mod == null) { log.ErrorFormat("Trying to uninstall {0} but it's not installed", modName); throw new ModNotInstalledKraken(modName); } // Walk our registry to find all files for this mod. IEnumerable <string> files = mod.Files; var directoriesToDelete = new HashSet <string>(); foreach (string file in files) { string path = ksp.ToAbsoluteGameDir(file); try { FileAttributes attr = File.GetAttributes(path); if ((attr & FileAttributes.Directory) == FileAttributes.Directory) { directoriesToDelete.Add(path); } else { log.InfoFormat("Removing {0}", file); file_transaction.Delete(path); } } catch (Exception ex) { // XXX: This is terrible, we're catching all exceptions. log.ErrorFormat("Failure in locating file {0} : {1}", path, ex.Message); } } // Remove from registry. registry_manager.registry.DeregisterModule(ksp, modName); // Sort our directories from longest to shortest, to make sure we remove child directories // before parents. GH #78. foreach (string directory in directoriesToDelete.OrderBy(dir => dir.Length).Reverse()) { if (!Directory.EnumerateFileSystemEntries(directory).Any()) { // Skip Ships/VAB ans Ships/SPH if (directory == KSPPathUtils.ToAbsolute("VAB", ksp.Ships()) || directory == KSPPathUtils.ToAbsolute("SPH", ksp.Ships())) { continue; } // We *don't* use our file_transaction to delete files here, because // it fails if the system's temp directory is on a different device // to KSP. However we *can* safely delete it now we know it's empty, // because the TxFileMgr *will* put it back if there's a file inside that // needs it. // // This works around GH #251. // The filesystem boundry bug is described in https://transactionalfilemgr.codeplex.com/workitem/20 log.InfoFormat("Removing {0}", directory); Directory.Delete(directory); } else { log.InfoFormat("Not removing directory {0}, it's not empty", directory); } } transaction.Complete(); } }