diff --git a/.travis.yml b/.travis.yml index a8723a1..7ebf23d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,10 +10,6 @@ env: - rel=0.6.4.2 - rel=0.6.5.4 -matrix: - allow_failures: - - env: rel=0.6.5.4 - go: - 1.5 diff --git a/utils.go b/utils.go index 404ab2b..3de29a8 100644 --- a/utils.go +++ b/utils.go @@ -133,26 +133,63 @@ func (ds *Dataset) parseLine(line []string) error { * application. */ func unescapeFilepath(path string) (string, error) { - buf := make([]byte, 0, len(path)) - llen := len(path) - for i := 0; i < llen; { - if path[i] == '\\' { - if llen < i+4 { - return "", fmt.Errorf("Invalid octal code: too short") - } - octalCode := path[(i + 1):(i + 4)] - val, err := strconv.ParseUint(octalCode, 8, 8) + pattern := "\\0040" + if strings.Index(path, pattern) == -1 { + pattern = "\\040" + if strings.Index(path, pattern) == -1 { + return path, nil + } + + } + plen := len(pattern) + + orig := make([]byte, len(path)) + buf := orig + blen := 0 + + for { + index := strings.Index(path, pattern) + if index > 0 { + l := copy(buf, path[:index]) + path = path[index:] + buf = buf[l:] + blen += l + continue + + } else if index == -1 { + l := copy(buf, path) + buf = buf[l:] + blen += l + break + + } + + path = path[index+plen:] + index = strings.Index(path, pattern) + if index == -1 { + return "", fmt.Errorf("invalid octal code: too short") + } + + // skip leading '\' char + escaped := path[1:index] + + bytes := strings.Split(escaped, "\\") + blen += len(bytes) + + for _, b := range bytes { + + val, err := strconv.ParseUint(b, 8, 8) if err != nil { - return "", fmt.Errorf("Invalid octal code: %v", err) + return "", fmt.Errorf("invalid octal code: %v", err) } - buf = append(buf, byte(val)) - i += 4 - } else { - buf = append(buf, path[i]) - i++ + + buf[0] = byte(val) + buf = buf[1:] } + + path = path[index+plen:] } - return string(buf), nil + return string(orig[:blen]), nil } var changeTypeMap = map[string]ChangeType{ diff --git a/utils_test.go b/utils_test.go new file mode 100644 index 0000000..1e0f19e --- /dev/null +++ b/utils_test.go @@ -0,0 +1,40 @@ +package zfs + +import ( + "errors" + "testing" +) + +func TestUnescapeFilePath(t *testing.T) { + var tests = []struct { + escaped string + unescaped string + err error + }{ + {"i heart unicode", "i heart unicode", nil}, + {"i \\0040\\0342\\0235\\0244 unicode", "", errors.New("invalid octal code: too short")}, + {"i \\0040\\0999\\0235\\0244\\0040 unicode", "", errors.New(`invalid octal code: strconv.ParseUint: parsing "0999": invalid syntax`)}, + {"i \\0040\\0342\\0235\\0244\\0040 unicode", "i ❤ unicode", nil}, + {"i \\0040\\0342\\0235\\0244\\0040\\0040\\0342\\0235\\0244\\0040 unicode", "i ❤❤ unicode", nil}, + {"i \\0040\\0342\\0235\\0244\\0040 \\0040\\0342\\0235\\0244\\0040 unicode", "i ❤ ❤ unicode", nil}, + {"i \\040\\342\\235\\244\\040 unicode", "i ❤ unicode", nil}, + {"i \\040\\342\\235\\244\\040\\040\\342\\235\\244\\040 unicode", "i ❤❤ unicode", nil}, + {"i \\040\\342\\235\\244\\040 \\040\\342\\235\\244\\040 unicode", "i ❤ ❤ unicode", nil}, + } + + for _, test := range tests { + t.Log(test.unescaped) + unescaped, err := unescapeFilepath(test.escaped) + if err != nil && test.err != nil { + if err.Error() != test.err.Error() { + t.Fatalf("1mismatched errors: want:%v, got:%v\n", test.err, err) + } + } else if err != test.err { + t.Fatalf("mismatched errors: want:%v, got:%v\n", test.err, err) + } + if unescaped != test.unescaped { + t.Fatalf("mismatched unescaped:\nwant:|%v|\n got:|%v|\n", + []byte(test.unescaped), []byte(unescaped)) + } + } +}