2024-01-11 17:59:26 +00:00
|
|
|
// Copyright (C) 2024 Umorpha Systems
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
|
2024-01-11 20:36:03 +00:00
|
|
|
package gitcache
|
2024-01-11 17:59:26 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2024-01-28 05:56:36 +00:00
|
|
|
func NormalizeURL(url string) string {
|
2024-01-11 17:59:26 +00:00
|
|
|
// > Clients MUST strip a trailing `/`, if present, from the
|
|
|
|
// > user supplied `$GIT_URL` string
|
|
|
|
// -- gitprotocol-http(5)
|
|
|
|
url = strings.TrimRight(url, "/")
|
2024-01-28 05:56:36 +00:00
|
|
|
return url
|
|
|
|
}
|
|
|
|
|
2024-01-28 18:30:19 +00:00
|
|
|
func needsEscaped(b byte) bool {
|
|
|
|
switch {
|
|
|
|
case b == '!', // our own escape
|
|
|
|
// see: git-check-ref-format(1)
|
|
|
|
b <= ' ', b == '\x7F', // never: ASCII control characters
|
|
|
|
b == '~', b == '^', b == ':', // never: gitrevision meaning
|
|
|
|
b == '?', b == '*', b == '[', // never: refspec pattern
|
|
|
|
b == '\\', // never: backslash
|
|
|
|
b == '.', b == '@', b == '/': // sometimes: special rules
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
2024-01-28 05:56:36 +00:00
|
|
|
}
|
|
|
|
|
2024-01-28 18:30:19 +00:00
|
|
|
func URL2NS(url string) (string, bool) {
|
2024-01-28 05:56:36 +00:00
|
|
|
url = NormalizeURL(url)
|
2024-01-28 18:30:19 +00:00
|
|
|
if url == "" {
|
|
|
|
return "", false
|
|
|
|
}
|
2024-01-11 17:59:26 +00:00
|
|
|
|
2024-01-28 18:30:19 +00:00
|
|
|
l := len(url)
|
|
|
|
for i := 0; i < len(url); i++ {
|
|
|
|
if needsEscaped(url[i]) {
|
|
|
|
l += 2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var ns strings.Builder
|
|
|
|
ns.Grow(l)
|
|
|
|
var buf [3]byte
|
|
|
|
buf[0] = '!'
|
|
|
|
const digits = "0123456789abcdef"
|
2024-01-28 05:31:51 +00:00
|
|
|
for i := 0; i < len(url); i++ {
|
|
|
|
b := url[i]
|
2024-01-28 18:30:19 +00:00
|
|
|
if needsEscaped(b) {
|
|
|
|
buf[1] = digits[b>>4]
|
|
|
|
buf[2] = digits[b&0xf]
|
|
|
|
ns.Write(buf[:])
|
|
|
|
} else {
|
|
|
|
ns.WriteByte(b)
|
2024-01-11 17:59:26 +00:00
|
|
|
}
|
|
|
|
}
|
2024-01-28 18:30:19 +00:00
|
|
|
return ns.String(), true
|
2024-01-11 17:59:26 +00:00
|
|
|
}
|
|
|
|
|
2024-01-28 18:30:19 +00:00
|
|
|
func hex2int(h byte) (byte, bool) {
|
2024-01-11 17:59:26 +00:00
|
|
|
switch {
|
|
|
|
case '0' <= h && h <= '9':
|
2024-01-28 18:30:19 +00:00
|
|
|
return h - '0', true
|
2024-01-11 17:59:26 +00:00
|
|
|
case 'a' <= h && h <= 'f':
|
2024-01-28 18:30:19 +00:00
|
|
|
return h - 'a' + 0xa, true
|
2024-01-11 17:59:26 +00:00
|
|
|
default:
|
2024-01-28 18:30:19 +00:00
|
|
|
return 0, false
|
2024-01-11 17:59:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-28 18:30:19 +00:00
|
|
|
func NS2URL(ns string) (string, bool) {
|
|
|
|
l := len(ns) - (strings.Count(ns, "!") * 2)
|
|
|
|
if l < 0 {
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
var url strings.Builder
|
|
|
|
url.Grow(l)
|
|
|
|
for i := 0; i < len(ns); i++ {
|
|
|
|
b := ns[i]
|
|
|
|
if b == '!' {
|
|
|
|
if i+2 >= len(ns) {
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
h, hOK := hex2int(ns[i+1])
|
|
|
|
l, lOK := hex2int(ns[i+2])
|
|
|
|
if !hOK || !lOK {
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
b = h<<4 | l
|
|
|
|
i += 2
|
|
|
|
}
|
|
|
|
url.WriteByte(b)
|
|
|
|
}
|
|
|
|
return url.String(), true
|
2024-01-11 17:59:26 +00:00
|
|
|
}
|