// Copyright 2015 CoreOS, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Implements systemd-escape [--unescape] [--path] package unit import ( "fmt" "strconv" "strings" ) const ( allowed = `:_.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789` ) // If isPath is true: // // We remove redundant '/'s, the leading '/', and trailing '/'. // If the result is empty, a '/' is inserted. // // We always: // // Replace the following characters with `\x%x`: Leading `.`, // `-`, `\`, and anything not in this set: `:-_.\[0-9a-zA-Z]` // Replace '/' with '-'. func escape(unescaped string, isPath bool) string { e := []byte{} inSlashes := false start := true for i := 0; i < len(unescaped); i++ { c := unescaped[i] if isPath { if c == '/' { inSlashes = true continue } else if inSlashes { inSlashes = false if !start { e = append(e, '-') } } } if c == '/' { e = append(e, '-') } else if start && c == '.' || strings.IndexByte(allowed, c) == -1 { e = append(e, []byte(fmt.Sprintf(`\x%x`, c))...) } else { e = append(e, c) } start = false } if isPath && len(e) == 0 { e = append(e, '-') } return string(e) } // If isPath is true: // // We always return a string beginning with '/'. // // We always: // // Replace '-' with '/'. // Replace `\x%x` with the value represented in hex. func unescape(escaped string, isPath bool) string { u := []byte{} for i := 0; i < len(escaped); i++ { c := escaped[i] if c == '-' { c = '/' } else if c == '\\' && len(escaped)-i >= 4 && escaped[i+1] == 'x' { n, err := strconv.ParseInt(escaped[i+2:i+4], 16, 8) if err == nil { c = byte(n) i += 3 } } u = append(u, c) } if isPath && (len(u) == 0 || u[0] != '/') { u = append([]byte("/"), u...) } return string(u) } // UnitNameEscape escapes a string as `systemd-escape` would func UnitNameEscape(unescaped string) string { return escape(unescaped, false) } // UnitNameUnescape unescapes a string as `systemd-escape --unescape` would func UnitNameUnescape(escaped string) string { return unescape(escaped, false) } // UnitNamePathEscape escapes a string as `systemd-escape --path` would func UnitNamePathEscape(unescaped string) string { return escape(unescaped, true) } // UnitNamePathUnescape unescapes a string as `systemd-escape --path --unescape` would func UnitNamePathUnescape(escaped string) string { return unescape(escaped, true) }