From f6dd32046eabfc31671c39d98581bbc3899d6b27 Mon Sep 17 00:00:00 2001
From: Giulio Iotti <dullgiulio@gmail.com>
Date: Mon, 8 Jun 2015 06:36:21 +0000
Subject: [PATCH] add support to nil-byte separated input strings, closes #121

---
 src/core.go    |  4 ++--
 src/options.go |  5 +++++
 src/reader.go  | 36 +++++++++++++++---------------------
 3 files changed, 22 insertions(+), 23 deletions(-)

diff --git a/src/core.go b/src/core.go
index 61cabf9e..c7277080 100644
--- a/src/core.go
+++ b/src/core.go
@@ -113,7 +113,7 @@ func Run(opts *Options) {
 	// Reader
 	streamingFilter := opts.Filter != nil && !sort && !opts.Tac && !opts.Sync
 	if !streamingFilter {
-		reader := Reader{func(str string) { chunkList.Push(str) }, eventBox}
+		reader := Reader{func(str string) { chunkList.Push(str) }, eventBox, opts.ReadZero}
 		go reader.ReadSource()
 	}
 
@@ -139,7 +139,7 @@ func Run(opts *Options) {
 					if pattern.MatchItem(item) {
 						fmt.Println(*item.text)
 					}
-				}, eventBox}
+				}, eventBox, opts.ReadZero}
 			reader.ReadSource()
 		} else {
 			eventBox.Unwatch(EvtReadNew)
diff --git a/src/options.go b/src/options.go
index 63037eaa..70d81cd2 100644
--- a/src/options.go
+++ b/src/options.go
@@ -50,6 +50,7 @@ const usage = `usage: fzf [options]
     -1, --select-1        Automatically select the only match
     -0, --exit-0          Exit immediately when there's no match
     -f, --filter=STR      Filter mode. Do not start interactive finder.
+        --null            Read null-byte separated strings from input
         --print-query     Print query as the first line
         --expect=KEYS     Comma-separated list of keys to complete fzf
         --sync            Synchronous search for multi-staged filtering
@@ -117,6 +118,7 @@ type Options struct {
 	Expect     []int
 	Keymap     map[int]actionType
 	PrintQuery bool
+	ReadZero   bool
 	Sync       bool
 	Version    bool
 }
@@ -155,6 +157,7 @@ func defaultOptions() *Options {
 		Expect:     []int{},
 		Keymap:     defaultKeymap(),
 		PrintQuery: false,
+		ReadZero:   false,
 		Sync:       false,
 		Version:    false}
 }
@@ -525,6 +528,8 @@ func parseOptions(opts *Options, allArgs []string) {
 			opts.Exit0 = true
 		case "+0", "--no-exit-0":
 			opts.Exit0 = false
+		case "--null":
+			opts.ReadZero = true
 		case "--print-query":
 			opts.PrintQuery = true
 		case "--no-print-query":
diff --git a/src/reader.go b/src/reader.go
index 7496b775..356c2db8 100644
--- a/src/reader.go
+++ b/src/reader.go
@@ -13,6 +13,7 @@ import (
 type Reader struct {
 	pusher   func(string)
 	eventBox *util.EventBox
+	delimNil bool
 }
 
 // ReadSource reads data from the default command or from standard input
@@ -30,31 +31,24 @@ func (r *Reader) ReadSource() {
 }
 
 func (r *Reader) feed(src io.Reader) {
+	delim := byte('\n')
+	if r.delimNil {
+		delim = '\000'
+	}
 	reader := bufio.NewReader(src)
-	eof := false
-Loop:
-	for !eof {
-		buf := []byte{}
-		iter := 0 // TODO: max size?
-		for {
-			// "ReadLine either returns a non-nil line or it returns an error, never both"
-			line, isPrefix, err := reader.ReadLine()
-			eof = err == io.EOF
-			if eof {
-				break
-			} else if err != nil {
-				break Loop
-			}
-			iter++
-			buf = append(buf, line...)
-			if !isPrefix {
-				break
+	for {
+		line, err := reader.ReadString(delim)
+		if line != "" {
+			// "ReadString returns err != nil if and only if the returned data does not end in delim."
+			if err == nil {
+				line = line[:len(line)-1]
 			}
-		}
-		if iter > 0 {
-			r.pusher(string(buf))
+			r.pusher(line)
 			r.eventBox.Set(EvtReadNew, nil)
 		}
+		if err != nil {
+			break
+		}
 	}
 }
 
-- 
GitLab