...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package unit
18
19 import (
20 "fmt"
21 "strconv"
22 "strings"
23 )
24
25 const (
26 allowed = `:_.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`
27 )
28
29
30
31
32
33
34
35
36
37
38
39 func escape(unescaped string, isPath bool) string {
40 e := []byte{}
41 inSlashes := false
42 start := true
43 for i := 0; i < len(unescaped); i++ {
44 c := unescaped[i]
45 if isPath {
46 if c == '/' {
47 inSlashes = true
48 continue
49 } else if inSlashes {
50 inSlashes = false
51 if !start {
52 e = append(e, '-')
53 }
54 }
55 }
56
57 if c == '/' {
58 e = append(e, '-')
59 } else if start && c == '.' || strings.IndexByte(allowed, c) == -1 {
60 e = append(e, []byte(fmt.Sprintf(`\x%x`, c))...)
61 } else {
62 e = append(e, c)
63 }
64 start = false
65 }
66 if isPath && len(e) == 0 {
67 e = append(e, '-')
68 }
69 return string(e)
70 }
71
72
73
74
75
76
77
78
79
80 func unescape(escaped string, isPath bool) string {
81 u := []byte{}
82 for i := 0; i < len(escaped); i++ {
83 c := escaped[i]
84 if c == '-' {
85 c = '/'
86 } else if c == '\\' && len(escaped)-i >= 4 && escaped[i+1] == 'x' {
87 n, err := strconv.ParseInt(escaped[i+2:i+4], 16, 8)
88 if err == nil {
89 c = byte(n)
90 i += 3
91 }
92 }
93 u = append(u, c)
94 }
95 if isPath && (len(u) == 0 || u[0] != '/') {
96 u = append([]byte("/"), u...)
97 }
98 return string(u)
99 }
100
101
102 func UnitNameEscape(unescaped string) string {
103 return escape(unescaped, false)
104 }
105
106
107 func UnitNameUnescape(escaped string) string {
108 return unescape(escaped, false)
109 }
110
111
112 func UnitNamePathEscape(unescaped string) string {
113 return escape(unescaped, true)
114 }
115
116
117 func UnitNamePathUnescape(escaped string) string {
118 return unescape(escaped, true)
119 }
120
View as plain text