Skip to content
Permalink
Browse files
WIP: K/V line.
  • Loading branch information
ahf committed Dec 22, 2018
1 parent 2d4cadf commit c70bebb95b2a9585a31013bbee74fecc593802ea
Showing with 111 additions and 0 deletions.
  1. +87 −0 kvline.go
  2. +24 −0 kvline_test.go
@@ -0,0 +1,87 @@
package pt

import (
"bytes"
"fmt"
"sort"
"unicode"
)

func kvline_value_needs_escape(input string) bool {
for _, c := range input {
if c == '\'' || c == '"' || !unicode.IsPrint(c) || unicode.IsSpace(c) {
return true
}
}

return false
}

func kvline_escape_value(input string) string {
if !kvline_value_needs_escape(input) {
return input
}

// This code could benefit from the strings.Builder, but we cannot use that
// because we want to work on Debian releases that have older Go versions.
var result bytes.Buffer

// Worst case all characters needs to be escaped.
result.Grow(2 * len(input))

for _, c := range input {
switch c {
case '\'':
result.WriteRune('\\')
result.WriteRune('\'')
case '"':
result.WriteRune('\\')
result.WriteRune('"')
case '\n':
result.WriteRune('\\')
result.WriteRune('n')
case '\t':
result.WriteRune('\\')
result.WriteRune('t')
case '\r':
result.WriteRune('\\')
result.WriteRune('r')
default:
result.WriteRune(c)
}
}

return fmt.Sprintf("\"%s\"", result.String())
}

func kvline_encode(input map[string]string) string {
// We need to make sure our ordering is stable. Go's map's are for sensible
// reasons non-stable.
keys := make([]string, 0, len(input))

for key := range input {
keys = append(keys, key)
}

sort.Strings(keys)

// This code could be refactored into using Go 1.10+ `strings.Builder`
// type, but for now we can perfectly fine with the additional allocated
// done by doing `+=` on strings :-)
var result string

for index := range keys {
key := keys[index]
value := input[key]

// Are we not the first iteration we need to add a space.
if index != 0 {
result += " "
}

result += fmt.Sprintf("%s=%s",
key, kvline_escape_value(value))
}

return result
}
@@ -0,0 +1,24 @@
package pt

import (
"testing"
)

func TestKVLineEncoder(t *testing.T) {
tests := [...]struct {
value map[string]string
expected string
}{
{map[string]string{"A": "B", "CD": "EF"}, "A=B CD=EF"},
{map[string]string{"AB": "C", "CDE": "F G"}, "AB=C CDE=\"F G\""},
{map[string]string{"A": "Foo Bar Baz\r\t\n\"'"}, "A=\"Foo Bar Baz\\r\\t\\n\\\"\\'\""},
}

for _, input := range tests {
encoded := kvline_encode(input.value)
if input.expected != encoded {
t.Errorf("kvline_encode(%v) → \"%v\" (expected \"%v\")",
input.value, encoded, input.expected)
}
}
}

0 comments on commit c70bebb

Please sign in to comment.