Documentation ¶
Overview ¶
Package ini provides INI file read and write functionality in Go.
Index ¶
- Constants
- Variables
- func IsErrDelimiterNotFound(err error) bool
- func MapTo(v, source interface{}, others ...interface{}) error
- func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error
- func ReflectFrom(cfg *File, v interface{}) error
- func ReflectFromWithMapper(cfg *File, v interface{}, mapper NameMapper) error
- func StrictMapTo(v, source interface{}, others ...interface{}) error
- func StrictMapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error
- type DebugFunc
- type ErrDelimiterNotFound
- type File
- func Empty(opts ...LoadOptions) *File
- func InsensitiveLoad(source interface{}, others ...interface{}) (*File, error)
- func Load(source interface{}, others ...interface{}) (*File, error)
- func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error)
- func LooseLoad(source interface{}, others ...interface{}) (*File, error)
- func ShadowLoad(source interface{}, others ...interface{}) (*File, error)
- func (f *File) Append(source interface{}, others ...interface{}) error
- func (f *File) ChildSections(name string) []*Section
- func (f *File) DeleteSection(name string)
- func (f *File) DeleteSectionWithIndex(name string, index int) error
- func (f *File) GetSection(name string) (*Section, error)
- func (f *File) MapTo(v interface{}) error
- func (f *File) NewRawSection(name, body string) (*Section, error)
- func (f *File) NewSection(name string) (*Section, error)
- func (f *File) NewSections(names ...string) (err error)
- func (f *File) ReflectFrom(v interface{}) error
- func (f *File) Reload() (err error)
- func (f *File) SaveTo(filename string) error
- func (f *File) SaveToIndent(filename, indent string) error
- func (f *File) Section(name string) *Section
- func (f *File) SectionStrings() []string
- func (f *File) SectionWithIndex(name string, index int) *Section
- func (f *File) Sections() []*Section
- func (f *File) SectionsByName(name string) ([]*Section, error)
- func (f *File) StrictMapTo(v interface{}) error
- func (f *File) WriteTo(w io.Writer) (int64, error)
- func (f *File) WriteToIndent(w io.Writer, indent string) (int64, error)
- type Key
- func (k *Key) AddNestedValue(val string) error
- func (k *Key) AddShadow(val string) error
- func (k *Key) Bool() (bool, error)
- func (k *Key) Bools(delim string) []bool
- func (k *Key) Duration() (time.Duration, error)
- func (k *Key) Float64() (float64, error)
- func (k *Key) Float64s(delim string) []float64
- func (k *Key) In(defaultVal string, candidates []string) string
- func (k *Key) InFloat64(defaultVal float64, candidates []float64) float64
- func (k *Key) InInt(defaultVal int, candidates []int) int
- func (k *Key) InInt64(defaultVal int64, candidates []int64) int64
- func (k *Key) InTime(defaultVal time.Time, candidates []time.Time) time.Time
- func (k *Key) InTimeFormat(format string, defaultVal time.Time, candidates []time.Time) time.Time
- func (k *Key) InUint(defaultVal uint, candidates []uint) uint
- func (k *Key) InUint64(defaultVal uint64, candidates []uint64) uint64
- func (k *Key) Int() (int, error)
- func (k *Key) Int64() (int64, error)
- func (k *Key) Int64s(delim string) []int64
- func (k *Key) Ints(delim string) []int
- func (k *Key) MustBool(defaultVal ...bool) bool
- func (k *Key) MustDuration(defaultVal ...time.Duration) time.Duration
- func (k *Key) MustFloat64(defaultVal ...float64) float64
- func (k *Key) MustInt(defaultVal ...int) int
- func (k *Key) MustInt64(defaultVal ...int64) int64
- func (k *Key) MustString(defaultVal string) string
- func (k *Key) MustTime(defaultVal ...time.Time) time.Time
- func (k *Key) MustTimeFormat(format string, defaultVal ...time.Time) time.Time
- func (k *Key) MustUint(defaultVal ...uint) uint
- func (k *Key) MustUint64(defaultVal ...uint64) uint64
- func (k *Key) Name() string
- func (k *Key) NestedValues() []string
- func (k *Key) RangeFloat64(defaultVal, min, max float64) float64
- func (k *Key) RangeInt(defaultVal, min, max int) int
- func (k *Key) RangeInt64(defaultVal, min, max int64) int64
- func (k *Key) RangeTime(defaultVal, min, max time.Time) time.Time
- func (k *Key) RangeTimeFormat(format string, defaultVal, min, max time.Time) time.Time
- func (k *Key) SetValue(v string)
- func (k *Key) StrictBools(delim string) ([]bool, error)
- func (k *Key) StrictFloat64s(delim string) ([]float64, error)
- func (k *Key) StrictInt64s(delim string) ([]int64, error)
- func (k *Key) StrictInts(delim string) ([]int, error)
- func (k *Key) StrictTimes(delim string) ([]time.Time, error)
- func (k *Key) StrictTimesFormat(format, delim string) ([]time.Time, error)
- func (k *Key) StrictUint64s(delim string) ([]uint64, error)
- func (k *Key) StrictUints(delim string) ([]uint, error)
- func (k *Key) String() string
- func (k *Key) Strings(delim string) []string
- func (k *Key) StringsWithShadows(delim string) []string
- func (k *Key) Time() (time.Time, error)
- func (k *Key) TimeFormat(format string) (time.Time, error)
- func (k *Key) Times(delim string) []time.Time
- func (k *Key) TimesFormat(format, delim string) []time.Time
- func (k *Key) Uint() (uint, error)
- func (k *Key) Uint64() (uint64, error)
- func (k *Key) Uint64s(delim string) []uint64
- func (k *Key) Uints(delim string) []uint
- func (k *Key) ValidBools(delim string) []bool
- func (k *Key) ValidFloat64s(delim string) []float64
- func (k *Key) ValidInt64s(delim string) []int64
- func (k *Key) ValidInts(delim string) []int
- func (k *Key) ValidTimes(delim string) []time.Time
- func (k *Key) ValidTimesFormat(format, delim string) []time.Time
- func (k *Key) ValidUint64s(delim string) []uint64
- func (k *Key) ValidUints(delim string) []uint
- func (k *Key) Validate(fn func(string) string) string
- func (k *Key) Value() string
- func (k *Key) ValueWithShadows() []string
- type LoadOptions
- type NameMapper
- type Parser
- type Section
- func (s *Section) Body() string
- func (s *Section) ChildSections() []*Section
- func (s *Section) DeleteKey(name string)
- func (s *Section) GetKey(name string) (*Key, error)
- func (s *Section) HasKey(name string) bool
- func (s *Section) HasValue(value string) bool
- func (s *Section) Haskey(name string) booldeprecated
- func (s *Section) Key(name string) *Key
- func (s *Section) KeyStrings() []string
- func (s *Section) Keys() []*Key
- func (s *Section) KeysHash() map[string]string
- func (s *Section) MapTo(v interface{}) error
- func (s *Section) Name() string
- func (s *Section) NewBooleanKey(name string) (*Key, error)
- func (s *Section) NewKey(name, val string) (*Key, error)
- func (s *Section) ParentKeys() []*Key
- func (s *Section) ReflectFrom(v interface{}) error
- func (s *Section) SetBody(body string)
- func (s *Section) StrictMapTo(v interface{}) error
- type StructReflector
- type ValueMapper
Constants ¶
const (
//Default name for the default section. Can be overwritten in var
DefaultSectionConstant = "DEFAULT"
)
Variables ¶
var ( // Deprecated: Use "DefaultSection" instead. DEFAULT_SECTION = DefaultSection // Deprecated: AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE. AllCapsUnderscore = SnackCase )
var ( // DefaultSection is the name of default section. You can use this constant or the string literal. // In most of cases, an empty string is all you need to access the section. DefaultSection = "DefaultSectionConstant" // LineBreak is the delimiter to determine or compose a new line. // This variable will be changed to "\r\n" automatically on Windows at package init time. LineBreak = "\n" // DefaultHeader explicitly writes default section header. DefaultHeader = false // PrettySection indicates whether to put a line between sections. PrettySection = true // PrettyFormat indicates whether to align "=" sign with spaces to produce pretty output // or reduce all possible spaces for compact format. PrettyFormat = true // PrettyEqual places spaces around "=" sign even when PrettyFormat is false. PrettyEqual = false // DefaultFormatLeft places custom spaces on the left when PrettyFormat and PrettyEqual are both disabled. DefaultFormatLeft = "" // DefaultFormatRight places custom spaces on the right when PrettyFormat and PrettyEqual are both disabled. DefaultFormatRight = "" )
Functions ¶
func IsErrDelimiterNotFound ¶ added in v1.21.0
IsErrDelimiterNotFound returns true if the given error is an instance of ErrDelimiterNotFound.
func MapTo ¶
func MapTo(v, source interface{}, others ...interface{}) error
MapTo maps data sources to given struct.
func MapToWithMapper ¶
func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error
MapToWithMapper maps data sources to given struct with name mapper.
func ReflectFrom ¶
ReflectFrom reflects data sources from given struct.
func ReflectFromWithMapper ¶
func ReflectFromWithMapper(cfg *File, v interface{}, mapper NameMapper) error
ReflectFromWithMapper reflects data sources from given struct with name mapper.
func StrictMapTo ¶ added in v1.28.0
func StrictMapTo(v, source interface{}, others ...interface{}) error
StrictMapTo maps data sources to given struct in strict mode, which returns all possible error including value parsing error.
func StrictMapToWithMapper ¶ added in v1.28.0
func StrictMapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error
StrictMapToWithMapper maps data sources to given struct with name mapper in strict mode, which returns all possible error including value parsing error.
Types ¶
type DebugFunc ¶ added in v1.50.0
type DebugFunc func(message string)
DebugFunc is the type of function called to log parse events.
type ErrDelimiterNotFound ¶ added in v1.21.0
type ErrDelimiterNotFound struct {
Line string
}
ErrDelimiterNotFound indicates the error type of no delimiter is found which there should be one.
func (ErrDelimiterNotFound) Error ¶ added in v1.21.0
func (err ErrDelimiterNotFound) Error() string
type File ¶
type File struct { // Should make things safe, but sometimes doesn't matter. BlockMode bool NameMapper ValueMapper // contains filtered or unexported fields }
File represents a combination of one or more INI files in memory.
func InsensitiveLoad ¶ added in v1.14.0
InsensitiveLoad has exactly same functionality as Load function except it forces all section and key names to be lowercased.
func Load ¶
Load loads and parses from INI data sources. Arguments can be mixed of file name with string type, or raw data in []byte. It will return error if list contains nonexistent files.
func LoadSources ¶ added in v1.14.0
func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error)
LoadSources allows caller to apply customized options for loading from data source(s).
func LooseLoad ¶ added in v1.10.0
LooseLoad has exactly same functionality as Load function except it ignores nonexistent files instead of returning error.
func ShadowLoad ¶ added in v1.25.2
ShadowLoad has exactly same functionality as Load function except it allows have shadow keys.
func (*File) ChildSections ¶ added in v1.27.0
ChildSections returns a list of child sections of given section name.
func (*File) DeleteSection ¶
DeleteSection deletes a section or all sections with given name.
func (*File) DeleteSectionWithIndex ¶ added in v1.55.0
DeleteSectionWithIndex deletes a section with given name and index.
func (*File) GetSection ¶
GetSection returns section by given name.
func (*File) NewRawSection ¶ added in v1.22.0
NewRawSection creates a new section with an unparseable body.
func (*File) NewSection ¶
NewSection creates a new section.
func (*File) NewSections ¶
NewSections creates a list of sections.
func (*File) ReflectFrom ¶
ReflectFrom reflects file from given struct.
func (*File) SaveToIndent ¶
SaveToIndent writes content to file system with given value indention.
func (*File) SectionStrings ¶
SectionStrings returns list of section names.
func (*File) SectionWithIndex ¶ added in v1.55.0
SectionWithIndex assumes named section exists and returns a new section when not.
func (*File) SectionsByName ¶ added in v1.55.0
SectionsByName returns all sections with given name.
func (*File) StrictMapTo ¶ added in v1.28.0
StrictMapTo maps file to given struct in strict mode, which returns all possible error including value parsing error.
type Key ¶
type Key struct { Comment string // contains filtered or unexported fields }
Key represents a key under a section.
func (*Key) AddNestedValue ¶ added in v1.32.0
AddNestedValue adds a nested value to the key.
func (*Key) Bools ¶ added in v1.47.0
Bools returns list of bool divided by given delimiter. Any invalid input will be treated as zero value.
func (*Key) Float64s ¶
Float64s returns list of float64 divided by given delimiter. Any invalid input will be treated as zero value.
func (*Key) In ¶
In always returns value without error, it returns default value if error occurs or doesn't fit into candidates.
func (*Key) InFloat64 ¶
InFloat64 always returns value without error, it returns default value if error occurs or doesn't fit into candidates.
func (*Key) InInt ¶
InInt always returns value without error, it returns default value if error occurs or doesn't fit into candidates.
func (*Key) InInt64 ¶
InInt64 always returns value without error, it returns default value if error occurs or doesn't fit into candidates.
func (*Key) InTime ¶
InTime always parses with RFC3339 format and returns value without error, it returns default value if error occurs or doesn't fit into candidates.
func (*Key) InTimeFormat ¶
InTimeFormat always parses with given format and returns value without error, it returns default value if error occurs or doesn't fit into candidates.
func (*Key) InUint ¶
InUint always returns value without error, it returns default value if error occurs or doesn't fit into candidates.
func (*Key) InUint64 ¶
InUint64 always returns value without error, it returns default value if error occurs or doesn't fit into candidates.
func (*Key) Int64s ¶
Int64s returns list of int64 divided by given delimiter. Any invalid input will be treated as zero value.
func (*Key) Ints ¶
Ints returns list of int divided by given delimiter. Any invalid input will be treated as zero value.
func (*Key) MustBool ¶
MustBool always returns value without error, it returns false if error occurs.
func (*Key) MustDuration ¶
MustDuration always returns value without error, it returns zero value if error occurs.
func (*Key) MustFloat64 ¶
MustFloat64 always returns value without error, it returns 0.0 if error occurs.
func (*Key) MustString ¶
MustString returns default value if key value is empty.
func (*Key) MustTime ¶
MustTime always parses with RFC3339 format and returns value without error, it returns zero value if error occurs.
func (*Key) MustTimeFormat ¶
MustTimeFormat always parses with given format and returns value without error, it returns zero value if error occurs.
func (*Key) MustUint64 ¶
MustUint64 always returns value without error, it returns 0 if error occurs.
func (*Key) NestedValues ¶ added in v1.32.0
NestedValues returns nested values stored in the key. It is possible returned value is nil if no nested values stored in the key.
func (*Key) RangeFloat64 ¶
RangeFloat64 checks if value is in given range inclusively, and returns default value if it's not.
func (*Key) RangeInt ¶
RangeInt checks if value is in given range inclusively, and returns default value if it's not.
func (*Key) RangeInt64 ¶
RangeInt64 checks if value is in given range inclusively, and returns default value if it's not.
func (*Key) RangeTime ¶
RangeTime checks if value with RFC3339 format is in given range inclusively, and returns default value if it's not.
func (*Key) RangeTimeFormat ¶
RangeTimeFormat checks if value with given format is in given range inclusively, and returns default value if it's not.
func (*Key) StrictBools ¶ added in v1.47.0
StrictBools returns list of bool divided by given delimiter or error on first invalid input.
func (*Key) StrictFloat64s ¶ added in v1.9.0
StrictFloat64s returns list of float64 divided by given delimiter or error on first invalid input.
func (*Key) StrictInt64s ¶ added in v1.9.0
StrictInt64s returns list of int64 divided by given delimiter or error on first invalid input.
func (*Key) StrictInts ¶ added in v1.9.0
StrictInts returns list of int divided by given delimiter or error on first invalid input.
func (*Key) StrictTimes ¶ added in v1.9.0
StrictTimes parses with RFC3339 format and returns list of time.Time divided by given delimiter or error on first invalid input.
func (*Key) StrictTimesFormat ¶ added in v1.9.0
StrictTimesFormat parses with given format and returns list of time.Time divided by given delimiter or error on first invalid input.
func (*Key) StrictUint64s ¶ added in v1.9.0
StrictUint64s returns list of uint64 divided by given delimiter or error on first invalid input.
func (*Key) StrictUints ¶ added in v1.9.0
StrictUints returns list of uint divided by given delimiter or error on first invalid input.
func (*Key) StringsWithShadows ¶ added in v1.25.3
StringsWithShadows returns list of string divided by given delimiter. Shadows will also be appended if any.
func (*Key) TimeFormat ¶
TimeFormat parses with given format and returns time.Time type value.
func (*Key) Times ¶
Times parses with RFC3339 format and returns list of time.Time divided by given delimiter. Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC).
func (*Key) TimesFormat ¶
TimesFormat parses with given format and returns list of time.Time divided by given delimiter. Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC).
func (*Key) Uint64s ¶
Uint64s returns list of uint64 divided by given delimiter. Any invalid input will be treated as zero value.
func (*Key) Uints ¶
Uints returns list of uint divided by given delimiter. Any invalid input will be treated as zero value.
func (*Key) ValidBools ¶ added in v1.47.0
ValidBools returns list of bool divided by given delimiter. If some value is not 64-bit unsigned integer, then it will not be included to result list.
func (*Key) ValidFloat64s ¶ added in v1.9.0
ValidFloat64s returns list of float64 divided by given delimiter. If some value is not float, then it will not be included to result list.
func (*Key) ValidInt64s ¶ added in v1.9.0
ValidInt64s returns list of int64 divided by given delimiter. If some value is not 64-bit integer, then it will not be included to result list.
func (*Key) ValidInts ¶ added in v1.9.0
ValidInts returns list of int divided by given delimiter. If some value is not integer, then it will not be included to result list.
func (*Key) ValidTimes ¶ added in v1.9.0
ValidTimes parses with RFC3339 format and returns list of time.Time divided by given delimiter.
func (*Key) ValidTimesFormat ¶ added in v1.9.0
ValidTimesFormat parses with given format and returns list of time.Time divided by given delimiter.
func (*Key) ValidUint64s ¶ added in v1.9.0
ValidUint64s returns list of uint64 divided by given delimiter. If some value is not 64-bit unsigned integer, then it will not be included to result list.
func (*Key) ValidUints ¶ added in v1.9.0
ValidUints returns list of uint divided by given delimiter. If some value is not unsigned integer, then it will not be included to result list.
func (*Key) Validate ¶
Validate accepts a validate function which can return modifed result as key value.
func (*Key) ValueWithShadows ¶ added in v1.25.0
ValueWithShadows returns raw values of key and its shadows if any.
type LoadOptions ¶ added in v1.14.0
type LoadOptions struct { // Loose indicates whether the parser should ignore nonexistent files or return error. Loose bool // Insensitive indicates whether the parser forces all section and key names to lowercase. Insensitive bool // InsensitiveSections indicates whether the parser forces all section to lowercase. InsensitiveSections bool // InsensitiveKeys indicates whether the parser forces all key names to lowercase. InsensitiveKeys bool // IgnoreContinuation indicates whether to ignore continuation lines while parsing. IgnoreContinuation bool // IgnoreInlineComment indicates whether to ignore comments at the end of value and treat it as part of value. IgnoreInlineComment bool // SkipUnrecognizableLines indicates whether to skip unrecognizable lines that do not conform to key/value pairs. SkipUnrecognizableLines bool // ShortCircuit indicates whether to ignore other configuration sources after loaded the first available configuration source. ShortCircuit bool // AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing. // This type of keys are mostly used in my.cnf. AllowBooleanKeys bool // AllowShadows indicates whether to keep track of keys with same name under same section. AllowShadows bool // AllowNestedValues indicates whether to allow AWS-like nested values. // Docs: http://docs.aws.amazon.com/cli/latest/topic/config-vars.html#nested-values AllowNestedValues bool // AllowPythonMultilineValues indicates whether to allow Python-like multi-line values. // Docs: https://docs.python.org/3/library/configparser.html#supported-ini-file-structure // Relevant quote: Values can also span multiple lines, as long as they are indented deeper // than the first line of the value. AllowPythonMultilineValues bool // SpaceBeforeInlineComment indicates whether to allow comment symbols (\# and \;) inside value. // Docs: https://docs.python.org/2/library/configparser.html // Quote: Comments may appear on their own in an otherwise empty line, or may be entered in lines holding values or section names. // In the latter case, they need to be preceded by a whitespace character to be recognized as a comment. SpaceBeforeInlineComment bool // UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format // when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value" UnescapeValueDoubleQuotes bool // UnescapeValueCommentSymbols indicates to unescape comment symbols (\# and \;) inside value to regular format // when value is NOT surrounded by any quotes. // Note: UNSTABLE, behavior might change to only unescape inside double quotes but may noy necessary at all. UnescapeValueCommentSymbols bool // UnparseableSections stores a list of blocks that are allowed with raw content which do not otherwise // conform to key/value pairs. Specify the names of those blocks here. UnparseableSections []string // KeyValueDelimiters is the sequence of delimiters that are used to separate key and value. By default, it is "=:". KeyValueDelimiters string // KeyValueDelimiterOnWrite is the delimiter that are used to separate key and value output. By default, it is "=". KeyValueDelimiterOnWrite string // ChildSectionDelimiter is the delimiter that is used to separate child sections. By default, it is ".". ChildSectionDelimiter string // PreserveSurroundedQuote indicates whether to preserve surrounded quote (single and double quotes). PreserveSurroundedQuote bool // DebugFunc is called to collect debug information (currently only useful to debug parsing Python-style multiline values). DebugFunc DebugFunc // ReaderBufferSize is the buffer size of the reader in bytes. ReaderBufferSize int // AllowNonUniqueSections indicates whether to allow sections with the same name multiple times. AllowNonUniqueSections bool }
LoadOptions contains all customized options used for load data source(s).
type NameMapper ¶
NameMapper represents a ini tag name mapper.
var ( // SnackCase converts to format SNACK_CASE. SnackCase NameMapper = func(raw string) string { newstr := make([]rune, 0, len(raw)) for i, chr := range raw { if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { if i > 0 { newstr = append(newstr, '_') } } newstr = append(newstr, unicode.ToUpper(chr)) } return string(newstr) } // TitleUnderscore converts to format title_underscore. TitleUnderscore NameMapper = func(raw string) string { newstr := make([]rune, 0, len(raw)) for i, chr := range raw { if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { if i > 0 { newstr = append(newstr, '_') } chr -= 'A' - 'a' } newstr = append(newstr, chr) } return string(newstr) } )
Built-in name getters.
type Section ¶
type Section struct { Comment string // contains filtered or unexported fields }
Section represents a config section.
func (*Section) Body ¶ added in v1.22.0
Body returns rawBody of Section if the section was marked as unparseable. It still follows the other rules of the INI format surrounding leading/trailing whitespace.
func (*Section) ChildSections ¶ added in v1.27.0
ChildSections returns a list of child sections of current section. For example, "[parent.child1]" and "[parent.child12]" are child sections of section "[parent]".
func (*Section) KeyStrings ¶
KeyStrings returns list of key names of section.
func (*Section) NewBooleanKey ¶ added in v1.24.0
NewBooleanKey creates a new boolean type key to given section.
func (*Section) ParentKeys ¶ added in v1.12.0
ParentKeys returns list of keys of parent section.
func (*Section) ReflectFrom ¶
ReflectFrom reflects section from given struct. It overwrites existing ones.
func (*Section) StrictMapTo ¶ added in v1.28.0
StrictMapTo maps section to given struct in strict mode, which returns all possible error including value parsing error.
type StructReflector ¶ added in v1.52.0
StructReflector is the interface implemented by struct types that can extract themselves into INI objects.
type ValueMapper ¶ added in v1.20.0
ValueMapper represents a mapping function for values, e.g. os.ExpandEnv