1// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package objabi
6
7import (
8	"internal/buildcfg"
9	"os"
10	"path/filepath"
11	"runtime"
12	"strings"
13)
14
15// WorkingDir returns the current working directory
16// (or "/???" if the directory cannot be identified),
17// with "/" as separator.
18func WorkingDir() string {
19	var path string
20	path, _ = os.Getwd()
21	if path == "" {
22		path = "/???"
23	}
24	return filepath.ToSlash(path)
25}
26
27// AbsFile returns the absolute filename for file in the given directory,
28// as rewritten by the rewrites argument.
29// For unrewritten paths, AbsFile rewrites a leading $GOROOT prefix to the literal "$GOROOT".
30// If the resulting path is the empty string, the result is "??".
31//
32// The rewrites argument is a ;-separated list of rewrites.
33// Each rewrite is of the form "prefix" or "prefix=>replace",
34// where prefix must match a leading sequence of path elements
35// and is either removed entirely or replaced by the replacement.
36func AbsFile(dir, file, rewrites string) string {
37	abs := file
38	if dir != "" && !filepath.IsAbs(file) {
39		abs = filepath.Join(dir, file)
40	}
41
42	abs, rewritten := ApplyRewrites(abs, rewrites)
43	if !rewritten && buildcfg.GOROOT != "" && hasPathPrefix(abs, buildcfg.GOROOT) {
44		abs = "$GOROOT" + abs[len(buildcfg.GOROOT):]
45	}
46
47	// Rewrite paths to match the slash convention of the target.
48	// This helps ensure that cross-compiled distributions remain
49	// bit-for-bit identical to natively compiled distributions.
50	if runtime.GOOS == "windows" {
51		abs = strings.ReplaceAll(abs, `\`, "/")
52	}
53
54	if abs == "" {
55		abs = "??"
56	}
57	return abs
58}
59
60// ApplyRewrites returns the filename for file in the given directory,
61// as rewritten by the rewrites argument.
62//
63// The rewrites argument is a ;-separated list of rewrites.
64// Each rewrite is of the form "prefix" or "prefix=>replace",
65// where prefix must match a leading sequence of path elements
66// and is either removed entirely or replaced by the replacement.
67func ApplyRewrites(file, rewrites string) (string, bool) {
68	start := 0
69	for i := 0; i <= len(rewrites); i++ {
70		if i == len(rewrites) || rewrites[i] == ';' {
71			if new, ok := applyRewrite(file, rewrites[start:i]); ok {
72				return new, true
73			}
74			start = i + 1
75		}
76	}
77
78	return file, false
79}
80
81// applyRewrite applies the rewrite to the path,
82// returning the rewritten path and a boolean
83// indicating whether the rewrite applied at all.
84func applyRewrite(path, rewrite string) (string, bool) {
85	prefix, replace := rewrite, ""
86	if j := strings.LastIndex(rewrite, "=>"); j >= 0 {
87		prefix, replace = rewrite[:j], rewrite[j+len("=>"):]
88	}
89
90	if prefix == "" || !hasPathPrefix(path, prefix) {
91		return path, false
92	}
93	if len(path) == len(prefix) {
94		return replace, true
95	}
96	if replace == "" {
97		return path[len(prefix)+1:], true
98	}
99	return replace + path[len(prefix):], true
100}
101
102// Does s have t as a path prefix?
103// That is, does s == t or does s begin with t followed by a slash?
104// For portability, we allow ASCII case folding, so that hasPathPrefix("a/b/c", "A/B") is true.
105// Similarly, we allow slash folding, so that hasPathPrefix("a/b/c", "a\\b") is true.
106// We do not allow full Unicode case folding, for fear of causing more confusion
107// or harm than good. (For an example of the kinds of things that can go wrong,
108// see http://article.gmane.org/gmane.linux.kernel/1853266.)
109func hasPathPrefix(s string, t string) bool {
110	if len(t) > len(s) {
111		return false
112	}
113	var i int
114	for i = 0; i < len(t); i++ {
115		cs := int(s[i])
116		ct := int(t[i])
117		if 'A' <= cs && cs <= 'Z' {
118			cs += 'a' - 'A'
119		}
120		if 'A' <= ct && ct <= 'Z' {
121			ct += 'a' - 'A'
122		}
123		if cs == '\\' {
124			cs = '/'
125		}
126		if ct == '\\' {
127			ct = '/'
128		}
129		if cs != ct {
130			return false
131		}
132	}
133	return i >= len(s) || s[i] == '/' || s[i] == '\\'
134}
135