diff options
author | Jake Champlin <jake.champlin.27@gmail.com> | 2017-06-06 12:40:07 -0400 |
---|---|---|
committer | Jake Champlin <jake.champlin.27@gmail.com> | 2017-06-06 12:40:07 -0400 |
commit | bae9f6d2fd5eb5bc80929bd393932b23f14d7c93 (patch) | |
tree | ca9ab12a7d78b1fc27a8f734729081357ce6d252 /vendor/github.com/go-ini/ini/parser.go | |
parent | 254c495b6bebab3fb72a243c4bce858d79e6ee99 (diff) | |
download | terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.tar.gz terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.tar.zst terraform-provider-statuscake-bae9f6d2fd5eb5bc80929bd393932b23f14d7c93.zip |
Initial transfer of provider code
Diffstat (limited to 'vendor/github.com/go-ini/ini/parser.go')
-rw-r--r-- | vendor/github.com/go-ini/ini/parser.go | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/vendor/github.com/go-ini/ini/parser.go b/vendor/github.com/go-ini/ini/parser.go new file mode 100644 index 0000000..b0aabe3 --- /dev/null +++ b/vendor/github.com/go-ini/ini/parser.go | |||
@@ -0,0 +1,356 @@ | |||
1 | // Copyright 2015 Unknwon | ||
2 | // | ||
3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may | ||
4 | // not use this file except in compliance with the License. You may obtain | ||
5 | // a copy of the License at | ||
6 | // | ||
7 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | // | ||
9 | // Unless required by applicable law or agreed to in writing, software | ||
10 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
12 | // License for the specific language governing permissions and limitations | ||
13 | // under the License. | ||
14 | |||
15 | package ini | ||
16 | |||
17 | import ( | ||
18 | "bufio" | ||
19 | "bytes" | ||
20 | "fmt" | ||
21 | "io" | ||
22 | "strconv" | ||
23 | "strings" | ||
24 | "unicode" | ||
25 | ) | ||
26 | |||
27 | type tokenType int | ||
28 | |||
29 | const ( | ||
30 | _TOKEN_INVALID tokenType = iota | ||
31 | _TOKEN_COMMENT | ||
32 | _TOKEN_SECTION | ||
33 | _TOKEN_KEY | ||
34 | ) | ||
35 | |||
36 | type parser struct { | ||
37 | buf *bufio.Reader | ||
38 | isEOF bool | ||
39 | count int | ||
40 | comment *bytes.Buffer | ||
41 | } | ||
42 | |||
43 | func newParser(r io.Reader) *parser { | ||
44 | return &parser{ | ||
45 | buf: bufio.NewReader(r), | ||
46 | count: 1, | ||
47 | comment: &bytes.Buffer{}, | ||
48 | } | ||
49 | } | ||
50 | |||
51 | // BOM handles header of UTF-8, UTF-16 LE and UTF-16 BE's BOM format. | ||
52 | // http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding | ||
53 | func (p *parser) BOM() error { | ||
54 | mask, err := p.buf.Peek(2) | ||
55 | if err != nil && err != io.EOF { | ||
56 | return err | ||
57 | } else if len(mask) < 2 { | ||
58 | return nil | ||
59 | } | ||
60 | |||
61 | switch { | ||
62 | case mask[0] == 254 && mask[1] == 255: | ||
63 | fallthrough | ||
64 | case mask[0] == 255 && mask[1] == 254: | ||
65 | p.buf.Read(mask) | ||
66 | case mask[0] == 239 && mask[1] == 187: | ||
67 | mask, err := p.buf.Peek(3) | ||
68 | if err != nil && err != io.EOF { | ||
69 | return err | ||
70 | } else if len(mask) < 3 { | ||
71 | return nil | ||
72 | } | ||
73 | if mask[2] == 191 { | ||
74 | p.buf.Read(mask) | ||
75 | } | ||
76 | } | ||
77 | return nil | ||
78 | } | ||
79 | |||
80 | func (p *parser) readUntil(delim byte) ([]byte, error) { | ||
81 | data, err := p.buf.ReadBytes(delim) | ||
82 | if err != nil { | ||
83 | if err == io.EOF { | ||
84 | p.isEOF = true | ||
85 | } else { | ||
86 | return nil, err | ||
87 | } | ||
88 | } | ||
89 | return data, nil | ||
90 | } | ||
91 | |||
92 | func cleanComment(in []byte) ([]byte, bool) { | ||
93 | i := bytes.IndexAny(in, "#;") | ||
94 | if i == -1 { | ||
95 | return nil, false | ||
96 | } | ||
97 | return in[i:], true | ||
98 | } | ||
99 | |||
100 | func readKeyName(in []byte) (string, int, error) { | ||
101 | line := string(in) | ||
102 | |||
103 | // Check if key name surrounded by quotes. | ||
104 | var keyQuote string | ||
105 | if line[0] == '"' { | ||
106 | if len(line) > 6 && string(line[0:3]) == `"""` { | ||
107 | keyQuote = `"""` | ||
108 | } else { | ||
109 | keyQuote = `"` | ||
110 | } | ||
111 | } else if line[0] == '`' { | ||
112 | keyQuote = "`" | ||
113 | } | ||
114 | |||
115 | // Get out key name | ||
116 | endIdx := -1 | ||
117 | if len(keyQuote) > 0 { | ||
118 | startIdx := len(keyQuote) | ||
119 | // FIXME: fail case -> """"""name"""=value | ||
120 | pos := strings.Index(line[startIdx:], keyQuote) | ||
121 | if pos == -1 { | ||
122 | return "", -1, fmt.Errorf("missing closing key quote: %s", line) | ||
123 | } | ||
124 | pos += startIdx | ||
125 | |||
126 | // Find key-value delimiter | ||
127 | i := strings.IndexAny(line[pos+startIdx:], "=:") | ||
128 | if i < 0 { | ||
129 | return "", -1, ErrDelimiterNotFound{line} | ||
130 | } | ||
131 | endIdx = pos + i | ||
132 | return strings.TrimSpace(line[startIdx:pos]), endIdx + startIdx + 1, nil | ||
133 | } | ||
134 | |||
135 | endIdx = strings.IndexAny(line, "=:") | ||
136 | if endIdx < 0 { | ||
137 | return "", -1, ErrDelimiterNotFound{line} | ||
138 | } | ||
139 | return strings.TrimSpace(line[0:endIdx]), endIdx + 1, nil | ||
140 | } | ||
141 | |||
142 | func (p *parser) readMultilines(line, val, valQuote string) (string, error) { | ||
143 | for { | ||
144 | data, err := p.readUntil('\n') | ||
145 | if err != nil { | ||
146 | return "", err | ||
147 | } | ||
148 | next := string(data) | ||
149 | |||
150 | pos := strings.LastIndex(next, valQuote) | ||
151 | if pos > -1 { | ||
152 | val += next[:pos] | ||
153 | |||
154 | comment, has := cleanComment([]byte(next[pos:])) | ||
155 | if has { | ||
156 | p.comment.Write(bytes.TrimSpace(comment)) | ||
157 | } | ||
158 | break | ||
159 | } | ||
160 | val += next | ||
161 | if p.isEOF { | ||
162 | return "", fmt.Errorf("missing closing key quote from '%s' to '%s'", line, next) | ||
163 | } | ||
164 | } | ||
165 | return val, nil | ||
166 | } | ||
167 | |||
168 | func (p *parser) readContinuationLines(val string) (string, error) { | ||
169 | for { | ||
170 | data, err := p.readUntil('\n') | ||
171 | if err != nil { | ||
172 | return "", err | ||
173 | } | ||
174 | next := strings.TrimSpace(string(data)) | ||
175 | |||
176 | if len(next) == 0 { | ||
177 | break | ||
178 | } | ||
179 | val += next | ||
180 | if val[len(val)-1] != '\\' { | ||
181 | break | ||
182 | } | ||
183 | val = val[:len(val)-1] | ||
184 | } | ||
185 | return val, nil | ||
186 | } | ||
187 | |||
188 | // hasSurroundedQuote check if and only if the first and last characters | ||
189 | // are quotes \" or \'. | ||
190 | // It returns false if any other parts also contain same kind of quotes. | ||
191 | func hasSurroundedQuote(in string, quote byte) bool { | ||
192 | return len(in) > 2 && in[0] == quote && in[len(in)-1] == quote && | ||
193 | strings.IndexByte(in[1:], quote) == len(in)-2 | ||
194 | } | ||
195 | |||
196 | func (p *parser) readValue(in []byte, ignoreContinuation bool) (string, error) { | ||
197 | line := strings.TrimLeftFunc(string(in), unicode.IsSpace) | ||
198 | if len(line) == 0 { | ||
199 | return "", nil | ||
200 | } | ||
201 | |||
202 | var valQuote string | ||
203 | if len(line) > 3 && string(line[0:3]) == `"""` { | ||
204 | valQuote = `"""` | ||
205 | } else if line[0] == '`' { | ||
206 | valQuote = "`" | ||
207 | } | ||
208 | |||
209 | if len(valQuote) > 0 { | ||
210 | startIdx := len(valQuote) | ||
211 | pos := strings.LastIndex(line[startIdx:], valQuote) | ||
212 | // Check for multi-line value | ||
213 | if pos == -1 { | ||
214 | return p.readMultilines(line, line[startIdx:], valQuote) | ||
215 | } | ||
216 | |||
217 | return line[startIdx : pos+startIdx], nil | ||
218 | } | ||
219 | |||
220 | // Won't be able to reach here if value only contains whitespace. | ||
221 | line = strings.TrimSpace(line) | ||
222 | |||
223 | // Check continuation lines when desired. | ||
224 | if !ignoreContinuation && line[len(line)-1] == '\\' { | ||
225 | return p.readContinuationLines(line[:len(line)-1]) | ||
226 | } | ||
227 | |||
228 | i := strings.IndexAny(line, "#;") | ||
229 | if i > -1 { | ||
230 | p.comment.WriteString(line[i:]) | ||
231 | line = strings.TrimSpace(line[:i]) | ||
232 | } | ||
233 | |||
234 | // Trim single quotes | ||
235 | if hasSurroundedQuote(line, '\'') || | ||
236 | hasSurroundedQuote(line, '"') { | ||
237 | line = line[1 : len(line)-1] | ||
238 | } | ||
239 | return line, nil | ||
240 | } | ||
241 | |||
242 | // parse parses data through an io.Reader. | ||
243 | func (f *File) parse(reader io.Reader) (err error) { | ||
244 | p := newParser(reader) | ||
245 | if err = p.BOM(); err != nil { | ||
246 | return fmt.Errorf("BOM: %v", err) | ||
247 | } | ||
248 | |||
249 | // Ignore error because default section name is never empty string. | ||
250 | section, _ := f.NewSection(DEFAULT_SECTION) | ||
251 | |||
252 | var line []byte | ||
253 | var inUnparseableSection bool | ||
254 | for !p.isEOF { | ||
255 | line, err = p.readUntil('\n') | ||
256 | if err != nil { | ||
257 | return err | ||
258 | } | ||
259 | |||
260 | line = bytes.TrimLeftFunc(line, unicode.IsSpace) | ||
261 | if len(line) == 0 { | ||
262 | continue | ||
263 | } | ||
264 | |||
265 | // Comments | ||
266 | if line[0] == '#' || line[0] == ';' { | ||
267 | // Note: we do not care ending line break, | ||
268 | // it is needed for adding second line, | ||
269 | // so just clean it once at the end when set to value. | ||
270 | p.comment.Write(line) | ||
271 | continue | ||
272 | } | ||
273 | |||
274 | // Section | ||
275 | if line[0] == '[' { | ||
276 | // Read to the next ']' (TODO: support quoted strings) | ||
277 | // TODO(unknwon): use LastIndexByte when stop supporting Go1.4 | ||
278 | closeIdx := bytes.LastIndex(line, []byte("]")) | ||
279 | if closeIdx == -1 { | ||
280 | return fmt.Errorf("unclosed section: %s", line) | ||
281 | } | ||
282 | |||
283 | name := string(line[1:closeIdx]) | ||
284 | section, err = f.NewSection(name) | ||
285 | if err != nil { | ||
286 | return err | ||
287 | } | ||
288 | |||
289 | comment, has := cleanComment(line[closeIdx+1:]) | ||
290 | if has { | ||
291 | p.comment.Write(comment) | ||
292 | } | ||
293 | |||
294 | section.Comment = strings.TrimSpace(p.comment.String()) | ||
295 | |||
296 | // Reset aotu-counter and comments | ||
297 | p.comment.Reset() | ||
298 | p.count = 1 | ||
299 | |||
300 | inUnparseableSection = false | ||
301 | for i := range f.options.UnparseableSections { | ||
302 | if f.options.UnparseableSections[i] == name || | ||
303 | (f.options.Insensitive && strings.ToLower(f.options.UnparseableSections[i]) == strings.ToLower(name)) { | ||
304 | inUnparseableSection = true | ||
305 | continue | ||
306 | } | ||
307 | } | ||
308 | continue | ||
309 | } | ||
310 | |||
311 | if inUnparseableSection { | ||
312 | section.isRawSection = true | ||
313 | section.rawBody += string(line) | ||
314 | continue | ||
315 | } | ||
316 | |||
317 | kname, offset, err := readKeyName(line) | ||
318 | if err != nil { | ||
319 | // Treat as boolean key when desired, and whole line is key name. | ||
320 | if IsErrDelimiterNotFound(err) && f.options.AllowBooleanKeys { | ||
321 | key, err := section.NewKey(string(line), "true") | ||
322 | if err != nil { | ||
323 | return err | ||
324 | } | ||
325 | key.isBooleanType = true | ||
326 | key.Comment = strings.TrimSpace(p.comment.String()) | ||
327 | p.comment.Reset() | ||
328 | continue | ||
329 | } | ||
330 | return err | ||
331 | } | ||
332 | |||
333 | // Auto increment. | ||
334 | isAutoIncr := false | ||
335 | if kname == "-" { | ||
336 | isAutoIncr = true | ||
337 | kname = "#" + strconv.Itoa(p.count) | ||
338 | p.count++ | ||
339 | } | ||
340 | |||
341 | key, err := section.NewKey(kname, "") | ||
342 | if err != nil { | ||
343 | return err | ||
344 | } | ||
345 | key.isAutoIncrement = isAutoIncr | ||
346 | |||
347 | value, err := p.readValue(line[offset:], f.options.IgnoreContinuation) | ||
348 | if err != nil { | ||
349 | return err | ||
350 | } | ||
351 | key.SetValue(value) | ||
352 | key.Comment = strings.TrimSpace(p.comment.String()) | ||
353 | p.comment.Reset() | ||
354 | } | ||
355 | return nil | ||
356 | } | ||