From fa44e9c3dc7c121ad456d6759ed0c2f5998daf89 Mon Sep 17 00:00:00 2001 From: Matt Johnstone Date: Mon, 26 Aug 2024 21:02:16 +0200 Subject: [PATCH 1/3] added IgnoreIncompleteLines option --- cmd.go | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/cmd.go b/cmd.go index ac989b3..5bbe594 100644 --- a/cmd.go +++ b/cmd.go @@ -177,6 +177,10 @@ type Options struct { // value DEFAULT_LINE_BUFFER_SIZE is usually sufficient, but if // ErrLineBufferOverflow errors occur, try increasing the size with this field. LineBufferSize uint + + // IgnoreIncompleteLines configures the 'ignoreIncompleteLines' option of the command's + // OutputStream's. If set to true, lines that do not end in '\n' are ignored. + IgnoreIncompleteLines bool } // NewCmdOptions creates a new Cmd with options. The command is not started @@ -214,11 +218,11 @@ func NewCmdOptions(options Options, name string, args ...string) *Cmd { if options.Streaming { c.Stdout = make(chan string, DEFAULT_STREAM_CHAN_SIZE) - c.stdoutStream = NewOutputStream(c.Stdout) + c.stdoutStream = NewOutputStream(c.Stdout, options.IgnoreIncompleteLines) c.stdoutStream.SetLineBufferSize(int(options.LineBufferSize)) c.Stderr = make(chan string, DEFAULT_STREAM_CHAN_SIZE) - c.stderrStream = NewOutputStream(c.Stderr) + c.stderrStream = NewOutputStream(c.Stderr, options.IgnoreIncompleteLines) c.stderrStream.SetLineBufferSize(int(options.LineBufferSize)) } @@ -682,22 +686,24 @@ func (e ErrLineBufferOverflow) Error() string { // While runnableCmd is running, lines are sent to the channel as soon as they // are written and newline-terminated by the command. type OutputStream struct { - streamChan chan string - bufSize int - buf []byte - lastChar int + streamChan chan string + bufSize int + buf []byte + lastChar int + ignoreIncompleteLines bool } // NewOutputStream creates a new streaming output on the given channel. The // caller must begin receiving on the channel before the command is started. // The OutputStream never closes the channel. -func NewOutputStream(streamChan chan string) *OutputStream { +func NewOutputStream(streamChan chan string, ignoreIncompleteLines bool) *OutputStream { out := &OutputStream{ streamChan: streamChan, // -- - bufSize: DEFAULT_LINE_BUFFER_SIZE, - buf: make([]byte, DEFAULT_LINE_BUFFER_SIZE), - lastChar: 0, + bufSize: DEFAULT_LINE_BUFFER_SIZE, + buf: make([]byte, DEFAULT_LINE_BUFFER_SIZE), + lastChar: 0, + ignoreIncompleteLines: ignoreIncompleteLines, } return out } @@ -738,7 +744,13 @@ func (rw *OutputStream) Write(p []byte) (n int, err error) { firstChar += newlineOffset + 1 } + // if the stream (p) does not end in a '\n', then we will have firstChar < n if firstChar < n { + // if we are ignoring incomplete lines, then we can just exit here + if rw.ignoreIncompleteLines { + return // implicit + } + remain := len(p[firstChar:]) bufFree := len(rw.buf[rw.lastChar:]) if remain > bufFree { From 09554ee9b4e06e0df1634887d13fc5deba2fde9e Mon Sep 17 00:00:00 2001 From: Matt Johnstone Date: Mon, 26 Aug 2024 21:12:15 +0200 Subject: [PATCH 2/3] updated tests with ignoreIncompleteLines --- cmd_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd_test.go b/cmd_test.go index 0d51cdc..570962c 100644 --- a/cmd_test.go +++ b/cmd_test.go @@ -752,7 +752,7 @@ func TestCmdOnlyStreamingOutput(t *testing.T) { func TestStreamingMultipleLines(t *testing.T) { lines := make(chan string, 5) - out := cmd.NewOutputStream(lines) + out := cmd.NewOutputStream(lines, false) // Quick side test: Lines() chan string should be the same chan string // we created the object with @@ -799,7 +799,7 @@ func TestStreamingMultipleLinesLastNotTerminated(t *testing.T) { // If last line isn't \n terminated, go-cmd should flush it anyway // https://github.com/go-cmd/cmd/pull/48 lines := make(chan string, 5) - out := cmd.NewOutputStream(lines) + out := cmd.NewOutputStream(lines, false) // Quick side test: Lines() chan string should be the same chan string // we created the object with @@ -846,7 +846,7 @@ func TestStreamingMultipleLinesLastNotTerminated(t *testing.T) { func TestStreamingBlankLines(t *testing.T) { lines := make(chan string, 5) - out := cmd.NewOutputStream(lines) + out := cmd.NewOutputStream(lines, false) // Blank line in the middle input := "foo\n\nbar\n" @@ -924,7 +924,7 @@ LINES3: func TestStreamingCarriageReturn(t *testing.T) { // Carriage return should be stripped lines := make(chan string, 5) - out := cmd.NewOutputStream(lines) + out := cmd.NewOutputStream(lines, false) input := "foo\r\nbar\r\n" expectLines := []string{"foo", "bar"} @@ -955,7 +955,7 @@ func TestStreamingLineBuffering(t *testing.T) { // write. When line is later terminated with newline, we prepend the buffered // line and send the complete line. lines := make(chan string, 1) - out := cmd.NewOutputStream(lines) + out := cmd.NewOutputStream(lines, false) // Write 3 unterminated lines. Without a newline, they'll be buffered until... for i := 0; i < 3; i++ { @@ -1018,7 +1018,7 @@ func TestStreamingErrLineBufferOverflow1(t *testing.T) { longLine[cmd.DEFAULT_LINE_BUFFER_SIZE+1] = 'z' lines := make(chan string, 5) - out := cmd.NewOutputStream(lines) + out := cmd.NewOutputStream(lines, false) // Write the long line, it should only write (n) 3 bytes for "bc\n" n, err := out.Write(longLine) @@ -1079,7 +1079,7 @@ func TestStreamingErrLineBufferOverflow2(t *testing.T) { // Overflow line buffer on 2nd write. So first write puts something in the // buffer, and then 2nd overflows it instead of completing the line. lines := make(chan string, 1) - out := cmd.NewOutputStream(lines) + out := cmd.NewOutputStream(lines, false) // Get "bar" into the buffer by omitting its newline input := "foo\nbar" @@ -1143,7 +1143,7 @@ func TestStreamingSetLineBufferSize(t *testing.T) { longLine[cmd.DEFAULT_LINE_BUFFER_SIZE+1] = '\n' lines := make(chan string, 5) - out := cmd.NewOutputStream(lines) + out := cmd.NewOutputStream(lines, false) out.SetLineBufferSize(cmd.DEFAULT_LINE_BUFFER_SIZE * 2) n, err := out.Write(longLine) From 3903712a7fb2d59857f7f76f1abc7a05779776c1 Mon Sep 17 00:00:00 2001 From: Matt Johnstone Date: Mon, 26 Aug 2024 21:52:42 +0200 Subject: [PATCH 3/3] updated module name --- cmd_test.go | 2 +- cmd_windows_test.go | 2 +- examples/blocking-buffered/main.go | 2 +- examples/blocking-streaming/main.go | 2 +- go.mod | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd_test.go b/cmd_test.go index 570962c..f448300 100644 --- a/cmd_test.go +++ b/cmd_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - "github.com/go-cmd/cmd" + "github.com/asymmetric-research/cmd" "github.com/go-test/deep" ) diff --git a/cmd_windows_test.go b/cmd_windows_test.go index cc33c81..e946469 100644 --- a/cmd_windows_test.go +++ b/cmd_windows_test.go @@ -11,7 +11,7 @@ import ( "testing" "time" - "github.com/go-cmd/cmd" + "github.com/asymmetric-research/cmd" "github.com/go-test/deep" ) diff --git a/examples/blocking-buffered/main.go b/examples/blocking-buffered/main.go index 4b3d5e3..bf92f80 100644 --- a/examples/blocking-buffered/main.go +++ b/examples/blocking-buffered/main.go @@ -3,7 +3,7 @@ package main import ( "fmt" - "github.com/go-cmd/cmd" + "github.com/asymmetric-research/cmd" ) func main() { diff --git a/examples/blocking-streaming/main.go b/examples/blocking-streaming/main.go index f065aa3..d2046e7 100644 --- a/examples/blocking-streaming/main.go +++ b/examples/blocking-streaming/main.go @@ -6,7 +6,7 @@ import ( "fmt" "os" - "github.com/go-cmd/cmd" + "github.com/asymmetric-research/cmd" ) func main() { diff --git a/go.mod b/go.mod index eda6304..f894fe2 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/go-cmd/cmd +module github.com/asymmetric-research/cmd go 1.20