2
2
Copyright (c) Microsoft Corporation. All rights reserved.
3
3
--********************************************************************/
4
4
5
+ using Microsoft . PowerShell . Commands ;
5
6
using System . Collections ;
6
7
using System . Collections . ObjectModel ;
8
+ using System . IO ;
9
+ using System . Linq ;
7
10
using System . Management . Automation . Internal ;
8
11
using System . Text ;
9
12
10
13
namespace System . Management . Automation
11
14
{
15
+ using Language ;
16
+
12
17
/// <summary>
13
18
/// The parameter binder for native commands.
14
19
/// </summary>
@@ -79,7 +84,7 @@ internal void BindParameters(Collection<CommandParameterInternal> parameters)
79
84
if ( parameter . ParameterNameSpecified )
80
85
{
81
86
Diagnostics . Assert ( parameter . ParameterText . IndexOf ( ' ' ) == - 1 , "Parameters cannot have whitespace" ) ;
82
- _arguments . Append ( parameter . ParameterText ) ;
87
+ PossiblyGlobArg ( parameter . ParameterText , usedQuotes : false ) ;
83
88
84
89
if ( parameter . SpaceAfterParameter )
85
90
{
@@ -105,9 +110,23 @@ internal void BindParameters(Collection<CommandParameterInternal> parameters)
105
110
// windbg -k com:port=\\devbox\pipe\debug,pipe,resets=0,reconnect
106
111
// The parser produced an array of strings but marked the parameter so we
107
112
// can properly reconstruct the correct command line.
113
+ bool usedQuotes = false ;
114
+ var pAst = parameter . ArgumentAst ;
115
+ if ( pAst != null )
116
+ {
117
+ if ( pAst is StringConstantExpressionAst sce )
118
+ {
119
+ usedQuotes = sce . StringConstantType != StringConstantType . BareWord ;
120
+ }
121
+ else if ( pAst is ExpandableStringExpressionAst ese )
122
+ {
123
+ usedQuotes = ese . StringConstantType != StringConstantType . BareWord ;
124
+ }
125
+ }
108
126
appendOneNativeArgument ( Context , argValue ,
109
127
parameter . ArrayIsSingleArgumentForNativeCommand ? ',' : ' ' ,
110
- sawVerbatimArgumentMarker ) ;
128
+ sawVerbatimArgumentMarker ,
129
+ usedQuotes ) ;
111
130
}
112
131
}
113
132
}
@@ -141,7 +160,8 @@ internal String Arguments
141
160
/// <param name="obj">The object to append</param>
142
161
/// <param name="separator">A space or comma used when obj is enumerable</param>
143
162
/// <param name="sawVerbatimArgumentMarker">true if the argument occurs after --%</param>
144
- private void appendOneNativeArgument ( ExecutionContext context , object obj , char separator , bool sawVerbatimArgumentMarker )
163
+ /// <param name="usedQuotes">True if the argument was a quoted string (single or double)</param>
164
+ private void appendOneNativeArgument ( ExecutionContext context , object obj , char separator , bool sawVerbatimArgumentMarker , bool usedQuotes )
145
165
{
146
166
IEnumerator list = LanguagePrimitives . GetEnumerator ( obj ) ;
147
167
bool needSeparator = false ;
@@ -207,105 +227,103 @@ private void appendOneNativeArgument(ExecutionContext context, object obj, char
207
227
}
208
228
else
209
229
{
230
+ PossiblyGlobArg ( arg , usedQuotes ) ;
231
+ }
232
+ }
233
+ }
234
+ } while ( list != null ) ;
235
+ }
236
+
237
+ /// <summary>
238
+ /// On Windows, just append <paramref name="arg"/>.
239
+ /// On Unix, do globbing as appropriate, otherwise just append <paramref name="arg"/>.
240
+ /// </summary>
241
+ /// <param name="arg">The argument that possibly needs expansion</param>
242
+ /// <param name="usedQuotes">True if the argument was a quoted string (single or double)</param>
243
+ private void PossiblyGlobArg ( string arg , bool usedQuotes )
244
+ {
245
+ var argExpanded = false ;
246
+
210
247
#if UNIX
211
- // On UNIX systems, we expand arguments containing wildcard expressions against
212
- // the file system just like bash, etc.
213
- if ( System . Management . Automation . WildcardPattern . ContainsWildcardCharacters ( arg ) )
214
- {
215
- // See if the current working directory is a filesystem provider location
216
- // We won't do the expansion if it isn't since native commands can only access the file system.
217
- var cwdinfo = Context . EngineSessionState . CurrentLocation ;
248
+ // On UNIX systems, we expand arguments containing wildcard expressions against
249
+ // the file system just like bash, etc.
250
+ if ( ! usedQuotes && WildcardPattern . ContainsWildcardCharacters ( arg ) )
251
+ {
252
+ // See if the current working directory is a filesystem provider location
253
+ // We won't do the expansion if it isn't since native commands can only access the file system.
254
+ var cwdinfo = Context . EngineSessionState . CurrentLocation ;
255
+
256
+ // If it's a filesystem location then expand the wildcards
257
+ if ( cwdinfo . Provider . Name . Equals ( FileSystemProvider . ProviderName , StringComparison . OrdinalIgnoreCase ) )
258
+ {
259
+ // On UNIX, paths starting with ~ are not normalized
260
+ bool normalizePath = arg . Length == 0 || arg [ 0 ] != '~' ;
218
261
219
- // If it's a filesystem location then expand the wildcards
220
- if ( string . Equals ( cwdinfo . Provider . Name , Microsoft . PowerShell . Commands . FileSystemProvider . ProviderName ,
221
- StringComparison . OrdinalIgnoreCase ) )
222
- {
223
- bool normalizePath = true ;
224
- // On UNIX, paths starting with ~ are not normalized
225
- if ( arg . Length > 0 && arg [ 0 ] == '~' )
226
- {
227
- normalizePath = false ;
228
- }
262
+ // See if there are any matching paths otherwise just add the pattern as the argument
263
+ Collection < PSObject > paths = null ;
264
+ try
265
+ {
266
+ paths = Context . EngineSessionState . InvokeProvider . ChildItem . Get ( arg , false ) ;
267
+ }
268
+ catch
269
+ {
270
+ // Fallthrough will append the pattern unchanged.
271
+ }
229
272
230
- // See if there are any matching paths otherwise just add the pattern as the argument
231
- var paths = Context . EngineSessionState . InvokeProvider . ChildItem . Get ( arg , false ) ;
232
- if ( paths . Count > 0 )
233
- {
234
- bool first = true ;
235
- foreach ( var path in paths )
236
- {
237
- object pbo = path . BaseObject ;
238
- if ( ! first )
239
- {
240
- _arguments . Append ( " " ) ;
241
- }
242
- else
243
- {
244
- if ( ! ( pbo is System . IO . FileSystemInfo ) )
245
- {
246
- // If the object is not a filesystem object, then just append
247
- // the pattern unchanged
248
- _arguments . Append ( arg ) ;
249
- break ;
250
- }
251
- first = false ;
252
- }
253
- var expandedPath = ( pbo as System . IO . FileSystemInfo ) . FullName ;
254
- if ( normalizePath )
255
- {
256
- expandedPath = Context . SessionState . Path . NormalizeRelativePath ( expandedPath , cwdinfo . ProviderPath ) ;
257
- }
258
- // If the path contains spaces, then add quotes around it.
259
- if ( NeedQuotes ( expandedPath ) )
260
- {
261
- _arguments . Append ( "\" " ) ;
262
- _arguments . Append ( expandedPath ) ;
263
- _arguments . Append ( "\" " ) ;
264
- }
265
- else
266
- {
267
- _arguments . Append ( expandedPath ) ;
268
- }
269
- }
270
- }
271
- else
272
- {
273
- _arguments . Append ( arg ) ;
274
- }
275
- }
276
- else
277
- {
278
- _arguments . Append ( arg ) ;
279
- }
273
+ // Expand paths, but only from the file system.
274
+ if ( paths ? . Count > 0 && paths . All ( p => p . BaseObject is FileSystemInfo ) )
275
+ {
276
+ var sep = "" ;
277
+ foreach ( var path in paths )
278
+ {
279
+ _arguments . Append ( sep ) ;
280
+ sep = " " ;
281
+ var expandedPath = ( path . BaseObject as FileSystemInfo ) . FullName ;
282
+ if ( normalizePath )
283
+ {
284
+ expandedPath =
285
+ Context . SessionState . Path . NormalizeRelativePath ( expandedPath , cwdinfo . ProviderPath ) ;
286
+ }
287
+ // If the path contains spaces, then add quotes around it.
288
+ if ( NeedQuotes ( expandedPath ) )
289
+ {
290
+ _arguments . Append ( "\" " ) ;
291
+ _arguments . Append ( expandedPath ) ;
292
+ _arguments . Append ( "\" " ) ;
280
293
}
281
294
else
282
295
{
283
- // Even if there are no wildcards, we still need to possibly
284
- // expand ~ into the filesystem provider home directory path
285
- ProviderInfo fileSystemProvider = Context . EngineSessionState . GetSingleProvider (
286
- Microsoft . PowerShell . Commands . FileSystemProvider . ProviderName ) ;
287
- string home = fileSystemProvider . Home ;
288
- if ( string . Equals ( arg , "~" ) )
289
- {
290
- _arguments . Append ( home ) ;
291
- }
292
- else if ( arg . StartsWith ( "~/" , StringComparison . OrdinalIgnoreCase ) )
293
- {
294
- var replacementString = home + arg . Substring ( 1 ) ;
295
- _arguments . Append ( replacementString ) ;
296
- }
297
- else
298
- {
299
- _arguments . Append ( arg ) ;
300
- }
296
+ _arguments . Append ( expandedPath ) ;
301
297
}
302
- #else
303
- _arguments . Append ( arg ) ;
304
- #endif
298
+ argExpanded = true ;
305
299
}
306
300
}
307
301
}
308
- } while ( list != null ) ;
302
+ }
303
+ else if ( ! usedQuotes )
304
+ {
305
+ // Even if there are no wildcards, we still need to possibly
306
+ // expand ~ into the filesystem provider home directory path
307
+ ProviderInfo fileSystemProvider = Context . EngineSessionState . GetSingleProvider ( FileSystemProvider . ProviderName ) ;
308
+ string home = fileSystemProvider . Home ;
309
+ if ( string . Equals ( arg , "~" ) )
310
+ {
311
+ _arguments . Append ( home ) ;
312
+ argExpanded = true ;
313
+ }
314
+ else if ( arg . StartsWith ( "~/" , StringComparison . OrdinalIgnoreCase ) )
315
+ {
316
+ var replacementString = home + arg . Substring ( 1 ) ;
317
+ _arguments . Append ( replacementString ) ;
318
+ argExpanded = true ;
319
+ }
320
+ }
321
+ #endif // UNIX
322
+
323
+ if ( ! argExpanded )
324
+ {
325
+ _arguments . Append ( arg ) ;
326
+ }
309
327
}
310
328
311
329
/// <summary>
@@ -336,6 +354,6 @@ internal static bool NeedQuotes(string stringToCheck)
336
354
/// The native command to bind to
337
355
/// </summary>
338
356
private NativeCommand _nativeCommand ;
339
- #endregion private members
357
+ #endregion private members
340
358
}
341
359
} // namespace System.Management.Automation
0 commit comments