/// <summary> /// Same as <see cref="Normalize"/>, but skips full-path checking. /// s should be full path. If not full and not null/"", combines with current directory. /// </summary> internal static string Normalize_(string s, PNFlags flags = 0, bool noExpandEV = false) { if (!s.NE()) { if (!noExpandEV) { s = ExpandEnvVar(s); } Debug.Assert(!IsShellPath_(s) && !IsUrl(s)); if (_EndsWithDriveWithoutSep(s)) { s += "\\"; //API would append current directory } //note: although slower, call GetFullPathName always, not just when contains @"..\" etc. // Because it does many things (see Normalize doc), not all documented. // We still ~2 times faster than Path.GetFullPath. for (int na = 300; ;) { var b = Util.AMemoryArray.Char_(ref na); int nr = Api.GetFullPathName(s, na, b, null); if (nr > na) { na = nr; } else { if (nr > 0) { s = b.ToString(nr); } break; } } if (0 == (flags & PNFlags.DontExpandDosPath) && IsPossiblyDos_(s)) { s = ExpandDosPath_(s); } if (0 == (flags & PNFlags.DontRemoveEndSeparator)) { s = _AddRemoveSep(s); } else if (_EndsWithDriveWithoutSep(s)) { s += "\\"; } if (0 == (flags & PNFlags.DontPrefixLongPath)) { s = PrefixLongPathIfNeed(s); } } return(s); }
/// <summary> /// Makes normal full path from path that can contain special substrings etc. /// </summary> /// <param name="path">Any path.</param> /// <param name="defaultParentDirectory">If path is not full path, combine it with defaultParentDirectory to make full path.</param> /// <param name="flags"></param> /// <exception cref="ArgumentException">path is not full path, and <i>defaultParentDirectory</i> is not used or does not make it full path.</exception> /// <remarks> /// The sequence of actions: /// 1. If path starts with '%' character, expands environment variables and special folder names. See <see cref="ExpandEnvVar"/>. /// 2. If path is not full path but looks like URL, and used flag CanBeUrl, returns path. /// 3. If path is not full path, and defaultParentDirectory is not null/"", combines path with ExpandEnvVar(defaultParentDirectory). /// 4. If path is not full path, throws exception. /// 5. Calls API <msdn>GetFullPathName</msdn>. It replaces <c>'/'</c> with <c>'\\'</c>, replaces multiple <c>'\\'</c> with single (where need), processes <c>@"\.."</c> etc, trims spaces, etc. /// 6. If no flag DontExpandDosPath, if path looks like a short DOS path version (contains <c>'~'</c> etc), calls API <msdn>GetLongPathName</msdn>. It converts short DOS path to normal path, if possible, for example <c>@"c:\progra~1"</c> to <c>@"c:\program files"</c>. It is slow. It converts path only if the file exists. /// 7. If no flag DontRemoveEndSeparator, removes <c>'\\'</c> character at the end, unless it is like <c>@"C:\"</c>. /// 8. Appends <c>'\\'</c> character if ends with a drive name (eg <c>"C:"</c> -> <c>@"C:\"</c>). /// 9. If no flag DontPrefixLongPath, calls <see cref="PrefixLongPathIfNeed"/>, which adds <c>@"\\?\"</c> etc prefix if path is very long. /// /// Similar to <see cref="Path.GetFullPath"/>. Main differences: this function expands environment variables, does not support relative paths, supports <c>@"\\?\very long path"</c>, trims <c>'\\'</c> at the end if need, does not throw exceptions when it thinks that path is invalid (except when path is not full). /// </remarks> public static string Normalize(string path, string defaultParentDirectory = null, PNFlags flags = 0) { path = ExpandEnvVar(path); if (!IsFullPath(path)) //note: not EEV { if (0 != (flags & PNFlags.CanBeUrlOrShell)) { if (IsShellPath_(path) || IsUrl(path)) { return(path); } } if (defaultParentDirectory.NE()) { goto ge; } path = Combine_(ExpandEnvVar(defaultParentDirectory), path); if (!IsFullPath(path)) { goto ge; } } return(Normalize_(path, flags, true)); ge: throw new ArgumentException($"Not full path: '{path}'."); }