/// <summary> /// On Windows, just append <paramref name="arg"/>. /// On Unix, do globbing as appropriate, otherwise just append <paramref name="arg"/>. /// </summary> /// <param name="arg">The argument that possibly needs expansion.</param> /// <param name="parameter">The parameter associated with the operation.</param> /// <param name="stringConstantType">Bare, SingleQuoted, or DoubleQuoted.</param> private void PossiblyGlobArg(string arg, CommandParameterInternal parameter, StringConstantType stringConstantType) { var argExpanded = false; #if UNIX // On UNIX systems, we expand arguments containing wildcard expressions against // the file system just like bash, etc. if (stringConstantType == StringConstantType.BareWord) { if (WildcardPattern.ContainsWildcardCharacters(arg)) { // See if the current working directory is a filesystem provider location // We won't do the expansion if it isn't since native commands can only access the file system. var cwdinfo = Context.EngineSessionState.CurrentLocation; // If it's a filesystem location then expand the wildcards if (cwdinfo.Provider.Name.Equals(FileSystemProvider.ProviderName, StringComparison.OrdinalIgnoreCase)) { // On UNIX, paths starting with ~ or absolute paths are not normalized bool normalizePath = arg.Length == 0 || !(arg[0] == '~' || arg[0] == '/'); // See if there are any matching paths otherwise just add the pattern as the argument Collection <PSObject> paths = null; try { paths = Context.EngineSessionState.InvokeProvider.ChildItem.Get(arg, false); } catch { // Fallthrough will append the pattern unchanged. } // Expand paths, but only from the file system. if (paths?.Count > 0 && paths.All(static p => p.BaseObject is FileSystemInfo))
public ExpandableStringExpressionAst(IScriptExtent extent, string value, StringConstantType stringConstantType) : base(extent) { this.Value = value; this.StringConstantType = stringConstantType; ParseExpandableString(value); }
internal ExpandableStringExpressionAst(IScriptExtent extent, IList <ExpressionAst> expressions, string value, StringConstantType stringConstantType) : base(extent) { this.StringConstantType = stringConstantType; NestedExpressions = new ReadOnlyCollection <ExpressionAst>(expressions); Value = value; }
internal ExpandableStringExpressionAst(IScriptExtent extent, IList<ExpressionAst> expressions, string value, StringConstantType stringConstantType) : base(extent) { this.StringConstantType = stringConstantType; NestedExpressions = new ReadOnlyCollection<ExpressionAst>(expressions); Value = value; }
public StringConstantExpressionAst String( string value, StringConstantType stringConstantType = StringConstantType.BareWord) { return(new StringConstantExpressionAst( _currentExtent, value, stringConstantType)); }
public static string ResolveEscapeCharacters(string orig, StringConstantType quoteType) { // look at this: http://technet.microsoft.com/en-us/library/hh847755.aspx // it's the about_Escape_Characters page which shows that escape characters are different in PS var value = orig; if (quoteType.Equals(StringConstantType.DoubleQuoted)) { var sb = new StringBuilder(value.Length); for (int i = 0; i < value.Length; i++) { // TODO: It *should* be safe here to use the index i + 1 because we cannot have a string // literal ending in ` or " when it's not part of an escape sequence. // Should we check anyway? If so, how? if (value[i] == '"' && value[i + 1] == '"') { sb.Append('"'); // Skip the next character i++; } else if (value[i] == '`') { switch (value[i + 1]) { case '0': sb.Append('\0'); break; case 't': sb.Append('\t'); break; case 'b': sb.Append('\b'); break; case 'f': sb.Append('\f'); break; case 'v': sb.Append('\v'); break; case 'n': sb.Append('\n'); break; case 'r': sb.Append('\r'); break; case 'a': sb.Append('\a'); break; case '\'': case '$': case '"': case '`': default: sb.Append(value[i + 1]); break; } // Skip the next character i++; } else { sb.Append(value[i]); } } value = sb.ToString(); } else if (quoteType.Equals(StringConstantType.SingleQuoted)) { value = value.Replace("''", "'"); } return value; }
public static StringConstantExpressionAst As( this Ast source, AstType <StringConstantExpressionAst> targetType, string value, StringConstantType stringConstantType = StringConstantType.BareWord) { return(new StringConstantExpressionAst( GetExtentFromConstant(value, source.Extent), value, stringConstantType)); }
public static string ResolveEscapeCharacters(string orig, StringConstantType quoteType) { // look at this: http://technet.microsoft.com/en-us/library/hh847755.aspx // it's the about_Escape_Characters page which shows that escape characters are different in PS var value = orig; if (quoteType.Equals(StringConstantType.DoubleQuoted)) { foreach (var tuple in _escapeCharacterReplacements) { value = tuple.Item1.Replace(value, tuple.Item2); } } else if (quoteType.Equals(StringConstantType.SingleQuoted)) { value = value.Replace("''", "'"); } return(value); }
public ExpandableStringExpressionAst(IScriptExtent extent, string value, StringConstantType stringConstantType) : this(extent, ParseExpandableString(extent, value), value, stringConstantType) { }
public static string ResolveEscapeCharacters(string orig, StringConstantType quoteType) { // look at this: http://technet.microsoft.com/en-us/library/hh847755.aspx // it's the about_Escape_Characters page which shows that escape characters are different in PS var value = orig; if (quoteType.Equals(StringConstantType.DoubleQuoted)) { var sb = new StringBuilder(value.Length); for (int i = 0; i < value.Length; i++) { // TODO: It *should* be safe here to use the index i + 1 because we cannot have a string // literal ending in ` or " when it's not part of an escape sequence. // Should we check anyway? If so, how? if (value[i] == '"' && value[i + 1] == '"') { sb.Append('"'); // Skip the next character i++; } else if (value[i] == '`') { switch (value[i + 1]) { case '0': sb.Append('\0'); break; case 't': sb.Append('\t'); break; case 'b': sb.Append('\b'); break; case 'f': sb.Append('\f'); break; case 'v': sb.Append('\v'); break; case 'n': sb.Append('\n'); break; case 'r': sb.Append('\r'); break; case 'a': sb.Append('\a'); break; case '\'': case '$': case '"': case '`': default: sb.Append(value[i + 1]); break; } // Skip the next character i++; } else { sb.Append(value[i]); } } value = sb.ToString(); } else if (quoteType.Equals(StringConstantType.SingleQuoted)) { value = value.Replace("''", "'"); } return(value); }
public StringConstantExpressionAst(IScriptExtent extent, string value, StringConstantType stringConstantType) : base(extent, value) { this.StringConstantType = stringConstantType; this.Value = value; }
/// <summary> /// On Windows, just append <paramref name="arg"/>. /// On Unix, do globbing as appropriate, otherwise just append <paramref name="arg"/>. /// </summary> /// <param name="arg">The argument that possibly needs expansion.</param> /// <param name="stringConstantType">Bare, SingleQuoted, or DoubleQuoted.</param> private void PossiblyGlobArg(string arg, StringConstantType stringConstantType) { var argExpanded = false; #if UNIX // On UNIX systems, we expand arguments containing wildcard expressions against // the file system just like bash, etc. if (stringConstantType == StringConstantType.BareWord) { if (WildcardPattern.ContainsWildcardCharacters(arg)) { // See if the current working directory is a filesystem provider location // We won't do the expansion if it isn't since native commands can only access the file system. var cwdinfo = Context.EngineSessionState.CurrentLocation; // If it's a filesystem location then expand the wildcards if (cwdinfo.Provider.Name.Equals(FileSystemProvider.ProviderName, StringComparison.OrdinalIgnoreCase)) { // On UNIX, paths starting with ~ or absolute paths are not normalized bool normalizePath = arg.Length == 0 || !(arg[0] == '~' || arg[0] == '/'); // See if there are any matching paths otherwise just add the pattern as the argument Collection <PSObject> paths = null; try { paths = Context.EngineSessionState.InvokeProvider.ChildItem.Get(arg, false); } catch { // Fallthrough will append the pattern unchanged. } // Expand paths, but only from the file system. if (paths?.Count > 0 && paths.All(p => p.BaseObject is FileSystemInfo)) { var sep = string.Empty; foreach (var path in paths) { _arguments.Append(sep); sep = " "; var expandedPath = (path.BaseObject as FileSystemInfo).FullName; if (normalizePath) { expandedPath = Context.SessionState.Path.NormalizeRelativePath(expandedPath, cwdinfo.ProviderPath); } // If the path contains spaces, then add quotes around it. if (NeedQuotes(expandedPath)) { _arguments.Append("\""); _arguments.Append(expandedPath); _arguments.Append("\""); } else { _arguments.Append(expandedPath); } argExpanded = true; } } } } else { // Even if there are no wildcards, we still need to possibly // expand ~ into the filesystem provider home directory path ProviderInfo fileSystemProvider = Context.EngineSessionState.GetSingleProvider(FileSystemProvider.ProviderName); string home = fileSystemProvider.Home; if (string.Equals(arg, "~")) { _arguments.Append(home); argExpanded = true; } else if (arg.StartsWith("~/", StringComparison.OrdinalIgnoreCase)) { var replacementString = home + arg.Substring(1); _arguments.Append(replacementString); argExpanded = true; } } } #endif // UNIX if (stringConstantType != StringConstantType.SingleQuoted) { arg = ResolvePath(arg, Context); } if (!argExpanded) { _arguments.Append(arg); } }
private List <CompletionResult> GetResultForString(CompletionContext completionContext, ref int replacementIndex, ref int replacementLength, bool isQuotedString) { if (isQuotedString) { return(null); } Token tokenAtCursor = completionContext.TokenAtCursor; Ast ast = completionContext.RelatedAsts.Last <Ast>(); List <CompletionResult> list = null; ExpandableStringExpressionAst ast2 = ast as ExpandableStringExpressionAst; StringConstantExpressionAst ast3 = ast as StringConstantExpressionAst; if ((ast3 != null) || (ast2 != null)) { string input = (ast3 != null) ? ast3.Value : ast2.Value; StringConstantType type = (ast3 != null) ? ast3.StringConstantType : ast2.StringConstantType; string str2 = null; if (type == StringConstantType.DoubleQuoted) { Match match = Regex.Match(input, @"(\$[\w\d]+\.[\w\d\*]*)$"); if (match.Success) { str2 = match.Groups[1].Value; } else if ((match = Regex.Match(input, @"(\[[\w\d\.]+\]::[\w\d\*]*)$")).Success) { str2 = match.Groups[1].Value; } } if (str2 != null) { int num3; int num4; int offset = tokenAtCursor.Extent.StartScriptPosition.Offset; int length = (this._cursorPosition.Offset - offset) - 1; if (length >= input.Length) { length = input.Length; } CompletionAnalysis analysis = new CompletionAnalysis(this._ast, this._tokens, this._cursorPosition, this._options); CompletionContext context = analysis.CreateCompletionContext(completionContext.ExecutionContext); context.Helper = completionContext.Helper; List <CompletionResult> list2 = analysis.GetResultHelper(context, out num3, out num4, true); if ((list2 != null) && (list2.Count > 0)) { list = new List <CompletionResult>(); replacementIndex = (offset + 1) + (length - str2.Length); replacementLength = str2.Length; string str3 = str2.Substring(0, num3); foreach (CompletionResult result in list2) { string completionText = str3 + result.CompletionText; if (result.ResultType.Equals(CompletionResultType.Property)) { completionText = TokenKind.DollarParen.Text() + completionText + TokenKind.RParen.Text(); } else if (result.ResultType.Equals(CompletionResultType.Method)) { completionText = TokenKind.DollarParen.Text() + completionText; } completionText = completionText + "\""; list.Add(new CompletionResult(completionText, result.ListItemText, result.ResultType, result.ToolTip)); } } return(list); } CommandElementAst stringAst = ast as CommandElementAst; string str5 = CompletionCompleters.ConcatenateStringPathArguments(stringAst, string.Empty, completionContext); if (str5 == null) { return(list); } completionContext.WordToComplete = str5; if ((ast.Parent is CommandAst) || (ast.Parent is CommandParameterAst)) { list = CompletionCompleters.CompleteCommandArgument(completionContext); replacementIndex = completionContext.ReplacementIndex; replacementLength = completionContext.ReplacementLength; return(list); } list = new List <CompletionResult>(CompletionCompleters.CompleteFilename(completionContext)); if (str5.IndexOf('-') != -1) { List <CompletionResult> collection = CompletionCompleters.CompleteCommand(completionContext); if ((collection != null) && (collection.Count > 0)) { list.AddRange(collection); } } } return(list); }
/// <summary> /// Stringize a non-IEnum argument to a native command, adding quotes /// and trailing spaces as appropriate. An array gets added as multiple arguments /// each of which will be stringized. /// </summary> /// <param name="context">Execution context instance.</param> /// <param name="parameter">The parameter associated with the operation.</param> /// <param name="obj">The object to append.</param> /// <param name="argArrayAst">If the argument was an array literal, the Ast, otherwise null.</param> /// <param name="sawVerbatimArgumentMarker">True if the argument occurs after --%.</param> /// <param name="stringConstantType">Bare, SingleQuoted, or DoubleQuoted.</param> private void AppendOneNativeArgument(ExecutionContext context, CommandParameterInternal parameter, object obj, ArrayLiteralAst argArrayAst, bool sawVerbatimArgumentMarker, StringConstantType stringConstantType) { IEnumerator list = LanguagePrimitives.GetEnumerator(obj); Diagnostics.Assert((argArrayAst == null) || (obj is object[] && ((object[])obj).Length == argArrayAst.Elements.Count), "array argument and ArrayLiteralAst differ in number of elements"); int currentElement = -1; string separator = string.Empty; do { string arg; object currentObj; if (list == null) { arg = PSObject.ToStringParser(context, obj); currentObj = obj; } else { if (!ParserOps.MoveNext(context, null, list)) { break; } currentObj = ParserOps.Current(null, list); arg = PSObject.ToStringParser(context, currentObj); currentElement += 1; if (currentElement != 0) { separator = GetEnumerableArgSeparator(argArrayAst, currentElement); } } if (!string.IsNullOrEmpty(arg)) { // Only add the separator to the argument string rather than adding a separator to the ArgumentList. _arguments.Append(separator); if (sawVerbatimArgumentMarker) { arg = Environment.ExpandEnvironmentVariables(arg); _arguments.Append(arg); // we need to split the argument on spaces _argumentList.AddRange(arg.Split(' ', StringSplitOptions.RemoveEmptyEntries)); } else { // We need to add quotes if the argument has unquoted spaces. The // quotes could appear anywhere inside the string, not just at the start, // e.g. // $a = 'a"b c"d' // echoargs $a 'a"b c"d' a"b c"d // // The above should see 3 identical arguments in argv (the command line will // actually have quotes in different places, but the Win32 command line=>argv parser // erases those differences. // // We need to check quotes that the win32 argument parser checks which is currently // just the normal double quotes, no other special quotes. Also note that mismatched // quotes are supported if (NeedQuotes(arg)) { _arguments.Append('"'); if (stringConstantType == StringConstantType.DoubleQuoted) { _arguments.Append(ResolvePath(arg, Context)); AddToArgumentList(parameter, ResolvePath(arg, Context)); } else { _arguments.Append(arg); AddToArgumentList(parameter, arg); } // need to escape all trailing backslashes so the native command receives it correctly // according to http://www.daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULESDOC for (int i = arg.Length - 1; i >= 0 && arg[i] == '\\'; i--) { _arguments.Append('\\'); } _arguments.Append('"'); } else { if (argArrayAst != null && UseArgumentList) { // We have a literal array, so take the extent, break it on spaces and add them to the argument list. foreach (string element in argArrayAst.Extent.Text.Split(' ', StringSplitOptions.RemoveEmptyEntries)) { PossiblyGlobArg(element, parameter, stringConstantType); } break; } else { PossiblyGlobArg(arg, parameter, stringConstantType); } } } } else if (UseArgumentList && currentObj != null) { // add empty strings to arglist, but not nulls AddToArgumentList(parameter, arg); } }while (list != null); }
public static StringConstantExpressionAst String( string value, StringConstantType stringConstantType = StringConstantType.BareWord) => Current.String(value, stringConstantType);
internal void BindParameters(Collection <CommandParameterInternal> parameters) { bool sawVerbatimArgumentMarker = false; bool first = true; foreach (CommandParameterInternal parameter in parameters) { if (!first) { _arguments.Append(' '); } first = false; if (parameter.ParameterNameSpecified) { Diagnostics.Assert(!parameter.ParameterText.Contains(' '), "Parameters cannot have whitespace"); PossiblyGlobArg(parameter.ParameterText, parameter, StringConstantType.BareWord); if (parameter.SpaceAfterParameter) { _arguments.Append(' '); } } if (parameter.ArgumentSpecified) { // If this is the verbatim argument marker, we don't pass it on to the native command. // We do need to remember it though - we'll expand environment variables in subsequent args. object argValue = parameter.ArgumentValue; if (string.Equals("--%", argValue as string, StringComparison.OrdinalIgnoreCase)) { sawVerbatimArgumentMarker = true; continue; } if (argValue != AutomationNull.Value && argValue != UnboundParameter.Value) { // ArrayLiteralAst is used to reconstruct the correct argument, e.g. // windbg -k com:port=\\devbox\pipe\debug,pipe,resets=0,reconnect // The parser produced an array of strings but marked the parameter so we // can properly reconstruct the correct command line. StringConstantType stringConstantType = StringConstantType.BareWord; ArrayLiteralAst arrayLiteralAst = null; switch (parameter?.ArgumentAst) { case StringConstantExpressionAst sce: stringConstantType = sce.StringConstantType; break; case ExpandableStringExpressionAst ese: stringConstantType = ese.StringConstantType; break; case ArrayLiteralAst ala: arrayLiteralAst = ala; break; } // Prior to PSNativePSPathResolution experimental feature, a single quote worked the same as a double quote // so if the feature is not enabled, we treat any quotes as double quotes. When this feature is no longer // experimental, this code here needs to be removed. if (!ExperimentalFeature.IsEnabled("PSNativePSPathResolution") && stringConstantType == StringConstantType.SingleQuoted) { stringConstantType = StringConstantType.DoubleQuoted; } AppendOneNativeArgument(Context, parameter, argValue, arrayLiteralAst, sawVerbatimArgumentMarker, stringConstantType); } } } }
public static StringConstantExpressionAst StringConstantExpression(string value, StringConstantType stringConstantType) { return(new StringConstantExpressionAst(new EmptyScriptExtent(true), value, stringConstantType)); }