@@ -3,8 +3,10 @@ package main
33import (
44 "encoding/json"
55 "fmt"
6+ "io"
67 "io/ioutil"
78 "os"
9+ "sort"
810
911 "github.com/jmespath/jp/Godeps/_workspace/src/github.com/codegangsta/cli"
1012 "github.com/jmespath/jp/Godeps/_workspace/src/github.com/jmespath/go-jmespath"
@@ -83,19 +85,28 @@ func runMain(c *cli.Context) int {
8385 return 0
8486 }
8587 var input interface {}
86- var jsonParser * json. Decoder
88+ var inputStream io. Reader
8789 if c .String ("filename" ) != "" {
8890 f , err := os .Open (c .String ("filename" ))
8991 if err != nil {
9092 return errMsg ("Error opening input file: %s" , err )
9193 }
92- jsonParser = json . NewDecoder ( f )
94+ inputStream = f
9395
9496 } else {
95- jsonParser = json . NewDecoder ( os .Stdin )
97+ inputStream = os .Stdin
9698 }
99+ newlineNumberReader := NewLineNumberReader (inputStream )
100+ jsonParser := json .NewDecoder (newlineNumberReader )
97101 if err := jsonParser .Decode (& input ); err != nil {
98- errMsg ("Error parsing input json: %s\n " , err )
102+ syntaxError , ok := err .(* json.SyntaxError )
103+ if ok && syntaxError .Offset == int64 (int (syntaxError .Offset )) {
104+ line , char := newlineNumberReader .ConvertOffset (int (syntaxError .Offset ))
105+ errMsg ("Error parsing input json: %s (line: %d, char: %d)\n " ,
106+ syntaxError , line , char )
107+ } else {
108+ errMsg ("Error parsing input json: %s" , err )
109+ }
99110 return 2
100111 }
101112 result , err := jmespath .Search (expression , input )
@@ -121,3 +132,47 @@ func runMain(c *cli.Context) int {
121132 os .Stdout .WriteString ("\n " )
122133 return 0
123134}
135+
136+ type LineNumberReader struct {
137+ actualReader io.Reader
138+ newlinePositions []int
139+ bytesRead int
140+ }
141+
142+ func NewLineNumberReader (actualReader io.Reader ) * LineNumberReader {
143+ return & LineNumberReader {
144+ actualReader : actualReader ,
145+ }
146+ }
147+
148+ func (lnr * LineNumberReader ) Read (p []byte ) (n int , err error ) {
149+ n , err = lnr .actualReader .Read (p )
150+
151+ if err != nil || n == 0 {
152+ return
153+ }
154+
155+ for i , v := range p {
156+ if i >= n {
157+ return
158+ }
159+
160+ if v == '\n' {
161+ // add 1 so we record the position of the first character, not the '\n'
162+ lnr .newlinePositions = append (lnr .newlinePositions , lnr .bytesRead + i + 1 )
163+ }
164+ }
165+
166+ lnr .bytesRead = lnr .bytesRead + n
167+ return
168+ }
169+
170+ func (lnr * LineNumberReader ) ConvertOffset (offset int ) (linePos int , charPos int ) {
171+ index := sort .SearchInts (lnr .newlinePositions , offset )
172+ // Humans are 1 indexed...
173+ if index == 0 {
174+ return 1 , offset
175+ } else {
176+ return index + 1 , offset - lnr .newlinePositions [index - 1 ]
177+ }
178+ }
0 commit comments