/// <summary> /// Returns true if the string is full path, like <c>@"C:\a\b.txt"</c> or <c>@"C:"</c> or <c>@"\\server\share\..."</c>: /// </summary> /// <param name="path">Any string. Can be null.</param> /// <remarks> /// Returns true if <i>path</i> matches one of these wildcard patterns: /// - <c>@"?:\*"</c> - local path, like <c>@"C:\a\b.txt"</c>. Here ? is A-Z, a-z. /// - <c>@"?:"</c> - drive name, like <c>@"C:"</c>. Here ? is A-Z, a-z. /// - <c>@"\\*"</c> - network path, like <c>@"\\server\share\..."</c>. Or has prefix <c>@"\\?\"</c>. /// /// Supports <c>'/'</c> characters too. /// /// Supports only file-system paths. Returns false if path is URL (<see cref="IsUrl"/>) or starts with <c>"::"</c>. /// /// If path starts with <c>"%environmentVariable%"</c>, shows warning and returns false. You should at first expand environment variables with <see cref="ExpandEnvVar"/> or instead use <see cref="IsFullPathExpandEnvVar"/>. /// </remarks> public static bool IsFullPath(string path) { var s = path; int len = s.Lenn(); if (len >= 2) { if (s[1] == ':' && AChar.IsAsciiAlpha(s[0])) { return(len == 2 || IsSepChar_(s[2])); //info: returns false if eg "c:abc" which means "abc" in current directory of drive "c:" } switch (s[0]) { case '\\': case '/': return(IsSepChar_(s[1])); case '%': #if true if (!ExpandEnvVar(s).Starts('%')) { AWarning.Write("Path starts with %environmentVariable%. Use APath.IsFullPathExpandEnvVar instead."); } #else s = ExpandEnvVar(s); //quite fast. 70% slower than just EnvVarExists_, but reliable. return(!s.Starts('%') && IsFullPath(s)); #endif break; } } return(false); }
/// <summary> /// Returns true if ends with ':' preceded by a drive letter, like "C:" or "more\C:", but not like "moreC:". /// </summary> /// <param name="s">Can be null.</param> /// <param name="length">Use when want to check drive at a middle, not at the end. Eg returns true if s is <c>@"C:\more"</c> and length is 2.</param> static bool _EndsWithDriveWithoutSep(string s, int length = -1) { if (s == null) { return(false); } int i = ((length < 0) ? s.Length : length) - 1; if (i < 1 || s[i] != ':') { return(false); } if (!AChar.IsAsciiAlpha(s[--i])) { return(false); } if (i > 0 && !IsSepChar_(s[i - 1])) { return(false); } return(true); }
/// <summary> /// Gets the length of the URL protocol name (also known as URI scheme) in string, including ':'. /// If the string does not start with a protocol name, returns 0. /// </summary> /// <param name="s">A URL or path or any string. Can be null.</param> /// <remarks> /// URL examples: <c>"http:"</c> (returns 5), <c>"http://www.x.com"</c> (returns 5), <c>"file:///path"</c> (returns 5), <c>"shell:etc"</c> (returns 6). /// /// The protocol can be unknown. The function just checks string format, which is an ASCII alpha character followed by one or more ASCII alpha-numeric, '.', '-', '+' characters, followed by ':' character. /// </remarks> public static int GetUrlProtocolLength(string s) { int len = (s == null) ? 0 : s.Length; if (len > 2 && AChar.IsAsciiAlpha(s[0]) && s[1] != ':') { for (int i = 1; i < len; i++) { var c = s[i]; if (c == ':') { return(i + 1); } if (!(AChar.IsAsciiAlphaDigit(c) || c == '.' || c == '-' || c == '+')) { break; } } } return(0); //info: API PathIsURL lies, like most shlwapi.dll functions. }
/// <summary> /// Returns true if pathOrFilename looks like a DOS filename or path. /// Examples: <c>"abcde~12"</c>, <c>"abcde~12.txt"</c>, <c>@"c:\path\abcde~12.txt"</c>, <c>"c:\abcde~12\path"</c>. /// </summary> /// <param name="s">Can be null.</param> internal static bool IsPossiblyDos_(string s) { //AOutput.Write(s); if (s != null && s.Length >= 8) { for (int i = 0; (i = s.IndexOf('~', i + 1)) > 0;) { int j = i + 1, k = 0; for (; k < 6 && j < s.Length; k++, j++) { if (!AChar.IsAsciiDigit(s[j])) { break; } } if (k == 0) { continue; } char c = j < s.Length ? s[j] : '\\'; if (c == '\\' || c == '/' || (c == '.' && j == s.Length - 4)) { for (j = i; j > 0; j--) { c = s[j - 1]; if (c == '\\' || c == '/') { break; } } if (j == i - (7 - k)) { return(true); } } } } return(false); }
/// <summary> /// Converts part of string to <see cref="KKey"/>. /// The substring should contain single key name, eg "Esc", "A", "=". /// Returns 0 if invalid key name. /// </summary> static unsafe KKey _KeynameToKey(string s, int i, int len) { //AOutput.Write(s, i, len); if (len < 1) { return(0); } char c = s[i]; //character keys, like K, 9, - if (len == 1) { if (c >= 'a' && c <= 'z') { return((KKey)(c - 32)); } if (c >= 'A' && c <= 'Z') { return((KKey)c); } if (c >= '0' && c <= '9') { return((KKey)c); } switch (c) { case '=': return(KKey.OemPlus); case '`': case '~': return(KKey.OemTilde); case '-': case '_': return(KKey.OemMinus); case '[': case '{': return(KKey.OemOpenBrackets); case ']': case '}': return(KKey.OemCloseBrackets); case '\\': case '|': return(KKey.OemPipe); case ';': case ':': return(KKey.OemSemicolon); case '\'': case '\"': return(KKey.OemQuotes); case ',': case '<': return(KKey.OemComma); case '.': case '>': return(KKey.OemPeriod); case '/': case '?': return(KKey.OemQuestion); } return(0); //special //+ //eg Ctrl+A //* //*nTimes, *down, *up //( //eg Alt+(A F) //) //# //numpad keys, eg #5, #* //$ //Shift+ //reserved //! @ % ^ & } //numpad keys if (c == '#') { if (len == 2) { switch (s[i + 1]) { case '.': return(KKey.Decimal); case '+': return(KKey.Add); case '/': return(KKey.Divide); case '*': return(KKey.Multiply); case '-': return(KKey.Subtract); case '0': return(KKey.NumPad0); case '1': return(KKey.NumPad1); case '2': return(KKey.NumPad2); case '3': return(KKey.NumPad3); case '4': return(KKey.NumPad4); case '5': return(KKey.NumPad5); case '6': return(KKey.NumPad6); case '7': return(KKey.NumPad7); case '8': return(KKey.NumPad8); case '9': return(KKey.NumPad9); } } return(0); } //F keys if (c == 'F' && AChar.IsAsciiDigit(s[i + 1])) { int n = s.ToInt(i + 1, out int e, STIFlags.NoHex); if (n > 0 && n <= 24 && e == i + len) { return((KKey)(0x6F + n)); } } //named keys //names start with an uppercase letter and must have at least 2 other anycase letters, except: Up, AltG (RAl), PageU (PgU), PageD (PgD), some alternative names (PU, PD, PB, PS, HM, SL, CL, NL, BS). KKey k = 0; char c1 = Char.ToLowerInvariant(s[i + 1]), //note: Util.Tables_.LowerCase would make startup slow c2 = len > 2 ? Char.ToLowerInvariant(s[i + 2]) : ' ', c3 = len > 3 ? Char.ToLowerInvariant(s[i + 3]) : ' ', c4 = len > 4 ? Char.ToLowerInvariant(s[i + 4]) : ' '; uint u = (uint)c1 << 16 | c2; switch (c) { case 'A': if (_U('l', 't')) { k = c3 == 'g' ? KKey.RAlt : KKey.Alt; } else if (_U('p', 'p')) { k = KKey.Apps; } break; case 'B': if (_U('a', 'c') || _U('s', ' ')) { k = KKey.Back; } break; case 'C': if (_U('t', 'r')) { k = KKey.Ctrl; } else if (_U('a', 'p') || _U('l', ' ')) { k = KKey.CapsLock; } break; case 'D': if (_U('e', 'l')) { k = KKey.Delete; } else if (_U('o', 'w')) { k = KKey.Down; } break; case 'E': if (_U('n', 't')) { k = KKey.Enter; } else if (_U('n', 'd')) { k = KKey.End; } else if (_U('s', 'c')) { k = KKey.Escape; } break; case 'H': if (_U('o', 'm') || _U('m', ' ')) { k = KKey.Home; } break; case 'I': if (_U('n', 's')) { k = KKey.Insert; } break; case 'L': if (_U('e', 'f')) { k = KKey.Left; } //don't need LShift etc break; case 'M': if (_U('e', 'n')) { k = KKey.Apps; } break; case 'N': if (_U('u', 'm') || _U('l', ' ')) { k = KKey.NumLock; } //for NumEnter use AKeys.Key((KKey.Enter, 0, true)) break; case 'P': if (_U('a', 'g') && c3 == 'e') { k = c4 == 'u' ? KKey.PageUp : (c4 == 'd' ? KKey.PageDown : 0); } else if (_U('g', 'u') || _U('u', ' ')) { k = KKey.PageUp; } else if (_U('g', 'd') || _U('d', ' ')) { k = KKey.PageDown; } else if (_U('a', 'u') || _U('b', ' ')) { k = KKey.Pause; } else if (_U('r', 'i') || _U('r', 't') || _U('s', ' ')) { k = KKey.PrintScreen; } break; case 'R': if (_U('i', 'g')) { k = KKey.Right; } else if (_U('a', 'l')) { k = KKey.RAlt; } else if (_U('c', 't')) { k = KKey.RCtrl; } else if (_U('s', 'h')) { k = KKey.RShift; } else if (_U('w', 'i')) { k = KKey.RWin; } break; case 'S': if (_U('h', 'i')) { k = KKey.Shift; } else if (_U('p', 'a')) { k = KKey.Space; } else if (_U('c', 'r') || _U('l', ' ')) { k = KKey.ScrollLock; } //SysRq not used on Windows break; case 'T': if (_U('a', 'b')) { k = KKey.Tab; } break; case 'U': if (c1 == 'p') { k = KKey.Up; } break; case 'V': if (c1 == 'k') { int v = s.ToInt(i + 2, out int end, STIFlags.DontSkipSpaces); if (end != i + len || (uint)v > 255) { v = 0; } return((KKey)v); } break; case 'W': if (_U('i', 'n')) { k = KKey.Win; } break; } if (k != 0) { for (int i2 = i + len; i < i2; i++) { if (!AChar.IsAsciiAlpha(s[i])) { return(0); } } return(k); } if (c >= 'A' && c <= 'Z') { var s1 = s.Substring(i, len); #if false if (Enum.TryParse(s1, true, out KKey r1)) { return(r1); } //if(Enum.TryParse(s1, true, out System.Windows.Forms.Keys r2) && (uint)r2 <= 0xff) return (KKey)r2; #else //20-50 times faster and less garbage. Good JIT speed. return(_FindKeyInEnums(s1)); #endif } return(0); bool _U(char cc1, char cc2) { return(u == ((uint)cc1 << 16 | cc2)); } }
/// <summary> /// Gets the length of the drive or network folder part in path, including its separator if any. /// If the string does not start with a drive or network folder path, returns 0 or prefix length (<c>@"\\?\"</c> or <c>@"\\?\UNC\"</c>). /// </summary> /// <param name="path">Full path or any string. Can be null. Should not be <c>@"%environmentVariable%\..."</c>.</param> /// <remarks> /// Supports prefixes <c>@"\\?\"</c> and <c>@"\\?\UNC\"</c>. /// Supports separators <c>'\\'</c> and <c>'/'</c>. /// </remarks> public static int GetRootLength(string path) { var s = path; int i = 0, len = (s == null) ? 0 : s.Length; if (len >= 2) { switch (s[1]) { case ':': if (AChar.IsAsciiAlpha(s[i])) { int j = i + 2; if (len == j) { return(j); } if (IsSepChar_(s[j])) { return(j + 1); } //else invalid } break; case '\\': case '/': if (IsSepChar_(s[0])) { i = _GetPrefixLength(s); if (i == 0) { i = 2; //no prefix } else if (i == 4) { if (len >= 6 && s[5] == ':') { goto case ':'; //like @"\\?\C:\..." } break; //invalid, no UNC } //else like @"\\?\UNC\server\share\..." int i0 = i, nSep = 0; for (; i < len && nSep < 2; i++) { char c = s[i]; if (IsSepChar_(c)) { nSep++; } else if (c == ':') { return(i0); } else if (c == '0') { break; } } } break; } } return(i); }