| // Copyright 2009 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 ( | |
| "internal/buildcfg" | |
| "os" | |
| "path/filepath" | |
| "runtime" | |
| "strings" | |
| ) | |
| // WorkingDir returns the current working directory | |
| // (or "/???" if the directory cannot be identified), | |
| // with "/" as separator. | |
| func WorkingDir() string { | |
| var path string | |
| path, _ = os.Getwd() | |
| if path == "" { | |
| path = "/???" | |
| } | |
| return filepath.ToSlash(path) | |
| } | |
| // 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 "??". | |
| // | |
| // 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) | |
| } | |
| abs, rewritten := ApplyRewrites(abs, rewrites) | |
| if !rewritten && buildcfg.GOROOT != "" && hasPathPrefix(abs, buildcfg.GOROOT) { | |
| abs = "$GOROOT" + abs[len(buildcfg.GOROOT):] | |
| } | |
| // Rewrite paths to match the slash convention of the target. | |
| // This helps ensure that cross-compiled distributions remain | |
| // bit-for-bit identical to natively compiled distributions. | |
| if runtime.GOOS == "windows" { | |
| abs = strings.ReplaceAll(abs, `\`, "/") | |
| } | |
| if abs == "" { | |
| abs = "??" | |
| } | |
| return abs | |
| } | |
| // ApplyRewrites returns the filename for file in the given directory, | |
| // as rewritten by the rewrites argument. | |
| // | |
| // 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 ApplyRewrites(file, rewrites string) (string, bool) { | |
| start := 0 | |
| for i := 0; i <= len(rewrites); i++ { | |
| if i == len(rewrites) || rewrites[i] == ';' { | |
| if new, ok := applyRewrite(file, rewrites[start:i]); ok { | |
| return new, true | |
| } | |
| start = i + 1 | |
| } | |
| } | |
| return file, false | |
| } | |
| // 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. | |
| // Similarly, we allow slash folding, so that hasPathPrefix("a/b/c", "a\\b") is true. | |
| // We do not allow full Unicode case folding, for fear of causing more confusion | |
| // or harm than good. (For an example of the kinds of things that can go wrong, | |
| // see http://article.gmane.org/gmane.linux.kernel/1853266.) | |
| func hasPathPrefix(s string, t string) bool { | |
| if len(t) > len(s) { | |
| return false | |
| } | |
| var i int | |
| for i = 0; i < len(t); i++ { | |
| cs := int(s[i]) | |
| ct := int(t[i]) | |
| if 'A' <= cs && cs <= 'Z' { | |
| cs += 'a' - 'A' | |
| } | |
| if 'A' <= ct && ct <= 'Z' { | |
| ct += 'a' - 'A' | |
| } | |
| if cs == '\\' { | |
| cs = '/' | |
| } | |
| if ct == '\\' { | |
| ct = '/' | |
| } | |
| if cs != ct { | |
| return false | |
| } | |
| } | |
| return i >= len(s) || s[i] == '/' || s[i] == '\\' | |
| } | |