diff --git a/handlers/ssh_config/Config.g4 b/handlers/ssh_config/Config.g4 new file mode 100644 index 0000000..ad4cbe8 --- /dev/null +++ b/handlers/ssh_config/Config.g4 @@ -0,0 +1,41 @@ +grammar Config; + +lineStatement + : (entry | leadingComment | WHITESPACE?) EOF + ; + +entry + : WHITESPACE? key? separator? value? leadingComment? + ; + +separator + : WHITESPACE + ; + +key + : STRING + ; + +value + : (STRING WHITESPACE)* STRING? WHITESPACE? + ; + +leadingComment + : HASH WHITESPACE? (STRING WHITESPACE?)+ + ; + +HASH + : '#' + ; + +WHITESPACE + : [ \t]+ + ; + +STRING + : ~(' ' | '\t' | '\r' | '\n' | '#')+ + ; + +NEWLINE + : '\r'? '\n' + ; diff --git a/handlers/ssh_config/ast/parser/Config.interp b/handlers/ssh_config/ast/parser/Config.interp new file mode 100644 index 0000000..2832f42 --- /dev/null +++ b/handlers/ssh_config/ast/parser/Config.interp @@ -0,0 +1,25 @@ +token literal names: +null +'#' +null +null +null + +token symbolic names: +null +HASH +WHITESPACE +STRING +NEWLINE + +rule names: +lineStatement +entry +separator +key +value +leadingComment + + +atn: +[4, 1, 4, 66, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 1, 0, 1, 0, 1, 0, 3, 0, 16, 8, 0, 3, 0, 18, 8, 0, 1, 0, 1, 0, 1, 1, 3, 1, 23, 8, 1, 1, 1, 3, 1, 26, 8, 1, 1, 1, 3, 1, 29, 8, 1, 1, 1, 3, 1, 32, 8, 1, 1, 1, 3, 1, 35, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 5, 4, 43, 8, 4, 10, 4, 12, 4, 46, 9, 4, 1, 4, 3, 4, 49, 8, 4, 1, 4, 3, 4, 52, 8, 4, 1, 5, 1, 5, 3, 5, 56, 8, 5, 1, 5, 1, 5, 3, 5, 60, 8, 5, 4, 5, 62, 8, 5, 11, 5, 12, 5, 63, 1, 5, 0, 0, 6, 0, 2, 4, 6, 8, 10, 0, 0, 73, 0, 17, 1, 0, 0, 0, 2, 22, 1, 0, 0, 0, 4, 36, 1, 0, 0, 0, 6, 38, 1, 0, 0, 0, 8, 44, 1, 0, 0, 0, 10, 53, 1, 0, 0, 0, 12, 18, 3, 2, 1, 0, 13, 18, 3, 10, 5, 0, 14, 16, 5, 2, 0, 0, 15, 14, 1, 0, 0, 0, 15, 16, 1, 0, 0, 0, 16, 18, 1, 0, 0, 0, 17, 12, 1, 0, 0, 0, 17, 13, 1, 0, 0, 0, 17, 15, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 20, 5, 0, 0, 1, 20, 1, 1, 0, 0, 0, 21, 23, 5, 2, 0, 0, 22, 21, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 25, 1, 0, 0, 0, 24, 26, 3, 6, 3, 0, 25, 24, 1, 0, 0, 0, 25, 26, 1, 0, 0, 0, 26, 28, 1, 0, 0, 0, 27, 29, 3, 4, 2, 0, 28, 27, 1, 0, 0, 0, 28, 29, 1, 0, 0, 0, 29, 31, 1, 0, 0, 0, 30, 32, 3, 8, 4, 0, 31, 30, 1, 0, 0, 0, 31, 32, 1, 0, 0, 0, 32, 34, 1, 0, 0, 0, 33, 35, 3, 10, 5, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 35, 3, 1, 0, 0, 0, 36, 37, 5, 2, 0, 0, 37, 5, 1, 0, 0, 0, 38, 39, 5, 3, 0, 0, 39, 7, 1, 0, 0, 0, 40, 41, 5, 3, 0, 0, 41, 43, 5, 2, 0, 0, 42, 40, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1, 0, 0, 0, 44, 45, 1, 0, 0, 0, 45, 48, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 47, 49, 5, 3, 0, 0, 48, 47, 1, 0, 0, 0, 48, 49, 1, 0, 0, 0, 49, 51, 1, 0, 0, 0, 50, 52, 5, 2, 0, 0, 51, 50, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, 9, 1, 0, 0, 0, 53, 55, 5, 1, 0, 0, 54, 56, 5, 2, 0, 0, 55, 54, 1, 0, 0, 0, 55, 56, 1, 0, 0, 0, 56, 61, 1, 0, 0, 0, 57, 59, 5, 3, 0, 0, 58, 60, 5, 2, 0, 0, 59, 58, 1, 0, 0, 0, 59, 60, 1, 0, 0, 0, 60, 62, 1, 0, 0, 0, 61, 57, 1, 0, 0, 0, 62, 63, 1, 0, 0, 0, 63, 61, 1, 0, 0, 0, 63, 64, 1, 0, 0, 0, 64, 11, 1, 0, 0, 0, 13, 15, 17, 22, 25, 28, 31, 34, 44, 48, 51, 55, 59, 63] \ No newline at end of file diff --git a/handlers/ssh_config/ast/parser/Config.tokens b/handlers/ssh_config/ast/parser/Config.tokens new file mode 100644 index 0000000..aacc14c --- /dev/null +++ b/handlers/ssh_config/ast/parser/Config.tokens @@ -0,0 +1,5 @@ +HASH=1 +WHITESPACE=2 +STRING=3 +NEWLINE=4 +'#'=1 diff --git a/handlers/ssh_config/ast/parser/ConfigLexer.interp b/handlers/ssh_config/ast/parser/ConfigLexer.interp new file mode 100644 index 0000000..d61e14d --- /dev/null +++ b/handlers/ssh_config/ast/parser/ConfigLexer.interp @@ -0,0 +1,29 @@ +token literal names: +null +'#' +null +null +null + +token symbolic names: +null +HASH +WHITESPACE +STRING +NEWLINE + +rule names: +HASH +WHITESPACE +STRING +NEWLINE + +channel names: +DEFAULT_TOKEN_CHANNEL +HIDDEN + +mode names: +DEFAULT_MODE + +atn: +[4, 0, 4, 26, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 1, 0, 1, 0, 1, 1, 4, 1, 13, 8, 1, 11, 1, 12, 1, 14, 1, 2, 4, 2, 18, 8, 2, 11, 2, 12, 2, 19, 1, 3, 3, 3, 23, 8, 3, 1, 3, 1, 3, 0, 0, 4, 1, 1, 3, 2, 5, 3, 7, 4, 1, 0, 2, 2, 0, 9, 9, 32, 32, 4, 0, 9, 10, 13, 13, 32, 32, 35, 35, 28, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 1, 9, 1, 0, 0, 0, 3, 12, 1, 0, 0, 0, 5, 17, 1, 0, 0, 0, 7, 22, 1, 0, 0, 0, 9, 10, 5, 35, 0, 0, 10, 2, 1, 0, 0, 0, 11, 13, 7, 0, 0, 0, 12, 11, 1, 0, 0, 0, 13, 14, 1, 0, 0, 0, 14, 12, 1, 0, 0, 0, 14, 15, 1, 0, 0, 0, 15, 4, 1, 0, 0, 0, 16, 18, 8, 1, 0, 0, 17, 16, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 17, 1, 0, 0, 0, 19, 20, 1, 0, 0, 0, 20, 6, 1, 0, 0, 0, 21, 23, 5, 13, 0, 0, 22, 21, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 24, 1, 0, 0, 0, 24, 25, 5, 10, 0, 0, 25, 8, 1, 0, 0, 0, 4, 0, 14, 19, 22, 0] \ No newline at end of file diff --git a/handlers/ssh_config/ast/parser/ConfigLexer.tokens b/handlers/ssh_config/ast/parser/ConfigLexer.tokens new file mode 100644 index 0000000..aacc14c --- /dev/null +++ b/handlers/ssh_config/ast/parser/ConfigLexer.tokens @@ -0,0 +1,5 @@ +HASH=1 +WHITESPACE=2 +STRING=3 +NEWLINE=4 +'#'=1 diff --git a/handlers/ssh_config/ast/parser/config_base_listener.go b/handlers/ssh_config/ast/parser/config_base_listener.go new file mode 100644 index 0000000..ac8bdac --- /dev/null +++ b/handlers/ssh_config/ast/parser/config_base_listener.go @@ -0,0 +1,58 @@ +// Code generated from Config.g4 by ANTLR 4.13.0. DO NOT EDIT. + +package parser // Config + +import "github.com/antlr4-go/antlr/v4" + +// BaseConfigListener is a complete listener for a parse tree produced by ConfigParser. +type BaseConfigListener struct{} + +var _ ConfigListener = &BaseConfigListener{} + +// VisitTerminal is called when a terminal node is visited. +func (s *BaseConfigListener) VisitTerminal(node antlr.TerminalNode) {} + +// VisitErrorNode is called when an error node is visited. +func (s *BaseConfigListener) VisitErrorNode(node antlr.ErrorNode) {} + +// EnterEveryRule is called when any rule is entered. +func (s *BaseConfigListener) EnterEveryRule(ctx antlr.ParserRuleContext) {} + +// ExitEveryRule is called when any rule is exited. +func (s *BaseConfigListener) ExitEveryRule(ctx antlr.ParserRuleContext) {} + +// EnterLineStatement is called when production lineStatement is entered. +func (s *BaseConfigListener) EnterLineStatement(ctx *LineStatementContext) {} + +// ExitLineStatement is called when production lineStatement is exited. +func (s *BaseConfigListener) ExitLineStatement(ctx *LineStatementContext) {} + +// EnterEntry is called when production entry is entered. +func (s *BaseConfigListener) EnterEntry(ctx *EntryContext) {} + +// ExitEntry is called when production entry is exited. +func (s *BaseConfigListener) ExitEntry(ctx *EntryContext) {} + +// EnterSeparator is called when production separator is entered. +func (s *BaseConfigListener) EnterSeparator(ctx *SeparatorContext) {} + +// ExitSeparator is called when production separator is exited. +func (s *BaseConfigListener) ExitSeparator(ctx *SeparatorContext) {} + +// EnterKey is called when production key is entered. +func (s *BaseConfigListener) EnterKey(ctx *KeyContext) {} + +// ExitKey is called when production key is exited. +func (s *BaseConfigListener) ExitKey(ctx *KeyContext) {} + +// EnterValue is called when production value is entered. +func (s *BaseConfigListener) EnterValue(ctx *ValueContext) {} + +// ExitValue is called when production value is exited. +func (s *BaseConfigListener) ExitValue(ctx *ValueContext) {} + +// EnterLeadingComment is called when production leadingComment is entered. +func (s *BaseConfigListener) EnterLeadingComment(ctx *LeadingCommentContext) {} + +// ExitLeadingComment is called when production leadingComment is exited. +func (s *BaseConfigListener) ExitLeadingComment(ctx *LeadingCommentContext) {} diff --git a/handlers/ssh_config/ast/parser/config_lexer.go b/handlers/ssh_config/ast/parser/config_lexer.go new file mode 100644 index 0000000..ace491d --- /dev/null +++ b/handlers/ssh_config/ast/parser/config_lexer.go @@ -0,0 +1,112 @@ +// Code generated from Config.g4 by ANTLR 4.13.0. DO NOT EDIT. + +package parser + +import ( + "fmt" + "github.com/antlr4-go/antlr/v4" + "sync" + "unicode" +) + +// Suppress unused import error +var _ = fmt.Printf +var _ = sync.Once{} +var _ = unicode.IsLetter + +type ConfigLexer struct { + *antlr.BaseLexer + channelNames []string + modeNames []string + // TODO: EOF string +} + +var ConfigLexerLexerStaticData struct { + once sync.Once + serializedATN []int32 + ChannelNames []string + ModeNames []string + LiteralNames []string + SymbolicNames []string + RuleNames []string + PredictionContextCache *antlr.PredictionContextCache + atn *antlr.ATN + decisionToDFA []*antlr.DFA +} + +func configlexerLexerInit() { + staticData := &ConfigLexerLexerStaticData + staticData.ChannelNames = []string{ + "DEFAULT_TOKEN_CHANNEL", "HIDDEN", + } + staticData.ModeNames = []string{ + "DEFAULT_MODE", + } + staticData.LiteralNames = []string{ + "", "'#'", + } + staticData.SymbolicNames = []string{ + "", "HASH", "WHITESPACE", "STRING", "NEWLINE", + } + staticData.RuleNames = []string{ + "HASH", "WHITESPACE", "STRING", "NEWLINE", + } + staticData.PredictionContextCache = antlr.NewPredictionContextCache() + staticData.serializedATN = []int32{ + 4, 0, 4, 26, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 1, + 0, 1, 0, 1, 1, 4, 1, 13, 8, 1, 11, 1, 12, 1, 14, 1, 2, 4, 2, 18, 8, 2, + 11, 2, 12, 2, 19, 1, 3, 3, 3, 23, 8, 3, 1, 3, 1, 3, 0, 0, 4, 1, 1, 3, 2, + 5, 3, 7, 4, 1, 0, 2, 2, 0, 9, 9, 32, 32, 4, 0, 9, 10, 13, 13, 32, 32, 35, + 35, 28, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, + 0, 0, 0, 1, 9, 1, 0, 0, 0, 3, 12, 1, 0, 0, 0, 5, 17, 1, 0, 0, 0, 7, 22, + 1, 0, 0, 0, 9, 10, 5, 35, 0, 0, 10, 2, 1, 0, 0, 0, 11, 13, 7, 0, 0, 0, + 12, 11, 1, 0, 0, 0, 13, 14, 1, 0, 0, 0, 14, 12, 1, 0, 0, 0, 14, 15, 1, + 0, 0, 0, 15, 4, 1, 0, 0, 0, 16, 18, 8, 1, 0, 0, 17, 16, 1, 0, 0, 0, 18, + 19, 1, 0, 0, 0, 19, 17, 1, 0, 0, 0, 19, 20, 1, 0, 0, 0, 20, 6, 1, 0, 0, + 0, 21, 23, 5, 13, 0, 0, 22, 21, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 24, + 1, 0, 0, 0, 24, 25, 5, 10, 0, 0, 25, 8, 1, 0, 0, 0, 4, 0, 14, 19, 22, 0, + } + deserializer := antlr.NewATNDeserializer(nil) + staticData.atn = deserializer.Deserialize(staticData.serializedATN) + atn := staticData.atn + staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState)) + decisionToDFA := staticData.decisionToDFA + for index, state := range atn.DecisionToState { + decisionToDFA[index] = antlr.NewDFA(state, index) + } +} + +// ConfigLexerInit initializes any static state used to implement ConfigLexer. By default the +// static state used to implement the lexer is lazily initialized during the first call to +// NewConfigLexer(). You can call this function if you wish to initialize the static state ahead +// of time. +func ConfigLexerInit() { + staticData := &ConfigLexerLexerStaticData + staticData.once.Do(configlexerLexerInit) +} + +// NewConfigLexer produces a new lexer instance for the optional input antlr.CharStream. +func NewConfigLexer(input antlr.CharStream) *ConfigLexer { + ConfigLexerInit() + l := new(ConfigLexer) + l.BaseLexer = antlr.NewBaseLexer(input) + staticData := &ConfigLexerLexerStaticData + l.Interpreter = antlr.NewLexerATNSimulator(l, staticData.atn, staticData.decisionToDFA, staticData.PredictionContextCache) + l.channelNames = staticData.ChannelNames + l.modeNames = staticData.ModeNames + l.RuleNames = staticData.RuleNames + l.LiteralNames = staticData.LiteralNames + l.SymbolicNames = staticData.SymbolicNames + l.GrammarFileName = "Config.g4" + // TODO: l.EOF = antlr.TokenEOF + + return l +} + +// ConfigLexer tokens. +const ( + ConfigLexerHASH = 1 + ConfigLexerWHITESPACE = 2 + ConfigLexerSTRING = 3 + ConfigLexerNEWLINE = 4 +) diff --git a/handlers/ssh_config/ast/parser/config_listener.go b/handlers/ssh_config/ast/parser/config_listener.go new file mode 100644 index 0000000..0384e3c --- /dev/null +++ b/handlers/ssh_config/ast/parser/config_listener.go @@ -0,0 +1,46 @@ +// Code generated from Config.g4 by ANTLR 4.13.0. DO NOT EDIT. + +package parser // Config + +import "github.com/antlr4-go/antlr/v4" + +// ConfigListener is a complete listener for a parse tree produced by ConfigParser. +type ConfigListener interface { + antlr.ParseTreeListener + + // EnterLineStatement is called when entering the lineStatement production. + EnterLineStatement(c *LineStatementContext) + + // EnterEntry is called when entering the entry production. + EnterEntry(c *EntryContext) + + // EnterSeparator is called when entering the separator production. + EnterSeparator(c *SeparatorContext) + + // EnterKey is called when entering the key production. + EnterKey(c *KeyContext) + + // EnterValue is called when entering the value production. + EnterValue(c *ValueContext) + + // EnterLeadingComment is called when entering the leadingComment production. + EnterLeadingComment(c *LeadingCommentContext) + + // ExitLineStatement is called when exiting the lineStatement production. + ExitLineStatement(c *LineStatementContext) + + // ExitEntry is called when exiting the entry production. + ExitEntry(c *EntryContext) + + // ExitSeparator is called when exiting the separator production. + ExitSeparator(c *SeparatorContext) + + // ExitKey is called when exiting the key production. + ExitKey(c *KeyContext) + + // ExitValue is called when exiting the value production. + ExitValue(c *ValueContext) + + // ExitLeadingComment is called when exiting the leadingComment production. + ExitLeadingComment(c *LeadingCommentContext) +} diff --git a/handlers/ssh_config/ast/parser/config_parser.go b/handlers/ssh_config/ast/parser/config_parser.go new file mode 100644 index 0000000..018bbb8 --- /dev/null +++ b/handlers/ssh_config/ast/parser/config_parser.go @@ -0,0 +1,1082 @@ +// Code generated from Config.g4 by ANTLR 4.13.0. DO NOT EDIT. + +package parser // Config + +import ( + "fmt" + "strconv" + "sync" + + "github.com/antlr4-go/antlr/v4" +) + +// Suppress unused import errors +var _ = fmt.Printf +var _ = strconv.Itoa +var _ = sync.Once{} + +type ConfigParser struct { + *antlr.BaseParser +} + +var ConfigParserStaticData struct { + once sync.Once + serializedATN []int32 + LiteralNames []string + SymbolicNames []string + RuleNames []string + PredictionContextCache *antlr.PredictionContextCache + atn *antlr.ATN + decisionToDFA []*antlr.DFA +} + +func configParserInit() { + staticData := &ConfigParserStaticData + staticData.LiteralNames = []string{ + "", "'#'", + } + staticData.SymbolicNames = []string{ + "", "HASH", "WHITESPACE", "STRING", "NEWLINE", + } + staticData.RuleNames = []string{ + "lineStatement", "entry", "separator", "key", "value", "leadingComment", + } + staticData.PredictionContextCache = antlr.NewPredictionContextCache() + staticData.serializedATN = []int32{ + 4, 1, 4, 66, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, + 2, 5, 7, 5, 1, 0, 1, 0, 1, 0, 3, 0, 16, 8, 0, 3, 0, 18, 8, 0, 1, 0, 1, + 0, 1, 1, 3, 1, 23, 8, 1, 1, 1, 3, 1, 26, 8, 1, 1, 1, 3, 1, 29, 8, 1, 1, + 1, 3, 1, 32, 8, 1, 1, 1, 3, 1, 35, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, + 1, 4, 5, 4, 43, 8, 4, 10, 4, 12, 4, 46, 9, 4, 1, 4, 3, 4, 49, 8, 4, 1, + 4, 3, 4, 52, 8, 4, 1, 5, 1, 5, 3, 5, 56, 8, 5, 1, 5, 1, 5, 3, 5, 60, 8, + 5, 4, 5, 62, 8, 5, 11, 5, 12, 5, 63, 1, 5, 0, 0, 6, 0, 2, 4, 6, 8, 10, + 0, 0, 73, 0, 17, 1, 0, 0, 0, 2, 22, 1, 0, 0, 0, 4, 36, 1, 0, 0, 0, 6, 38, + 1, 0, 0, 0, 8, 44, 1, 0, 0, 0, 10, 53, 1, 0, 0, 0, 12, 18, 3, 2, 1, 0, + 13, 18, 3, 10, 5, 0, 14, 16, 5, 2, 0, 0, 15, 14, 1, 0, 0, 0, 15, 16, 1, + 0, 0, 0, 16, 18, 1, 0, 0, 0, 17, 12, 1, 0, 0, 0, 17, 13, 1, 0, 0, 0, 17, + 15, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 20, 5, 0, 0, 1, 20, 1, 1, 0, 0, + 0, 21, 23, 5, 2, 0, 0, 22, 21, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 25, + 1, 0, 0, 0, 24, 26, 3, 6, 3, 0, 25, 24, 1, 0, 0, 0, 25, 26, 1, 0, 0, 0, + 26, 28, 1, 0, 0, 0, 27, 29, 3, 4, 2, 0, 28, 27, 1, 0, 0, 0, 28, 29, 1, + 0, 0, 0, 29, 31, 1, 0, 0, 0, 30, 32, 3, 8, 4, 0, 31, 30, 1, 0, 0, 0, 31, + 32, 1, 0, 0, 0, 32, 34, 1, 0, 0, 0, 33, 35, 3, 10, 5, 0, 34, 33, 1, 0, + 0, 0, 34, 35, 1, 0, 0, 0, 35, 3, 1, 0, 0, 0, 36, 37, 5, 2, 0, 0, 37, 5, + 1, 0, 0, 0, 38, 39, 5, 3, 0, 0, 39, 7, 1, 0, 0, 0, 40, 41, 5, 3, 0, 0, + 41, 43, 5, 2, 0, 0, 42, 40, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1, + 0, 0, 0, 44, 45, 1, 0, 0, 0, 45, 48, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 47, + 49, 5, 3, 0, 0, 48, 47, 1, 0, 0, 0, 48, 49, 1, 0, 0, 0, 49, 51, 1, 0, 0, + 0, 50, 52, 5, 2, 0, 0, 51, 50, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, 9, 1, + 0, 0, 0, 53, 55, 5, 1, 0, 0, 54, 56, 5, 2, 0, 0, 55, 54, 1, 0, 0, 0, 55, + 56, 1, 0, 0, 0, 56, 61, 1, 0, 0, 0, 57, 59, 5, 3, 0, 0, 58, 60, 5, 2, 0, + 0, 59, 58, 1, 0, 0, 0, 59, 60, 1, 0, 0, 0, 60, 62, 1, 0, 0, 0, 61, 57, + 1, 0, 0, 0, 62, 63, 1, 0, 0, 0, 63, 61, 1, 0, 0, 0, 63, 64, 1, 0, 0, 0, + 64, 11, 1, 0, 0, 0, 13, 15, 17, 22, 25, 28, 31, 34, 44, 48, 51, 55, 59, + 63, + } + deserializer := antlr.NewATNDeserializer(nil) + staticData.atn = deserializer.Deserialize(staticData.serializedATN) + atn := staticData.atn + staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState)) + decisionToDFA := staticData.decisionToDFA + for index, state := range atn.DecisionToState { + decisionToDFA[index] = antlr.NewDFA(state, index) + } +} + +// ConfigParserInit initializes any static state used to implement ConfigParser. By default the +// static state used to implement the parser is lazily initialized during the first call to +// NewConfigParser(). You can call this function if you wish to initialize the static state ahead +// of time. +func ConfigParserInit() { + staticData := &ConfigParserStaticData + staticData.once.Do(configParserInit) +} + +// NewConfigParser produces a new parser instance for the optional input antlr.TokenStream. +func NewConfigParser(input antlr.TokenStream) *ConfigParser { + ConfigParserInit() + this := new(ConfigParser) + this.BaseParser = antlr.NewBaseParser(input) + staticData := &ConfigParserStaticData + this.Interpreter = antlr.NewParserATNSimulator(this, staticData.atn, staticData.decisionToDFA, staticData.PredictionContextCache) + this.RuleNames = staticData.RuleNames + this.LiteralNames = staticData.LiteralNames + this.SymbolicNames = staticData.SymbolicNames + this.GrammarFileName = "Config.g4" + + return this +} + +// ConfigParser tokens. +const ( + ConfigParserEOF = antlr.TokenEOF + ConfigParserHASH = 1 + ConfigParserWHITESPACE = 2 + ConfigParserSTRING = 3 + ConfigParserNEWLINE = 4 +) + +// ConfigParser rules. +const ( + ConfigParserRULE_lineStatement = 0 + ConfigParserRULE_entry = 1 + ConfigParserRULE_separator = 2 + ConfigParserRULE_key = 3 + ConfigParserRULE_value = 4 + ConfigParserRULE_leadingComment = 5 +) + +// ILineStatementContext is an interface to support dynamic dispatch. +type ILineStatementContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + EOF() antlr.TerminalNode + Entry() IEntryContext + LeadingComment() ILeadingCommentContext + WHITESPACE() antlr.TerminalNode + + // IsLineStatementContext differentiates from other interfaces. + IsLineStatementContext() +} + +type LineStatementContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyLineStatementContext() *LineStatementContext { + var p = new(LineStatementContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = ConfigParserRULE_lineStatement + return p +} + +func InitEmptyLineStatementContext(p *LineStatementContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = ConfigParserRULE_lineStatement +} + +func (*LineStatementContext) IsLineStatementContext() {} + +func NewLineStatementContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *LineStatementContext { + var p = new(LineStatementContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = ConfigParserRULE_lineStatement + + return p +} + +func (s *LineStatementContext) GetParser() antlr.Parser { return s.parser } + +func (s *LineStatementContext) EOF() antlr.TerminalNode { + return s.GetToken(ConfigParserEOF, 0) +} + +func (s *LineStatementContext) Entry() IEntryContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IEntryContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IEntryContext) +} + +func (s *LineStatementContext) LeadingComment() ILeadingCommentContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(ILeadingCommentContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(ILeadingCommentContext) +} + +func (s *LineStatementContext) WHITESPACE() antlr.TerminalNode { + return s.GetToken(ConfigParserWHITESPACE, 0) +} + +func (s *LineStatementContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *LineStatementContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *LineStatementContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(ConfigListener); ok { + listenerT.EnterLineStatement(s) + } +} + +func (s *LineStatementContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(ConfigListener); ok { + listenerT.ExitLineStatement(s) + } +} + +func (p *ConfigParser) LineStatement() (localctx ILineStatementContext) { + localctx = NewLineStatementContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 0, ConfigParserRULE_lineStatement) + var _la int + + p.EnterOuterAlt(localctx, 1) + p.SetState(17) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + + switch p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 1, p.GetParserRuleContext()) { + case 1: + { + p.SetState(12) + p.Entry() + } + + case 2: + { + p.SetState(13) + p.LeadingComment() + } + + case 3: + p.SetState(15) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == ConfigParserWHITESPACE { + { + p.SetState(14) + p.Match(ConfigParserWHITESPACE) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + + case antlr.ATNInvalidAltNumber: + goto errorExit + } + { + p.SetState(19) + p.Match(ConfigParserEOF) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IEntryContext is an interface to support dynamic dispatch. +type IEntryContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + WHITESPACE() antlr.TerminalNode + Key() IKeyContext + Separator() ISeparatorContext + Value() IValueContext + LeadingComment() ILeadingCommentContext + + // IsEntryContext differentiates from other interfaces. + IsEntryContext() +} + +type EntryContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyEntryContext() *EntryContext { + var p = new(EntryContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = ConfigParserRULE_entry + return p +} + +func InitEmptyEntryContext(p *EntryContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = ConfigParserRULE_entry +} + +func (*EntryContext) IsEntryContext() {} + +func NewEntryContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *EntryContext { + var p = new(EntryContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = ConfigParserRULE_entry + + return p +} + +func (s *EntryContext) GetParser() antlr.Parser { return s.parser } + +func (s *EntryContext) WHITESPACE() antlr.TerminalNode { + return s.GetToken(ConfigParserWHITESPACE, 0) +} + +func (s *EntryContext) Key() IKeyContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IKeyContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IKeyContext) +} + +func (s *EntryContext) Separator() ISeparatorContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(ISeparatorContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(ISeparatorContext) +} + +func (s *EntryContext) Value() IValueContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IValueContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IValueContext) +} + +func (s *EntryContext) LeadingComment() ILeadingCommentContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(ILeadingCommentContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(ILeadingCommentContext) +} + +func (s *EntryContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *EntryContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *EntryContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(ConfigListener); ok { + listenerT.EnterEntry(s) + } +} + +func (s *EntryContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(ConfigListener); ok { + listenerT.ExitEntry(s) + } +} + +func (p *ConfigParser) Entry() (localctx IEntryContext) { + localctx = NewEntryContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 2, ConfigParserRULE_entry) + var _la int + + p.EnterOuterAlt(localctx, 1) + p.SetState(22) + p.GetErrorHandler().Sync(p) + + if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 2, p.GetParserRuleContext()) == 1 { + { + p.SetState(21) + p.Match(ConfigParserWHITESPACE) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } else if p.HasError() { // JIM + goto errorExit + } + p.SetState(25) + p.GetErrorHandler().Sync(p) + + if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 3, p.GetParserRuleContext()) == 1 { + { + p.SetState(24) + p.Key() + } + + } else if p.HasError() { // JIM + goto errorExit + } + p.SetState(28) + p.GetErrorHandler().Sync(p) + + if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 4, p.GetParserRuleContext()) == 1 { + { + p.SetState(27) + p.Separator() + } + + } else if p.HasError() { // JIM + goto errorExit + } + p.SetState(31) + p.GetErrorHandler().Sync(p) + + if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 5, p.GetParserRuleContext()) == 1 { + { + p.SetState(30) + p.Value() + } + + } else if p.HasError() { // JIM + goto errorExit + } + p.SetState(34) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == ConfigParserHASH { + { + p.SetState(33) + p.LeadingComment() + } + + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// ISeparatorContext is an interface to support dynamic dispatch. +type ISeparatorContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + WHITESPACE() antlr.TerminalNode + + // IsSeparatorContext differentiates from other interfaces. + IsSeparatorContext() +} + +type SeparatorContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptySeparatorContext() *SeparatorContext { + var p = new(SeparatorContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = ConfigParserRULE_separator + return p +} + +func InitEmptySeparatorContext(p *SeparatorContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = ConfigParserRULE_separator +} + +func (*SeparatorContext) IsSeparatorContext() {} + +func NewSeparatorContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *SeparatorContext { + var p = new(SeparatorContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = ConfigParserRULE_separator + + return p +} + +func (s *SeparatorContext) GetParser() antlr.Parser { return s.parser } + +func (s *SeparatorContext) WHITESPACE() antlr.TerminalNode { + return s.GetToken(ConfigParserWHITESPACE, 0) +} + +func (s *SeparatorContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *SeparatorContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *SeparatorContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(ConfigListener); ok { + listenerT.EnterSeparator(s) + } +} + +func (s *SeparatorContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(ConfigListener); ok { + listenerT.ExitSeparator(s) + } +} + +func (p *ConfigParser) Separator() (localctx ISeparatorContext) { + localctx = NewSeparatorContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 4, ConfigParserRULE_separator) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(36) + p.Match(ConfigParserWHITESPACE) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IKeyContext is an interface to support dynamic dispatch. +type IKeyContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + STRING() antlr.TerminalNode + + // IsKeyContext differentiates from other interfaces. + IsKeyContext() +} + +type KeyContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyKeyContext() *KeyContext { + var p = new(KeyContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = ConfigParserRULE_key + return p +} + +func InitEmptyKeyContext(p *KeyContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = ConfigParserRULE_key +} + +func (*KeyContext) IsKeyContext() {} + +func NewKeyContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *KeyContext { + var p = new(KeyContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = ConfigParserRULE_key + + return p +} + +func (s *KeyContext) GetParser() antlr.Parser { return s.parser } + +func (s *KeyContext) STRING() antlr.TerminalNode { + return s.GetToken(ConfigParserSTRING, 0) +} + +func (s *KeyContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *KeyContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *KeyContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(ConfigListener); ok { + listenerT.EnterKey(s) + } +} + +func (s *KeyContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(ConfigListener); ok { + listenerT.ExitKey(s) + } +} + +func (p *ConfigParser) Key() (localctx IKeyContext) { + localctx = NewKeyContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 6, ConfigParserRULE_key) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(38) + p.Match(ConfigParserSTRING) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// IValueContext is an interface to support dynamic dispatch. +type IValueContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + AllSTRING() []antlr.TerminalNode + STRING(i int) antlr.TerminalNode + AllWHITESPACE() []antlr.TerminalNode + WHITESPACE(i int) antlr.TerminalNode + + // IsValueContext differentiates from other interfaces. + IsValueContext() +} + +type ValueContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyValueContext() *ValueContext { + var p = new(ValueContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = ConfigParserRULE_value + return p +} + +func InitEmptyValueContext(p *ValueContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = ConfigParserRULE_value +} + +func (*ValueContext) IsValueContext() {} + +func NewValueContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ValueContext { + var p = new(ValueContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = ConfigParserRULE_value + + return p +} + +func (s *ValueContext) GetParser() antlr.Parser { return s.parser } + +func (s *ValueContext) AllSTRING() []antlr.TerminalNode { + return s.GetTokens(ConfigParserSTRING) +} + +func (s *ValueContext) STRING(i int) antlr.TerminalNode { + return s.GetToken(ConfigParserSTRING, i) +} + +func (s *ValueContext) AllWHITESPACE() []antlr.TerminalNode { + return s.GetTokens(ConfigParserWHITESPACE) +} + +func (s *ValueContext) WHITESPACE(i int) antlr.TerminalNode { + return s.GetToken(ConfigParserWHITESPACE, i) +} + +func (s *ValueContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ValueContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ValueContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(ConfigListener); ok { + listenerT.EnterValue(s) + } +} + +func (s *ValueContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(ConfigListener); ok { + listenerT.ExitValue(s) + } +} + +func (p *ConfigParser) Value() (localctx IValueContext) { + localctx = NewValueContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 8, ConfigParserRULE_value) + var _la int + + var _alt int + + p.EnterOuterAlt(localctx, 1) + p.SetState(44) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 7, p.GetParserRuleContext()) + if p.HasError() { + goto errorExit + } + for _alt != 2 && _alt != antlr.ATNInvalidAltNumber { + if _alt == 1 { + { + p.SetState(40) + p.Match(ConfigParserSTRING) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(41) + p.Match(ConfigParserWHITESPACE) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + p.SetState(46) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 7, p.GetParserRuleContext()) + if p.HasError() { + goto errorExit + } + } + p.SetState(48) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == ConfigParserSTRING { + { + p.SetState(47) + p.Match(ConfigParserSTRING) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + p.SetState(51) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == ConfigParserWHITESPACE { + { + p.SetState(50) + p.Match(ConfigParserWHITESPACE) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} + +// ILeadingCommentContext is an interface to support dynamic dispatch. +type ILeadingCommentContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + HASH() antlr.TerminalNode + AllWHITESPACE() []antlr.TerminalNode + WHITESPACE(i int) antlr.TerminalNode + AllSTRING() []antlr.TerminalNode + STRING(i int) antlr.TerminalNode + + // IsLeadingCommentContext differentiates from other interfaces. + IsLeadingCommentContext() +} + +type LeadingCommentContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyLeadingCommentContext() *LeadingCommentContext { + var p = new(LeadingCommentContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = ConfigParserRULE_leadingComment + return p +} + +func InitEmptyLeadingCommentContext(p *LeadingCommentContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = ConfigParserRULE_leadingComment +} + +func (*LeadingCommentContext) IsLeadingCommentContext() {} + +func NewLeadingCommentContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *LeadingCommentContext { + var p = new(LeadingCommentContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = ConfigParserRULE_leadingComment + + return p +} + +func (s *LeadingCommentContext) GetParser() antlr.Parser { return s.parser } + +func (s *LeadingCommentContext) HASH() antlr.TerminalNode { + return s.GetToken(ConfigParserHASH, 0) +} + +func (s *LeadingCommentContext) AllWHITESPACE() []antlr.TerminalNode { + return s.GetTokens(ConfigParserWHITESPACE) +} + +func (s *LeadingCommentContext) WHITESPACE(i int) antlr.TerminalNode { + return s.GetToken(ConfigParserWHITESPACE, i) +} + +func (s *LeadingCommentContext) AllSTRING() []antlr.TerminalNode { + return s.GetTokens(ConfigParserSTRING) +} + +func (s *LeadingCommentContext) STRING(i int) antlr.TerminalNode { + return s.GetToken(ConfigParserSTRING, i) +} + +func (s *LeadingCommentContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *LeadingCommentContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *LeadingCommentContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(ConfigListener); ok { + listenerT.EnterLeadingComment(s) + } +} + +func (s *LeadingCommentContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(ConfigListener); ok { + listenerT.ExitLeadingComment(s) + } +} + +func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) { + localctx = NewLeadingCommentContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 10, ConfigParserRULE_leadingComment) + var _la int + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(53) + p.Match(ConfigParserHASH) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + p.SetState(55) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == ConfigParserWHITESPACE { + { + p.SetState(54) + p.Match(ConfigParserWHITESPACE) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + p.SetState(61) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + for ok := true; ok; ok = _la == ConfigParserSTRING { + { + p.SetState(57) + p.Match(ConfigParserSTRING) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + p.SetState(59) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == ConfigParserWHITESPACE { + { + p.SetState(58) + p.Match(ConfigParserWHITESPACE) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + + p.SetState(63) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} diff --git a/handlers/ssh_config/ast/ssh_config.go b/handlers/ssh_config/ast/ssh_config.go new file mode 100644 index 0000000..fb15e51 --- /dev/null +++ b/handlers/ssh_config/ast/ssh_config.go @@ -0,0 +1,62 @@ +package ast + +import ( + "config-lsp/common" + commonparser "config-lsp/common/parser" + matchparser "config-lsp/handlers/sshd_config/fields/match-parser" + "github.com/emirpasic/gods/maps/treemap" +) + +type SSHKey struct { + common.LocationRange + Value commonparser.ParsedString + Key string +} + +type SSHSeparator struct { + common.LocationRange + Value commonparser.ParsedString +} + +type SSHValue struct { + common.LocationRange + Value commonparser.ParsedString +} + +type SSHOption struct { + common.LocationRange + Value commonparser.ParsedString + + Key *SSHKey + Separator *SSHSeparator + OptionValue *SSHValue +} + +type SSHMatchBlock struct { + common.LocationRange + MatchOption *SSHOption + MatchValue *matchparser.Match + + // [uint32]*SSHOption -> line number -> *SSHOption + Options *treemap.Map +} + +type SSHHostBlock struct { + common.LocationRange + HostOption *SSHOption + HostValue string + + // [uint32]*SSHOption -> line number -> *SSHOption + Others *treemap.Map +} + +type SSHConfig struct { + // [uint32]SSHOption -> line number -> *SSHEntry + RootOptions *treemap.Map + + MatchBlosks []*SSHMatchBlock + HostBlocks []*SSHHostBlock + + // [uint32]{} -> line number -> {} + CommentLines map[uint32]struct{} +} diff --git a/handlers/ssh_config/ast/ssh_config_fields.go b/handlers/ssh_config/ast/ssh_config_fields.go new file mode 100644 index 0000000..bd41296 --- /dev/null +++ b/handlers/ssh_config/ast/ssh_config_fields.go @@ -0,0 +1 @@ +package ast diff --git a/handlers/sshd_config/Config.g4 b/handlers/sshd_config/Config.g4 index ad4cbe8..3033862 100644 --- a/handlers/sshd_config/Config.g4 +++ b/handlers/sshd_config/Config.g4 @@ -13,17 +13,23 @@ separator ; key - : STRING + : string ; value - : (STRING WHITESPACE)* STRING? WHITESPACE? + : (string WHITESPACE)* string? WHITESPACE? ; leadingComment - : HASH WHITESPACE? (STRING WHITESPACE?)+ + : HASH WHITESPACE? (string WHITESPACE?)+ ; +string + : (QUOTED_STRING | STRING) + ; + +/////////////////////////////////////////////// + HASH : '#' ; @@ -33,9 +39,13 @@ WHITESPACE ; STRING - : ~(' ' | '\t' | '\r' | '\n' | '#')+ + : ~('#' | '\r' | '\n' | '"' | ' ' | '\t')+ ; NEWLINE : '\r'? '\n' ; + +QUOTED_STRING + : '"' WHITESPACE? (STRING WHITESPACE)* STRING? ('"')? + ; diff --git a/handlers/sshd_config/analyzer/analyzer.go b/handlers/sshd_config/analyzer/analyzer.go index 571d548..ffb15b9 100644 --- a/handlers/sshd_config/analyzer/analyzer.go +++ b/handlers/sshd_config/analyzer/analyzer.go @@ -12,7 +12,7 @@ import ( func Analyze( d *sshdconfig.SSHDocument, ) []protocol.Diagnostic { - errors := analyzeOptionsAreValid(d) + errors := analyzeStructureIsValid(d) if len(errors) > 0 { return errsToDiagnostics(errors) diff --git a/handlers/sshd_config/analyzer/options.go b/handlers/sshd_config/analyzer/options.go index b6b07a1..1b3eba7 100644 --- a/handlers/sshd_config/analyzer/options.go +++ b/handlers/sshd_config/analyzer/options.go @@ -11,7 +11,7 @@ import ( "fmt" ) -func analyzeOptionsAreValid( +func analyzeStructureIsValid( d *sshdconfig.SSHDocument, ) []common.LSPError { errs := make([]common.LSPError, 0) @@ -39,48 +39,66 @@ func checkOption( ) []common.LSPError { errs := make([]common.LSPError, 0) - if option.Key != nil { - docOption, found := fields.Options[option.Key.Key] + if option.Key == nil { + return errs + } - if !found { - errs = append(errs, common.LSPError{ - Range: option.Key.LocationRange, - Err: errors.New(fmt.Sprintf("Unknown option: %s", option.Key.Key)), - }) + errs = append(errs, checkIsUsingDoubleQuotes(option.Key.Value, option.Key.LocationRange)...) + errs = append(errs, checkQuotesAreClosed(option.Key.Value, option.Key.LocationRange)...) - return errs - } + docOption, found := fields.Options[option.Key.Key] - if _, found := fields.MatchAllowedOptions[option.Key.Key]; !found && isInMatchBlock { - errs = append(errs, common.LSPError{ - Range: option.Key.LocationRange, - Err: errors.New(fmt.Sprintf("Option '%s' is not allowed inside Match blocks", option.Key.Key)), - }) + if !found { + errs = append(errs, common.LSPError{ + Range: option.Key.LocationRange, + Err: errors.New(fmt.Sprintf("Unknown option: %s", option.Key.Key)), + }) - return errs - } + return errs + } - if option.OptionValue == nil || option.OptionValue.Value.Value == "" { - errs = append(errs, common.LSPError{ - Range: option.Key.LocationRange, - Err: errors.New(fmt.Sprintf("Option '%s' requires a value", option.Key.Key)), - }) - } else { - invalidValues := docOption.CheckIsValid(option.OptionValue.Value.Value) + if _, found := fields.MatchAllowedOptions[option.Key.Key]; !found && isInMatchBlock { + errs = append(errs, common.LSPError{ + Range: option.Key.LocationRange, + Err: errors.New(fmt.Sprintf("Option '%s' is not allowed inside Match blocks", option.Key.Key)), + }) - errs = append( - errs, - utils.Map( - invalidValues, - func(invalidValue *docvalues.InvalidValue) common.LSPError { - err := docvalues.LSPErrorFromInvalidValue(option.Start.Line, *invalidValue) - err.ShiftCharacter(option.OptionValue.Start.Character) + return errs + } - return err - }, - )..., - ) - } + if option.OptionValue == nil || option.OptionValue.Value.Value == "" { + errs = append(errs, common.LSPError{ + Range: option.Key.LocationRange, + Err: errors.New(fmt.Sprintf("Option '%s' requires a value", option.Key.Key)), + }) + } else { + errs = append(errs, checkIsUsingDoubleQuotes(option.OptionValue.Value, option.OptionValue.LocationRange)...) + errs = append(errs, checkQuotesAreClosed(option.OptionValue.Value, option.OptionValue.LocationRange)...) + + invalidValues := docOption.CheckIsValid(option.OptionValue.Value.Value) + + errs = append( + errs, + utils.Map( + invalidValues, + func(invalidValue *docvalues.InvalidValue) common.LSPError { + err := docvalues.LSPErrorFromInvalidValue(option.Start.Line, *invalidValue) + err.ShiftCharacter(option.OptionValue.Start.Character) + + return err + }, + )..., + ) + } + + if option.Separator == nil || option.Separator.Value.Value == "" { + errs = append(errs, common.LSPError{ + Range: option.Key.LocationRange, + Err: errors.New(fmt.Sprintf("There should be a separator between an option and its value")), + }) + } else { + errs = append(errs, checkIsUsingDoubleQuotes(option.Separator.Value, option.Separator.LocationRange)...) + errs = append(errs, checkQuotesAreClosed(option.Separator.Value, option.Separator.LocationRange)...) } return errs diff --git a/handlers/sshd_config/analyzer/quotes.go b/handlers/sshd_config/analyzer/quotes.go new file mode 100644 index 0000000..33677c1 --- /dev/null +++ b/handlers/sshd_config/analyzer/quotes.go @@ -0,0 +1,59 @@ +package analyzer + +import ( + "config-lsp/common" + commonparser "config-lsp/common/parser" + sshdconfig "config-lsp/handlers/sshd_config" + "errors" + "strings" +) + +func analyzeQuotesAreValid( + d *sshdconfig.SSHDocument, +) []common.LSPError { + errs := make([]common.LSPError, 0) + + for _, option := range d.Config.GetAllOptions() { + errs = append(errs, checkIsUsingDoubleQuotes(option.Key.Value, option.Key.LocationRange)...) + errs = append(errs, checkIsUsingDoubleQuotes(option.OptionValue.Value, option.OptionValue.LocationRange)...) + + errs = append(errs, checkQuotesAreClosed(option.Key.Value, option.Key.LocationRange)...) + errs = append(errs, checkQuotesAreClosed(option.OptionValue.Value, option.OptionValue.LocationRange)...) + } + + return errs +} + +func checkIsUsingDoubleQuotes( + value commonparser.ParsedString, + valueRange common.LocationRange, +) []common.LSPError { + singleQuotePosition := strings.Index(value.Raw, "'") + + if singleQuotePosition != -1 { + return []common.LSPError{ + { + Range: valueRange, + Err: errors.New("sshd_config does not support single quotes. Use double quotes (\") instead."), + }, + } + } + + return nil +} + +func checkQuotesAreClosed( + value commonparser.ParsedString, + valueRange common.LocationRange, +) []common.LSPError { + if strings.Count(value.Raw, "\"")%2 != 0 { + return []common.LSPError{ + { + Range: valueRange, + Err: errors.New("There are unclosed quotes here. Make sure all quotes are closed."), + }, + } + } + + return nil +} diff --git a/handlers/sshd_config/analyzer/quotes_test.go b/handlers/sshd_config/analyzer/quotes_test.go new file mode 100644 index 0000000..ba9ff55 --- /dev/null +++ b/handlers/sshd_config/analyzer/quotes_test.go @@ -0,0 +1,62 @@ +package analyzer + +import ( + testutils_test "config-lsp/handlers/sshd_config/test_utils" + "testing" +) + +func TestSimpleInvalidQuotesExample( + t *testing.T, +) { + d := testutils_test.DocumentFromInput(t, ` +PermitRootLogin 'yes' +`) + + errors := analyzeQuotesAreValid(d) + + if !(len(errors) == 1) { + t.Errorf("Expected 1 error, got %v", len(errors)) + } +} + +func TestSingleQuotesKeyAndOptionExample( + t *testing.T, +) { + d := testutils_test.DocumentFromInput(t, ` +'Port' '22' +`) + + errors := analyzeQuotesAreValid(d) + + if !(len(errors) == 2) { + t.Errorf("Expected 2 errors, got %v", len(errors)) + } +} + +func TestSimpleUnclosedQuoteExample( + t *testing.T, +) { + d := testutils_test.DocumentFromInput(t, ` +PermitRootLogin "yes +`) + + errors := analyzeQuotesAreValid(d) + + if !(len(errors) == 1) { + t.Errorf("Expected 1 error, got %v", len(errors)) + } +} + +func TestIncompleteQuotesExample( + t *testing.T, +) { + d := testutils_test.DocumentFromInput(t, ` +"Port +`) + + errors := analyzeQuotesAreValid(d) + + if !(len(errors) == 1) { + t.Errorf("Expected 1 error, got %v", len(errors)) + } +} diff --git a/handlers/sshd_config/ast/listener.go b/handlers/sshd_config/ast/listener.go index aa744d8..d41e6a6 100644 --- a/handlers/sshd_config/ast/listener.go +++ b/handlers/sshd_config/ast/listener.go @@ -84,8 +84,12 @@ func (s *sshParserListener) EnterSeparator(ctx *parser.SeparatorContext) { location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location.ChangeBothLines(s.sshContext.line) + text := ctx.GetText() + value := commonparser.ParseRawString(text, commonparser.FullFeatures) + s.sshContext.currentOption.Separator = &SSHDSeparator{ LocationRange: location, + Value: value, } } diff --git a/handlers/sshd_config/ast/parser/Config.interp b/handlers/sshd_config/ast/parser/Config.interp index 2832f42..e02ab96 100644 --- a/handlers/sshd_config/ast/parser/Config.interp +++ b/handlers/sshd_config/ast/parser/Config.interp @@ -4,6 +4,7 @@ null null null null +null token symbolic names: null @@ -11,6 +12,7 @@ HASH WHITESPACE STRING NEWLINE +QUOTED_STRING rule names: lineStatement @@ -19,7 +21,8 @@ separator key value leadingComment +string atn: -[4, 1, 4, 66, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 1, 0, 1, 0, 1, 0, 3, 0, 16, 8, 0, 3, 0, 18, 8, 0, 1, 0, 1, 0, 1, 1, 3, 1, 23, 8, 1, 1, 1, 3, 1, 26, 8, 1, 1, 1, 3, 1, 29, 8, 1, 1, 1, 3, 1, 32, 8, 1, 1, 1, 3, 1, 35, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 5, 4, 43, 8, 4, 10, 4, 12, 4, 46, 9, 4, 1, 4, 3, 4, 49, 8, 4, 1, 4, 3, 4, 52, 8, 4, 1, 5, 1, 5, 3, 5, 56, 8, 5, 1, 5, 1, 5, 3, 5, 60, 8, 5, 4, 5, 62, 8, 5, 11, 5, 12, 5, 63, 1, 5, 0, 0, 6, 0, 2, 4, 6, 8, 10, 0, 0, 73, 0, 17, 1, 0, 0, 0, 2, 22, 1, 0, 0, 0, 4, 36, 1, 0, 0, 0, 6, 38, 1, 0, 0, 0, 8, 44, 1, 0, 0, 0, 10, 53, 1, 0, 0, 0, 12, 18, 3, 2, 1, 0, 13, 18, 3, 10, 5, 0, 14, 16, 5, 2, 0, 0, 15, 14, 1, 0, 0, 0, 15, 16, 1, 0, 0, 0, 16, 18, 1, 0, 0, 0, 17, 12, 1, 0, 0, 0, 17, 13, 1, 0, 0, 0, 17, 15, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 20, 5, 0, 0, 1, 20, 1, 1, 0, 0, 0, 21, 23, 5, 2, 0, 0, 22, 21, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 25, 1, 0, 0, 0, 24, 26, 3, 6, 3, 0, 25, 24, 1, 0, 0, 0, 25, 26, 1, 0, 0, 0, 26, 28, 1, 0, 0, 0, 27, 29, 3, 4, 2, 0, 28, 27, 1, 0, 0, 0, 28, 29, 1, 0, 0, 0, 29, 31, 1, 0, 0, 0, 30, 32, 3, 8, 4, 0, 31, 30, 1, 0, 0, 0, 31, 32, 1, 0, 0, 0, 32, 34, 1, 0, 0, 0, 33, 35, 3, 10, 5, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 35, 3, 1, 0, 0, 0, 36, 37, 5, 2, 0, 0, 37, 5, 1, 0, 0, 0, 38, 39, 5, 3, 0, 0, 39, 7, 1, 0, 0, 0, 40, 41, 5, 3, 0, 0, 41, 43, 5, 2, 0, 0, 42, 40, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1, 0, 0, 0, 44, 45, 1, 0, 0, 0, 45, 48, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 47, 49, 5, 3, 0, 0, 48, 47, 1, 0, 0, 0, 48, 49, 1, 0, 0, 0, 49, 51, 1, 0, 0, 0, 50, 52, 5, 2, 0, 0, 51, 50, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, 9, 1, 0, 0, 0, 53, 55, 5, 1, 0, 0, 54, 56, 5, 2, 0, 0, 55, 54, 1, 0, 0, 0, 55, 56, 1, 0, 0, 0, 56, 61, 1, 0, 0, 0, 57, 59, 5, 3, 0, 0, 58, 60, 5, 2, 0, 0, 59, 58, 1, 0, 0, 0, 59, 60, 1, 0, 0, 0, 60, 62, 1, 0, 0, 0, 61, 57, 1, 0, 0, 0, 62, 63, 1, 0, 0, 0, 63, 61, 1, 0, 0, 0, 63, 64, 1, 0, 0, 0, 64, 11, 1, 0, 0, 0, 13, 15, 17, 22, 25, 28, 31, 34, 44, 48, 51, 55, 59, 63] \ No newline at end of file +[4, 1, 5, 71, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 1, 0, 1, 0, 1, 0, 3, 0, 18, 8, 0, 3, 0, 20, 8, 0, 1, 0, 1, 0, 1, 1, 3, 1, 25, 8, 1, 1, 1, 3, 1, 28, 8, 1, 1, 1, 3, 1, 31, 8, 1, 1, 1, 3, 1, 34, 8, 1, 1, 1, 3, 1, 37, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 5, 4, 46, 8, 4, 10, 4, 12, 4, 49, 9, 4, 1, 4, 3, 4, 52, 8, 4, 1, 4, 3, 4, 55, 8, 4, 1, 5, 1, 5, 3, 5, 59, 8, 5, 1, 5, 1, 5, 3, 5, 63, 8, 5, 4, 5, 65, 8, 5, 11, 5, 12, 5, 66, 1, 6, 1, 6, 1, 6, 0, 0, 7, 0, 2, 4, 6, 8, 10, 12, 0, 1, 2, 0, 3, 3, 5, 5, 77, 0, 19, 1, 0, 0, 0, 2, 24, 1, 0, 0, 0, 4, 38, 1, 0, 0, 0, 6, 40, 1, 0, 0, 0, 8, 47, 1, 0, 0, 0, 10, 56, 1, 0, 0, 0, 12, 68, 1, 0, 0, 0, 14, 20, 3, 2, 1, 0, 15, 20, 3, 10, 5, 0, 16, 18, 5, 2, 0, 0, 17, 16, 1, 0, 0, 0, 17, 18, 1, 0, 0, 0, 18, 20, 1, 0, 0, 0, 19, 14, 1, 0, 0, 0, 19, 15, 1, 0, 0, 0, 19, 17, 1, 0, 0, 0, 20, 21, 1, 0, 0, 0, 21, 22, 5, 0, 0, 1, 22, 1, 1, 0, 0, 0, 23, 25, 5, 2, 0, 0, 24, 23, 1, 0, 0, 0, 24, 25, 1, 0, 0, 0, 25, 27, 1, 0, 0, 0, 26, 28, 3, 6, 3, 0, 27, 26, 1, 0, 0, 0, 27, 28, 1, 0, 0, 0, 28, 30, 1, 0, 0, 0, 29, 31, 3, 4, 2, 0, 30, 29, 1, 0, 0, 0, 30, 31, 1, 0, 0, 0, 31, 33, 1, 0, 0, 0, 32, 34, 3, 8, 4, 0, 33, 32, 1, 0, 0, 0, 33, 34, 1, 0, 0, 0, 34, 36, 1, 0, 0, 0, 35, 37, 3, 10, 5, 0, 36, 35, 1, 0, 0, 0, 36, 37, 1, 0, 0, 0, 37, 3, 1, 0, 0, 0, 38, 39, 5, 2, 0, 0, 39, 5, 1, 0, 0, 0, 40, 41, 3, 12, 6, 0, 41, 7, 1, 0, 0, 0, 42, 43, 3, 12, 6, 0, 43, 44, 5, 2, 0, 0, 44, 46, 1, 0, 0, 0, 45, 42, 1, 0, 0, 0, 46, 49, 1, 0, 0, 0, 47, 45, 1, 0, 0, 0, 47, 48, 1, 0, 0, 0, 48, 51, 1, 0, 0, 0, 49, 47, 1, 0, 0, 0, 50, 52, 3, 12, 6, 0, 51, 50, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, 54, 1, 0, 0, 0, 53, 55, 5, 2, 0, 0, 54, 53, 1, 0, 0, 0, 54, 55, 1, 0, 0, 0, 55, 9, 1, 0, 0, 0, 56, 58, 5, 1, 0, 0, 57, 59, 5, 2, 0, 0, 58, 57, 1, 0, 0, 0, 58, 59, 1, 0, 0, 0, 59, 64, 1, 0, 0, 0, 60, 62, 3, 12, 6, 0, 61, 63, 5, 2, 0, 0, 62, 61, 1, 0, 0, 0, 62, 63, 1, 0, 0, 0, 63, 65, 1, 0, 0, 0, 64, 60, 1, 0, 0, 0, 65, 66, 1, 0, 0, 0, 66, 64, 1, 0, 0, 0, 66, 67, 1, 0, 0, 0, 67, 11, 1, 0, 0, 0, 68, 69, 7, 0, 0, 0, 69, 13, 1, 0, 0, 0, 13, 17, 19, 24, 27, 30, 33, 36, 47, 51, 54, 58, 62, 66] \ No newline at end of file diff --git a/handlers/sshd_config/ast/parser/Config.tokens b/handlers/sshd_config/ast/parser/Config.tokens index aacc14c..fa8c415 100644 --- a/handlers/sshd_config/ast/parser/Config.tokens +++ b/handlers/sshd_config/ast/parser/Config.tokens @@ -2,4 +2,5 @@ HASH=1 WHITESPACE=2 STRING=3 NEWLINE=4 +QUOTED_STRING=5 '#'=1 diff --git a/handlers/sshd_config/ast/parser/ConfigLexer.interp b/handlers/sshd_config/ast/parser/ConfigLexer.interp index d61e14d..c093cfb 100644 --- a/handlers/sshd_config/ast/parser/ConfigLexer.interp +++ b/handlers/sshd_config/ast/parser/ConfigLexer.interp @@ -4,6 +4,7 @@ null null null null +null token symbolic names: null @@ -11,12 +12,14 @@ HASH WHITESPACE STRING NEWLINE +QUOTED_STRING rule names: HASH WHITESPACE STRING NEWLINE +QUOTED_STRING channel names: DEFAULT_TOKEN_CHANNEL @@ -26,4 +29,4 @@ mode names: DEFAULT_MODE atn: -[4, 0, 4, 26, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 1, 0, 1, 0, 1, 1, 4, 1, 13, 8, 1, 11, 1, 12, 1, 14, 1, 2, 4, 2, 18, 8, 2, 11, 2, 12, 2, 19, 1, 3, 3, 3, 23, 8, 3, 1, 3, 1, 3, 0, 0, 4, 1, 1, 3, 2, 5, 3, 7, 4, 1, 0, 2, 2, 0, 9, 9, 32, 32, 4, 0, 9, 10, 13, 13, 32, 32, 35, 35, 28, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 1, 9, 1, 0, 0, 0, 3, 12, 1, 0, 0, 0, 5, 17, 1, 0, 0, 0, 7, 22, 1, 0, 0, 0, 9, 10, 5, 35, 0, 0, 10, 2, 1, 0, 0, 0, 11, 13, 7, 0, 0, 0, 12, 11, 1, 0, 0, 0, 13, 14, 1, 0, 0, 0, 14, 12, 1, 0, 0, 0, 14, 15, 1, 0, 0, 0, 15, 4, 1, 0, 0, 0, 16, 18, 8, 1, 0, 0, 17, 16, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 17, 1, 0, 0, 0, 19, 20, 1, 0, 0, 0, 20, 6, 1, 0, 0, 0, 21, 23, 5, 13, 0, 0, 22, 21, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 24, 1, 0, 0, 0, 24, 25, 5, 10, 0, 0, 25, 8, 1, 0, 0, 0, 4, 0, 14, 19, 22, 0] \ No newline at end of file +[4, 0, 5, 46, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 1, 0, 1, 0, 1, 1, 4, 1, 15, 8, 1, 11, 1, 12, 1, 16, 1, 2, 4, 2, 20, 8, 2, 11, 2, 12, 2, 21, 1, 3, 3, 3, 25, 8, 3, 1, 3, 1, 3, 1, 4, 1, 4, 3, 4, 31, 8, 4, 1, 4, 1, 4, 1, 4, 5, 4, 36, 8, 4, 10, 4, 12, 4, 39, 9, 4, 1, 4, 3, 4, 42, 8, 4, 1, 4, 3, 4, 45, 8, 4, 0, 0, 5, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 1, 0, 2, 2, 0, 9, 9, 32, 32, 4, 0, 9, 10, 13, 13, 32, 32, 34, 35, 52, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 1, 11, 1, 0, 0, 0, 3, 14, 1, 0, 0, 0, 5, 19, 1, 0, 0, 0, 7, 24, 1, 0, 0, 0, 9, 28, 1, 0, 0, 0, 11, 12, 5, 35, 0, 0, 12, 2, 1, 0, 0, 0, 13, 15, 7, 0, 0, 0, 14, 13, 1, 0, 0, 0, 15, 16, 1, 0, 0, 0, 16, 14, 1, 0, 0, 0, 16, 17, 1, 0, 0, 0, 17, 4, 1, 0, 0, 0, 18, 20, 8, 1, 0, 0, 19, 18, 1, 0, 0, 0, 20, 21, 1, 0, 0, 0, 21, 19, 1, 0, 0, 0, 21, 22, 1, 0, 0, 0, 22, 6, 1, 0, 0, 0, 23, 25, 5, 13, 0, 0, 24, 23, 1, 0, 0, 0, 24, 25, 1, 0, 0, 0, 25, 26, 1, 0, 0, 0, 26, 27, 5, 10, 0, 0, 27, 8, 1, 0, 0, 0, 28, 30, 5, 34, 0, 0, 29, 31, 3, 3, 1, 0, 30, 29, 1, 0, 0, 0, 30, 31, 1, 0, 0, 0, 31, 37, 1, 0, 0, 0, 32, 33, 3, 5, 2, 0, 33, 34, 3, 3, 1, 0, 34, 36, 1, 0, 0, 0, 35, 32, 1, 0, 0, 0, 36, 39, 1, 0, 0, 0, 37, 35, 1, 0, 0, 0, 37, 38, 1, 0, 0, 0, 38, 41, 1, 0, 0, 0, 39, 37, 1, 0, 0, 0, 40, 42, 3, 5, 2, 0, 41, 40, 1, 0, 0, 0, 41, 42, 1, 0, 0, 0, 42, 44, 1, 0, 0, 0, 43, 45, 5, 34, 0, 0, 44, 43, 1, 0, 0, 0, 44, 45, 1, 0, 0, 0, 45, 10, 1, 0, 0, 0, 8, 0, 16, 21, 24, 30, 37, 41, 44, 0] \ No newline at end of file diff --git a/handlers/sshd_config/ast/parser/ConfigLexer.tokens b/handlers/sshd_config/ast/parser/ConfigLexer.tokens index aacc14c..fa8c415 100644 --- a/handlers/sshd_config/ast/parser/ConfigLexer.tokens +++ b/handlers/sshd_config/ast/parser/ConfigLexer.tokens @@ -2,4 +2,5 @@ HASH=1 WHITESPACE=2 STRING=3 NEWLINE=4 +QUOTED_STRING=5 '#'=1 diff --git a/handlers/sshd_config/ast/parser/config_base_listener.go b/handlers/sshd_config/ast/parser/config_base_listener.go index ac8bdac..00e7840 100644 --- a/handlers/sshd_config/ast/parser/config_base_listener.go +++ b/handlers/sshd_config/ast/parser/config_base_listener.go @@ -56,3 +56,9 @@ func (s *BaseConfigListener) EnterLeadingComment(ctx *LeadingCommentContext) {} // ExitLeadingComment is called when production leadingComment is exited. func (s *BaseConfigListener) ExitLeadingComment(ctx *LeadingCommentContext) {} + +// EnterString is called when production string is entered. +func (s *BaseConfigListener) EnterString(ctx *StringContext) {} + +// ExitString is called when production string is exited. +func (s *BaseConfigListener) ExitString(ctx *StringContext) {} diff --git a/handlers/sshd_config/ast/parser/config_lexer.go b/handlers/sshd_config/ast/parser/config_lexer.go index ace491d..021f1fc 100644 --- a/handlers/sshd_config/ast/parser/config_lexer.go +++ b/handlers/sshd_config/ast/parser/config_lexer.go @@ -46,25 +46,34 @@ func configlexerLexerInit() { "", "'#'", } staticData.SymbolicNames = []string{ - "", "HASH", "WHITESPACE", "STRING", "NEWLINE", + "", "HASH", "WHITESPACE", "STRING", "NEWLINE", "QUOTED_STRING", } staticData.RuleNames = []string{ - "HASH", "WHITESPACE", "STRING", "NEWLINE", + "HASH", "WHITESPACE", "STRING", "NEWLINE", "QUOTED_STRING", } staticData.PredictionContextCache = antlr.NewPredictionContextCache() staticData.serializedATN = []int32{ - 4, 0, 4, 26, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 1, - 0, 1, 0, 1, 1, 4, 1, 13, 8, 1, 11, 1, 12, 1, 14, 1, 2, 4, 2, 18, 8, 2, - 11, 2, 12, 2, 19, 1, 3, 3, 3, 23, 8, 3, 1, 3, 1, 3, 0, 0, 4, 1, 1, 3, 2, - 5, 3, 7, 4, 1, 0, 2, 2, 0, 9, 9, 32, 32, 4, 0, 9, 10, 13, 13, 32, 32, 35, - 35, 28, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, - 0, 0, 0, 1, 9, 1, 0, 0, 0, 3, 12, 1, 0, 0, 0, 5, 17, 1, 0, 0, 0, 7, 22, - 1, 0, 0, 0, 9, 10, 5, 35, 0, 0, 10, 2, 1, 0, 0, 0, 11, 13, 7, 0, 0, 0, - 12, 11, 1, 0, 0, 0, 13, 14, 1, 0, 0, 0, 14, 12, 1, 0, 0, 0, 14, 15, 1, - 0, 0, 0, 15, 4, 1, 0, 0, 0, 16, 18, 8, 1, 0, 0, 17, 16, 1, 0, 0, 0, 18, - 19, 1, 0, 0, 0, 19, 17, 1, 0, 0, 0, 19, 20, 1, 0, 0, 0, 20, 6, 1, 0, 0, - 0, 21, 23, 5, 13, 0, 0, 22, 21, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 24, - 1, 0, 0, 0, 24, 25, 5, 10, 0, 0, 25, 8, 1, 0, 0, 0, 4, 0, 14, 19, 22, 0, + 4, 0, 5, 46, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, + 4, 7, 4, 1, 0, 1, 0, 1, 1, 4, 1, 15, 8, 1, 11, 1, 12, 1, 16, 1, 2, 4, 2, + 20, 8, 2, 11, 2, 12, 2, 21, 1, 3, 3, 3, 25, 8, 3, 1, 3, 1, 3, 1, 4, 1, + 4, 3, 4, 31, 8, 4, 1, 4, 1, 4, 1, 4, 5, 4, 36, 8, 4, 10, 4, 12, 4, 39, + 9, 4, 1, 4, 3, 4, 42, 8, 4, 1, 4, 3, 4, 45, 8, 4, 0, 0, 5, 1, 1, 3, 2, + 5, 3, 7, 4, 9, 5, 1, 0, 2, 2, 0, 9, 9, 32, 32, 4, 0, 9, 10, 13, 13, 32, + 32, 34, 35, 52, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, + 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 1, 11, 1, 0, 0, 0, 3, 14, 1, 0, 0, 0, + 5, 19, 1, 0, 0, 0, 7, 24, 1, 0, 0, 0, 9, 28, 1, 0, 0, 0, 11, 12, 5, 35, + 0, 0, 12, 2, 1, 0, 0, 0, 13, 15, 7, 0, 0, 0, 14, 13, 1, 0, 0, 0, 15, 16, + 1, 0, 0, 0, 16, 14, 1, 0, 0, 0, 16, 17, 1, 0, 0, 0, 17, 4, 1, 0, 0, 0, + 18, 20, 8, 1, 0, 0, 19, 18, 1, 0, 0, 0, 20, 21, 1, 0, 0, 0, 21, 19, 1, + 0, 0, 0, 21, 22, 1, 0, 0, 0, 22, 6, 1, 0, 0, 0, 23, 25, 5, 13, 0, 0, 24, + 23, 1, 0, 0, 0, 24, 25, 1, 0, 0, 0, 25, 26, 1, 0, 0, 0, 26, 27, 5, 10, + 0, 0, 27, 8, 1, 0, 0, 0, 28, 30, 5, 34, 0, 0, 29, 31, 3, 3, 1, 0, 30, 29, + 1, 0, 0, 0, 30, 31, 1, 0, 0, 0, 31, 37, 1, 0, 0, 0, 32, 33, 3, 5, 2, 0, + 33, 34, 3, 3, 1, 0, 34, 36, 1, 0, 0, 0, 35, 32, 1, 0, 0, 0, 36, 39, 1, + 0, 0, 0, 37, 35, 1, 0, 0, 0, 37, 38, 1, 0, 0, 0, 38, 41, 1, 0, 0, 0, 39, + 37, 1, 0, 0, 0, 40, 42, 3, 5, 2, 0, 41, 40, 1, 0, 0, 0, 41, 42, 1, 0, 0, + 0, 42, 44, 1, 0, 0, 0, 43, 45, 5, 34, 0, 0, 44, 43, 1, 0, 0, 0, 44, 45, + 1, 0, 0, 0, 45, 10, 1, 0, 0, 0, 8, 0, 16, 21, 24, 30, 37, 41, 44, 0, } deserializer := antlr.NewATNDeserializer(nil) staticData.atn = deserializer.Deserialize(staticData.serializedATN) @@ -105,8 +114,9 @@ func NewConfigLexer(input antlr.CharStream) *ConfigLexer { // ConfigLexer tokens. const ( - ConfigLexerHASH = 1 - ConfigLexerWHITESPACE = 2 - ConfigLexerSTRING = 3 - ConfigLexerNEWLINE = 4 + ConfigLexerHASH = 1 + ConfigLexerWHITESPACE = 2 + ConfigLexerSTRING = 3 + ConfigLexerNEWLINE = 4 + ConfigLexerQUOTED_STRING = 5 ) diff --git a/handlers/sshd_config/ast/parser/config_listener.go b/handlers/sshd_config/ast/parser/config_listener.go index 0384e3c..1acf598 100644 --- a/handlers/sshd_config/ast/parser/config_listener.go +++ b/handlers/sshd_config/ast/parser/config_listener.go @@ -26,6 +26,9 @@ type ConfigListener interface { // EnterLeadingComment is called when entering the leadingComment production. EnterLeadingComment(c *LeadingCommentContext) + // EnterString is called when entering the string production. + EnterString(c *StringContext) + // ExitLineStatement is called when exiting the lineStatement production. ExitLineStatement(c *LineStatementContext) @@ -43,4 +46,7 @@ type ConfigListener interface { // ExitLeadingComment is called when exiting the leadingComment production. ExitLeadingComment(c *LeadingCommentContext) + + // ExitString is called when exiting the string production. + ExitString(c *StringContext) } diff --git a/handlers/sshd_config/ast/parser/config_parser.go b/handlers/sshd_config/ast/parser/config_parser.go index 018bbb8..aefb1e3 100644 --- a/handlers/sshd_config/ast/parser/config_parser.go +++ b/handlers/sshd_config/ast/parser/config_parser.go @@ -36,42 +36,44 @@ func configParserInit() { "", "'#'", } staticData.SymbolicNames = []string{ - "", "HASH", "WHITESPACE", "STRING", "NEWLINE", + "", "HASH", "WHITESPACE", "STRING", "NEWLINE", "QUOTED_STRING", } staticData.RuleNames = []string{ "lineStatement", "entry", "separator", "key", "value", "leadingComment", + "string", } staticData.PredictionContextCache = antlr.NewPredictionContextCache() staticData.serializedATN = []int32{ - 4, 1, 4, 66, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, - 2, 5, 7, 5, 1, 0, 1, 0, 1, 0, 3, 0, 16, 8, 0, 3, 0, 18, 8, 0, 1, 0, 1, - 0, 1, 1, 3, 1, 23, 8, 1, 1, 1, 3, 1, 26, 8, 1, 1, 1, 3, 1, 29, 8, 1, 1, - 1, 3, 1, 32, 8, 1, 1, 1, 3, 1, 35, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, - 1, 4, 5, 4, 43, 8, 4, 10, 4, 12, 4, 46, 9, 4, 1, 4, 3, 4, 49, 8, 4, 1, - 4, 3, 4, 52, 8, 4, 1, 5, 1, 5, 3, 5, 56, 8, 5, 1, 5, 1, 5, 3, 5, 60, 8, - 5, 4, 5, 62, 8, 5, 11, 5, 12, 5, 63, 1, 5, 0, 0, 6, 0, 2, 4, 6, 8, 10, - 0, 0, 73, 0, 17, 1, 0, 0, 0, 2, 22, 1, 0, 0, 0, 4, 36, 1, 0, 0, 0, 6, 38, - 1, 0, 0, 0, 8, 44, 1, 0, 0, 0, 10, 53, 1, 0, 0, 0, 12, 18, 3, 2, 1, 0, - 13, 18, 3, 10, 5, 0, 14, 16, 5, 2, 0, 0, 15, 14, 1, 0, 0, 0, 15, 16, 1, - 0, 0, 0, 16, 18, 1, 0, 0, 0, 17, 12, 1, 0, 0, 0, 17, 13, 1, 0, 0, 0, 17, - 15, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 20, 5, 0, 0, 1, 20, 1, 1, 0, 0, - 0, 21, 23, 5, 2, 0, 0, 22, 21, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 25, - 1, 0, 0, 0, 24, 26, 3, 6, 3, 0, 25, 24, 1, 0, 0, 0, 25, 26, 1, 0, 0, 0, - 26, 28, 1, 0, 0, 0, 27, 29, 3, 4, 2, 0, 28, 27, 1, 0, 0, 0, 28, 29, 1, - 0, 0, 0, 29, 31, 1, 0, 0, 0, 30, 32, 3, 8, 4, 0, 31, 30, 1, 0, 0, 0, 31, - 32, 1, 0, 0, 0, 32, 34, 1, 0, 0, 0, 33, 35, 3, 10, 5, 0, 34, 33, 1, 0, - 0, 0, 34, 35, 1, 0, 0, 0, 35, 3, 1, 0, 0, 0, 36, 37, 5, 2, 0, 0, 37, 5, - 1, 0, 0, 0, 38, 39, 5, 3, 0, 0, 39, 7, 1, 0, 0, 0, 40, 41, 5, 3, 0, 0, - 41, 43, 5, 2, 0, 0, 42, 40, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1, - 0, 0, 0, 44, 45, 1, 0, 0, 0, 45, 48, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 47, - 49, 5, 3, 0, 0, 48, 47, 1, 0, 0, 0, 48, 49, 1, 0, 0, 0, 49, 51, 1, 0, 0, - 0, 50, 52, 5, 2, 0, 0, 51, 50, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, 9, 1, - 0, 0, 0, 53, 55, 5, 1, 0, 0, 54, 56, 5, 2, 0, 0, 55, 54, 1, 0, 0, 0, 55, - 56, 1, 0, 0, 0, 56, 61, 1, 0, 0, 0, 57, 59, 5, 3, 0, 0, 58, 60, 5, 2, 0, - 0, 59, 58, 1, 0, 0, 0, 59, 60, 1, 0, 0, 0, 60, 62, 1, 0, 0, 0, 61, 57, - 1, 0, 0, 0, 62, 63, 1, 0, 0, 0, 63, 61, 1, 0, 0, 0, 63, 64, 1, 0, 0, 0, - 64, 11, 1, 0, 0, 0, 13, 15, 17, 22, 25, 28, 31, 34, 44, 48, 51, 55, 59, - 63, + 4, 1, 5, 71, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, + 2, 5, 7, 5, 2, 6, 7, 6, 1, 0, 1, 0, 1, 0, 3, 0, 18, 8, 0, 3, 0, 20, 8, + 0, 1, 0, 1, 0, 1, 1, 3, 1, 25, 8, 1, 1, 1, 3, 1, 28, 8, 1, 1, 1, 3, 1, + 31, 8, 1, 1, 1, 3, 1, 34, 8, 1, 1, 1, 3, 1, 37, 8, 1, 1, 2, 1, 2, 1, 3, + 1, 3, 1, 4, 1, 4, 1, 4, 5, 4, 46, 8, 4, 10, 4, 12, 4, 49, 9, 4, 1, 4, 3, + 4, 52, 8, 4, 1, 4, 3, 4, 55, 8, 4, 1, 5, 1, 5, 3, 5, 59, 8, 5, 1, 5, 1, + 5, 3, 5, 63, 8, 5, 4, 5, 65, 8, 5, 11, 5, 12, 5, 66, 1, 6, 1, 6, 1, 6, + 0, 0, 7, 0, 2, 4, 6, 8, 10, 12, 0, 1, 2, 0, 3, 3, 5, 5, 77, 0, 19, 1, 0, + 0, 0, 2, 24, 1, 0, 0, 0, 4, 38, 1, 0, 0, 0, 6, 40, 1, 0, 0, 0, 8, 47, 1, + 0, 0, 0, 10, 56, 1, 0, 0, 0, 12, 68, 1, 0, 0, 0, 14, 20, 3, 2, 1, 0, 15, + 20, 3, 10, 5, 0, 16, 18, 5, 2, 0, 0, 17, 16, 1, 0, 0, 0, 17, 18, 1, 0, + 0, 0, 18, 20, 1, 0, 0, 0, 19, 14, 1, 0, 0, 0, 19, 15, 1, 0, 0, 0, 19, 17, + 1, 0, 0, 0, 20, 21, 1, 0, 0, 0, 21, 22, 5, 0, 0, 1, 22, 1, 1, 0, 0, 0, + 23, 25, 5, 2, 0, 0, 24, 23, 1, 0, 0, 0, 24, 25, 1, 0, 0, 0, 25, 27, 1, + 0, 0, 0, 26, 28, 3, 6, 3, 0, 27, 26, 1, 0, 0, 0, 27, 28, 1, 0, 0, 0, 28, + 30, 1, 0, 0, 0, 29, 31, 3, 4, 2, 0, 30, 29, 1, 0, 0, 0, 30, 31, 1, 0, 0, + 0, 31, 33, 1, 0, 0, 0, 32, 34, 3, 8, 4, 0, 33, 32, 1, 0, 0, 0, 33, 34, + 1, 0, 0, 0, 34, 36, 1, 0, 0, 0, 35, 37, 3, 10, 5, 0, 36, 35, 1, 0, 0, 0, + 36, 37, 1, 0, 0, 0, 37, 3, 1, 0, 0, 0, 38, 39, 5, 2, 0, 0, 39, 5, 1, 0, + 0, 0, 40, 41, 3, 12, 6, 0, 41, 7, 1, 0, 0, 0, 42, 43, 3, 12, 6, 0, 43, + 44, 5, 2, 0, 0, 44, 46, 1, 0, 0, 0, 45, 42, 1, 0, 0, 0, 46, 49, 1, 0, 0, + 0, 47, 45, 1, 0, 0, 0, 47, 48, 1, 0, 0, 0, 48, 51, 1, 0, 0, 0, 49, 47, + 1, 0, 0, 0, 50, 52, 3, 12, 6, 0, 51, 50, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, + 52, 54, 1, 0, 0, 0, 53, 55, 5, 2, 0, 0, 54, 53, 1, 0, 0, 0, 54, 55, 1, + 0, 0, 0, 55, 9, 1, 0, 0, 0, 56, 58, 5, 1, 0, 0, 57, 59, 5, 2, 0, 0, 58, + 57, 1, 0, 0, 0, 58, 59, 1, 0, 0, 0, 59, 64, 1, 0, 0, 0, 60, 62, 3, 12, + 6, 0, 61, 63, 5, 2, 0, 0, 62, 61, 1, 0, 0, 0, 62, 63, 1, 0, 0, 0, 63, 65, + 1, 0, 0, 0, 64, 60, 1, 0, 0, 0, 65, 66, 1, 0, 0, 0, 66, 64, 1, 0, 0, 0, + 66, 67, 1, 0, 0, 0, 67, 11, 1, 0, 0, 0, 68, 69, 7, 0, 0, 0, 69, 13, 1, + 0, 0, 0, 13, 17, 19, 24, 27, 30, 33, 36, 47, 51, 54, 58, 62, 66, } deserializer := antlr.NewATNDeserializer(nil) staticData.atn = deserializer.Deserialize(staticData.serializedATN) @@ -109,11 +111,12 @@ func NewConfigParser(input antlr.TokenStream) *ConfigParser { // ConfigParser tokens. const ( - ConfigParserEOF = antlr.TokenEOF - ConfigParserHASH = 1 - ConfigParserWHITESPACE = 2 - ConfigParserSTRING = 3 - ConfigParserNEWLINE = 4 + ConfigParserEOF = antlr.TokenEOF + ConfigParserHASH = 1 + ConfigParserWHITESPACE = 2 + ConfigParserSTRING = 3 + ConfigParserNEWLINE = 4 + ConfigParserQUOTED_STRING = 5 ) // ConfigParser rules. @@ -124,6 +127,7 @@ const ( ConfigParserRULE_key = 3 ConfigParserRULE_value = 4 ConfigParserRULE_leadingComment = 5 + ConfigParserRULE_string = 6 ) // ILineStatementContext is an interface to support dynamic dispatch. @@ -241,7 +245,7 @@ func (p *ConfigParser) LineStatement() (localctx ILineStatementContext) { var _la int p.EnterOuterAlt(localctx, 1) - p.SetState(17) + p.SetState(19) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -250,18 +254,18 @@ func (p *ConfigParser) LineStatement() (localctx ILineStatementContext) { switch p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 1, p.GetParserRuleContext()) { case 1: { - p.SetState(12) + p.SetState(14) p.Entry() } case 2: { - p.SetState(13) + p.SetState(15) p.LeadingComment() } case 3: - p.SetState(15) + p.SetState(17) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -270,7 +274,7 @@ func (p *ConfigParser) LineStatement() (localctx ILineStatementContext) { if _la == ConfigParserWHITESPACE { { - p.SetState(14) + p.SetState(16) p.Match(ConfigParserWHITESPACE) if p.HasError() { // Recognition error - abort rule @@ -284,7 +288,7 @@ func (p *ConfigParser) LineStatement() (localctx ILineStatementContext) { goto errorExit } { - p.SetState(19) + p.SetState(21) p.Match(ConfigParserEOF) if p.HasError() { // Recognition error - abort rule @@ -449,12 +453,12 @@ func (p *ConfigParser) Entry() (localctx IEntryContext) { var _la int p.EnterOuterAlt(localctx, 1) - p.SetState(22) + p.SetState(24) p.GetErrorHandler().Sync(p) if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 2, p.GetParserRuleContext()) == 1 { { - p.SetState(21) + p.SetState(23) p.Match(ConfigParserWHITESPACE) if p.HasError() { // Recognition error - abort rule @@ -465,43 +469,43 @@ func (p *ConfigParser) Entry() (localctx IEntryContext) { } else if p.HasError() { // JIM goto errorExit } - p.SetState(25) + p.SetState(27) p.GetErrorHandler().Sync(p) if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 3, p.GetParserRuleContext()) == 1 { { - p.SetState(24) + p.SetState(26) p.Key() } } else if p.HasError() { // JIM goto errorExit } - p.SetState(28) + p.SetState(30) p.GetErrorHandler().Sync(p) if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 4, p.GetParserRuleContext()) == 1 { { - p.SetState(27) + p.SetState(29) p.Separator() } } else if p.HasError() { // JIM goto errorExit } - p.SetState(31) + p.SetState(33) p.GetErrorHandler().Sync(p) if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 5, p.GetParserRuleContext()) == 1 { { - p.SetState(30) + p.SetState(32) p.Value() } } else if p.HasError() { // JIM goto errorExit } - p.SetState(34) + p.SetState(36) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -510,7 +514,7 @@ func (p *ConfigParser) Entry() (localctx IEntryContext) { if _la == ConfigParserHASH { { - p.SetState(33) + p.SetState(35) p.LeadingComment() } @@ -604,7 +608,7 @@ func (p *ConfigParser) Separator() (localctx ISeparatorContext) { p.EnterRule(localctx, 4, ConfigParserRULE_separator) p.EnterOuterAlt(localctx, 1) { - p.SetState(36) + p.SetState(38) p.Match(ConfigParserWHITESPACE) if p.HasError() { // Recognition error - abort rule @@ -633,7 +637,7 @@ type IKeyContext interface { GetParser() antlr.Parser // Getter signatures - STRING() antlr.TerminalNode + String_() IStringContext // IsKeyContext differentiates from other interfaces. IsKeyContext() @@ -671,8 +675,20 @@ func NewKeyContext(parser antlr.Parser, parent antlr.ParserRuleContext, invoking func (s *KeyContext) GetParser() antlr.Parser { return s.parser } -func (s *KeyContext) STRING() antlr.TerminalNode { - return s.GetToken(ConfigParserSTRING, 0) +func (s *KeyContext) String_() IStringContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IStringContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IStringContext) } func (s *KeyContext) GetRuleContext() antlr.RuleContext { @@ -700,12 +716,8 @@ func (p *ConfigParser) Key() (localctx IKeyContext) { p.EnterRule(localctx, 6, ConfigParserRULE_key) p.EnterOuterAlt(localctx, 1) { - p.SetState(38) - p.Match(ConfigParserSTRING) - if p.HasError() { - // Recognition error - abort rule - goto errorExit - } + p.SetState(40) + p.String_() } errorExit: @@ -729,8 +741,8 @@ type IValueContext interface { GetParser() antlr.Parser // Getter signatures - AllSTRING() []antlr.TerminalNode - STRING(i int) antlr.TerminalNode + AllString_() []IStringContext + String_(i int) IStringContext AllWHITESPACE() []antlr.TerminalNode WHITESPACE(i int) antlr.TerminalNode @@ -770,12 +782,45 @@ func NewValueContext(parser antlr.Parser, parent antlr.ParserRuleContext, invoki func (s *ValueContext) GetParser() antlr.Parser { return s.parser } -func (s *ValueContext) AllSTRING() []antlr.TerminalNode { - return s.GetTokens(ConfigParserSTRING) +func (s *ValueContext) AllString_() []IStringContext { + children := s.GetChildren() + len := 0 + for _, ctx := range children { + if _, ok := ctx.(IStringContext); ok { + len++ + } + } + + tst := make([]IStringContext, len) + i := 0 + for _, ctx := range children { + if t, ok := ctx.(IStringContext); ok { + tst[i] = t.(IStringContext) + i++ + } + } + + return tst } -func (s *ValueContext) STRING(i int) antlr.TerminalNode { - return s.GetToken(ConfigParserSTRING, i) +func (s *ValueContext) String_(i int) IStringContext { + var t antlr.RuleContext + j := 0 + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IStringContext); ok { + if j == i { + t = ctx.(antlr.RuleContext) + break + } + j++ + } + } + + if t == nil { + return nil + } + + return t.(IStringContext) } func (s *ValueContext) AllWHITESPACE() []antlr.TerminalNode { @@ -814,7 +859,7 @@ func (p *ConfigParser) Value() (localctx IValueContext) { var _alt int p.EnterOuterAlt(localctx, 1) - p.SetState(44) + p.SetState(47) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -826,15 +871,11 @@ func (p *ConfigParser) Value() (localctx IValueContext) { for _alt != 2 && _alt != antlr.ATNInvalidAltNumber { if _alt == 1 { { - p.SetState(40) - p.Match(ConfigParserSTRING) - if p.HasError() { - // Recognition error - abort rule - goto errorExit - } + p.SetState(42) + p.String_() } { - p.SetState(41) + p.SetState(43) p.Match(ConfigParserWHITESPACE) if p.HasError() { // Recognition error - abort rule @@ -843,7 +884,7 @@ func (p *ConfigParser) Value() (localctx IValueContext) { } } - p.SetState(46) + p.SetState(49) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -853,25 +894,21 @@ func (p *ConfigParser) Value() (localctx IValueContext) { goto errorExit } } - p.SetState(48) + p.SetState(51) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit } _la = p.GetTokenStream().LA(1) - if _la == ConfigParserSTRING { + if _la == ConfigParserSTRING || _la == ConfigParserQUOTED_STRING { { - p.SetState(47) - p.Match(ConfigParserSTRING) - if p.HasError() { - // Recognition error - abort rule - goto errorExit - } + p.SetState(50) + p.String_() } } - p.SetState(51) + p.SetState(54) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -880,7 +917,7 @@ func (p *ConfigParser) Value() (localctx IValueContext) { if _la == ConfigParserWHITESPACE { { - p.SetState(50) + p.SetState(53) p.Match(ConfigParserWHITESPACE) if p.HasError() { // Recognition error - abort rule @@ -914,8 +951,8 @@ type ILeadingCommentContext interface { HASH() antlr.TerminalNode AllWHITESPACE() []antlr.TerminalNode WHITESPACE(i int) antlr.TerminalNode - AllSTRING() []antlr.TerminalNode - STRING(i int) antlr.TerminalNode + AllString_() []IStringContext + String_(i int) IStringContext // IsLeadingCommentContext differentiates from other interfaces. IsLeadingCommentContext() @@ -965,12 +1002,45 @@ func (s *LeadingCommentContext) WHITESPACE(i int) antlr.TerminalNode { return s.GetToken(ConfigParserWHITESPACE, i) } -func (s *LeadingCommentContext) AllSTRING() []antlr.TerminalNode { - return s.GetTokens(ConfigParserSTRING) +func (s *LeadingCommentContext) AllString_() []IStringContext { + children := s.GetChildren() + len := 0 + for _, ctx := range children { + if _, ok := ctx.(IStringContext); ok { + len++ + } + } + + tst := make([]IStringContext, len) + i := 0 + for _, ctx := range children { + if t, ok := ctx.(IStringContext); ok { + tst[i] = t.(IStringContext) + i++ + } + } + + return tst } -func (s *LeadingCommentContext) STRING(i int) antlr.TerminalNode { - return s.GetToken(ConfigParserSTRING, i) +func (s *LeadingCommentContext) String_(i int) IStringContext { + var t antlr.RuleContext + j := 0 + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IStringContext); ok { + if j == i { + t = ctx.(antlr.RuleContext) + break + } + j++ + } + } + + if t == nil { + return nil + } + + return t.(IStringContext) } func (s *LeadingCommentContext) GetRuleContext() antlr.RuleContext { @@ -1000,14 +1070,14 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) { p.EnterOuterAlt(localctx, 1) { - p.SetState(53) + p.SetState(56) p.Match(ConfigParserHASH) if p.HasError() { // Recognition error - abort rule goto errorExit } } - p.SetState(55) + p.SetState(58) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -1016,7 +1086,7 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) { if _la == ConfigParserWHITESPACE { { - p.SetState(54) + p.SetState(57) p.Match(ConfigParserWHITESPACE) if p.HasError() { // Recognition error - abort rule @@ -1025,23 +1095,19 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) { } } - p.SetState(61) + p.SetState(64) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit } _la = p.GetTokenStream().LA(1) - for ok := true; ok; ok = _la == ConfigParserSTRING { + for ok := true; ok; ok = _la == ConfigParserSTRING || _la == ConfigParserQUOTED_STRING { { - p.SetState(57) - p.Match(ConfigParserSTRING) - if p.HasError() { - // Recognition error - abort rule - goto errorExit - } + p.SetState(60) + p.String_() } - p.SetState(59) + p.SetState(62) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -1050,7 +1116,7 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) { if _la == ConfigParserWHITESPACE { { - p.SetState(58) + p.SetState(61) p.Match(ConfigParserWHITESPACE) if p.HasError() { // Recognition error - abort rule @@ -1060,7 +1126,7 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) { } - p.SetState(63) + p.SetState(66) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -1080,3 +1146,109 @@ errorExit: return localctx goto errorExit // Trick to prevent compiler error if the label is not used } + +// IStringContext is an interface to support dynamic dispatch. +type IStringContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + QUOTED_STRING() antlr.TerminalNode + STRING() antlr.TerminalNode + + // IsStringContext differentiates from other interfaces. + IsStringContext() +} + +type StringContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyStringContext() *StringContext { + var p = new(StringContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = ConfigParserRULE_string + return p +} + +func InitEmptyStringContext(p *StringContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = ConfigParserRULE_string +} + +func (*StringContext) IsStringContext() {} + +func NewStringContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *StringContext { + var p = new(StringContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = ConfigParserRULE_string + + return p +} + +func (s *StringContext) GetParser() antlr.Parser { return s.parser } + +func (s *StringContext) QUOTED_STRING() antlr.TerminalNode { + return s.GetToken(ConfigParserQUOTED_STRING, 0) +} + +func (s *StringContext) STRING() antlr.TerminalNode { + return s.GetToken(ConfigParserSTRING, 0) +} + +func (s *StringContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *StringContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *StringContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(ConfigListener); ok { + listenerT.EnterString(s) + } +} + +func (s *StringContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(ConfigListener); ok { + listenerT.ExitString(s) + } +} + +func (p *ConfigParser) String_() (localctx IStringContext) { + localctx = NewStringContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 12, ConfigParserRULE_string) + var _la int + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(68) + _la = p.GetTokenStream().LA(1) + + if !(_la == ConfigParserSTRING || _la == ConfigParserQUOTED_STRING) { + p.GetErrorHandler().RecoverInline(p) + } else { + p.GetErrorHandler().ReportMatch(p) + p.Consume() + } + } + +errorExit: + if p.HasError() { + v := p.GetError() + localctx.SetException(v) + p.GetErrorHandler().ReportError(p, v) + p.GetErrorHandler().Recover(p, v) + p.SetError(nil) + } + p.ExitRule() + return localctx + goto errorExit // Trick to prevent compiler error if the label is not used +} diff --git a/handlers/sshd_config/ast/parser_test.go b/handlers/sshd_config/ast/parser_test.go index b309380..5aebf15 100644 --- a/handlers/sshd_config/ast/parser_test.go +++ b/handlers/sshd_config/ast/parser_test.go @@ -165,7 +165,7 @@ func TestIncompleteMatchBlock( t.Errorf("Expected first entry to be 'User lena', but got: %v", matchBlock.MatchValue.Entries[0]) } - if !(matchBlock.MatchValue.Entries[1].Value.Value == "User " && matchBlock.MatchValue.Entries[1].Criteria.Type == "User" && matchBlock.MatchValue.Entries[1].Values == nil) { + if !(matchBlock.MatchValue.Entries[1].Value.Value == "User " && matchBlock.MatchValue.Entries[1].Criteria.Type == "User" && len(matchBlock.MatchValue.Entries[1].Values.Values) == 0) { t.Errorf("Expected second entry to be 'User ', but got: %v", matchBlock.MatchValue.Entries[1]) } } @@ -544,3 +544,101 @@ Match Address 172.22.100.0/24,172.22.5.0/24,127.0.0.1 t.Errorf("Expected seventh entry to be 'PermitRootLogin without-password', but got: %v", seventhEntry.Value) } } + +func TestQuotedOptionExample( + t *testing.T, +) { + input := utils.Dedent(` +PermitRootLogin "no" +`) + p := NewSSHConfig() + errors := p.Parse(input) + + if len(errors) != 0 { + t.Fatalf("Expected no errors, got %v", errors) + } + + if !(p.Options.Size() == 1 && + len(utils.KeysOfMap(p.CommentLines)) == 0) { + t.Errorf("Expected 1 option and no comment lines, but got: %v, %v", p.Options, p.CommentLines) + } + + rawFirstEntry, _ := p.Options.Get(uint32(0)) + entry := rawFirstEntry.(*SSHDOption) + + if !(entry.Key.Value.Value == "PermitRootLogin" && entry.OptionValue.Value.Value == "no") { + t.Errorf("Expected first entry to be 'PermitRootLogin no', but got: %v", entry.Value) + } + + if !(entry.LocationRange.Start.Line == 0 && entry.LocationRange.Start.Character == 0 && entry.LocationRange.End.Line == 0 && entry.LocationRange.End.Character == 20) { + t.Errorf("Expected location range to be 0:0-0:20, but got: %v", entry.LocationRange) + } + + if !(entry.OptionValue.LocationRange.Start.Character == 16 && entry.OptionValue.LocationRange.End.Character == 20) { + t.Errorf("Expected option value location range to be 17-20, but got: %v", entry.OptionValue.LocationRange) + } +} + +func TestQuotedKeyExample( + t *testing.T, +) { + input := utils.Dedent(` +"PermitRootLogin" no +`) + p := NewSSHConfig() + errors := p.Parse(input) + + if len(errors) != 0 { + t.Fatalf("Expected no errors, got %v", errors) + } + + if !(p.Options.Size() == 1 && + len(utils.KeysOfMap(p.CommentLines)) == 0) { + t.Errorf("Expected 1 option and no comment lines, but got: %v, %v", p.Options, p.CommentLines) + } + + rawFirstEntry, _ := p.Options.Get(uint32(0)) + entry := rawFirstEntry.(*SSHDOption) + + if !(entry.Key.Value.Value == "PermitRootLogin" && entry.OptionValue.Value.Value == "no") { + t.Errorf("Expected first entry to be 'PermitRootLogin no', but got: %v", entry.Value) + } + + if !(entry.LocationRange.Start.Line == 0 && entry.LocationRange.Start.Character == 0 && entry.LocationRange.End.Line == 0 && entry.LocationRange.End.Character == 20) { + t.Errorf("Expected location range to be 0:0-0:20, but got: %v", entry.LocationRange) + } + + if !(entry.Key.LocationRange.Start.Character == 0 && entry.Key.LocationRange.End.Character == 17) { + t.Errorf("Expected key location range to be 0-17, but got: %v", entry.Key.LocationRange) + } + + if !(entry.Key.LocationRange.Start.Character == 0 && entry.Key.LocationRange.End.Character == 17) { + t.Errorf("Expected key location range to be 0-17, but got: %v", entry.Key.LocationRange) + } +} + +func TestQuotedValueWithSpacesExample( + t *testing.T, +) { + input := utils.Dedent(` +PermitRootLogin "no yes maybe" +`) + p := NewSSHConfig() + errors := p.Parse(input) + + if len(errors) != 0 { + t.Fatalf("Expected no errors, got %v", errors) + } + + if !(p.Options.Size() == 1 && + len(utils.KeysOfMap(p.CommentLines)) == 0) { + t.Errorf("Expected 1 option and no comment lines, but got: %v, %v", p.Options, p.CommentLines) + } + + rawFirstEntry, _ := p.Options.Get(uint32(0)) + entry := rawFirstEntry.(*SSHDOption) + + if !(entry.Key.Value.Value == "PermitRootLogin" && entry.OptionValue.Value.Value == "no yes maybe") { + t.Errorf("Expected first entry to be 'PermitRootLogin no yes maybe', but got: %v", entry.Value) + } +} diff --git a/handlers/sshd_config/ast/sshd_config.go b/handlers/sshd_config/ast/sshd_config.go index fda6f34..27b5aef 100644 --- a/handlers/sshd_config/ast/sshd_config.go +++ b/handlers/sshd_config/ast/sshd_config.go @@ -33,6 +33,7 @@ type SSHDEntry interface { type SSHDSeparator struct { common.LocationRange + Value commonparser.ParsedString } type SSHDOption struct { diff --git a/handlers/sshd_config/fields/match-parser/parser_test.go b/handlers/sshd_config/fields/match-parser/parser_test.go index e35647f..a7554e7 100644 --- a/handlers/sshd_config/fields/match-parser/parser_test.go +++ b/handlers/sshd_config/fields/match-parser/parser_test.go @@ -128,7 +128,7 @@ func TestIncompleteBetweenValuesExample( t.Errorf("Expected User, but got %v", match.Entries[0]) } - if !(match.Entries[0].Values == nil) { + if !(len(match.Entries[0].Values.Values) == 0) { t.Errorf("Expected 0 values, but got %v", match.Entries[0].Values) } } diff --git a/handlers/sshd_config/handlers/completions.go b/handlers/sshd_config/handlers/completions.go index 8314f3c..d6f2ca2 100644 --- a/handlers/sshd_config/handlers/completions.go +++ b/handlers/sshd_config/handlers/completions.go @@ -22,16 +22,20 @@ func GetRootCompletions( if parentMatchBlock == nil { for key, option := range fields.Options { - if _, found := d.Indexes.AllOptionsPerName[key]; !found { - availableOptions[key] = option + if d.Indexes != nil && utils.KeyExists(d.Indexes.AllOptionsPerName, key) && !utils.KeyExists(fields.AllowedDuplicateOptions, key) { + continue } + + availableOptions[key] = option } } else { for key := range fields.MatchAllowedOptions { if option, found := fields.Options[key]; found { - if _, found := d.Indexes.AllOptionsPerName[key]; !found { - availableOptions[key] = option + if d.Indexes != nil && utils.KeyExists(d.Indexes.AllOptionsPerName, key) && !utils.KeyExists(fields.AllowedDuplicateOptions, key) { + continue } + + availableOptions[key] = option } } } diff --git a/handlers/sshd_config/lsp/text-document-completion.go b/handlers/sshd_config/lsp/text-document-completion.go index d779ba2..96c1ce9 100644 --- a/handlers/sshd_config/lsp/text-document-completion.go +++ b/handlers/sshd_config/lsp/text-document-completion.go @@ -33,7 +33,7 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa d, matchBlock, // Empty line, or currently typing a new key - entry == nil || isEmptyPattern.Match([]byte(entry.Value.Value[cursor:])), + entry == nil || isEmptyPattern.Match([]byte(entry.Value.Raw[cursor:])), ) } diff --git a/handlers/sshd_config/test_utils/input.go b/handlers/sshd_config/test_utils/input.go new file mode 100644 index 0000000..4f6f5fd --- /dev/null +++ b/handlers/sshd_config/test_utils/input.go @@ -0,0 +1,33 @@ +package testutils_test + +import ( + sshdconfig "config-lsp/handlers/sshd_config" + "config-lsp/handlers/sshd_config/ast" + "config-lsp/handlers/sshd_config/indexes" + "config-lsp/utils" + "testing" +) + +func DocumentFromInput( + t *testing.T, + content string, +) *sshdconfig.SSHDocument { + input := utils.Dedent(content) + c := ast.NewSSHConfig() + errors := c.Parse(input) + + if len(errors) > 0 { + t.Fatalf("Parse error: %v", errors) + } + + i, errors := indexes.CreateIndexes(*c) + + if len(errors) > 0 { + t.Fatalf("Index error: %v", errors) + } + + return &sshdconfig.SSHDocument{ + Config: c, + Indexes: i, + } +}