5
5
package debug
6
6
7
7
import (
8
- "bytes"
9
8
"fmt"
10
9
"runtime"
10
+ "strconv"
11
11
"strings"
12
12
)
13
13
@@ -23,8 +23,8 @@ func ReadBuildInfo() (info *BuildInfo, ok bool) {
23
23
return nil , false
24
24
}
25
25
data = data [16 : len (data )- 16 ]
26
- bi := & BuildInfo {}
27
- if err := bi . UnmarshalText ([] byte ( data )); err != nil {
26
+ bi , err := ParseBuildInfo ( data )
27
+ if err != nil {
28
28
return nil , false
29
29
}
30
30
@@ -63,8 +63,18 @@ type BuildSetting struct {
63
63
Key , Value string
64
64
}
65
65
66
- func (bi * BuildInfo ) MarshalText () ([]byte , error ) {
67
- buf := & bytes.Buffer {}
66
+ // quoteKey reports whether key is required to be quoted.
67
+ func quoteKey (key string ) bool {
68
+ return len (key ) == 0 || strings .ContainsAny (key , "= \t \r \n \" `" )
69
+ }
70
+
71
+ // quoteValue reports whether value is required to be quoted.
72
+ func quoteValue (value string ) bool {
73
+ return strings .ContainsAny (value , " \t \r \n \" `" )
74
+ }
75
+
76
+ func (bi * BuildInfo ) String () string {
77
+ buf := new (strings.Builder )
68
78
if bi .GoVersion != "" {
69
79
fmt .Fprintf (buf , "go\t %s\n " , bi .GoVersion )
70
80
}
@@ -76,12 +86,8 @@ func (bi *BuildInfo) MarshalText() ([]byte, error) {
76
86
buf .WriteString (word )
77
87
buf .WriteByte ('\t' )
78
88
buf .WriteString (m .Path )
79
- mv := m .Version
80
- if mv == "" {
81
- mv = "(devel)"
82
- }
83
89
buf .WriteByte ('\t' )
84
- buf .WriteString (mv )
90
+ buf .WriteString (m . Version )
85
91
if m .Replace == nil {
86
92
buf .WriteByte ('\t' )
87
93
buf .WriteString (m .Sum )
@@ -91,27 +97,28 @@ func (bi *BuildInfo) MarshalText() ([]byte, error) {
91
97
}
92
98
buf .WriteByte ('\n' )
93
99
}
94
- if bi .Main . Path != "" {
100
+ if bi .Main != ( Module {}) {
95
101
formatMod ("mod" , bi .Main )
96
102
}
97
103
for _ , dep := range bi .Deps {
98
104
formatMod ("dep" , * dep )
99
105
}
100
106
for _ , s := range bi .Settings {
101
- if strings .ContainsAny (s .Key , "= \t \n " ) {
102
- return nil , fmt .Errorf ("invalid build setting key %q" , s .Key )
107
+ key := s .Key
108
+ if quoteKey (key ) {
109
+ key = strconv .Quote (key )
103
110
}
104
- if strings .Contains (s .Value , "\n " ) {
105
- return nil , fmt .Errorf ("invalid build setting value for key %q: contains newline" , s .Value )
111
+ value := s .Value
112
+ if quoteValue (value ) {
113
+ value = strconv .Quote (value )
106
114
}
107
- fmt .Fprintf (buf , "build\t %s=%s\n " , s . Key , s . Value )
115
+ fmt .Fprintf (buf , "build\t %s=%s\n " , key , value )
108
116
}
109
117
110
- return buf .Bytes (), nil
118
+ return buf .String ()
111
119
}
112
120
113
- func (bi * BuildInfo ) UnmarshalText (data []byte ) (err error ) {
114
- * bi = BuildInfo {}
121
+ func ParseBuildInfo (data string ) (bi * BuildInfo , err error ) {
115
122
lineNum := 1
116
123
defer func () {
117
124
if err != nil {
@@ -120,85 +127,133 @@ func (bi *BuildInfo) UnmarshalText(data []byte) (err error) {
120
127
}()
121
128
122
129
var (
123
- pathLine = [] byte ( "path\t " )
124
- modLine = [] byte ( "mod\t " )
125
- depLine = [] byte ( "dep\t " )
126
- repLine = [] byte ( "=>\t " )
127
- buildLine = [] byte ( "build\t " )
128
- newline = [] byte ( "\n " )
129
- tab = [] byte ( "\t " )
130
+ pathLine = "path\t "
131
+ modLine = "mod\t "
132
+ depLine = "dep\t "
133
+ repLine = "=>\t "
134
+ buildLine = "build\t "
135
+ newline = "\n "
136
+ tab = "\t "
130
137
)
131
138
132
- readModuleLine := func (elem [][] byte ) (Module , error ) {
139
+ readModuleLine := func (elem []string ) (Module , error ) {
133
140
if len (elem ) != 2 && len (elem ) != 3 {
134
141
return Module {}, fmt .Errorf ("expected 2 or 3 columns; got %d" , len (elem ))
135
142
}
143
+ version := elem [1 ]
136
144
sum := ""
137
145
if len (elem ) == 3 {
138
- sum = string ( elem [2 ])
146
+ sum = elem [2 ]
139
147
}
140
148
return Module {
141
- Path : string ( elem [0 ]) ,
142
- Version : string ( elem [ 1 ]) ,
149
+ Path : elem [0 ],
150
+ Version : version ,
143
151
Sum : sum ,
144
152
}, nil
145
153
}
146
154
155
+ bi = new (BuildInfo )
147
156
var (
148
157
last * Module
149
- line [] byte
158
+ line string
150
159
ok bool
151
160
)
152
161
// Reverse of BuildInfo.String(), except for go version.
153
162
for len (data ) > 0 {
154
- line , data , ok = bytes .Cut (data , newline )
163
+ line , data , ok = strings .Cut (data , newline )
155
164
if ! ok {
156
165
break
157
166
}
158
167
switch {
159
- case bytes .HasPrefix (line , pathLine ):
168
+ case strings .HasPrefix (line , pathLine ):
160
169
elem := line [len (pathLine ):]
161
170
bi .Path = string (elem )
162
- case bytes .HasPrefix (line , modLine ):
163
- elem := bytes .Split (line [len (modLine ):], tab )
171
+ case strings .HasPrefix (line , modLine ):
172
+ elem := strings .Split (line [len (modLine ):], tab )
164
173
last = & bi .Main
165
174
* last , err = readModuleLine (elem )
166
175
if err != nil {
167
- return err
176
+ return nil , err
168
177
}
169
- case bytes .HasPrefix (line , depLine ):
170
- elem := bytes .Split (line [len (depLine ):], tab )
178
+ case strings .HasPrefix (line , depLine ):
179
+ elem := strings .Split (line [len (depLine ):], tab )
171
180
last = new (Module )
172
181
bi .Deps = append (bi .Deps , last )
173
182
* last , err = readModuleLine (elem )
174
183
if err != nil {
175
- return err
184
+ return nil , err
176
185
}
177
- case bytes .HasPrefix (line , repLine ):
178
- elem := bytes .Split (line [len (repLine ):], tab )
186
+ case strings .HasPrefix (line , repLine ):
187
+ elem := strings .Split (line [len (repLine ):], tab )
179
188
if len (elem ) != 3 {
180
- return fmt .Errorf ("expected 3 columns for replacement; got %d" , len (elem ))
189
+ return nil , fmt .Errorf ("expected 3 columns for replacement; got %d" , len (elem ))
181
190
}
182
191
if last == nil {
183
- return fmt .Errorf ("replacement with no module on previous line" )
192
+ return nil , fmt .Errorf ("replacement with no module on previous line" )
184
193
}
185
194
last .Replace = & Module {
186
195
Path : string (elem [0 ]),
187
196
Version : string (elem [1 ]),
188
197
Sum : string (elem [2 ]),
189
198
}
190
199
last = nil
191
- case bytes .HasPrefix (line , buildLine ):
192
- key , val , ok := strings .Cut (string (line [len (buildLine ):]), "=" )
193
- if ! ok {
194
- return fmt .Errorf ("invalid build line" )
200
+ case strings .HasPrefix (line , buildLine ):
201
+ kv := line [len (buildLine ):]
202
+ if len (kv ) < 1 {
203
+ return nil , fmt .Errorf ("build line missing '='" )
204
+ }
205
+
206
+ var key , rawValue string
207
+ switch kv [0 ] {
208
+ case '=' :
209
+ return nil , fmt .Errorf ("build line with missing key" )
210
+
211
+ case '`' , '"' :
212
+ rawKey , err := strconv .QuotedPrefix (kv )
213
+ if err != nil {
214
+ return nil , fmt .Errorf ("invalid quoted key in build line" )
215
+ }
216
+ if len (kv ) == len (rawKey ) {
217
+ return nil , fmt .Errorf ("build line missing '=' after quoted key" )
218
+ }
219
+ if c := kv [len (rawKey )]; c != '=' {
220
+ return nil , fmt .Errorf ("unexpected character after quoted key: %q" , c )
221
+ }
222
+ key , _ = strconv .Unquote (rawKey )
223
+ rawValue = kv [len (rawKey )+ 1 :]
224
+
225
+ default :
226
+ var ok bool
227
+ key , rawValue , ok = strings .Cut (kv , "=" )
228
+ if ! ok {
229
+ return nil , fmt .Errorf ("build line missing '=' after key" )
230
+ }
231
+ if quoteKey (key ) {
232
+ return nil , fmt .Errorf ("unquoted key %q must be quoted" , key )
233
+ }
195
234
}
196
- if key == "" {
197
- return fmt .Errorf ("empty key" )
235
+
236
+ var value string
237
+ if len (rawValue ) > 0 {
238
+ switch rawValue [0 ] {
239
+ case '`' , '"' :
240
+ var err error
241
+ value , err = strconv .Unquote (rawValue )
242
+ if err != nil {
243
+ return nil , fmt .Errorf ("invalid quoted value in build line" )
244
+ }
245
+
246
+ default :
247
+ value = rawValue
248
+ if quoteValue (value ) {
249
+ return nil , fmt .Errorf ("unquoted value %q must be quoted" , value )
250
+ }
251
+ }
198
252
}
199
- bi .Settings = append (bi .Settings , BuildSetting {Key : key , Value : val })
253
+
254
+ bi .Settings = append (bi .Settings , BuildSetting {Key : key , Value : value })
200
255
}
201
256
lineNum ++
202
257
}
203
- return nil
258
+ return bi , nil
204
259
}
0 commit comments