Forge
csharpdeeb25a2
1#nullable enable
2
3using CascadeIDE.Contracts;
4namespace CascadeIDE.Features.EnvironmentReadiness.DataAcquisition;
5
6/// <summary>
7/// Проверка существования исполняемого файла: явный путь или поиск по PATH (+ PATHEXT на Windows), как для NETCOREDBG_PATH (ADR 0023, DAL).
8/// </summary>
9[IoBoundary]
10public static class EnvironmentReadinessExecutablePathProbe
11{
12 /// <summary>Возвращает полный путь к существующему файлу или null.</summary>
13 public static string? TryResolveExecutablePath(string raw)
14 {
15 var t = raw.Trim();
16 if (t.Length == 0)
17 return null;
18
19 if (File.Exists(t))
20 return CanonicalFilePath.Normalize(t);
21
22 try
23 {
24 var full = CanonicalFilePath.Normalize(t);
25 if (File.Exists(full))
26 return full;
27 }
28 catch
29 {
30 return null;
31 }
32
33 if (!IsBareExecutableName(t))
34 return null;
35
36 return SearchPathForExecutable(t);
37 }
38
39 /// <summary>Имя команды без каталога (ищем в PATH).</summary>
40 public static bool IsBareExecutableName(string t)
41 {
42 if (t.Length == 0)
43 return false;
44 return t.IndexOf(Path.DirectorySeparatorChar) < 0 && t.IndexOf(Path.AltDirectorySeparatorChar) < 0;
45 }
46
47 private static string? SearchPathForExecutable(string fileName)
48 {
49 var pathVar = Environment.GetEnvironmentVariable("PATH");
50 if (string.IsNullOrEmpty(pathVar))
51 return null;
52
53 foreach (var dir in pathVar.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries))
54 {
55 var d = dir.Trim().Trim('"');
56 if (d.Length == 0)
57 continue;
58
59 foreach (var name in CandidateFileNames(fileName))
60 {
61 string p;
62 try
63 {
64 p = Path.Combine(d, name);
65 }
66 catch
67 {
68 continue;
69 }
70
71 if (File.Exists(p))
72 {
73 try
74 {
75 return CanonicalFilePath.Normalize(p);
76 }
77 catch
78 {
79 return p;
80 }
81 }
82 }
83 }
84
85 return null;
86 }
87
88 private static IEnumerable<string> CandidateFileNames(string fileName)
89 {
90 if (OperatingSystem.IsWindows() && !Path.HasExtension(fileName))
91 {
92 var pathext = Environment.GetEnvironmentVariable("PATHEXT");
93 if (!string.IsNullOrEmpty(pathext))
94 {
95 foreach (var ext in pathext.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
96 {
97 if (ext.Length > 0)
98 yield return fileName + ext;
99 }
100 }
101 else
102 {
103 yield return fileName + ".EXE";
104 yield return fileName + ".CMD";
105 yield return fileName + ".BAT";
106 }
107
108 yield return fileName;
109 }
110 else
111 {
112 yield return fileName;
113 }
114 }
115}
116
View only · write via MCP/CIDE