import (
"os"
"path/filepath"
+ "strings"
)
// WorkingDir returns the current working directory
return filepath.ToSlash(path)
}
-// AbsFile returns the absolute filename for file in the given directory.
-// It also removes a leading pathPrefix, or else rewrites a leading $GOROOT
-// prefix to the literal "$GOROOT".
+// AbsFile returns the absolute filename for file in the given directory,
+// as rewritten by the rewrites argument.
+// For unrewritten paths, AbsFile rewrites a leading $GOROOT prefix to the literal "$GOROOT".
// If the resulting path is the empty string, the result is "??".
-func AbsFile(dir, file, pathPrefix string) string {
+//
+// The rewrites argument is a ;-separated list of rewrites.
+// Each rewrite is of the form "prefix" or "prefix=>replace",
+// where prefix must match a leading sequence of path elements
+// and is either removed entirely or replaced by the replacement.
+func AbsFile(dir, file, rewrites string) string {
abs := file
if dir != "" && !filepath.IsAbs(file) {
abs = filepath.Join(dir, file)
}
- if pathPrefix != "" && hasPathPrefix(abs, pathPrefix) {
- if abs == pathPrefix {
- abs = ""
- } else {
- abs = abs[len(pathPrefix)+1:]
+ start := 0
+ for i := 0; i <= len(rewrites); i++ {
+ if i == len(rewrites) || rewrites[i] == ';' {
+ if new, ok := applyRewrite(abs, rewrites[start:i]); ok {
+ abs = new
+ goto Rewritten
+ }
+ start = i + 1
}
- } else if hasPathPrefix(abs, GOROOT) {
+ }
+ if hasPathPrefix(abs, GOROOT) {
abs = "$GOROOT" + abs[len(GOROOT):]
}
+
+Rewritten:
if abs == "" {
abs = "??"
}
-
return abs
}
+// applyRewrite applies the rewrite to the path,
+// returning the rewritten path and a boolean
+// indicating whether the rewrite applied at all.
+func applyRewrite(path, rewrite string) (string, bool) {
+ prefix, replace := rewrite, ""
+ if j := strings.LastIndex(rewrite, "=>"); j >= 0 {
+ prefix, replace = rewrite[:j], rewrite[j+len("=>"):]
+ }
+
+ if prefix == "" || !hasPathPrefix(path, prefix) {
+ return path, false
+ }
+ if len(path) == len(prefix) {
+ return replace, true
+ }
+ if replace == "" {
+ return path[len(prefix)+1:], true
+ }
+ return replace + path[len(prefix):], true
+}
+
// Does s have t as a path prefix?
// That is, does s == t or does s begin with t followed by a slash?
// For portability, we allow ASCII case folding, so that hasPathPrefix("a/b/c", "A/B") is true.
--- /dev/null
+// Copyright 2019 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 (
+ "path/filepath"
+ "runtime"
+ "testing"
+)
+
+// On Windows, "/foo" is reported as a relative path
+// (it is relative to the current drive letter),
+// so we need add a drive letter to test absolute path cases.
+func drive() string {
+ if runtime.GOOS == "windows" {
+ return "c:"
+ }
+ return ""
+}
+
+var absFileTests = []struct {
+ dir string
+ file string
+ rewrites string
+ abs string
+}{
+ {"/d", "f", "", "/d/f"},
+ {"/d", drive() + "/f", "", drive() + "/f"},
+ {"/d", "f/g", "", "/d/f/g"},
+ {"/d", drive() + "/f/g", "", drive() + "/f/g"},
+
+ {"/d", "f", "/d/f", "??"},
+ {"/d", "f/g", "/d/f", "g"},
+ {"/d", "f/g", "/d/f=>h", "h/g"},
+ {"/d", "f/g", "/d/f=>/h", "/h/g"},
+ {"/d", "f/g", "/d/f=>/h;/d/e=>/i", "/h/g"},
+ {"/d", "e/f", "/d/f=>/h;/d/e=>/i", "/i/f"},
+}
+
+func TestAbsFile(t *testing.T) {
+ for _, tt := range absFileTests {
+ abs := filepath.FromSlash(AbsFile(filepath.FromSlash(tt.dir), filepath.FromSlash(tt.file), tt.rewrites))
+ want := filepath.FromSlash(tt.abs)
+ if abs != want {
+ t.Errorf("AbsFile(%q, %q, %q) = %q, want %q", tt.dir, tt.file, tt.rewrites, abs, want)
+ }
+ }
+}