1 #Region "Microsoft.VisualBasic::69ea48bcfccd3737ab9c2660a4589dd6, Microsoft.VisualBasic.Core\Extensions\IO\Path\ProgramPathSearchTool.vb"
2
3     ' Author:
4     
5     '       asuka (amethyst.asuka@gcmodeller.org)
6     '       xie (genetics@smrucc.org)
7     '       xieguigang (xie.guigang@live.com)
8     
9     ' Copyright (c) 2018 GPL3 Licensed
10     
11     
12     ' GNU GENERAL PUBLIC LICENSE (GPL3)
13     
14     
15     ' This program is free software: you can redistribute it and/or modify
16     ' it under the terms of the GNU General Public License as published by
17     ' the Free Software Foundation, either version 3 of the License, or
18     ' (at your option) any later version.
19     
20     ' This program is distributed in the hope that it will be useful,
21     ' but WITHOUT ANY WARRANTY; without even the implied warranty of
22     ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23     ' GNU General Public License for more details.
24     
25     ' You should have received a copy of the GNU General Public License
26     ' along with this program. If not, see <http://www.gnu.org/licenses/>.
27
28
29
30     ' /********************************************************************************/
31
32     ' Summaries:
33
34     '     Class ProgramPathSearchTool
35     
36     '         Properties: CustomDirectories, Directories
37     
38     '         Constructor: (+1 OverloadsSub New
39     '         Function: BranchRule, FindProgram, FindScript, safeGetFiles, SearchDirectory
40     '                   SearchDrive, searchImpl, SearchProgram, SearchScriptFile
41     
42     
43     ' /********************************************************************************/
44
45 #End Region
46
47 Imports System.Collections.ObjectModel
48 Imports System.ComponentModel
49 Imports System.IO
50 Imports System.Runtime.CompilerServices
51 Imports System.Text.RegularExpressions
52 Imports Microsoft.VisualBasic.CommandLine.Reflection
53 Imports Microsoft.VisualBasic.Language
54 Imports Microsoft.VisualBasic.Language.Default
55 Imports Microsoft.VisualBasic.Language.UnixBash
56 Imports Microsoft.VisualBasic.Linq
57 Imports ENV = System.Environment
58 Imports r = System.Text.RegularExpressions.Regex
59
60 Namespace FileIO.Path
61
62     ''' <summary>
63     ''' 
64     ''' </summary>
65     ''' <remarks>
66     ''' Works on windows, have not test on Linux/Mac yet.
67     ''' </remarks>
68     Public Class ProgramPathSearchTool
69
70         ReadOnly Environments As New List(Of String) From {"ProgramFiles(x86)""ProgramFiles"}
71
72         Const VERSION As String = "[-_`~.]\d+(\.\d+)*"
73         Const TopDirectory As SearchOption = SearchOption.SearchTopLevelOnly
74
75         Public ReadOnly Property Directories As IReadOnlyCollection(Of String)
76             <MethodImpl(MethodImplOptions.AggressiveInlining)>
77             Get
78                 Return Environments _
79                     .Select(Function(var) ENV.GetEnvironmentVariable(var)) _
80                     .Join(CustomDirectories.SafeQuery) _
81                     .Where(Function(dir)
82                                Return (Not dir.StringEmpty) AndAlso dir.DirectoryExists
83                            End Function) _
84                     .ToArray
85             End Get
86         End Property
87
88         Public Property CustomDirectories As String()
89
90         ''' <summary>
91         ''' 
92         ''' </summary>
93         ''' <param name="ENV$">
94         ''' 环境变量名称列表,如果不赋值这个参数,则会默认使用Program Files和Program Files(x86)这两个文件夹
95         ''' </param>
96         Sub New(ParamArray ENV$())
97             Environments += ENV
98         End Sub
99
100         Public Iterator Function FindScript(keyword$, Optional withExtension$ = NothingAs IEnumerable(Of String)
101             For Each dir As String In Directories
102                 For Each file As String In SearchScriptFile(dir, keyword, withExtension)
103                     Yield file
104                 Next
105             Next
106         End Function
107
108         ''' <summary>
109         ''' 
110         ''' </summary>
111         ''' <param name="keyword$">常规的文件名称之类的,不可以包含有通配符</param>
112         ''' <param name="includeDll"></param>
113         ''' <returns></returns>
114         Public Iterator Function FindProgram(keyword$, Optional includeDll As Boolean = TrueAs IEnumerable(Of String)
115             For Each dir As String In Directories
116                 For Each file As String In SearchProgram(dir, keyword, includeDll)
117                     Yield file
118                 Next
119                 For Each file As String In SearchProgram($"{dir}/{keyword}", keyword, includeDll)
120                     Yield file
121                 Next
122             Next
123         End Function
124
125 #Region "Search Implementation Internal"
126
127         ''' <summary>
128         ''' 商标搜索规则
129         ''' </summary>
130         ''' <param name="ProgramFiles"></param>
131         ''' <param name="Keyword"></param>
132         ''' <returns></returns>
133         ''' <remarks></remarks>
134         Private Shared Function BranchRule(programFiles$, keyword$) As IEnumerable(Of String)
135             Dim programFilesDirectories = FileSystem.GetDirectories(programFiles, TopDirectory, keyword)
136             Dim fsObjs As New List(Of String)
137
138             For Each dir As String In programFilesDirectories
139                 fsObjs += FileSystem.GetDirectories(dir, TopDirectory)
140             Next
141
142             Call fsObjs.Add(programFilesDirectories.ToArray)
143
144             If fsObjs = 0 Then
145                 ' 这个应用程序的安装文件夹可能是带有版本号标记的
146                 Dim dirs = FileSystem.GetDirectories(programFiles, TopDirectory)
147                 Dim version As String = keyword & ProgramPathSearchTool.VERSION
148                 Dim patterns$() = LinqAPI.Exec(Of String) _
149  _
150                     () <= From DIR As String
151                           In dirs
152                           Let name As String = FileSystem.GetDirectoryInfo(DIR).Name
153                           Let match = r.Match(name, version, RegexOptions.IgnoreCase)
154                           Where match.Success
155                           Select DIR
156
157                 Call fsObjs.Add(patterns)
158             End If
159
160             Return fsObjs
161         End Function
162
163         ''' <summary>
164         ''' Search for the path of a script file with a specific extension name.
165         ''' </summary>
166         ''' <param name="DIR"></param>
167         ''' <param name="Keyword"></param>
168         ''' <param name="withExtension">脚本文件的文件拓展名</param>
169         ''' <returns></returns>
170         ''' <remarks></remarks>
171         '''
172         <ExportAPI("Search.Scripts"Info:="Search for the path of a script file with a specific extension name.")>
173         Public Shared Function SearchScriptFile(dir$, keyword$, Optional withExtension$ = NothingAs IEnumerable(Of String)
174             Dim scriptFileNameRule$ = $"*{keyword}*{withExtension}"
175             Dim extNameAssert As Assert(Of String)
176
177             If withExtension.StringEmpty Then
178                 extNameAssert = Function(path) path.ExtensionSuffix.StringEmpty
179             Else
180                 extNameAssert = Function(path) True
181             End If
182
183             Return searchImpl(dir, {scriptFileNameRule}).Where(Function(file) extNameAssert(file))
184         End Function
185
186         Private Shared Function safeGetFiles(dir$, rules$()) As IEnumerable(Of String)
187             If dir.DirectoryExists Then
188                 Return FileSystem.GetFiles(dir, TopDirectory, rules)
189             Else
190                 Return {}
191             End If
192         End Function
193
194         Private Shared Iterator Function searchImpl(dir$, rules$()) As IEnumerable(Of String)
195             Dim files As IEnumerable(Of String) = safeGetFiles(dir, rules)
196             Dim binDIR As String = $"{dir}/bin/"
197             Dim programDIR As String = $"{dir}/Program"
198             Dim scriptsDIR As String = $"{dir}/scripts"
199
200             For Each folder As String In {binDIR, programDIR, scriptsDIR}
201                 If FileSystem.DirectoryExists(folder) Then
202                     For Each file As String In ls - l - rules <= folder
203                         Yield file
204                     Next
205                 End If
206             Next
207
208             For Each file As String In files
209                 Yield file
210             Next
211         End Function
212
213         ''' <summary>
214         ''' Search for the directories which its name was matched the keyword pattern.
215         ''' </summary>
216         ''' <param name="SpecificDrive">所指定进行搜索的驱动器,假若希望搜索整个硬盘,请留空字符串</param>
217         ''' <returns></returns>
218         ''' <remarks></remarks>
219         '''
220         <ExportAPI("DIR.Search.Program_Directory")>
221         <Description("Search for the directories which its name was matched the keyword pattern.")>
222         Public Shared Iterator Function SearchDirectory(keyword$, Optional specificDrive$ = NothingAs IEnumerable(Of String)
223             Dim drives As ReadOnlyCollection(Of DriveInfo)
224
225             If String.IsNullOrEmpty(specificDrive) Then
226                 drives = FileSystem.Drives
227             Else
228                 drives = New ReadOnlyCollection(Of DriveInfo)({FileSystem.GetDriveInfo(specificDrive)})
229             End If
230
231             For Each drive As DriveInfo In drives
232                 For Each dir As String In SearchDrive(drive, keyword)
233                     Yield dir
234                 Next
235             Next
236         End Function
237
238         Private Shared Function SearchDrive(drive As DriveInfo, keyword As StringAs String()
239             If Not drive.IsReady Then
240                 Return New String() {}
241             End If
242
243             Dim driveName$ = drive.RootDirectory.FullName
244             Dim driveRoot = FileSystem.GetDirectories(driveName, SearchOption.SearchTopLevelOnly, keyword)
245             Dim files As New List(Of String)
246             Dim ProgramFiles As String = String.Format("{0}/Program Files", drive.RootDirectory.FullName)
247
248             If FileSystem.DirectoryExists(ProgramFiles) Then
249                 Call files.AddRange(BranchRule(ProgramFiles, keyword))
250             End If
251
252             Dim ProgramFilesX86 = String.Format("{0}/Program Files(x86)", drive.RootDirectory.FullName)
253
254             If FileSystem.DirectoryExists(ProgramFilesX86) Then
255                 Call files.AddRange(BranchRule(ProgramFilesX86, keyword))
256             End If
257
258             Call files.AddRange(driveRoot)
259             Call files.AddRange(driveRoot.Select(Function(rootDir) BranchRule(rootDir, keyword)).Unlist)
260
261             Return files.ToArray
262         End Function
263
264         ''' <summary>
265         ''' Invoke the search session for the program file using a specific keyword string value.
266         ''' (使用某个关键词来搜索目标应用程序)
267         ''' </summary>
268         ''' <param name="DIR"></param>
269         ''' <param name="Keyword"></param>
270         ''' <returns></returns>
271         ''' <remarks></remarks>
272         <ExportAPI("File.Search.Program")>
273         <Description("Invoke the search session for the program file using a specific keyword string value.")>
274         Public Shared Function SearchProgram(dir$, keyword$, Optional includeDll As Boolean = TrueAs IEnumerable(Of String)
275             Dim exeNameRule As String = $"*{keyword}*.exe"
276             Dim dllNameRule As String = $"*{keyword}*.dll"
277             Dim rules$()
278
279             If includeDll Then
280                 rules = {exeNameRule, dllNameRule}
281             Else
282                 rules = {exeNameRule}
283             End If
284
285             Return searchImpl(dir, rules)
286         End Function
287 #End Region
288
289     End Class
290 End Namespace