~sircmpwn/aerc

msgviewer: simplify attemptCopy v1 PROPOSED

Reto Brunner: 2
 msgviewer: simplify attemptCopy
 msgviewer: set max line length to 1 GB

 2 files changed, 88 insertions(+), 66 deletions(-)
Both patches merged to master
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~sircmpwn/aerc/patches/11774/mbox | git am -3
Learn more about email & git
View this thread in the archives

[PATCH 1/2] msgviewer: simplify attemptCopy Export this patch

No functional changes, simply extract more complex stuff into
sub functions to help readability.
---

Any Opinions?
I like this more than inlining the functionality but I can revert this
if people prefer the original

 widgets/msgviewer.go | 146 ++++++++++++++++++++++++-------------------
 1 file changed, 80 insertions(+), 66 deletions(-)

diff --git a/widgets/msgviewer.go b/widgets/msgviewer.go
index a7b9fd6..d6085bf 100644
--- a/widgets/msgviewer.go
+++ b/widgets/msgviewer.go
@@ -562,75 +562,89 @@ func (pv *PartViewer) SetSource(reader io.Reader) {
}

func (pv *PartViewer) attemptCopy() {
	if pv.source != nil && pv.pager != nil && pv.pager.Process != nil {
		if pv.filter != nil {
			stdout, _ := pv.filter.StdoutPipe()
			stderr, _ := pv.filter.StderrPipe()
			pv.filter.Start()
			ch := make(chan interface{})
			go func() {
				_, err := io.Copy(pv.pagerin, stdout)
				if err != nil {
					pv.err = err
					pv.Invalidate()
				}
				stdout.Close()
				ch <- nil
			}()
			go func() {
				_, err := io.Copy(pv.pagerin, stderr)
				if err != nil {
					pv.err = err
					pv.Invalidate()
				}
				stderr.Close()
				ch <- nil
			}()
			go func() {
				<-ch
				<-ch
				pv.filter.Wait()
				pv.pagerin.Close()
			}()
	if pv.source == nil || pv.pager == nil || pv.pager.Process == nil {
		return
	}
	if pv.filter != nil {
		pv.copyFilterOutToPager() //delayed until we write to the sink
	}
	go func() {
		pv.writeMailHeaders()
		if pv.part.MIMEType == "text" {
			// if the content is plain we can strip ansi control chars
			pv.copySourceToSinkStripAnsi()
		} else {
			// if it's binary we have to rely on the filter to be sane
			io.Copy(pv.sink, pv.source)
		}
		go func() {
			info := pv.msg.MessageInfo()
			if pv.showHeaders && info.RFC822Headers != nil {
				// header need to bypass the filter, else we run into issues
				// with the filter messing with newlines etc.
				// hence all writes in this block go directly to the pager
				fields := info.RFC822Headers.Fields()
				for fields.Next() {
					var value string
					var err error
					if value, err = fields.Text(); err != nil {
						// better than nothing, use the non decoded version
						value = fields.Value()
					}
					field := fmt.Sprintf(
						"%s: %s\n", fields.Key(), value)
					pv.pagerin.Write([]byte(field))
				}
				// virtual header
				if len(info.Labels) != 0 {
					labels := fmtHeader(info, "Labels", "")
					pv.pagerin.Write([]byte(fmt.Sprintf("Labels: %s\n", labels)))
				}
				pv.pagerin.Write([]byte{'\n'})
		pv.sink.Close()
	}()
}

func (pv *PartViewer) writeMailHeaders() {
	info := pv.msg.MessageInfo()
	if pv.showHeaders && info.RFC822Headers != nil {
		// header need to bypass the filter, else we run into issues
		// with the filter messing with newlines etc.
		// hence all writes in this block go directly to the pager
		fields := info.RFC822Headers.Fields()
		for fields.Next() {
			var value string
			var err error
			if value, err = fields.Text(); err != nil {
				// better than nothing, use the non decoded version
				value = fields.Value()
			}
			field := fmt.Sprintf(
				"%s: %s\n", fields.Key(), value)
			pv.pagerin.Write([]byte(field))
		}
		// virtual header
		if len(info.Labels) != 0 {
			labels := fmtHeader(info, "Labels", "")
			pv.pagerin.Write([]byte(fmt.Sprintf("Labels: %s\n", labels)))
		}
		pv.pagerin.Write([]byte{'\n'})
	}
}

			if pv.part.MIMEType == "text" {
				scanner := bufio.NewScanner(pv.source)
				for scanner.Scan() {
					text := scanner.Text()
					text = ansi.ReplaceAllString(text, "")
					io.WriteString(pv.sink, text+"\n")
				}
			} else {
				io.Copy(pv.sink, pv.source)
			}
			pv.sink.Close()
		}()
func (pv *PartViewer) copyFilterOutToPager() {
	stdout, _ := pv.filter.StdoutPipe()
	stderr, _ := pv.filter.StderrPipe()
	pv.filter.Start()
	ch := make(chan interface{})
	go func() {
		_, err := io.Copy(pv.pagerin, stdout)
		if err != nil {
			pv.err = err
			pv.Invalidate()
		}
		stdout.Close()
		ch <- nil
	}()
	go func() {
		_, err := io.Copy(pv.pagerin, stderr)
		if err != nil {
			pv.err = err
			pv.Invalidate()
		}
		stderr.Close()
		ch <- nil
	}()
	go func() {
		<-ch
		<-ch
		pv.filter.Wait()
		pv.pagerin.Close()
	}()
}

func (pv *PartViewer) copySourceToSinkStripAnsi() {
	scanner := bufio.NewScanner(pv.source)
	for scanner.Scan() {
		text := scanner.Text()
		text = ansi.ReplaceAllString(text, "")
		io.WriteString(pv.sink, text+"\n")
	}
}

-- 
2.27.0

[PATCH 2/2] msgviewer: set max line length to 1 GB Export this patch

Some people send around huge html without any newline in between.
This did overflow the default 64KB buffer of bufio.Scanner.
If something can't fit in a GB there's no hope left

Also, ignoring errors is bad mkey
---
 widgets/msgviewer.go | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/widgets/msgviewer.go b/widgets/msgviewer.go
index d6085bf..107ff59 100644
--- a/widgets/msgviewer.go
+++ b/widgets/msgviewer.go
@@ -5,6 +5,7 @@ import (
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"regexp"
	"strings"
@@ -641,11 +642,18 @@ func (pv *PartViewer) copyFilterOutToPager() {

func (pv *PartViewer) copySourceToSinkStripAnsi() {
	scanner := bufio.NewScanner(pv.source)
	// some people send around huge html without any newline in between
	// this did overflow the default 64KB buffer of bufio.Scanner.
	// If something can't fit in a GB there's no hope left
	scanner.Buffer(nil, 1024*1024*1024)
	for scanner.Scan() {
		text := scanner.Text()
		text = ansi.ReplaceAllString(text, "")
		io.WriteString(pv.sink, text+"\n")
	}
	if err := scanner.Err(); err != nil {
		fmt.Fprintf(os.Stderr, "failed to read line: %v\n", err)
	}
}

func (pv *PartViewer) Invalidate() {
-- 
2.27.0
Both patches merged to master