1
+ using CommunityToolkit . Mvvm . DependencyInjection ;
2
+ using Files . App . Contexts ;
1
3
using Files . App . Extensions ;
2
4
using Files . App . Filesystem . StorageItems ;
3
5
using Files . App . Helpers ;
4
6
using Files . App . Views ;
5
7
using Files . Shared . Extensions ;
6
8
using System ;
7
9
using System . Collections . Generic ;
10
+ using System . Collections . Immutable ;
8
11
using System . IO ;
9
12
using System . Linq ;
13
+ using System . Text ;
10
14
using System . Threading . Tasks ;
11
15
using Windows . Storage ;
12
16
using Windows . Storage . Search ;
@@ -15,12 +19,17 @@ namespace Files.App.Filesystem
15
19
{
16
20
public static class StorageFileExtensions
17
21
{
18
- public static BaseStorageFile AsBaseStorageFile ( this IStorageItem item )
22
+ private const int SINGLE_DOT_DIRECTORY_LENGTH = 2 ;
23
+ private const int DOUBLE_DOT_DIRECTORY_LENGTH = 3 ;
24
+
25
+ public static readonly ImmutableHashSet < string > _ftpPaths =
26
+ new HashSet < string > ( ) { "ftp:/" , "ftps:/" , "ftpes:/" } . ToImmutableHashSet ( ) ;
27
+
28
+ public static BaseStorageFile ? AsBaseStorageFile ( this IStorageItem item )
19
29
{
20
30
if ( item is null || ! item . IsOfType ( StorageItemTypes . File ) )
21
- {
22
31
return null ;
23
- }
32
+
24
33
return item is StorageFile file ? ( BaseStorageFile ) file : item as BaseStorageFile ;
25
34
}
26
35
@@ -55,7 +64,8 @@ public static bool AreItemsInSameDrive(this IEnumerable<string> itemsPath, strin
55
64
{
56
65
try
57
66
{
58
- return itemsPath . Any ( itemPath => Path . GetPathRoot ( itemPath ) . Equals ( Path . GetPathRoot ( destinationPath ) , StringComparison . OrdinalIgnoreCase ) ) ;
67
+ var destinationRoot = Path . GetPathRoot ( destinationPath ) ;
68
+ return itemsPath . Any ( itemPath => Path . GetPathRoot ( itemPath ) . Equals ( destinationRoot , StringComparison . OrdinalIgnoreCase ) ) ;
59
69
}
60
70
catch
61
71
{
@@ -71,7 +81,8 @@ public static bool AreItemsAlreadyInFolder(this IEnumerable<string> itemsPath, s
71
81
{
72
82
try
73
83
{
74
- return itemsPath . All ( itemPath => Path . GetDirectoryName ( itemPath ) . Equals ( destinationPath . TrimPath ( ) , StringComparison . OrdinalIgnoreCase ) ) ;
84
+ var trimmedPath = destinationPath . TrimPath ( ) ;
85
+ return itemsPath . All ( itemPath => Path . GetDirectoryName ( itemPath ) . Equals ( trimmedPath , StringComparison . OrdinalIgnoreCase ) ) ;
75
86
}
76
87
catch
77
88
{
@@ -83,23 +94,11 @@ public static bool AreItemsAlreadyInFolder(this IEnumerable<IStorageItem> storag
83
94
public static bool AreItemsAlreadyInFolder ( this IEnumerable < IStorageItemWithPath > storageItems , string destinationPath )
84
95
=> storageItems . Select ( x => x . Path ) . AreItemsAlreadyInFolder ( destinationPath ) ;
85
96
86
- public static BaseStorageFolder AsBaseStorageFolder ( this IStorageItem item )
97
+ public static BaseStorageFolder ? AsBaseStorageFolder ( this IStorageItem item )
87
98
{
88
- if ( item is null )
89
- {
90
- return null ;
91
- }
92
- else if ( item . IsOfType ( StorageItemTypes . Folder ) )
93
- {
94
- if ( item is StorageFolder folder )
95
- {
96
- return ( BaseStorageFolder ) folder ;
97
- }
98
- else
99
- {
100
- return item as BaseStorageFolder ;
101
- }
102
- }
99
+ if ( item is not null && item . IsOfType ( StorageItemTypes . Folder ) )
100
+ return item is StorageFolder folder ? ( BaseStorageFolder ) folder : item as BaseStorageFolder ;
101
+
103
102
return null ;
104
103
}
105
104
@@ -110,9 +109,7 @@ public static List<PathBoxItem> GetDirectoryPathComponents(string value)
110
109
if ( value . Contains ( '/' , StringComparison . Ordinal ) )
111
110
{
112
111
if ( ! value . EndsWith ( '/' ) )
113
- {
114
112
value += "/" ;
115
- }
116
113
}
117
114
else if ( ! value . EndsWith ( '\\ ' ) )
118
115
{
@@ -133,10 +130,8 @@ public static List<PathBoxItem> GetDirectoryPathComponents(string value)
133
130
134
131
var component = value . Substring ( lastIndex , i - lastIndex ) ;
135
132
var path = value . Substring ( 0 , i + 1 ) ;
136
- if ( ! new [ ] { "ftp:/" , "ftps:/" , "ftpes:/" } . Contains ( path , StringComparer . OrdinalIgnoreCase ) )
137
- {
133
+ if ( ! _ftpPaths . Contains ( path , StringComparer . OrdinalIgnoreCase ) )
138
134
pathBoxItems . Add ( GetPathItem ( component , path ) ) ;
139
- }
140
135
141
136
lastIndex = i + 1 ;
142
137
}
@@ -145,29 +140,10 @@ public static List<PathBoxItem> GetDirectoryPathComponents(string value)
145
140
return pathBoxItems ;
146
141
}
147
142
148
- public static string GetPathWithoutEnvironmentVariable ( string path )
143
+ public static string GetResolvedPath ( string path , bool isFtp )
149
144
{
150
- if ( path . StartsWith ( "~\\ " , StringComparison . Ordinal ) )
151
- {
152
- path = $ "{ CommonPaths . HomePath } { path . Remove ( 0 , 1 ) } ";
153
- }
154
- if ( path . Contains ( "%temp%" , StringComparison . OrdinalIgnoreCase ) )
155
- {
156
- path = path . Replace ( "%temp%" , CommonPaths . TempPath , StringComparison . OrdinalIgnoreCase ) ;
157
- }
158
- if ( path . Contains ( "%tmp%" , StringComparison . OrdinalIgnoreCase ) )
159
- {
160
- path = path . Replace ( "%tmp%" , CommonPaths . TempPath , StringComparison . OrdinalIgnoreCase ) ;
161
- }
162
- if ( path . Contains ( "%localappdata%" , StringComparison . OrdinalIgnoreCase ) )
163
- {
164
- path = path . Replace ( "%localappdata%" , CommonPaths . LocalAppDataPath , StringComparison . OrdinalIgnoreCase ) ;
165
- }
166
- if ( path . Contains ( "%homepath%" , StringComparison . OrdinalIgnoreCase ) )
167
- {
168
- path = path . Replace ( "%homepath%" , CommonPaths . HomePath , StringComparison . OrdinalIgnoreCase ) ;
169
- }
170
- return Environment . ExpandEnvironmentVariables ( path ) ;
145
+ var withoutEnvirnment = GetPathWithoutEnvironmentVariable ( path ) ;
146
+ return ResolvePath ( withoutEnvirnment , isFtp ) ;
171
147
}
172
148
173
149
public async static Task < BaseStorageFile > DangerousGetFileFromPathAsync
@@ -280,9 +256,7 @@ public async static Task<IList<StorageFolderWithPath>> GetFoldersWithPathAsync
280
256
( this StorageFolderWithPath parentFolder , string nameFilter , uint maxNumberOfItems = uint . MaxValue )
281
257
{
282
258
if ( parentFolder is null )
283
- {
284
259
return null ;
285
- }
286
260
287
261
var queryOptions = new QueryOptions
288
262
{
@@ -327,5 +301,104 @@ private static PathBoxItem GetPathItem(string component, string path)
327
301
} ;
328
302
}
329
303
}
304
+
305
+ private static string GetPathWithoutEnvironmentVariable ( string path )
306
+ {
307
+ if ( path . StartsWith ( "~\\ " , StringComparison . Ordinal ) )
308
+ path = $ "{ CommonPaths . HomePath } { path . Remove ( 0 , 1 ) } ";
309
+
310
+ path = path . Replace ( "%temp%" , CommonPaths . TempPath , StringComparison . OrdinalIgnoreCase ) ;
311
+
312
+ path = path . Replace ( "%tmp%" , CommonPaths . TempPath , StringComparison . OrdinalIgnoreCase ) ;
313
+
314
+ path = path . Replace ( "%localappdata%" , CommonPaths . LocalAppDataPath , StringComparison . OrdinalIgnoreCase ) ;
315
+
316
+ path = path . Replace ( "%homepath%" , CommonPaths . HomePath , StringComparison . OrdinalIgnoreCase ) ;
317
+
318
+ return Environment . ExpandEnvironmentVariables ( path ) ;
319
+ }
320
+
321
+ private static string ResolvePath ( string path , bool isFtp )
322
+ {
323
+ if ( path . StartsWith ( "Home" ) )
324
+ return "Home" ;
325
+
326
+ var pathBuilder = new StringBuilder ( path ) ;
327
+ var lastPathIndex = path . Length - 1 ;
328
+ var separatorChar = isFtp || path . Contains ( '/' , StringComparison . Ordinal ) ? '/' : '\\ ' ;
329
+ var rootIndex = isFtp ? FtpHelpers . GetRootIndex ( path ) + 1 : path . IndexOf ( $ ":{ separatorChar } ", StringComparison . Ordinal ) + 2 ;
330
+
331
+ for ( int i = 0 , lastIndex = 0 ; i < pathBuilder . Length ; i ++ )
332
+ {
333
+ if ( pathBuilder [ i ] is not '?' &&
334
+ pathBuilder [ i ] != Path . DirectorySeparatorChar &&
335
+ pathBuilder [ i ] != Path . AltDirectorySeparatorChar &&
336
+ i != lastIndex )
337
+ continue ;
338
+
339
+ if ( lastIndex == i )
340
+ {
341
+ ++ lastIndex ;
342
+ continue ;
343
+ }
344
+
345
+ var component = pathBuilder . ToString ( ) . Substring ( lastIndex , i - lastIndex ) ;
346
+ if ( component is ".." )
347
+ {
348
+ if ( lastIndex is 0 )
349
+ {
350
+ SetCurrentWorkingDirectory ( pathBuilder , separatorChar , lastIndex , ref i ) ;
351
+ }
352
+ else if ( lastIndex == rootIndex )
353
+ {
354
+ pathBuilder . Remove ( lastIndex , DOUBLE_DOT_DIRECTORY_LENGTH ) ;
355
+ i = lastIndex - 1 ;
356
+ }
357
+ else
358
+ {
359
+ var directoryIndex = pathBuilder . ToString ( ) . LastIndexOf (
360
+ separatorChar ,
361
+ lastIndex - DOUBLE_DOT_DIRECTORY_LENGTH ) ;
362
+
363
+ if ( directoryIndex is not - 1 )
364
+ {
365
+ pathBuilder . Remove ( directoryIndex , i - directoryIndex ) ;
366
+ i = directoryIndex ;
367
+ }
368
+ }
369
+
370
+ lastPathIndex = pathBuilder . Length - 1 ;
371
+ }
372
+ else if ( component is "." )
373
+ {
374
+ if ( lastIndex is 0 )
375
+ {
376
+ SetCurrentWorkingDirectory ( pathBuilder , separatorChar , lastIndex , ref i ) ;
377
+ }
378
+ else
379
+ {
380
+ pathBuilder . Remove ( lastIndex , SINGLE_DOT_DIRECTORY_LENGTH ) ;
381
+ i -= 3 ;
382
+ }
383
+ lastPathIndex = pathBuilder . Length - 1 ;
384
+ }
385
+
386
+ lastIndex = i + 1 ;
387
+ }
388
+
389
+ return pathBuilder . ToString ( ) ;
390
+ }
391
+
392
+ private static void SetCurrentWorkingDirectory ( StringBuilder path , char separator , int substringIndex , ref int i )
393
+ {
394
+ var context = Ioc . Default . GetRequiredService < IContentPageContext > ( ) ;
395
+ var subPath = path . ToString ( ) . Substring ( substringIndex ) ;
396
+
397
+ path . Clear ( ) ;
398
+ path . Append ( context . ShellPage ? . FilesystemViewModel . WorkingDirectory ) ;
399
+ path . Append ( separator ) ;
400
+ path . Append ( subPath ) ;
401
+ i = - 1 ;
402
+ }
330
403
}
331
404
}
0 commit comments