// 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 import ( "internal/testlog" "syscall" ) // Expand replaces ${var} or $var in the string based on the mapping function. // For example, os.ExpandEnv(s) is equivalent to os.Expand(s, os.Getenv). func Expand(s string, mapping func(string) string) string { var buf []byte // ${} is all ASCII, so bytes are fine for this operation. i := 0 for j := 0; j < len(s); j++ { if s[j] == '$' && j+1 < len(s) { if buf == nil { buf = make([]byte, 0, 2*len(s)) } buf = append(buf, s[i:j]...) name, w := getShellName(s[j+1:]) if name == "" && w > 0 { // Encountered invalid syntax; eat the // characters. } else if name == "" { // Valid syntax, but $ was not followed by a // name. Leave the dollar character untouched. buf = append(buf, s[j]) } else { buf = append(buf, mapping(name)...) } j += w i = j + 1 } } if buf == nil { return s } return string(buf) + s[i:] } // ExpandEnv replaces ${var} or $var in the string according to the values // of the current environment variables. References to undefined // variables are replaced by the empty string. func ExpandEnv(s string) string { return Expand(s, Getenv) } // isShellSpecialVar 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' } // getShellName 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] == '}' { if i == 1 { return "", 2 // Bad syntax; eat "${}" } return s[1:i], i + 1 } } return "", 1 // Bad syntax; eat "${" 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 } // Getenv retrieves the value of the environment variable named by the key. // It returns the value, which will be empty if the variable is not present. // To distinguish between an empty value and an unset value, use LookupEnv. func Getenv(key string) string { testlog.Getenv(key) v, _ := syscall.Getenv(key) return v } // LookupEnv retrieves the value of the environment variable named // by the key. If the variable is present in the environment the // value (which may be empty) is returned and the boolean is true. // Otherwise the returned value will be empty and the boolean will // be false. func LookupEnv(key string) (string, bool) { testlog.Getenv(key) return syscall.Getenv(key) } // Setenv sets the value of the environment variable named by the key. // It returns an error, if any. func Setenv(key, value string) error { err := syscall.Setenv(key, value) if err != nil { return NewSyscallError("setenv", err) } return nil } // Unsetenv unsets a single environment variable. func Unsetenv(key string) error { return syscall.Unsetenv(key) } // Clearenv deletes all environment variables. func Clearenv() { syscall.Clearenv() } // Environ returns a copy of strings representing the environment, // in the form "key=value". func Environ() []string { return syscall.Environ() }