diff --git a/caddy/parse/import_glob0.txt b/caddy/parse/import_glob0.txt new file mode 100644 index 00000000..e610b5e7 --- /dev/null +++ b/caddy/parse/import_glob0.txt @@ -0,0 +1,6 @@ +glob0.host0 { + dir2 arg1 +} + +glob0.host1 { +} diff --git a/caddy/parse/import_glob1.txt b/caddy/parse/import_glob1.txt new file mode 100644 index 00000000..111eb044 --- /dev/null +++ b/caddy/parse/import_glob1.txt @@ -0,0 +1,4 @@ +glob1.host0 { + dir1 + dir2 arg1 +} diff --git a/caddy/parse/import_glob2.txt b/caddy/parse/import_glob2.txt new file mode 100644 index 00000000..c09f784e --- /dev/null +++ b/caddy/parse/import_glob2.txt @@ -0,0 +1,3 @@ +glob2.host0 { + dir2 arg1 +} diff --git a/caddy/parse/parsing.go b/caddy/parse/parsing.go index 03d9d800..6ef908b0 100644 --- a/caddy/parse/parsing.go +++ b/caddy/parse/parsing.go @@ -176,19 +176,52 @@ func (p *parser) directives() error { } // doImport swaps out the import directive and its argument -// (a total of 2 tokens) with the tokens in the file specified. -// When the function returns, the cursor is on the token before -// where the import directive was. In other words, call Next() -// to access the first token that was imported. +// (a total of 2 tokens) with the tokens in the specified file +// or globbing pattern. When the function returns, the cursor +// is on the token before where the import directive was. In +// other words, call Next() to access the first token that was +// imported. func (p *parser) doImport() error { if !p.NextArg() { return p.ArgErr() } - importFile := p.Val() + importPattern := p.Val() if p.NextArg() { - return p.Err("Import allows only one file to import") + return p.Err("Import allows only one expression, either file or glob pattern") } + matches, err := filepath.Glob(importPattern) + if err != nil { + return p.Errf("Failed to use import pattern %s - %s", importPattern, err.Error()) + } + + if len(matches) == 0 { + return p.Errf("No files matching the import pattern %s", importPattern) + } + + // Splice out the import directive and its argument (2 tokens total) + // and insert the imported tokens in their place. + tokensBefore := p.tokens[:p.cursor-1] + tokensAfter := p.tokens[p.cursor+1:] + // cursor was advanced one position to read filename; rewind it + p.cursor-- + + p.tokens = tokensBefore + + for _, importFile := range matches { + if err := p.doSingleImport(importFile); err != nil { + return err + } + } + + p.tokens = append(p.tokens, append(tokensAfter)...) + + return nil +} + +// doSingleImport lexes the individual files matching the +// globbing pattern from of the import directive. +func (p *parser) doSingleImport(importFile string) error { file, err := os.Open(importFile) if err != nil { return p.Errf("Could not import %s - %v", importFile, err) @@ -203,10 +236,7 @@ func (p *parser) doImport() error { // Splice out the import directive and its argument (2 tokens total) // and insert the imported tokens in their place. - tokensBefore := p.tokens[:p.cursor-1] - tokensAfter := p.tokens[p.cursor+1:] - p.tokens = append(tokensBefore, append(importedTokens, tokensAfter...)...) - p.cursor-- // cursor was advanced one position to read the filename; rewind it + p.tokens = append(p.tokens, append(importedTokens)...) return nil } diff --git a/caddy/parse/parsing_test.go b/caddy/parse/parsing_test.go index 97c86808..bda6b29b 100644 --- a/caddy/parse/parsing_test.go +++ b/caddy/parse/parsing_test.go @@ -329,6 +329,13 @@ func TestParseAll(t *testing.T) { []address{{"host1.com", "http"}, {"host2.com", "http"}}, []address{{"host3.com", "https"}, {"host4.com", "https"}}, }}, + + {`import import_glob*.txt`, false, [][]address{ + []address{{"glob0.host0", ""}}, + []address{{"glob0.host1", ""}}, + []address{{"glob1.host0", ""}}, + []address{{"glob2.host0", ""}}, + }}, } { p := testParser(test.input) blocks, err := p.parseAll()