ecadlabs/signatory

View on GitHub
pkg/utils/utils.go

Summary

Maintainability
C
7 hrs
Test Coverage
C
73%
package utils

import (
    "errors"
    "fmt"
    "strings"
    "syscall"
    "unicode"
    "unicode/utf8"

    "golang.org/x/term"
)

var (
    errRune   = errors.New("invalid rune")
    errQuoted = errors.New("unexpected end of the quoted string")
    errEOF    = errors.New("unexpected end of the string")
)

func readString(p []byte, end rune) (rem []byte, out string, err error) {
    r, sz := utf8.DecodeRune(p)
    if r == utf8.RuneError {
        return nil, "", errRune
    }
    p = p[sz:]

    var (
        quoted bool
        qRune  rune
        o      strings.Builder
    )
    if r == '\'' || r == '"' {
        quoted = true
        qRune = r
    } else {
        o.WriteRune(r)
    }

    var esc bool
    for {
        if len(p) == 0 {
            if quoted || esc {
                return nil, "", errQuoted
            }
            return p, o.String(), nil
        }

        r, sz := utf8.DecodeRune(p)
        if r == utf8.RuneError {
            return nil, "", errRune
        }

        if !esc && !quoted && (r == end || unicode.IsSpace(r)) {
            return p, o.String(), nil
        }
        p = p[sz:]

        switch {
        case esc:
            o.WriteRune(r)
            esc = false
        case r == '\\':
            esc = true
        case quoted && r == qRune:
            return p, o.String(), nil
        default:
            o.WriteRune(r)
        }
    }
}

func eatSpace(p []byte) (rem []byte, err error) {
    for len(p) != 0 {
        r, sz := utf8.DecodeRune(p)
        if r == utf8.RuneError {
            return nil, errRune
        }
        if !unicode.IsSpace(r) {
            break
        }
        p = p[sz:]
    }
    return p, nil
}

// ParseMap returns parsed key/val map
func ParseMap(s string, namevalSep, tuplesSep rune) (res map[string]string, err error) {
    res = make(map[string]string)
    p := []byte(s)
    for {
        p, err = eatSpace(p)
        if err != nil {
            return nil, err
        }
        if len(p) == 0 {
            break
        }

        var name, val string
        p, name, err = readString(p, namevalSep)
        if err != nil {
            return nil, err
        }
        p, err = eatSpace(p)
        if err != nil {
            return nil, err
        }
        if namevalSep >= 0 {
            r, sz := utf8.DecodeRune(p)
            if r == utf8.RuneError {
                return nil, errRune
            }
            p = p[sz:]
            if r != namevalSep {
                return nil, fmt.Errorf("unexpected character: %c", r)
            }
            p, err = eatSpace(p)
            if err != nil {
                return nil, err
            }
        }
        p, val, err = readString(p, tuplesSep)
        if err != nil {
            return nil, err
        }
        res[name] = val

        if tuplesSep >= 0 {
            p, err = eatSpace(p)
            if err != nil {
                return nil, err
            }
            if len(p) == 0 {
                break
            }
            r, sz := utf8.DecodeRune(p)
            if r == utf8.RuneError {
                return nil, errRune
            }
            p = p[sz:]
            if r != tuplesSep {
                return nil, fmt.Errorf("unexpected character: %c", r)
            }
            p, err = eatSpace(p)
            if err != nil {
                return nil, err
            }
            if len(p) == 0 {
                return nil, errEOF
            }
        }
    }
    return res, nil
}

func KeyboardInteractivePassphraseFunc(prompt string) func() ([]byte, error) {
    return func() ([]byte, error) {
        fmt.Print(prompt)
        defer fmt.Println()
        return term.ReadPassword(int(syscall.Stdin))
    }
}