コード例 #1
0
        /// <summary>
        /// parse and evaluate a command line<br/>
        /// 1. parse command line<br/>
        /// error or:<br/>
        /// 2. execute command<br/>
        ///     A. internal command (modules) or alias<br/>
        ///     B. underlying shell command (found in scan paths)<br/>
        ///      file: <br/>
        ///         C. file (batch)<br/>
        ///         D. non executable file<br/>
        ///     not a file:<br/>
        ///         E. unknown command<br/>
        /// </summary>
        /// <param name="context">command evaluation context</param>
        /// <param name="expr">expression to be evaluated</param>
        /// <param name="outputX">begin x location of the command line expression in the console if applyable</param>
        /// <param name="postAnalysisPreExecOutput">text to be outputed before any analysis output</param>
        /// <returns>data returned by the analysis and the evaluation of an expression (analysis error or commmand returns or command error)</returns>
        public ExpressionEvaluationResult Eval(
            CommandEvaluationContext context,
            string expr,
            int outputX = 0,
            string postAnalysisPreExecOutput = null)        // TODO: an eval options object would be nice
        {
            try
            {
                var  pipelineParseResults = ParseCommandLine(context, SyntaxAnalyzer, expr, ExternalParserExtension);
                bool allValid             = true;
                var  evalParses           = new List <ExpressionEvaluationResult>();

                // check pipeline syntax analysis
                foreach (var pipelineParseResult in pipelineParseResults)
                {
                    allValid &= pipelineParseResult.ParseResult.ParseResultType == ParseResultType.Valid;
                    var evalParse = AnalysisPipelineParseResult(
                        context,
                        pipelineParseResult,
                        expr,
                        outputX,
                        pipelineParseResult.ParseResult
                        );

                    evalParses.Add(evalParse);
                }

                // eventually output the post analysis pre exec content
                if (!string.IsNullOrEmpty(postAnalysisPreExecOutput))
                {
                    context.Out.Echo(postAnalysisPreExecOutput);
                }

                if (!allValid)
                {
                    // 💥syntax error in pipeline - break exec
                    var err = evalParses.FirstOrDefault();
                    context.ShellEnv.UpdateVarLastCommandReturn(expr, null, err == null ? ReturnCode.OK : GetReturnCode(err), err?.SyntaxError);
                    return(err);
                }

                // run pipeline
                var evalRes = PipelineProcessor.RunPipeline(context, pipelineParseResults.FirstOrDefault());

                // update shell env
                context.ShellEnv.UpdateVarLastCommandReturn(expr, evalRes.Result, GetReturnCode(evalRes), evalRes.EvalErrorText, evalRes.EvalError);
                return(evalRes);
            }
            catch (Exception pipelineException)
            {
                // code pipeline parse or execution error
                // update shell env
                context.ShellEnv.UpdateVarLastCommandReturn(expr, ReturnCode.Error, ReturnCode.Unknown, pipelineException.Message, pipelineException);
                context.Errorln(pipelineException.Message);
                return(new ExpressionEvaluationResult(expr, null, ParseResultType.NotIdentified, null, (int)ReturnCode.Error, pipelineException, pipelineException.Message));
            }
        }
コード例 #2
0
        ExpressionEvaluationResult AnalysisPipelineParseResult(
            CommandEvaluationContext context,
            PipelineParseResult pipelineParseResult,
            string expr,
            int outputX,
            ParseResult parseResult
            )
        {
            ExpressionEvaluationResult r = null;
            var errorText = "";

            string[] t;
            int      idx;
            string   serr;

            switch (parseResult.ParseResultType)
            {
            case ParseResultType.Empty:

                r = new ExpressionEvaluationResult(expr, null, parseResult.ParseResultType, null, (int)ReturnCode.OK, null);
                break;

            case ParseResultType.NotValid:      /* command syntax not valid */

                var perComErrs = new Dictionary <string, List <CommandSyntaxParsingResult> >();
                foreach (var prs in parseResult.SyntaxParsingResults)
                {
                    if (prs.CommandSyntax != null)
                    {
                        if (perComErrs.TryGetValue(prs.CommandSyntax?.CommandSpecification?.Name, out var lst))
                        {
                            lst.Add(prs);
                        }
                        else
                        {
                            perComErrs.Add(prs.CommandSyntax.CommandSpecification.Name, new List <CommandSyntaxParsingResult> {
                                prs
                            });
                        }
                    }
                }

                var errs           = new List <string>();
                var minErrPosition = int.MaxValue;
                var errPositions   = new List <int>();
                foreach (var kvp in perComErrs)
                {
                    var comSyntax = kvp.Value.First().CommandSyntax;
                    foreach (var prs in kvp.Value)
                    {
                        foreach (var perr in prs.ParseErrors)
                        {
                            minErrPosition = Math.Min(minErrPosition, perr.Position);
                            errPositions.Add(perr.Position);
                            if (!errs.Contains(perr.Description))
                            {
                                errs.Add(perr.Description);
                            }
                        }
                        errorText += Br + Red + string.Join(Br + Red, errs);
                    }
                    errorText += $"{Br}{Red}for syntax: {comSyntax}{Br}";
                }

                errPositions.Sort();
                errPositions = errPositions.Distinct().ToList();

                t = new string[expr.Length + 2];
                for (int i = 0; i < t.Length; i++)
                {
                    t[i] = " ";
                }
                foreach (var errPos in errPositions)
                {
                    t[GetIndex(context, errPos, expr)] = Settings.ErrorPositionMarker + "";
                }
                serr = string.Join("", t);
                Error(" ".PadLeft(outputX + 1) + serr, false, false);

                Error(errorText);
                r = new ExpressionEvaluationResult(expr, errorText, parseResult.ParseResultType, null, (int)ReturnCode.NotIdentified, null);
                break;

            case ParseResultType.Ambiguous:

                errorText += $"{Red}ambiguous syntaxes:{Br}";
                foreach (var prs in parseResult.SyntaxParsingResults)
                {
                    errorText += $"{Red}{prs.CommandSyntax}{Br}";
                }
                Error(errorText);
                r = new ExpressionEvaluationResult(expr, errorText, parseResult.ParseResultType, null, (int)ReturnCode.NotIdentified, null);
                break;

            case ParseResultType.NotIdentified:

                t = new string[expr.Length + 2];
                for (int j = 0; j < t.Length; j++)
                {
                    t[j] = " ";
                }
                var err = parseResult.SyntaxParsingResults.First().ParseErrors.First();
                idx        = err.Position;
                t[idx]     = Settings.ErrorPositionMarker + "";
                errorText += Red + err.Description;
                serr       = string.Join("", t);
                context.Errorln(" ".PadLeft(outputX) + serr);
                context.Errorln(errorText);
                r = new ExpressionEvaluationResult(expr, errorText, parseResult.ParseResultType, null, (int)ReturnCode.NotIdentified, null);
                break;

            case ParseResultType.SyntaxError:

                t = new string[expr.Length + 2];
                for (int j = 0; j < t.Length; j++)
                {
                    t[j] = " ";
                }
                var err2 = parseResult.SyntaxParsingResults.First().ParseErrors.First();
                idx        = err2.Index;
                t[idx]     = Settings.ErrorPositionMarker + "";
                errorText += Red + err2.Description;
                serr       = string.Join("", t);
                context.Errorln(" ".PadLeft(outputX) + serr);
                context.Errorln(errorText);
                r = new ExpressionEvaluationResult(expr, errorText, parseResult.ParseResultType, null, (int)ReturnCode.NotIdentified, null);
                break;
            }

            return(r);
        }
コード例 #3
0
        /// <summary>
        /// exec a file with os shell exec or orbsh shell exec
        /// </summary>
        /// <param name="context">command evaluation context</param>
        /// <param name="comPath">command filePath</param>
        /// <param name="args">command line arguments string</param>
        /// <param name="output">shell exec result if any</param>
        /// <param name="waitForExit">true if wait for exec process exits</param>
        /// <param name="isStreamsEchoEnabled">if true, exec process output stream is echoized to context out (dump command output)</param>
        /// <param name="isOutputCaptureEnabled">if true capture the exec process output and give the result in parameter 'output'</param>
        /// <param name="mergeErrorStreamIntoOutput">if true merge exec process err stream content to the process output content (if process out capture is enabled)</param>
        /// <returns>exec process return code</returns>
        public int ShellExec(
            CommandEvaluationContext context,
            string comPath,
            string args,
            string workingDirectory,
            out string output,
            bool waitForExit                = true,
            bool isStreamsEchoEnabled       = true,
            bool isOutputCaptureEnabled     = true,
            bool mergeErrorStreamIntoOutput = true,
            bool redirectStandardInput      = false
            )
        {
            Thread         inputTask = null;
            TextReader     stdin     = null;
            ProcessWrapper pw        = null;

            try
            {
                output = null;
                workingDirectory ??= Environment.CurrentDirectory;
                var processStartInfo = new ProcessStartInfo()
                {
                    UseShellExecute = false,
                    //StandardOutputEncoding = Encoding.UTF8,   // keep system default
                    //StandardErrorEncoding = Encoding.UTF8,    // keep system default

                    RedirectStandardError  = true,
                    RedirectStandardInput  = redirectStandardInput, // allows access to process StandardInput
                    RedirectStandardOutput = true,
                    CreateNoWindow         = true,
                    FileName         = comPath,
                    Arguments        = args,
                    WindowStyle      = ProcessWindowStyle.Normal,
                    WorkingDirectory = workingDirectory
                };
                var sb = new StringBuilder();

                // batch shell exec ?

                if (Path.GetExtension(comPath) == context.ShellEnv.GetValue <string>(ShellEnvironmentVar.settings_clp_shellExecBatchExt))
                {
                    var batchMethod = typeof(CommandLineProcessorCommands).GetMethod(nameof(CommandLineProcessorCommands.Batch));
                    var r           = Eval(context, batchMethod, "\"" + FileSystemPath.UnescapePathSeparators(comPath) + " " + args + "\"", 0);
                    output = sb.ToString();
                    return(r.EvalResultCode);
                }

                // process exec

                pw = ProcessWrapper.ThreadRun(
                    processStartInfo,
                    null,
                    (outStr) =>
                {
                    if (isStreamsEchoEnabled)
                    {
                        context.Out.Echoln(outStr);
                    }
                    if (isOutputCaptureEnabled)
                    {
                        sb.AppendLine(outStr);
                    }
                },
                    (errStr) =>
                {
                    if (isStreamsEchoEnabled)
                    {
                        context.Errorln(errStr);
                    }
                    if (isOutputCaptureEnabled && mergeErrorStreamIntoOutput)
                    {
                        sb.AppendLine(errStr);
                    }
                }
                    );

                if (context.ShellEnv.IsOptionSetted(ShellEnvironmentVar.settings_clp_enableShellExecTraceProcessStart))
                {
                    context.Out.Echoln($"{context.ShellEnv.Colors.TaskInformation}process '{Path.GetFileName(comPath)}' [{pw.Process.Id}] started(rdc)");
                }

                int retCode = 0;
                int c       = -1;

                if (waitForExit)
                {
                    if (redirectStandardInput)
                    {
                        inputTask = new Thread(
                            () =>
                        {
                            try
                            {
                                var cp = comPath;
                                stdin  = System.Console.In;

#if dbg
                                Debug.WriteLine($"input task started [ cp = {cp} ]");
#endif

                                while (!(bool)pw?.Process?.HasExited)
                                {
                                    if (System.Console.KeyAvailable)
                                    {
                                        var key = System.Console.ReadKey(true);
                                        c       = key.KeyChar;

                                        if (c > -1)
                                        {
                                            context.Out.ConsolePrint("" + (char)c);

#if dbg
                                            Debug.Write((char)c);
#endif

                                            pw?.Process.StandardInput.Write((char)c);
                                            pw?.Process.StandardInput.Flush();
                                        }
                                    }

                                    Thread.Sleep(1);
                                }

#if dbg
                                Debug.WriteLine("input task exited");
#endif
                            }
                            catch
#if dbg
                            (Exception ex)
#endif
                            {
#if dbg
                                Debug.WriteLine($"input task exited ({ex.Message})");
#endif
                            }
                        });
                        inputTask.Name = "forward input task";
                        inputTask.Start();
                    }

                    var cancellationTask = Task.Run(() =>
                    {
                        try
                        {
                            while (!(bool)pw?.Process?.HasExited)
                            {
                                if (IsCancellationRequested)
                                {
                                    pw?.Process?.Kill();
                                }
                                Thread.Sleep(1);
                            }

                            inputTask?.Interrupt();

#if dbg
                            Debug.WriteLine($"cancellation task exited");
#endif
                        }
                        catch
#if dbg
                        (Exception ex)
#endif
                        {
#if dbg
                            Debug.WriteLine($"cancellation task exited ({ex.Message})");
#endif
                        }
                    });

                    pw.Process.WaitForExit();

                    retCode = pw.Process.ExitCode;

                    pw.StdOutCallBackThread.Join();
                    pw.StdErrCallBackThread.Join();

                    output = sb.ToString();
                }

                if (context.ShellEnv.IsOptionSetted(ShellEnvironmentVar.settings_clp_enableShellExecTraceProcessEnd))
                {
                    context.Out.Echoln($"{context.ShellEnv.Colors.TaskInformation}process '{Path.GetFileName(comPath)}' exited with code: {retCode}(rdc)");
                }

                return(retCode);
            }
            catch (Exception shellExecException)
            {
                inputTask?.Interrupt();
                throw new Exception($"ShellExec error: {shellExecException}", shellExecException);
            }
            finally
            {
            }
        }