--- /dev/null
+package main
+
+import (
+ "log"
+ "path"
+ "strconv"
+ "strings"
+)
+
+func IsDir(s string) bool {
+ return s[len(s)-1] == '/'
+}
+
+type ByName [][]string
+
+func (a ByName) Len() int {
+ return len(a)
+}
+
+func (a ByName) Swap(i, j int) {
+ a[i], a[j] = a[j], a[i]
+}
+
+func (a ByName) Less(i, j int) bool {
+ return namesCmp(a[i], a[j]) < 0
+}
+
+func nameSplit(name string) []string {
+ cols := strings.Split(name, "/")
+ if IsDir(name) {
+ cols = cols[:len(cols)-1]
+ cols[len(cols)-1] += "/"
+ }
+ return cols
+}
+
+func nameJoin(name []string) (s string) {
+ s = path.Join(name...)
+ if IsDir(name[len(name)-1]) {
+ s += "/"
+ }
+ return
+}
+
+func namesCmp(n1, n2 []string) int {
+ min := len(n1)
+ if len(n2) < min {
+ min = len(n2)
+ }
+ var t1, t2 string
+ for i := 0; i < min; i++ {
+ t1 = strings.TrimSuffix(n1[i], "/")
+ t2 = strings.TrimSuffix(n2[i], "/")
+ if t1 < t2 {
+ return -1
+ }
+ if t1 > t2 {
+ return +1
+ }
+ }
+ if len(n1) > len(n2) {
+ return +1
+ }
+ if len(n1) < len(n2) {
+ return -1
+ }
+ return 0
+}
+
+func hasPrefix(name, prefix []string) bool {
+ if len(name) < len(prefix) {
+ return false
+ }
+ return namesCmp(name[:len(prefix)], prefix) == 0
+}
+
+func deoctalize(s string) string {
+ chars := make([]byte, 0, len(s))
+ for i := 0; i < len(s); i++ {
+ if s[i] == '\\' {
+ b, err := strconv.ParseUint("0"+s[i+1:i+1+3], 0, 8)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ chars = append(chars, byte(b))
+ i += 3
+ } else {
+ chars = append(chars, s[i])
+ }
+ }
+ return string(chars)
+}