1package ini
2
3import (
4	"fmt"
5)
6
7// getStringValue will return a quoted string and the amount
8// of bytes read
9//
10// an error will be returned if the string is not properly formatted
11func getStringValue(b []rune) (int, error) {
12	if b[0] != '"' {
13		return 0, NewParseError("strings must start with '\"'")
14	}
15
16	endQuote := false
17	i := 1
18
19	for ; i < len(b) && !endQuote; i++ {
20		if escaped := isEscaped(b[:i], b[i]); b[i] == '"' && !escaped {
21			endQuote = true
22			break
23		} else if escaped {
24			/*c, err := getEscapedByte(b[i])
25			if err != nil {
26				return 0, err
27			}
28
29			b[i-1] = c
30			b = append(b[:i], b[i+1:]...)
31			i--*/
32
33			continue
34		}
35	}
36
37	if !endQuote {
38		return 0, NewParseError("missing '\"' in string value")
39	}
40
41	return i + 1, nil
42}
43
44// getBoolValue will return a boolean and the amount
45// of bytes read
46//
47// an error will be returned if the boolean is not of a correct
48// value
49func getBoolValue(b []rune) (int, error) {
50	if len(b) < 4 {
51		return 0, NewParseError("invalid boolean value")
52	}
53
54	n := 0
55	for _, lv := range literalValues {
56		if len(lv) > len(b) {
57			continue
58		}
59
60		if isLitValue(lv, b) {
61			n = len(lv)
62		}
63	}
64
65	if n == 0 {
66		return 0, NewParseError("invalid boolean value")
67	}
68
69	return n, nil
70}
71
72// getNumericalValue will return a numerical string, the amount
73// of bytes read, and the base of the number
74//
75// an error will be returned if the number is not of a correct
76// value
77func getNumericalValue(b []rune) (int, int, error) {
78	if !isDigit(b[0]) {
79		return 0, 0, NewParseError("invalid digit value")
80	}
81
82	i := 0
83	helper := numberHelper{}
84
85loop:
86	for negativeIndex := 0; i < len(b); i++ {
87		negativeIndex++
88
89		if !isDigit(b[i]) {
90			switch b[i] {
91			case '-':
92				if helper.IsNegative() || negativeIndex != 1 {
93					return 0, 0, NewParseError("parse error '-'")
94				}
95
96				n := getNegativeNumber(b[i:])
97				i += (n - 1)
98				helper.Determine(b[i])
99				continue
100			case '.':
101				if err := helper.Determine(b[i]); err != nil {
102					return 0, 0, err
103				}
104			case 'e', 'E':
105				if err := helper.Determine(b[i]); err != nil {
106					return 0, 0, err
107				}
108
109				negativeIndex = 0
110			case 'b':
111				if helper.numberFormat == hex {
112					break
113				}
114				fallthrough
115			case 'o', 'x':
116				if i == 0 && b[i] != '0' {
117					return 0, 0, NewParseError("incorrect base format, expected leading '0'")
118				}
119
120				if i != 1 {
121					return 0, 0, NewParseError(fmt.Sprintf("incorrect base format found %s at %d index", string(b[i]), i))
122				}
123
124				if err := helper.Determine(b[i]); err != nil {
125					return 0, 0, err
126				}
127			default:
128				if isWhitespace(b[i]) {
129					break loop
130				}
131
132				if isNewline(b[i:]) {
133					break loop
134				}
135
136				if !(helper.numberFormat == hex && isHexByte(b[i])) {
137					if i+2 < len(b) && !isNewline(b[i:i+2]) {
138						return 0, 0, NewParseError("invalid numerical character")
139					} else if !isNewline([]rune{b[i]}) {
140						return 0, 0, NewParseError("invalid numerical character")
141					}
142
143					break loop
144				}
145			}
146		}
147	}
148
149	return helper.Base(), i, nil
150}
151
152// isDigit will return whether or not something is an integer
153func isDigit(b rune) bool {
154	return b >= '0' && b <= '9'
155}
156
157func hasExponent(v []rune) bool {
158	return contains(v, 'e') || contains(v, 'E')
159}
160
161func isBinaryByte(b rune) bool {
162	switch b {
163	case '0', '1':
164		return true
165	default:
166		return false
167	}
168}
169
170func isOctalByte(b rune) bool {
171	switch b {
172	case '0', '1', '2', '3', '4', '5', '6', '7':
173		return true
174	default:
175		return false
176	}
177}
178
179func isHexByte(b rune) bool {
180	if isDigit(b) {
181		return true
182	}
183	return (b >= 'A' && b <= 'F') ||
184		(b >= 'a' && b <= 'f')
185}
186
187func getValue(b []rune) (int, error) {
188	i := 0
189
190	for i < len(b) {
191		if isNewline(b[i:]) {
192			break
193		}
194
195		if isOp(b[i:]) {
196			break
197		}
198
199		valid, n, err := isValid(b[i:])
200		if err != nil {
201			return 0, err
202		}
203
204		if !valid {
205			break
206		}
207
208		i += n
209	}
210
211	return i, nil
212}
213
214// getNegativeNumber will return a negative number from a
215// byte slice. This will iterate through all characters until
216// a non-digit has been found.
217func getNegativeNumber(b []rune) int {
218	if b[0] != '-' {
219		return 0
220	}
221
222	i := 1
223	for ; i < len(b); i++ {
224		if !isDigit(b[i]) {
225			return i
226		}
227	}
228
229	return i
230}
231
232// isEscaped will return whether or not the character is an escaped
233// character.
234func isEscaped(value []rune, b rune) bool {
235	if len(value) == 0 {
236		return false
237	}
238
239	switch b {
240	case '\'': // single quote
241	case '"': // quote
242	case 'n': // newline
243	case 't': // tab
244	case '\\': // backslash
245	default:
246		return false
247	}
248
249	return value[len(value)-1] == '\\'
250}
251
252func getEscapedByte(b rune) (rune, error) {
253	switch b {
254	case '\'': // single quote
255		return '\'', nil
256	case '"': // quote
257		return '"', nil
258	case 'n': // newline
259		return '\n', nil
260	case 't': // table
261		return '\t', nil
262	case '\\': // backslash
263		return '\\', nil
264	default:
265		return b, NewParseError(fmt.Sprintf("invalid escaped character %c", b))
266	}
267}
268
269func removeEscapedCharacters(b []rune) []rune {
270	for i := 0; i < len(b); i++ {
271		if isEscaped(b[:i], b[i]) {
272			c, err := getEscapedByte(b[i])
273			if err != nil {
274				return b
275			}
276
277			b[i-1] = c
278			b = append(b[:i], b[i+1:]...)
279			i--
280		}
281	}
282
283	return b
284}
285