source: code/trunk/config/config.go@ 62

Last change on this file since 62 was 62, checked in by contact, 5 years ago

Add config file

File size: 3.0 KB
Line 
1package config
2
3import (
4 "bufio"
5 "fmt"
6 "io"
7 "os"
8 "strings"
9 "unicode"
10)
11
12type TLS struct {
13 CertPath, KeyPath string
14}
15
16type Server struct {
17 Addr string
18 Hostname string
19 TLS *TLS
20}
21
22func Defaults() *Server {
23 hostname, err := os.Hostname()
24 if err != nil {
25 hostname = "localhost"
26 }
27 return &Server{
28 Addr: ":6667",
29 Hostname: hostname,
30 }
31}
32
33func Load(path string) (*Server, error) {
34 f, err := os.Open(path)
35 if err != nil {
36 return nil, err
37 }
38 defer f.Close()
39
40 return Parse(f)
41}
42
43func Parse(r io.Reader) (*Server, error) {
44 p := parser{br: bufio.NewReader(r)}
45 directives, err := p.file()
46 if err != nil {
47 return nil, fmt.Errorf("failed to parse config file: %v", err)
48 }
49
50 srv := Defaults()
51 for _, d := range directives {
52 switch d.Name {
53 case "listen":
54 if err := d.parseParams(&srv.Addr); err != nil {
55 return nil, err
56 }
57 case "hostname":
58 if err := d.parseParams(&srv.Hostname); err != nil {
59 return nil, err
60 }
61 case "tls":
62 tls := &TLS{}
63 if err := d.parseParams(&tls.CertPath, &tls.KeyPath); err != nil {
64 return nil, err
65 }
66 srv.TLS = tls
67 default:
68 return nil, fmt.Errorf("unknown directive %q", d.Name)
69 }
70 }
71
72 return srv, nil
73}
74
75type directive struct {
76 Name string
77 Params []string
78}
79
80func (d *directive) parseParams(out ...*string) error {
81 if len(d.Params) != len(out) {
82 return fmt.Errorf("directive %q has wrong number of parameters: expected %v, got %v", d.Name, len(out), len(d.Params))
83 }
84 for i := range out {
85 *out[i] = d.Params[i]
86 }
87 return nil
88}
89
90type parser struct {
91 br *bufio.Reader
92}
93
94func (p *parser) skipSpace() error {
95 for {
96 r, _, err := p.br.ReadRune()
97 if err == io.EOF {
98 break
99 } else if err != nil {
100 return err
101 }
102 if !unicode.IsSpace(r) || r == '\n' {
103 p.br.UnreadRune()
104 break
105 }
106 }
107 return nil
108}
109
110func (p *parser) atom() (string, error) {
111 var sb strings.Builder
112 for {
113 r, _, err := p.br.ReadRune()
114 if err == io.EOF && sb.Len() > 0 {
115 break
116 } else if err != nil {
117 return "", err
118 }
119 if unicode.IsSpace(r) {
120 p.br.UnreadRune()
121 if err := p.skipSpace(); err != nil {
122 return "", err
123 }
124 break
125 }
126 sb.WriteRune(r)
127 }
128 return sb.String(), nil
129}
130
131func (p *parser) directive() (*directive, error) {
132 name, err := p.atom()
133 if err == io.EOF {
134 return nil, io.EOF
135 } else if err != nil {
136 return nil, fmt.Errorf("failed to read directive name: %v", err)
137 }
138
139 var params []string
140 for {
141 r, _, err := p.br.ReadRune()
142 if err == io.EOF {
143 break
144 } else if err != nil {
145 return nil, err
146 }
147 if r == '\n' {
148 break
149 }
150 p.br.UnreadRune()
151
152 param, err := p.atom()
153 if err == io.EOF {
154 break
155 } else if err != nil {
156 return nil, fmt.Errorf("failed to read directive param: %v", err)
157 }
158 params = append(params, param)
159 }
160
161 return &directive{name, params}, nil
162}
163
164func (p *parser) file() ([]directive, error) {
165 var l []directive
166 for {
167 d, err := p.directive()
168 if err == io.EOF {
169 break
170 } else if err != nil {
171 return nil, err
172 }
173 l = append(l, *d)
174 }
175 return l, nil
176}
Note: See TracBrowser for help on using the repository browser.