Support expansion of environment variables in value expressions and filenames.
diff --git a/load.go b/load.go
index e543d40..3f6c414 100644
--- a/load.go
+++ b/load.go
@@ -62,7 +62,12 @@
 	buff := make([]byte, 0, 4096)
 
 	for _, filename := range filenames {
-		buf, err := ioutil.ReadFile(filename)
+		f, err := expandFilename(filename)
+		if err != nil {
+			return nil, err
+		}
+
+		buf, err := ioutil.ReadFile(f)
 		if err != nil {
 			if ignoreMissing && os.IsNotExist(err) {
 				// TODO(frank): should we log that we are skipping the file?
@@ -87,6 +92,14 @@
 	return p
 }
 
+// expandFilename expands ${ENV_VAR} expressions in a filename.
+// If the environment variable does not exist then it will be replaced
+// with an empty string. Malformed expressions like "${ENV_VAR" will
+// be reported as error.
+func expandFilename(filename string) (string, error) {
+	return expand(filename, make(map[string]bool), "${", "}", make(map[string]string))
+}
+
 // Interprets a byte buffer either as an ISO-8859-1 or UTF-8 encoded string.
 // For ISO-8859-1 we can convert each byte straight into a rune since the
 // first 256 unicode code points cover ISO-8859-1.
diff --git a/load_test.go b/load_test.go
index 0d9ef75..e2086a4 100644
--- a/load_test.go
+++ b/load_test.go
@@ -8,6 +8,7 @@
 	"fmt"
 	"io/ioutil"
 	"os"
+	"strings"
 
 	. "launchpad.net/gocheck"
 )
@@ -65,6 +66,15 @@
 
 // ----------------------------------------------------------------------------
 
+func (s *LoadSuite) TestLoadExpandedFile(c *C) {
+	filename := s.makeFilePrefix(c, os.Getenv("USER"), "key=value")
+	filename = strings.Replace(filename, os.Getenv("USER"), "${USER}", -1)
+	p := MustLoadFile(filename, ISO_8859_1)
+	assertKeyValues(c, "", p, "key", "value")
+}
+
+// ----------------------------------------------------------------------------
+
 func (s *LoadSuite) TestLoadFilesAndIgnoreMissing(c *C) {
 	filename := s.makeFile(c, "key=value")
 	filename2 := s.makeFile(c, "key2=value2")
@@ -92,7 +102,13 @@
 // ----------------------------------------------------------------------------
 
 func (s *LoadSuite) makeFile(c *C, data string) string {
-	f, err := ioutil.TempFile("", "properties")
+	return s.makeFilePrefix(c, "properties", data)
+}
+
+// ----------------------------------------------------------------------------
+
+func (s *LoadSuite) makeFilePrefix(c *C, prefix, data string) string {
+	f, err := ioutil.TempFile("", prefix)
 	if err != nil {
 		fmt.Printf("ioutil.TempFile: %v", err)
 		c.FailNow()
diff --git a/properties.go b/properties.go
index 944b2ee..daa03db 100644
--- a/properties.go
+++ b/properties.go
@@ -10,6 +10,7 @@
 import (
 	"fmt"
 	"io"
+	"os"
 	"strconv"
 	"strings"
 	"unicode/utf8"
@@ -171,44 +172,46 @@
 	return nil
 }
 
-// expand recursively expands expressions of '(prefix)key(postfix)' to their corresponding values.
-// The function keeps track of the keys that were already expanded and stops if it
-// detects a circular reference or a malformed expression.
 func (p *Properties) expand(input string) (string, error) {
 	// no pre/postfix -> nothing to expand
 	if p.Prefix == "" && p.Postfix == "" {
 		return input, nil
 	}
 
-	return p.doExpand(input, make(map[string]bool))
+	return expand(input, make(map[string]bool), p.Prefix, p.Postfix, p.m)
 }
 
-func (p *Properties) doExpand(s string, keys map[string]bool) (string, error) {
-	a := strings.Index(s, p.Prefix)
+// expand recursively expands expressions of '(prefix)key(postfix)' to their corresponding values.
+// The function keeps track of the keys that were already expanded and stops if it
+// detects a circular reference or a malformed expression of the form '(prefix)key'.
+func expand(s string, keys map[string]bool, prefix, postfix string, values map[string]string) (string, error) {
+	a := strings.Index(s, prefix)
 	if a == -1 {
 		return s, nil
 	}
 
-	b := strings.Index(s[a:], p.Postfix)
+	b := strings.Index(s[a:], postfix)
 	if b == -1 {
 		return "", fmt.Errorf("Malformed expression")
 	}
 
-	key := s[a+len(p.Prefix) : b-len(p.Postfix)+1]
+	keyStart := a + len(prefix)
+	keyEnd := keyStart + b - len(postfix) - 1
+	key := s[keyStart:keyEnd]
 
 	if _, ok := keys[key]; ok {
 		return "", fmt.Errorf("Circular reference")
 	}
 
-	val, ok := p.m[key]
+	val, ok := values[key]
 	if !ok {
-		val = ""
+		val = os.Getenv(key)
 	}
 
 	// remember that we've seen the key
 	keys[key] = true
 
-	return p.doExpand(s[:a]+val+s[b+1:], keys)
+	return expand(s[:a]+val+s[a+b+1:], keys, prefix, postfix, values)
 }
 
 // encode encodes a UTF-8 string to ISO-8859-1 and escapes some characters.
diff --git a/properties_test.go b/properties_test.go
index da11855..69e1cfc 100644
--- a/properties_test.go
+++ b/properties_test.go
@@ -85,6 +85,7 @@
 	// expansion tests
 	{"key=value\nkey2=${key}", "key", "value", "key2", "value"},
 	{"key=value\nkey2=${key}\nkey3=${key2}", "key", "value", "key2", "value", "key3", "value"},
+	{"key=${USER}", "key", os.Getenv("USER")},
 }
 
 // define error test cases in the form of
@@ -240,7 +241,7 @@
 
 // ----------------------------------------------------------------------------
 
-// TestBasic tests basic single key/value combinations with all possible 
+// TestBasic tests basic single key/value combinations with all possible
 // whitespace, delimiter and newline permutations.
 func (l *TestSuite) TestBasic(c *C) {
 	testWhitespaceAndDelimiterCombinations(c, "key", "")