Christopher Ramey
4 years ago
2 changed files with 237 additions and 229 deletions
@ -0,0 +1,235 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"bufio" |
||||
|
"fmt" |
||||
|
"os" |
||||
|
"strconv" |
||||
|
"strings" |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
TK_NONE = iota |
||||
|
TK_SET |
||||
|
TK_MONITOR |
||||
|
TK_GROUP |
||||
|
TK_HOST |
||||
|
) |
||||
|
|
||||
|
type Parser struct { |
||||
|
Line int |
||||
|
states []int |
||||
|
} |
||||
|
|
||||
|
func (p *Parser) Parse(fn string) (*AlrmConfig, error) { |
||||
|
file, err := os.Open(fn) |
||||
|
if err != nil { |
||||
|
return nil, fmt.Errorf("cannot open config \"%s\": %s", fn, err.Error()) |
||||
|
} |
||||
|
defer file.Close() |
||||
|
|
||||
|
config := &AlrmConfig{} |
||||
|
var group *AlrmGroup |
||||
|
var host *AlrmHost |
||||
|
|
||||
|
scan := bufio.NewScanner(file) |
||||
|
scan.Split(p.Split) |
||||
|
for scan.Scan() { |
||||
|
tk := scan.Text() |
||||
|
stateswitch: |
||||
|
switch p.state() { |
||||
|
case TK_NONE: |
||||
|
switch strings.ToLower(tk) { |
||||
|
case "monitor": |
||||
|
p.setState(TK_MONITOR) |
||||
|
case "set": |
||||
|
p.setState(TK_SET) |
||||
|
default: |
||||
|
return nil, fmt.Errorf("invalid token in %s, line %d: \"%s\"", |
||||
|
fn, p.Line+1, tk) |
||||
|
} |
||||
|
|
||||
|
case TK_SET: |
||||
|
key := strings.ToLower(tk) |
||||
|
if scan.Scan() { |
||||
|
value := scan.Text() |
||||
|
switch key { |
||||
|
case "interval": |
||||
|
config.Interval, err = strconv.Atoi(value) |
||||
|
if err != nil { |
||||
|
return nil, fmt.Errorf( |
||||
|
"invalid number for interval, line %d: \"%s\"", |
||||
|
p.Line+1, value, |
||||
|
) |
||||
|
} |
||||
|
default: |
||||
|
return nil, fmt.Errorf("invalid key for set, line %d: \"%s\"", |
||||
|
p.Line+1, tk, |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
p.prevState() |
||||
|
|
||||
|
case TK_MONITOR: |
||||
|
switch strings.ToLower(tk) { |
||||
|
case "host": |
||||
|
host = config.NewGroup().NewHost() |
||||
|
p.setState(TK_HOST) |
||||
|
|
||||
|
case "group": |
||||
|
group = config.NewGroup() |
||||
|
p.setState(TK_GROUP) |
||||
|
|
||||
|
default: |
||||
|
p.prevState() |
||||
|
goto stateswitch |
||||
|
} |
||||
|
|
||||
|
case TK_GROUP: |
||||
|
if group == nil { |
||||
|
return nil, fmt.Errorf("Group token without initialization") |
||||
|
} |
||||
|
|
||||
|
switch strings.ToLower(tk) { |
||||
|
case "host": |
||||
|
host = group.NewHost() |
||||
|
p.setState(TK_HOST) |
||||
|
continue |
||||
|
|
||||
|
default: |
||||
|
if group.Name == "" { |
||||
|
group.Name = tk |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
p.prevState() |
||||
|
goto stateswitch |
||||
|
} |
||||
|
|
||||
|
case TK_HOST: |
||||
|
if host == nil { |
||||
|
return nil, fmt.Errorf("Host token without initialization") |
||||
|
} |
||||
|
|
||||
|
if host.Name == "" { |
||||
|
host.Name = tk |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
switch strings.ToLower(tk) { |
||||
|
case "address": |
||||
|
if scan.Scan() { |
||||
|
host.Address = scan.Text() |
||||
|
} |
||||
|
|
||||
|
default: |
||||
|
p.prevState() |
||||
|
goto stateswitch |
||||
|
} |
||||
|
|
||||
|
default: |
||||
|
return nil, fmt.Errorf("unknown parser state: %d", p.state()) |
||||
|
} |
||||
|
} |
||||
|
if err := scan.Err(); err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
return config, nil |
||||
|
} |
||||
|
|
||||
|
func (p *Parser) state() int { |
||||
|
if len(p.states) < 1 { |
||||
|
return TK_NONE |
||||
|
} |
||||
|
return p.states[len(p.states)-1] |
||||
|
} |
||||
|
|
||||
|
func (p *Parser) setState(state int) { |
||||
|
// fmt.Printf("%s", p.stateName())
|
||||
|
p.states = append(p.states, state) |
||||
|
// fmt.Printf(" -> %s\n", p.stateName())
|
||||
|
} |
||||
|
|
||||
|
func (p *Parser) prevState() int { |
||||
|
if len(p.states) > 0 { |
||||
|
p.states = p.states[:len(p.states)-1] |
||||
|
} |
||||
|
return p.state() |
||||
|
} |
||||
|
|
||||
|
func (p *Parser) stateName() string { |
||||
|
switch p.state() { |
||||
|
case TK_NONE: |
||||
|
return "TK_NONE" |
||||
|
case TK_SET: |
||||
|
return "TK_SET" |
||||
|
case TK_MONITOR: |
||||
|
return "TK_MONTIOR" |
||||
|
case TK_GROUP: |
||||
|
return "TK_GROUP" |
||||
|
case TK_HOST: |
||||
|
return "TK_HOST" |
||||
|
default: |
||||
|
return "UNKNOWN" |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (p *Parser) Split(data []byte, atEOF bool) (int, []byte, error) { |
||||
|
if atEOF && len(data) == 0 { |
||||
|
return 0, nil, nil |
||||
|
} |
||||
|
|
||||
|
var ignoreline bool |
||||
|
var started bool |
||||
|
var startidx int |
||||
|
var quote byte |
||||
|
|
||||
|
for i := 0; i < len(data); i++ { |
||||
|
c := data[i] |
||||
|
switch c { |
||||
|
case '\f', '\n', '\r': |
||||
|
p.Line++ |
||||
|
if ignoreline { |
||||
|
ignoreline = false |
||||
|
continue |
||||
|
} |
||||
|
fallthrough |
||||
|
|
||||
|
case ' ', '\t', '\v': |
||||
|
if started && quote == 0 { |
||||
|
return i + 1, data[startidx:i], nil |
||||
|
} |
||||
|
|
||||
|
case '\'', '"', '`': |
||||
|
if started && quote == c { |
||||
|
return i + 1, data[startidx:i], nil |
||||
|
} |
||||
|
|
||||
|
if quote == 0 { |
||||
|
quote = c |
||||
|
} |
||||
|
|
||||
|
case '#': |
||||
|
if !started { |
||||
|
ignoreline = true |
||||
|
} |
||||
|
|
||||
|
default: |
||||
|
if !ignoreline && !started { |
||||
|
started = true |
||||
|
startidx = i |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if atEOF { |
||||
|
if ignoreline { |
||||
|
return len(data), nil, nil |
||||
|
} |
||||
|
if started { |
||||
|
return len(data), data[startidx:], nil |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return 0, nil, nil |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue