|  |  | @ -15,37 +15,35 @@ | 
			
		
	
		
			
				
					|  |  |  | package caddyfile | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | import ( | 
			
		
	
		
			
				
					|  |  |  | 	"log" | 
			
		
	
		
			
				
					|  |  |  | 	"strings" | 
			
		
	
		
			
				
					|  |  |  | 	"testing" | 
			
		
	
		
			
				
					|  |  |  | ) | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | type lexerTestCase struct { | 
			
		
	
		
			
				
					|  |  |  | 	input    string | 
			
		
	
		
			
				
					|  |  |  | 	input    []byte | 
			
		
	
		
			
				
					|  |  |  | 	expected []Token | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | func TestLexer(t *testing.T) { | 
			
		
	
		
			
				
					|  |  |  | 	testCases := []lexerTestCase{ | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: `host:123`, | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte(`host:123`), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "host:123"}, | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: `host:123 | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte(`host:123 | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | 					directive`, | 
			
		
	
		
			
				
					|  |  |  | 					directive`), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "host:123"}, | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 3, Text: "directive"}, | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: `host:123 { | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte(`host:123 { | 
			
		
	
		
			
				
					|  |  |  | 						directive | 
			
		
	
		
			
				
					|  |  |  | 					}`, | 
			
		
	
		
			
				
					|  |  |  | 					}`), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "host:123"}, | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "{"}, | 
			
		
	
	
		
			
				
					|  |  | @ -54,7 +52,7 @@ func TestLexer(t *testing.T) { | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: `host:123 { directive }`, | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte(`host:123 { directive }`), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "host:123"}, | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "{"}, | 
			
		
	
	
		
			
				
					|  |  | @ -63,12 +61,12 @@ func TestLexer(t *testing.T) { | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: `host:123 { | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte(`host:123 { | 
			
		
	
		
			
				
					|  |  |  | 						#comment | 
			
		
	
		
			
				
					|  |  |  | 						directive | 
			
		
	
		
			
				
					|  |  |  | 						# comment | 
			
		
	
		
			
				
					|  |  |  | 						foobar # another comment | 
			
		
	
		
			
				
					|  |  |  | 					}`, | 
			
		
	
		
			
				
					|  |  |  | 					}`), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "host:123"}, | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "{"}, | 
			
		
	
	
		
			
				
					|  |  | @ -78,10 +76,10 @@ func TestLexer(t *testing.T) { | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: `host:123 { | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte(`host:123 { | 
			
		
	
		
			
				
					|  |  |  | 						# hash inside string is not a comment | 
			
		
	
		
			
				
					|  |  |  | 						redir / /some/#/path | 
			
		
	
		
			
				
					|  |  |  | 					}`, | 
			
		
	
		
			
				
					|  |  |  | 					}`), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "host:123"}, | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "{"}, | 
			
		
	
	
		
			
				
					|  |  | @ -92,14 +90,14 @@ func TestLexer(t *testing.T) { | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: "# comment at beginning of file\n# comment at beginning of line\nhost:123", | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte("# comment at beginning of file\n# comment at beginning of line\nhost:123"), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 3, Text: "host:123"}, | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: `a "quoted value" b | 
			
		
	
		
			
				
					|  |  |  | 					foobar`, | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte(`a "quoted value" b | 
			
		
	
		
			
				
					|  |  |  | 					foobar`), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "a"}, | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "quoted value"}, | 
			
		
	
	
		
			
				
					|  |  | @ -108,7 +106,7 @@ func TestLexer(t *testing.T) { | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: `A "quoted \"value\" inside" B`, | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte(`A "quoted \"value\" inside" B`), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "A"}, | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: `quoted "value" inside`}, | 
			
		
	
	
		
			
				
					|  |  | @ -116,7 +114,7 @@ func TestLexer(t *testing.T) { | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: "An escaped \"newline\\\ninside\" quotes", | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte("An escaped \"newline\\\ninside\" quotes"), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "An"}, | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "escaped"}, | 
			
		
	
	
		
			
				
					|  |  | @ -125,7 +123,7 @@ func TestLexer(t *testing.T) { | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: "An escaped newline\\\noutside quotes", | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte("An escaped newline\\\noutside quotes"), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "An"}, | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "escaped"}, | 
			
		
	
	
		
			
				
					|  |  | @ -135,7 +133,7 @@ func TestLexer(t *testing.T) { | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: "line1\\\nescaped\nline2\nline3", | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte("line1\\\nescaped\nline2\nline3"), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "line1"}, | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "escaped"}, | 
			
		
	
	
		
			
				
					|  |  | @ -144,7 +142,7 @@ func TestLexer(t *testing.T) { | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: "line1\\\nescaped1\\\nescaped2\nline4\nline5", | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte("line1\\\nescaped1\\\nescaped2\nline4\nline5"), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "line1"}, | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "escaped1"}, | 
			
		
	
	
		
			
				
					|  |  | @ -154,34 +152,34 @@ func TestLexer(t *testing.T) { | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: `"unescapable\ in quotes"`, | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte(`"unescapable\ in quotes"`), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: `unescapable\ in quotes`}, | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: `"don't\escape"`, | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte(`"don't\escape"`), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: `don't\escape`}, | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: `"don't\\escape"`, | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte(`"don't\\escape"`), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: `don't\\escape`}, | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: `un\escapable`, | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte(`un\escapable`), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: `un\escapable`}, | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: `A "quoted value with line | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte(`A "quoted value with line | 
			
		
	
		
			
				
					|  |  |  | 					break inside" { | 
			
		
	
		
			
				
					|  |  |  | 						foobar | 
			
		
	
		
			
				
					|  |  |  | 					}`, | 
			
		
	
		
			
				
					|  |  |  | 					}`), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "A"}, | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "quoted value with line\n\t\t\t\t\tbreak inside"}, | 
			
		
	
	
		
			
				
					|  |  | @ -191,13 +189,13 @@ func TestLexer(t *testing.T) { | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: `"C:\php\php-cgi.exe"`, | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte(`"C:\php\php-cgi.exe"`), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: `C:\php\php-cgi.exe`}, | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: `empty "" string`, | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte(`empty "" string`), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: `empty`}, | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: ``}, | 
			
		
	
	
		
			
				
					|  |  | @ -205,7 +203,7 @@ func TestLexer(t *testing.T) { | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: "skip those\r\nCR characters", | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte("skip those\r\nCR characters"), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "skip"}, | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "those"}, | 
			
		
	
	
		
			
				
					|  |  | @ -214,13 +212,13 @@ func TestLexer(t *testing.T) { | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: "\xEF\xBB\xBF:8080", // test with leading byte order mark
 | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte("\xEF\xBB\xBF:8080"), // test with leading byte order mark
 | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: ":8080"}, | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: "simple `backtick quoted` string", | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte("simple `backtick quoted` string"), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: `simple`}, | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: `backtick quoted`}, | 
			
		
	
	
		
			
				
					|  |  | @ -228,7 +226,7 @@ func TestLexer(t *testing.T) { | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: "multiline `backtick\nquoted\n` string", | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte("multiline `backtick\nquoted\n` string"), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: `multiline`}, | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "backtick\nquoted\n"}, | 
			
		
	
	
		
			
				
					|  |  | @ -236,7 +234,7 @@ func TestLexer(t *testing.T) { | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: "nested `\"quotes inside\" backticks` string", | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte("nested `\"quotes inside\" backticks` string"), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: `nested`}, | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: `"quotes inside" backticks`}, | 
			
		
	
	
		
			
				
					|  |  | @ -244,7 +242,7 @@ func TestLexer(t *testing.T) { | 
			
		
	
		
			
				
					|  |  |  | 			}, | 
			
		
	
		
			
				
					|  |  |  | 		}, | 
			
		
	
		
			
				
					|  |  |  | 		{ | 
			
		
	
		
			
				
					|  |  |  | 			input: "reverse-nested \"`backticks` inside\" quotes", | 
			
		
	
		
			
				
					|  |  |  | 			input: []byte("reverse-nested \"`backticks` inside\" quotes"), | 
			
		
	
		
			
				
					|  |  |  | 			expected: []Token{ | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: `reverse-nested`}, | 
			
		
	
		
			
				
					|  |  |  | 				{Line: 1, Text: "`backticks` inside"}, | 
			
		
	
	
		
			
				
					|  |  | @ -254,20 +252,12 @@ func TestLexer(t *testing.T) { | 
			
		
	
		
			
				
					|  |  |  | 	} | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | 	for i, testCase := range testCases { | 
			
		
	
		
			
				
					|  |  |  | 		actual := tokenize(testCase.input) | 
			
		
	
		
			
				
					|  |  |  | 		lexerCompare(t, i, testCase.expected, actual) | 
			
		
	
		
			
				
					|  |  |  | 		actual, err := Tokenize(testCase.input, "") | 
			
		
	
		
			
				
					|  |  |  | 		if err != nil { | 
			
		
	
		
			
				
					|  |  |  | 			t.Errorf("%v", err) | 
			
		
	
		
			
				
					|  |  |  | 		} | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | func tokenize(input string) (tokens []Token) { | 
			
		
	
		
			
				
					|  |  |  | 	l := lexer{} | 
			
		
	
		
			
				
					|  |  |  | 	if err := l.load(strings.NewReader(input)); err != nil { | 
			
		
	
		
			
				
					|  |  |  | 		log.Printf("[ERROR] load failed: %v", err) | 
			
		
	
		
			
				
					|  |  |  | 	} | 
			
		
	
		
			
				
					|  |  |  | 	for l.next() { | 
			
		
	
		
			
				
					|  |  |  | 		tokens = append(tokens, l.token) | 
			
		
	
		
			
				
					|  |  |  | 		lexerCompare(t, i, testCase.expected, actual) | 
			
		
	
		
			
				
					|  |  |  | 	} | 
			
		
	
		
			
				
					|  |  |  | 	return | 
			
		
	
		
			
				
					|  |  |  | } | 
			
		
	
		
			
				
					|  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  | func lexerCompare(t *testing.T, n int, expected, actual []Token) { | 
			
		
	
	
		
			
				
					|  |  | 
 |