shithub: furgit

ref: ab174c473618dd3743881cf44e02c2db4d1ecd5f
dir: /config/value.go/

View raw version
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)
	}
}