blob: 30301b15f1e72451e144cff9cc20c2868c1322be [file] [log] [blame]
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package objabi
import (
"fmt"
"strconv"
"strings"
)
// PathToPrefix converts raw string to the prefix that will be used in the
// symbol table. All control characters, space, '%' and '"', as well as
// non-7-bit clean bytes turn into %xx. The period needs escaping only in the
// last segment of the path, and it makes for happier users if we escape that as
// little as possible.
func PathToPrefix(s string) string {
slash := strings.LastIndex(s, "/")
// check for chars that need escaping
n := 0
for r := 0; r < len(s); r++ {
if c := s[r]; c <= ' ' || (c == '.' && r > slash) || c == '%' || c == '"' || c >= 0x7F {
n++
}
}
// quick exit
if n == 0 {
return s
}
// escape
const hex = "0123456789abcdef"
p := make([]byte, 0, len(s)+2*n)
for r := 0; r < len(s); r++ {
if c := s[r]; c <= ' ' || (c == '.' && r > slash) || c == '%' || c == '"' || c >= 0x7F {
p = append(p, '%', hex[c>>4], hex[c&0xF])
} else {
p = append(p, c)
}
}
return string(p)
}
// PrefixToPath is the inverse of PathToPrefix, replacing escape sequences with
// the original character.
func PrefixToPath(s string) (string, error) {
percent := strings.IndexByte(s, '%')
if percent == -1 {
return s, nil
}
p := make([]byte, 0, len(s))
for i := 0; i < len(s); {
if s[i] != '%' {
p = append(p, s[i])
i++
continue
}
if i+2 >= len(s) {
// Not enough characters remaining to be a valid escape
// sequence.
return "", fmt.Errorf("malformed prefix %q: escape sequence must contain two hex digits", s)
}
b, err := strconv.ParseUint(s[i+1:i+3], 16, 8)
if err != nil {
// Not a valid escape sequence.
return "", fmt.Errorf("malformed prefix %q: escape sequence %q must contain two hex digits", s, s[i:i+3])
}
p = append(p, byte(b))
i += 3
}
return string(p), nil
}