Skip to content

Instantly share code, notes, and snippets.

@wking
Created November 4, 2016 07:49
Show Gist options
  • Save wking/4ec91412fbd72f4bf7b22193b96b6bc0 to your computer and use it in GitHub Desktop.
Save wking/4ec91412fbd72f4bf7b22193b96b6bc0 to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"regexp"
"strings"
)
var multiSlashRegexp = regexp.MustCompile("//*")
var multiBackslashRegexp = regexp.MustCompile("\\\\*")
func main() {
for _, test := range []struct {
os string
pathA string
pathB string
cwd string
expected bool
}{
{
os: "linux",
pathA: "/",
pathB: "/a",
cwd: "/cwd",
expected: true,
},
{
os: "linux",
pathA: "/a",
pathB: "/a",
cwd: "/cwd",
expected: false,
},
{
os: "linux",
pathA: "/a",
pathB: "/",
cwd: "/cwd",
expected: false,
},
{
os: "linux",
pathA: "/a",
pathB: "/ab",
cwd: "/cwd",
expected: false,
},
{
os: "linux",
pathA: "/a/",
pathB: "/a",
cwd: "/cwd",
expected: false,
},
{
os: "linux",
pathA: "//a",
pathB: "/a",
cwd: "/cwd",
expected: false,
},
{
os: "linux",
pathA: "//a",
pathB: "/a/b",
cwd: "/cwd",
expected: true,
},
{
os: "linux",
pathA: "/a",
pathB: "/a/",
cwd: "/cwd",
expected: false,
},
{
os: "linux",
pathA: "/a",
pathB: ".",
cwd: "/cwd",
expected: false,
},
{
os: "linux",
pathA: "/a",
pathB: "b",
cwd: "/a",
expected: true,
},
{
os: "linux",
pathA: "/a",
pathB: "../a",
cwd: "/cwd",
expected: false,
},
{
os: "linux",
pathA: "/a",
pathB: "../a/b",
cwd: "/cwd",
expected: true,
},
} {
ancestor, err := IsAncestor(test.os, test.pathA, test.pathB, test.cwd)
if err != nil {
fmt.Printf("error: %s %s %s %s -> %s\n", test.os, test.pathA, test.pathB, test.cwd, err)
} else if ancestor != test.expected {
fmt.Printf("unexpected result: %s %s %s %s -> %t\n", test.os, test.pathA, test.pathB, test.cwd, ancestor)
}
}
}
// Abs is an explicit-OS version of path/filepath's Abs.
func Abs(os, path, cwd string) (_ string, err error) {
if IsAbs(os, path) {
return path, nil
}
return Join(os, cwd, path), nil
}
// Clean is an explicit-OS version of path/filepath's Clean.
func Clean(os, path string) string {
// FIXME: implement all logic described for path/filepath's Clean.
sep := Separator(os)
if os == "windows" {
path = multiBackslashRegexp.ReplaceAllString(path, string(sep))
} else {
path = multiSlashRegexp.ReplaceAllString(path, string(sep))
}
// FIXME: eliminate .
// FIXME: eliminate inner ..
// FIXME: eliminate .. elements that begin with a rooted path
if os == "windows" {
if path == "C:\\" {
return path
}
} else if path == string(sep) {
return path
}
return strings.TrimRight(path, string(sep))
}
// IsAbs is an explicit-OS version of path/filepath's IsAbs.
func IsAbs(os, path string) bool {
if os == "windows" {
// FIXME: copy hideous logic from Go's
// src/path/filepath/path_windows.go into somewhere where we can
// put 3-clause BSD licensed code.
return false
}
sep := Separator(os)
return strings.HasPrefix(path, string(sep))
}
// Join is an explicit-OS version of path/filepath's Join.
func Join(os string, elem ...string) string {
sep := Separator(os)
// FIXME: do this right
return fmt.Sprintf("%s%c%s", elem[0], sep, elem[1])
}
// Separator is an explicit-OS version of path/filepath's Separator.
func Separator(os string) rune {
if os == "windows" {
return '\\'
}
return '/'
}
// IsAncestor returns true when pathB is an strict ancestor of pathA,
// and false where the paths are equal or pathB is outside of pathA.
// Paths that are not absolute will be made absolute with Abs.
func IsAncestor(os, pathA, pathB, cwd string) (_ bool, err error) {
if pathA == pathB {
return false, nil
}
pathA, err = Abs(os, pathA, cwd)
if err != nil {
return false, err
}
pathB, err = Abs(os, pathB, cwd)
if err != nil {
return false, err
}
sep := Separator(os)
if !strings.HasSuffix(pathA, string(sep)) {
pathA = fmt.Sprintf("%s%c", pathA, sep)
}
if pathA == pathB {
return false, nil
}
//fmt.Printf(" %s has prefix %s?\n", pathB, pathA)
return strings.HasPrefix(pathB, pathA), nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment