diff --git a/trzsz/comm_test.go b/trzsz/comm_test.go index b8e8ebf..0d80cf9 100644 --- a/trzsz/comm_test.go +++ b/trzsz/comm_test.go @@ -58,6 +58,10 @@ func (w *testWriter) Write(text []byte) (n int, err error) { return len(text), nil } +func (w *testWriter) clearBuffer() { + w.buffer = nil +} + func (w *testWriter) assertBufferCount(count int) { w.t.Helper() require.Equal(w.t, count, len(w.buffer)) diff --git a/trzsz/filter.go b/trzsz/filter.go index be1a628..c7188b1 100644 --- a/trzsz/filter.go +++ b/trzsz/filter.go @@ -794,47 +794,59 @@ func (filter *TrzszFilter) wrapOutput() { } func (filter *TrzszFilter) detectOSC52(buf []byte) { - if filter.osc52Sequence == nil { - pos := bytes.Index(buf, []byte("\x1b]52;c;")) - if pos < 0 { - return + for len(buf) > 0 { + if filter.osc52Sequence == nil { + for { + pos := bytes.Index(buf, []byte("\x1b]52;")) + if pos < 0 { + return + } + buf = buf[pos+5:] + if len(buf) < 2 { + return + } + if (buf[0] == 'c' || buf[0] == 'p') && buf[1] == ';' { + buf = buf[2:] + break + } + buf = buf[2:] + } + + pos := bytes.IndexAny(buf, "\a\x1b") + if pos < 0 { + filter.osc52Sequence = bytes.NewBuffer(nil) + filter.osc52Sequence.Write(buf) + return + } + + writeToClipboard(buf[:pos]) + buf = buf[pos+1:] + continue } - buf = buf[pos+7:] - pos = bytes.IndexByte(buf, '\a') + + pos := bytes.IndexAny(buf, "\a\x1b") if pos < 0 { - filter.osc52Sequence = bytes.NewBuffer(nil) filter.osc52Sequence.Write(buf) + if filter.osc52Sequence.Len() > 100000 { + for _, b := range buf { + if !((b >= 'A' && b <= 'Z') || (b >= 'a' && b <= 'z') || (b >= '0' && b <= '9') || b == '+' || b == '/' || b == '=') { + // something went wrong, just ignore it + filter.osc52Sequence = nil + return + } + } + } return } - writeToClipboard(buf[:pos]) - buf = buf[pos+1:] - if len(buf) > 0 { - filter.detectOSC52(buf) - } - return - } - - pos := bytes.IndexByte(buf, '\a') - if pos < 0 { - filter.osc52Sequence.Write(buf) - if filter.osc52Sequence.Len() > 100000 { - // something went wrong, just ignore it - filter.osc52Sequence = nil - } - return - } - - filter.osc52Sequence.Write(buf[:pos]) - writeToClipboard(filter.osc52Sequence.Bytes()) - filter.osc52Sequence = nil - buf = buf[pos+1:] - if len(buf) > 0 { - filter.detectOSC52(buf) + filter.osc52Sequence.Write(buf[:pos]) + writeToClipboard(filter.osc52Sequence.Bytes()) + filter.osc52Sequence = nil + buf = buf[pos+1:] } } -func writeToClipboard(buf []byte) { +var writeToClipboard = func(buf []byte) { text, err := base64.StdEncoding.DecodeString(string(buf)) if err != nil { return diff --git a/trzsz/filter_test.go b/trzsz/filter_test.go new file mode 100644 index 0000000..dbdbb98 --- /dev/null +++ b/trzsz/filter_test.go @@ -0,0 +1,111 @@ +/* +MIT License + +Copyright (c) 2022-2024 The Trzsz Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package trzsz + +import ( + "strings" + "testing" +) + +func TestDetectOSC52(t *testing.T) { + oriWriteToClipboard := writeToClipboard + defer func() { + writeToClipboard = oriWriteToClipboard + }() + + writer := newTestWriter(t) + writeToClipboard = func(buf []byte) { + _, _ = writer.Write(buf) + } + + filter := &TrzszFilter{} + + filter.detectOSC52([]byte("ABC")) + writer.assertBufferCount(0) + + filter.detectOSC52([]byte("\x1b]52;")) + writer.assertBufferCount(0) + + filter.detectOSC52([]byte("\x1b]52;a;")) + writer.assertBufferCount(0) + + filter.detectOSC52([]byte("\x1b]52;a;A\a")) + writer.assertBufferCount(0) + + filter.detectOSC52([]byte("\x1b]52;a;A\a\x1b]52;c;X\aZ")) + writer.assertBufferEqual(0, "X") + + writer.clearBuffer() + filter.detectOSC52([]byte("\x1b]52;a;A\a\x1b]52;b;B\a\x1b]52;c;X\aZ")) + writer.assertBufferEqual(0, "X") + + writer.clearBuffer() + filter.detectOSC52([]byte("\x1b]52;c;ABC")) + writer.assertBufferCount(0) + filter.detectOSC52([]byte("xyz\a")) + writer.assertBufferEqual(0, "ABCxyz") + + writer.clearBuffer() + filter.detectOSC52([]byte("\x1b]52;c;ABC")) + writer.assertBufferCount(0) + filter.detectOSC52([]byte("\x1bxyz\a")) + writer.assertBufferEqual(0, "ABC") + + writer.clearBuffer() + filter.detectOSC52([]byte("11\x1b]52;c;ABC\a\x1b]52;p;xyz\x1b00")) + writer.assertBufferEqual(0, "ABC") + writer.assertBufferEqual(1, "xyz") + + writer.clearBuffer() + filter.detectOSC52([]byte("\x1b]52;c;ABC")) + writer.assertBufferCount(0) + filter.detectOSC52([]byte("DEFG")) + writer.assertBufferCount(0) + filter.detectOSC52([]byte("HIJ\a111\x1b]52;p;xyz\x1b00\x1b]52;c;123")) + writer.assertBufferCount(2) + writer.assertBufferEqual(0, "ABCDEFGHIJ") + writer.assertBufferEqual(1, "xyz") + filter.detectOSC52([]byte("\a")) + writer.assertBufferEqual(2, "123") + + writer.clearBuffer() + filter.detectOSC52([]byte("\x1b]52;c;ABC")) + for i := 0; i < 2000; i++ { + filter.detectOSC52([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")) + } + filter.detectOSC52([]byte("==\x1b")) + writer.assertBufferEqual(0, "ABC"+ + strings.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 2000)+"==") + + writer.clearBuffer() + filter.detectOSC52([]byte("\x1b]52;c;ABC")) + for i := 0; i < 2000; i++ { + filter.detectOSC52([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")) + } + filter.detectOSC52([]byte("==$==")) + writer.assertBufferCount(0) + filter.detectOSC52([]byte("\x1b]52;c;ABC\a")) + writer.assertBufferEqual(0, "ABC") +}