aboutsummaryrefslogtreecommitdiff
path: root/config/value.go
blob: 3ade9c16bc42bbe02b8520ade90eab4d55bd07b7 (about) (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package config

import (
	"bytes"
	"errors"
	"fmt"
	"io"
)

func (p *configParser) parseValue() (string, error) {
	var (
		value     bytes.Buffer
		inQuote   bool
		inComment bool
	)

	trimLen := 0

	for {
		ch, err := p.nextChar()
		if errors.Is(err, io.EOF) {
			if inQuote {
				return "", errors.New("unexpected EOF in quoted value")
			}

			if trimLen > 0 {
				return truncateAtNUL(value.String()[:trimLen]), nil
			}

			return truncateAtNUL(value.String()), nil
		}

		if err != nil {
			return "", err
		}

		if ch == '\n' {
			if inQuote {
				return "", errors.New("newline in quoted value")
			}

			if trimLen > 0 {
				return truncateAtNUL(value.String()[:trimLen]), nil
			}

			return truncateAtNUL(value.String()), nil
		}

		if inComment {
			continue
		}

		if isWhitespace(ch) && !inQuote {
			if trimLen == 0 && value.Len() > 0 {
				trimLen = value.Len()
			}

			if value.Len() > 0 {
				value.WriteByte(ch)
			}

			continue
		}

		if !inQuote && (ch == '#' || ch == ';') {
			inComment = true

			continue
		}

		if trimLen > 0 {
			trimLen = 0
		}

		if ch == '\\' {
			next, err := p.nextChar()
			if errors.Is(err, io.EOF) {
				return "", errors.New("unexpected EOF after backslash")
			}

			if err != nil {
				return "", err
			}

			switch next {
			case '\n':
				continue
			case 'n':
				value.WriteByte('\n')
			case 't':
				value.WriteByte('\t')
			case 'b':
				value.WriteByte('\b')
			case '\\', '"':
				value.WriteByte(next)
			default:
				return "", fmt.Errorf("invalid escape sequence: \\%c", next)
			}

			continue
		}

		if ch == '"' {
			inQuote = !inQuote

			continue
		}

		value.WriteByte(ch)
	}
}