diff --git a/example/example.cfg b/example/example.cfg index f5bb5d0..d956b5e 100644 --- a/example/example.cfg +++ b/example/example.cfg @@ -9,7 +9,7 @@ primary { negative = FALSE; nothing = NULL; # Include external files - include "./include*.cfg"; + include "./*.cfg"; # Primary-sub stuff sub { key = "primary sub key value"; diff --git a/forge.go b/forge.go index 2db9b0c..6bc4fa7 100644 --- a/forge.go +++ b/forge.go @@ -101,7 +101,6 @@ package forge import ( "bytes" "io" - "os" "strings" ) @@ -116,11 +115,16 @@ func ParseBytes(data []byte) (*Section, error) { // and responds with `*Section` and potentially an `error` if it cannot // properly parse the configf func ParseFile(filename string) (*Section, error) { - reader, err := os.Open(filename) + parser, err := NewFileParser(filename) if err != nil { return nil, err } - return ParseReader(reader) + err = parser.Parse() + if err != nil { + return nil, err + } + + return parser.GetSettings(), nil } // ParseReader takes an `io.Reader` representation of the config file, parses it diff --git a/parser.go b/parser.go index de927c9..7924535 100644 --- a/parser.go +++ b/parser.go @@ -13,6 +13,7 @@ import ( // Parser is a struct to hold data necessary for parsing a config from a scanner type Parser struct { + files []string settings *Section scanner *Scanner curTok token.Token @@ -24,6 +25,7 @@ type Parser struct { func NewParser(reader io.Reader) *Parser { settings := NewSection() return &Parser{ + files: make([]string, 0), scanner: NewScanner(reader), settings: settings, curSection: settings, @@ -31,6 +33,30 @@ func NewParser(reader io.Reader) *Parser { } } +// NewParser will create and initialize a new Parser from a provided from a filename string +func NewFileParser(filename string) (*Parser, error) { + reader, err := os.Open(filename) + if err != nil { + return nil, err + } + parser := NewParser(reader) + parser.addFile(filename) + return parser, nil +} + +func (parser *Parser) addFile(filename string) { + parser.files = append(parser.files, filename) +} + +func (parser *Parser) hasParsed(search string) bool { + for _, filename := range parser.files { + if filename == search { + return true + } + } + return false +} + func (parser *Parser) syntaxError(msg string) error { msg = fmt.Sprintf( "syntax error line <%d> column <%d>: %s", @@ -165,6 +191,11 @@ func (parser *Parser) parseInclude() error { } oldScanner := parser.scanner for _, filename := range filenames { + // We have already visited this file, don't include again + // DEV: This can cause recursive includes if this isn't here :o + if parser.hasParsed(filename) { + continue + } reader, err := os.Open(filename) if err != nil { return err @@ -172,6 +203,9 @@ func (parser *Parser) parseInclude() error { parser.curSection.AddInclude(filename) parser.scanner = NewScanner(reader) parser.parse() + // Make sure to add the filename to the internal list to ensure we don't + // accidentally recursively include config files + parser.addFile(filename) } parser.scanner = oldScanner parser.readToken()