aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Pike <r@golang.org>2010-11-08 13:25:01 -0800
committerRob Pike <r@golang.org>2010-11-08 13:25:01 -0800
commit26cda27b0fc5179891a847fc870817272873cb5b (patch)
treea6622cab518efbad8bf94ba971309c459b390ad5
parentfbd92dba961419d74a16b86f92bf8f2a4dc593c3 (diff)
downloadgo-26cda27b0fc5179891a847fc870817272873cb5b.tar.gz
go-26cda27b0fc5179891a847fc870817272873cb5b.zip
os: add Expand function to evaluate environment variables.
Fixes #1258. R=gri, msolo, bradfitzpatrick, r2 CC=golang-dev https://golang.org/cl/2988041
-rw-r--r--src/pkg/os/Makefile1
-rw-r--r--src/pkg/os/env.go73
-rw-r--r--src/pkg/os/env_test.go59
3 files changed, 133 insertions, 0 deletions
diff --git a/src/pkg/os/Makefile b/src/pkg/os/Makefile
index 0b0881c0ac..e4b6fb98dd 100644
--- a/src/pkg/os/Makefile
+++ b/src/pkg/os/Makefile
@@ -8,6 +8,7 @@ TARG=os
GOFILES=\
dir_$(GOOS).go\
error.go\
+ env.go\
exec.go\
file.go\
getwd.go\
diff --git a/src/pkg/os/env.go b/src/pkg/os/env.go
new file mode 100644
index 0000000000..9783674a7f
--- /dev/null
+++ b/src/pkg/os/env.go
@@ -0,0 +1,73 @@
+// Copyright 2010 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.
+
+// General environment variables.
+
+package os
+
+// Expand replaces ${var} or $var in the string based on the mapping function.
+// Invocations of undefined variables are replaced with the empty string.
+func Expand(s string, mapping func(string) string) string {
+ buf := make([]byte, 0, 2*len(s))
+ // ${} is all ASCII, so bytes are fine for this operation.
+ for i := 0; i < len(s); {
+ if s[i] != '$' || i == len(s)-1 {
+ buf = append(buf, s[i])
+ i++
+ continue
+ }
+ name, w := getShellName(s[i+1:])
+ buf = append(buf, []byte(mapping(name))...)
+ i += 1 + w
+ }
+ return string(buf)
+}
+
+// ShellExpand replaces ${var} or $var in the string according to the values
+// of the operating system's environment variables. References to undefined
+// variables are replaced by the empty string.
+func ShellExpand(s string) string {
+ return Expand(s, Getenv)
+}
+
+// isSpellSpecialVar reports whether the character identifies a special
+// shell variable such as $*.
+func isShellSpecialVar(c uint8) bool {
+ switch c {
+ case '*', '#', '$', '@', '!', '?', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ return true
+ }
+ return false
+}
+
+// isAlphaNum reports whether the byte is an ASCII letter, number, or underscore
+func isAlphaNum(c uint8) bool {
+ return c == '_' || '0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z'
+}
+
+// getName returns the name that begins the string and the number of bytes
+// consumed to extract it. If the name is enclosed in {}, it's part of a ${}
+// expansion and two more bytes are needed than the length of the name.
+func getShellName(s string) (string, int) {
+ switch {
+ case s[0] == '{':
+ if len(s) > 2 && isShellSpecialVar(s[1]) && s[2] == '}' {
+ return s[1:2], 3
+ }
+ // Scan to closing brace
+ for i := 1; i < len(s); i++ {
+ if s[i] == '}' {
+ return s[1:i], i + 1
+ }
+ }
+ return "", 1 // Bad syntax; just eat the brace.
+ case isShellSpecialVar(s[0]):
+ return s[0:1], 1
+ }
+ // Scan alphanumerics.
+ var i int
+ for i = 0; i < len(s) && isAlphaNum(s[i]); i++ {
+ }
+ return s[:i], i
+}
diff --git a/src/pkg/os/env_test.go b/src/pkg/os/env_test.go
new file mode 100644
index 0000000000..04ff390727
--- /dev/null
+++ b/src/pkg/os/env_test.go
@@ -0,0 +1,59 @@
+// Copyright 2010 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 os_test
+
+import (
+ . "os"
+ "testing"
+)
+
+// testGetenv gives us a controlled set of variables for testing Expand.
+func testGetenv(s string) string {
+ switch s {
+ case "*":
+ return "all the args"
+ case "#":
+ return "NARGS"
+ case "$":
+ return "PID"
+ case "1":
+ return "ARGUMENT1"
+ case "HOME":
+ return "/usr/gopher"
+ case "H":
+ return "(Value of H)"
+ case "home_1":
+ return "/usr/foo"
+ case "_":
+ return "underscore"
+ }
+ return ""
+}
+
+var expandTests = []struct {
+ in, out string
+}{
+ {"", ""},
+ {"$*", "all the args"},
+ {"$$", "PID"},
+ {"${*}", "all the args"},
+ {"$1", "ARGUMENT1"},
+ {"${1}", "ARGUMENT1"},
+ {"now is the time", "now is the time"},
+ {"$HOME", "/usr/gopher"},
+ {"$home_1", "/usr/foo"},
+ {"${HOME}", "/usr/gopher"},
+ {"${H}OME", "(Value of H)OME"},
+ {"A$$$#$1$H$home_1*B", "APIDNARGSARGUMENT1(Value of H)/usr/foo*B"},
+}
+
+func TestExpand(t *testing.T) {
+ for _, test := range expandTests {
+ result := Expand(test.in, testGetenv)
+ if result != test.out {
+ t.Errorf("Expand(%q)=%q; expected %q", test.in, result, test.out)
+ }
+ }
+}