1 #Region "Microsoft.VisualBasic::a93697598f6f25ce85f4637f01afaf25, Microsoft.VisualBasic.Core\Language\Language\UnixBash\Shell\ls.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 Search
35     
36     '         Properties: SearchType, wildcards
37     
38     '         Function: Clone, ToString
39     '         Operators: (+3 Overloads) -, <, <<, (+2 Overloads) <=, >
40     '                    (+2 Overloads) >=
41     
42     '     Structure wildcardsCompatible
43     
44     '         FunctionIsMatch
45     
46     '     Structure SearchOpt
47     
48     '         Constructor: (+2 OverloadsSub New
49     '         FunctionToString
50     '         Enum Options
51     
52     '             Directory, Ext, LongName, None, Recursive
53     
54     
55     
56     '  
57     
58     
59     
60     
61     ' /********************************************************************************/
62
63 #End Region
64
65 Imports System.Runtime.CompilerServices
66 Imports System.Text.RegularExpressions
67 Imports Microsoft.VisualBasic.FileIO
68 Imports Microsoft.VisualBasic.Linq
69 Imports Microsoft.VisualBasic.Serialization.JSON
70 Imports Microsoft.VisualBasic.Text.Patterns
71 Imports SearchOptions = System.Int32
72
73 Namespace Language.UnixBash
74
75     Public Class Search : Implements ICloneable
76
77         ''' <summary>
78         ''' The search options
79         ''' </summary>
80         Dim __opts As New Dictionary(Of SearchOpt.Options, SearchOpt)
81
82         Public Overrides Function ToString() As String
83             Return __opts.GetJson
84         End Function
85
86         <MethodImpl(MethodImplOptions.AggressiveInlining)>
87         Public Function Clone() As Object Implements ICloneable.Clone
88             Return New Search With {
89                 .__opts = New Dictionary(Of SearchOpt.Options, SearchOpt)(__opts)
90             }
91         End Function
92
93         ''' <summary>
94         ''' Add a search options
95         ''' </summary>
96         ''' <param name="ls"></param>
97         ''' <param name="l"></param>
98         ''' <returns></returns>
99         Public Shared Operator -(ls As Search, l As SearchOpt) As Search
100             If l.opt <> SearchOpt.Options.None Then
101                 Dim clone As Search = DirectCast(ls.Clone, Search)
102                 Call clone.__opts.Add(l.opt, l)
103                 Return clone
104             Else
105                 Return ls
106             End If
107         End Operator
108
109         Public Shared Operator <<(ls As Search, opt As SearchOptions) As Search
110             If CType(opt, SearchOption) = SearchOption.SearchAllSubDirectories Then
111                 ls.__opts.Add(SearchOpt.Options.Recursive, r)
112             End If
113
114             Return ls
115         End Operator
116
117         ''' <summary>
118         ''' Add a set of wildcards patterns for path match
119         ''' </summary>
120         ''' <param name="ls"></param>
121         ''' <param name="wildcards$"></param>
122         ''' <returns></returns>
123         ''' 
124         <MethodImpl(MethodImplOptions.AggressiveInlining)>
125         Public Shared Operator -(ls As Search, wildcards$()) As Search
126             Return ls - ShellSyntax.wildcards(wildcards$)
127         End Operator
128
129         ''' <summary>
130         ''' Add wildcard pattern for path match
131         ''' </summary>
132         ''' <param name="ls"></param>
133         ''' <param name="wildcards$"></param>
134         ''' <returns></returns>
135         <MethodImpl(MethodImplOptions.AggressiveInlining)>
136         Public Shared Operator -(ls As Search, wildcards$) As Search
137             Return ls - wildcards.Split(","c)
138         End Operator
139
140         Public ReadOnly Property SearchType As FileIO.SearchOption
141             Get
142                 Dim opt As FileIO.SearchOption = FileIO.SearchOption.SearchTopLevelOnly
143                 If __opts.ContainsKey(SearchOpt.Options.Recursive) Then
144                     Return FileIO.SearchOption.SearchAllSubDirectories
145                 Else
146                     Return opt
147                 End If
148             End Get
149         End Property
150
151         Public ReadOnly Property wildcards As String()
152             Get
153                 If Not __opts.ContainsKey(SearchOpt.Options.Ext) Then
154                     Return Nothing
155                 Else
156                     Return __opts(SearchOpt.Options.Ext) _
157                         .wildcards _
158                         .ToArray
159                 End If
160             End Get
161         End Property
162
163         ''' <summary>
164         ''' Search the files in the specific directory
165         ''' </summary>
166         ''' <param name="ls"></param>
167         ''' <param name="DIR"></param>
168         ''' <returns></returns>
169         ''' 
170         <MethodImpl(MethodImplOptions.AggressiveInlining)>
171         Public Shared Operator <(ls As Search, DIR As StringAs IEnumerable(Of String)
172             Return ls <= DIR
173         End Operator
174
175         <MethodImpl(MethodImplOptions.AggressiveInlining)>
176         Public Overloads Shared Operator <=(ls As Search, directories As IEnumerable(Of String)) As IEnumerable(Of String)
177             Return directories _
178                 .SafeQuery _
179                 .Select(Function(dir) ls <= dir) _
180                 .IteratesALL
181         End Operator
182
183         ''' <summary>
184         ''' Search the files in the specific directory
185         ''' </summary>
186         ''' <param name="ls"></param>
187         ''' <param name="directory"></param>
188         ''' <returns></returns>
189         Public Overloads Shared Operator <=(ls As Search, directory$) As IEnumerable(Of String)
190             Dim l As Boolean = ls.__opts.ContainsKey(SearchOpt.Options.LongName)
191
192             If Not directory.DirectoryExists Then
193                 Call $"Directory {directory} is not valid on your file system!".Warning
194                 Return {}
195             End If
196
197             Dim wc$() = ls.wildcards
198             Dim isMatch As Func(Of StringBoolean) =
199                 AddressOf New wildcardsCompatible With {
200                     .regexp = If(wc.IsNullOrEmpty, {"*"}, wc)
201                 }.IsMatch
202             Dim list As IEnumerable(Of String)
203
204             With ls
205                 If .__opts.ContainsKey(SearchOpt.Options.Directory) Then
206                     list = directory.ListDirectory(.SearchType)
207                 Else
208                     list = directory.ReadDirectory(.SearchType)
209                 End If
210
211                 If .__opts.ContainsKey(SearchOpt.Options.Directory) Then
212                     If l Then
213                         Return list.Where(isMatch)
214                     Else
215                         Return list.Where(isMatch) _
216                             .Select(Function(s)
217                                         Return s.BaseName
218                                     End Function)
219                     End If
220                 Else
221                     If l Then
222                         Return list.Where(isMatch)
223                     Else
224                         Return From path As String
225                                In list
226                                Where isMatch(path)
227                                Let name As String = path.Replace("\""/") _
228                                                         .Split("/"c) _
229                                                         .Last
230                                Select name
231                     End If
232                 End If
233             End With
234         End Operator
235
236         Public Shared Operator >(ls As Search, DIR As StringAs IEnumerable(Of String)
237             Throw New NotSupportedException
238         End Operator
239
240         Public Shared Operator >=(ls As Search, DIR As StringAs IEnumerable(Of String)
241             Throw New NotSupportedException
242         End Operator
243
244         Public Shared Operator >=(ls As Search, dirs As IEnumerable(Of String)) As IEnumerable(Of String)
245             Throw New NotSupportedException
246         End Operator
247     End Class
248
249     ''' <summary>
250     ''' Using regular expression to find a match on the file name.
251     ''' </summary>
252     Public Structure wildcardsCompatible
253
254         Dim regexp As String()
255         ''' <summary>
256         ''' Using the regexp engine instead of the wildcard match engine?
257         ''' </summary>
258         Dim usingRegexp As Boolean
259
260         ''' <summary>
261         ''' Windows系统上面文件路径不区分大小写,但是Linux、Mac系统却区分大小写
262         ''' 所以使用这个来保持对Windows文件系统的兼容性
263         ''' </summary>
264         Shared ReadOnly opt As RegexOptions =
265             If(App.Platform = PlatformID.MacOSX OrElse
266                App.Platform = PlatformID.Unix,
267                RegexOptions.Singleline, RegexICSng)
268         Shared ReadOnly pathIgnoreCase As Boolean = App.IsMicrosoftPlatform
269
270         ''' <summary>
271         ''' Linux/Mac系统不支持Windows系统的通配符,所以在这里是用正则表达式来保持代码的兼容性
272         ''' </summary>
273         ''' <param name="path"></param>
274         ''' <returns></returns>
275         Public Function IsMatch(path As StringAs Boolean
276             If regexp.IsNullOrEmpty Then
277                 ' 匹配的规则是空的,则默认是允许所有的路径
278                 Return True
279             End If
280
281             Dim name As String = path.Replace("\""/").Split("/"c).Last
282
283             If usingRegexp Then
284                 For Each r As String In regexp
285                     If Regex.Match(name, r, opt).Success Then
286                         Return True
287                     End If
288                 Next
289             Else
290                 For Each r As String In regexp
291                     If WildcardsExtension.WildcardMatch(r, name, pathIgnoreCase) Then
292                         Return True
293                     End If
294                 Next
295             End If
296
297             Return False
298         End Function
299     End Structure
300
301     Public Structure SearchOpt
302
303         Dim opt As Options
304         Dim value As String
305         Dim wildcards As List(Of String)
306
307         Sub New(opt As Options, s As String)
308             Me.opt = opt
309             Me.value = s
310             Me.wildcards = New List(Of String)
311         End Sub
312
313         Sub New(opt As Options)
314             Call Me.New(opt, "")
315         End Sub
316
317         Public Overrides Function ToString() As String
318             Return Me.GetJson
319         End Function
320
321         Public Enum Options
322             None
323             Ext
324             LongName
325             ''' <summary>
326             ''' List directories, not files listing.
327             ''' </summary>
328             Directory
329             ''' <summary>
330             ''' 递归搜索所有的文件夹
331             ''' </summary>
332             Recursive
333         End Enum
334     End Structure
335 End Namespace