162 |
162 |
163 endIdx = strings.IndexAny(line, delimiters) |
163 endIdx = strings.IndexAny(line, delimiters) |
164 if endIdx < 0 { |
164 if endIdx < 0 { |
165 return "", -1, ErrDelimiterNotFound{line} |
165 return "", -1, ErrDelimiterNotFound{line} |
166 } |
166 } |
|
167 if endIdx == 0 { |
|
168 return "", -1, ErrEmptyKeyName{line} |
|
169 } |
|
170 |
167 return strings.TrimSpace(line[0:endIdx]), endIdx + 1, nil |
171 return strings.TrimSpace(line[0:endIdx]), endIdx + 1, nil |
168 } |
172 } |
169 |
173 |
170 func (p *parser) readMultilines(line, val, valQuote string) (string, error) { |
174 func (p *parser) readMultilines(line, val, valQuote string) (string, error) { |
171 for { |
175 for { |
287 // Trim single and double quotes |
291 // Trim single and double quotes |
288 if (hasSurroundedQuote(line, '\'') || |
292 if (hasSurroundedQuote(line, '\'') || |
289 hasSurroundedQuote(line, '"')) && !p.options.PreserveSurroundedQuote { |
293 hasSurroundedQuote(line, '"')) && !p.options.PreserveSurroundedQuote { |
290 line = line[1 : len(line)-1] |
294 line = line[1 : len(line)-1] |
291 } else if len(valQuote) == 0 && p.options.UnescapeValueCommentSymbols { |
295 } else if len(valQuote) == 0 && p.options.UnescapeValueCommentSymbols { |
292 if strings.Contains(line, `\;`) { |
296 line = strings.ReplaceAll(line, `\;`, ";") |
293 line = strings.Replace(line, `\;`, ";", -1) |
297 line = strings.ReplaceAll(line, `\#`, "#") |
294 } |
|
295 if strings.Contains(line, `\#`) { |
|
296 line = strings.Replace(line, `\#`, "#", -1) |
|
297 } |
|
298 } else if p.options.AllowPythonMultilineValues && lastChar == '\n' { |
298 } else if p.options.AllowPythonMultilineValues && lastChar == '\n' { |
299 return p.readPythonMultilines(line, bufferSize) |
299 return p.readPythonMultilines(line, bufferSize) |
300 } |
300 } |
301 |
301 |
302 return line, nil |
302 return line, nil |
304 |
304 |
305 func (p *parser) readPythonMultilines(line string, bufferSize int) (string, error) { |
305 func (p *parser) readPythonMultilines(line string, bufferSize int) (string, error) { |
306 parserBufferPeekResult, _ := p.buf.Peek(bufferSize) |
306 parserBufferPeekResult, _ := p.buf.Peek(bufferSize) |
307 peekBuffer := bytes.NewBuffer(parserBufferPeekResult) |
307 peekBuffer := bytes.NewBuffer(parserBufferPeekResult) |
308 |
308 |
309 indentSize := 0 |
|
310 for { |
309 for { |
311 peekData, peekErr := peekBuffer.ReadBytes('\n') |
310 peekData, peekErr := peekBuffer.ReadBytes('\n') |
312 if peekErr != nil { |
311 if peekErr != nil && peekErr != io.EOF { |
313 if peekErr == io.EOF { |
|
314 p.debug("readPythonMultilines: io.EOF, peekData: %q, line: %q", string(peekData), line) |
|
315 return line, nil |
|
316 } |
|
317 |
|
318 p.debug("readPythonMultilines: failed to peek with error: %v", peekErr) |
312 p.debug("readPythonMultilines: failed to peek with error: %v", peekErr) |
319 return "", peekErr |
313 return "", peekErr |
320 } |
314 } |
321 |
315 |
322 p.debug("readPythonMultilines: parsing %q", string(peekData)) |
316 p.debug("readPythonMultilines: parsing %q", string(peekData)) |
331 if len(peekMatches) != 3 { |
325 if len(peekMatches) != 3 { |
332 p.debug("readPythonMultilines: end of value, got: %q", line) |
326 p.debug("readPythonMultilines: end of value, got: %q", line) |
333 return line, nil |
327 return line, nil |
334 } |
328 } |
335 |
329 |
336 // Determine indent size and line prefix. |
|
337 currentIndentSize := len(peekMatches[1]) |
|
338 if indentSize < 1 { |
|
339 indentSize = currentIndentSize |
|
340 p.debug("readPythonMultilines: indent size is %d", indentSize) |
|
341 } |
|
342 |
|
343 // Make sure each line is indented at least as far as first line. |
|
344 if currentIndentSize < indentSize { |
|
345 p.debug("readPythonMultilines: end of value, current indent: %d, expected indent: %d, line: %q", currentIndentSize, indentSize, line) |
|
346 return line, nil |
|
347 } |
|
348 |
|
349 // Advance the parser reader (buffer) in-sync with the peek buffer. |
330 // Advance the parser reader (buffer) in-sync with the peek buffer. |
350 _, err := p.buf.Discard(len(peekData)) |
331 _, err := p.buf.Discard(len(peekData)) |
351 if err != nil { |
332 if err != nil { |
352 p.debug("readPythonMultilines: failed to skip to the end, returning error") |
333 p.debug("readPythonMultilines: failed to skip to the end, returning error") |
353 return "", err |
334 return "", err |
354 } |
335 } |
355 |
336 |
356 // Handle indented empty line. |
337 line += "\n" + peekMatches[0] |
357 line += "\n" + peekMatches[1][indentSize:] + peekMatches[2] |
|
358 } |
338 } |
359 } |
339 } |
360 |
340 |
361 // parse parses data through an io.Reader. |
341 // parse parses data through an io.Reader. |
362 func (f *File) parse(reader io.Reader) (err error) { |
342 func (f *File) parse(reader io.Reader) (err error) { |
463 section.Comment = strings.TrimSpace(p.comment.String()) |
443 section.Comment = strings.TrimSpace(p.comment.String()) |
464 |
444 |
465 // Reset auto-counter and comments |
445 // Reset auto-counter and comments |
466 p.comment.Reset() |
446 p.comment.Reset() |
467 p.count = 1 |
447 p.count = 1 |
|
448 // Nested values can't span sections |
|
449 isLastValueEmpty = false |
468 |
450 |
469 inUnparseableSection = false |
451 inUnparseableSection = false |
470 for i := range f.options.UnparseableSections { |
452 for i := range f.options.UnparseableSections { |
471 if f.options.UnparseableSections[i] == name || |
453 if f.options.UnparseableSections[i] == name || |
472 ((f.options.Insensitive || f.options.InsensitiveSections) && strings.EqualFold(f.options.UnparseableSections[i], name)) { |
454 ((f.options.Insensitive || f.options.InsensitiveSections) && strings.EqualFold(f.options.UnparseableSections[i], name)) { |
483 continue |
465 continue |
484 } |
466 } |
485 |
467 |
486 kname, offset, err := readKeyName(f.options.KeyValueDelimiters, line) |
468 kname, offset, err := readKeyName(f.options.KeyValueDelimiters, line) |
487 if err != nil { |
469 if err != nil { |
|
470 switch { |
488 // Treat as boolean key when desired, and whole line is key name. |
471 // Treat as boolean key when desired, and whole line is key name. |
489 if IsErrDelimiterNotFound(err) { |
472 case IsErrDelimiterNotFound(err): |
490 switch { |
473 switch { |
491 case f.options.AllowBooleanKeys: |
474 case f.options.AllowBooleanKeys: |
492 kname, err := p.readValue(line, parserBufferSize) |
475 kname, err := p.readValue(line, parserBufferSize) |
493 if err != nil { |
476 if err != nil { |
494 return err |
477 return err |