├── LICENSE
├── README.md
├── asallow.conf
├── asallow.go
├── go.mod
├── go.sum
└── vendor
├── gopkg.in
├── gcfg.v1
│ ├── LICENSE
│ ├── README
│ ├── doc.go
│ ├── errors.go
│ ├── go1_0.go
│ ├── go1_2.go
│ ├── read.go
│ ├── scanner
│ │ ├── errors.go
│ │ └── scanner.go
│ ├── set.go
│ ├── token
│ │ ├── position.go
│ │ ├── serialize.go
│ │ └── token.go
│ └── types
│ │ ├── bool.go
│ │ ├── doc.go
│ │ ├── enum.go
│ │ ├── int.go
│ │ └── scan.go
└── warnings.v0
│ ├── LICENSE
│ ├── README
│ └── warnings.go
└── modules.txt
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
341 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # asallow
2 |
3 | ## What
4 | Asallow creates 2 ipsets (AS_allow and AS_allow6), and populates them based on data from the RIPEstat Data API
5 | (https://stat.ripe.net/index/documentation/interfaces-apis) (and optionally your own manually specified subnets)
6 |
7 | ## Why
8 | An 'easy way' to whitelist providers and/or countries.
9 | E.g. to only allow ssh access from specific countries.
10 |
11 | ## Requirements
12 | * Linux kernel >= 2.6.32 supporting ipset (net:hash)
13 | * ipset
14 |
15 | > v6.20 and above for comment support
16 | < 6.20 see 'nocomment' flag in asallow.conf (tested on centos6/rhel6)
17 |
18 | * root/sudo access
19 | * iptables
20 |
21 | ## Building it
22 | Install go (binaries on https://golang.org/dl/ or use your favorite package manager)
23 | Set your GOPATH (https://code.google.com/p/go-wiki/wiki/GOPATH)
24 | run:
25 |
26 | ```
27 | go get github.com/42wim/asallow
28 | ```
29 |
30 | asallow will be found in $GOPATH/bin/asallow
31 |
32 | ## Config
33 | By default it searches for asallow.conf in the current directory, you can also explicitly specify a config with -conf switch.
34 | It will not run without a config file.
35 |
36 | ```
37 | ./asallow -h
38 | Usage of ./asallow:
39 | -conf="asallow.conf": a valid config file
40 | ```
41 | Config file as a [main] section and multivalue allow, ASN or country statements. Any combination works.
42 |
43 | * allow=range|ip
44 |
45 | Specify an IPv4 or IPv6 ip address or CIDR range.
46 | This will always be added to the ipset AS_allow or AS_allow6 (for IPv6)
47 |
48 | * ASN=AS number
49 |
50 | Specify an AS number.
51 | Ranges (currently) announced by this AS will be added to the ipset AS_allow or AS_allow6 (for IPv6) (AS number: see http://en.wikipedia.org/wiki/Autonomous_System_(Internet))
52 |
53 | * country=country code
54 |
55 | 2-digit ISO-3166 country code.
56 | Ranges (currently) associated with the selected country will be added to the ipset AS_allow or AS_allow6 (for IPv6)
57 |
58 | ### Example configuration
59 | ```
60 | [main]
61 | #ip ranges or ip addresses which should always be add
62 | allow=10.0.0.0/8
63 | allow=192.168.0.0/16
64 | allow=fe80::/10
65 |
66 | #prefixes of providers which should be looked up dynamically (using ripestat)
67 | ASN=2611 #belnet
68 | ASN=6848 #telenet
69 | #ASN=5432 #mobistar
70 |
71 | #allow a whole country
72 | country=be
73 | #country=nl
74 | #country=fr
75 |
76 | #uncomment 'nocomment' for older ipset versions < 6.20 (on centos6/rhel6)
77 | #nocomment
78 | ```
79 |
80 | ## Running it
81 | ```
82 | # ./asallow
83 | 760 ipv4 / 169 ipv6 subnets added
84 | AS_allow and AS_allow6 ipset created/modified
85 | ```
86 |
87 | Check the contents of the sets with ipset:
88 |
89 | ```
90 | # ipset list AS_allow
91 | Name: AS_allow
92 | Type: hash:net
93 | Revision: 5
94 | Header: family inet hashsize 1024 maxelem 65536 comment
95 | Size in memory: 47488
96 | References: 2
97 | Members:
98 | 217.72.224.0/20 comment "be"
99 | 86.39.128.0/17 comment "be"
100 | 193.33.52.0/23 comment "be"
101 | 194.42.208.0/24 comment "be"
102 | 80.66.128.0/20 comment "be"
103 | 193.58.40.0/24 comment "AS6848"
104 | 213.246.192.0/18 comment "be"
105 | ....
106 | ```
107 |
108 | ## Integration with IPtables
109 | Simple example:
110 | Only allow ssh from AS_allow ranges and drop the rest.
111 |
112 | ```
113 | iptables -A INPUT -p tcp -m tcp --dport 22 -m set --match-set AS_allow src -j ACCEPT
114 | iptables -A INPUT -p tcp -m tcp --dport 22 -j REJECT
115 | ```
116 |
--------------------------------------------------------------------------------
/asallow.conf:
--------------------------------------------------------------------------------
1 | [main]
2 | #ip ranges or ip addresses which should always be add
3 | allow=10.0.0.0/8
4 | allow=192.168.0.0/16
5 | allow=fe80::/10
6 |
7 | #prefixes of providers which should be looked up dynamically (using ripestat)
8 | ASN=2611 #belnet
9 | ASN=6848 #telenet
10 | #ASN=5432 #mobistar
11 |
12 | #allow a whole country
13 | country=be
14 | #country=nl
15 | #country=fr
16 |
17 | #uncomment 'nocomment' for older ipset versions < 6.20 (on centos6/rhel6)
18 | nocomment
19 |
--------------------------------------------------------------------------------
/asallow.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "flag"
6 | "fmt"
7 | "gopkg.in/gcfg.v1"
8 | "io/ioutil"
9 | "log"
10 | "net"
11 | "net/http"
12 | "os"
13 | "os/exec"
14 | "regexp"
15 | "runtime"
16 | "strings"
17 | "sync"
18 | "time"
19 | )
20 |
21 | const PREFIX_URI = "https://stat.ripe.net/data/announced-prefixes/data.json?resource="
22 | const COUNTRY_URI = "https://stat.ripe.net/data/country-resource-list/data.json?resource="
23 | const PREFIX = 0
24 | const COUNTRY = 1
25 |
26 | type ipsetInfo struct {
27 | sync.Mutex
28 | v4count int
29 | v6count int
30 | v4 string
31 | v6 string
32 | }
33 |
34 | type config struct {
35 | Main struct {
36 | Allow []string
37 | ASN []string
38 | Country []string
39 | Nocomment bool
40 | }
41 | }
42 |
43 | type resourceInfo struct {
44 | id int
45 | uri string
46 | cfg []string
47 | }
48 |
49 | var ipset ipsetInfo
50 |
51 | func readconfig(cfgfile string) config {
52 | var cfg config
53 | content, err := ioutil.ReadFile(cfgfile)
54 | if err != nil {
55 | log.Fatal(err)
56 | }
57 | err = gcfg.ReadStringInto(&cfg, string(content))
58 | if err != nil {
59 | log.Fatal("Failed to parse "+cfgfile+":", err)
60 | }
61 | return cfg
62 | }
63 |
64 | func getURI(uri string) []byte {
65 | resp, err := http.Get(uri)
66 | if err != nil {
67 | log.Fatal("site not available")
68 | }
69 | body, err := ioutil.ReadAll(resp.Body)
70 | if err != nil {
71 | log.Fatal("can not read body")
72 | }
73 | return body
74 | }
75 |
76 | func isIpOrCidr(ipcidr string) *net.IP {
77 | ip, _, err := net.ParseCIDR(ipcidr)
78 | if err != nil {
79 | mystr := strings.Split(ipcidr, "-")
80 | ip = net.ParseIP(mystr[0])
81 | if ip == nil {
82 | return nil
83 | }
84 | }
85 | return &ip
86 | }
87 |
88 | func doipset(cfg config) {
89 | ipset_header := "create AS_allow hash:net family inet comment\n"
90 | ipset_header += "create AS_allow6 hash:net family inet6 comment\n"
91 | ipset_header += "create AS_allow_swap hash:net family inet comment\n"
92 | ipset_header += "create AS_allow_swap6 hash:net family inet6 comment\n"
93 | ipset_footer := "swap AS_allow AS_allow_swap\n"
94 | ipset_footer += "swap AS_allow6 AS_allow_swap6\n"
95 | ipset_footer += "destroy AS_allow_swap\n"
96 | ipset_footer += "destroy AS_allow_swap6\n"
97 | ipset_string := ipset_header + ipset.v4 + ipset.v6 + ipset_footer
98 | if cfg.Main.Nocomment {
99 | re := regexp.MustCompile(" comment.*")
100 | ipset_string = re.ReplaceAllString(ipset_string, "")
101 | }
102 | cmd := exec.Command("ipset", "-!", "restore")
103 | cmd.Stdin = strings.NewReader(ipset_string)
104 | out, err := cmd.CombinedOutput()
105 | if err != nil {
106 | log.Println("ipset restore failed (see below)")
107 | log.Fatal(string(out))
108 | }
109 | }
110 |
111 | func parseCountry(mapstring map[string]interface{}) []string {
112 | var array []string
113 | datamap := mapstring["data"]
114 | mapstring = datamap.(map[string]interface{})
115 | resources := mapstring["resources"]
116 | mapstring = resources.(map[string]interface{})
117 | ipv6 := mapstring["ipv6"]
118 | ipv6_array := ipv6.([]interface{})
119 | ipv4 := mapstring["ipv4"]
120 | ipv4_array := ipv4.([]interface{})
121 | for _, prefix_element := range append(ipv4_array, ipv6_array...) {
122 | array = append(array, prefix_element.(string))
123 | }
124 | return array
125 | }
126 |
127 | func parseASN(mapstring map[string]interface{}) []string {
128 | var array []string
129 | datamap := mapstring["data"]
130 | mapstring = datamap.(map[string]interface{})
131 | prefixes := mapstring["prefixes"]
132 | prefixes_array := prefixes.([]interface{})
133 | for _, prefix_element := range prefixes_array {
134 | mapstring = prefix_element.(map[string]interface{})
135 | array = append(array, mapstring["prefix"].(string))
136 | }
137 | return array
138 | }
139 |
140 | func parseBody(body []byte, id int, comment string, sc chan string) {
141 | var array []string
142 | var comment_prefix string
143 | var mapstring map[string]interface{}
144 |
145 | dec := json.NewDecoder(strings.NewReader(string(body)))
146 | if err := dec.Decode(&mapstring); err != nil {
147 | log.Fatal(err)
148 | }
149 | if id == PREFIX {
150 | array = parseASN(mapstring)
151 | comment_prefix = "AS"
152 | } else {
153 | array = parseCountry(mapstring)
154 | }
155 | for _, prefix := range array {
156 | ip := isIpOrCidr(prefix) // input validation
157 | if ip != nil { // it really is an IP
158 | if ip.To4() != nil { // is it IPv4
159 | ipset.Lock()
160 | ipset.v4 += "add AS_allow_swap " + prefix + " comment " + comment_prefix + comment + "\n"
161 | ipset.v4count += 1
162 | ipset.Unlock()
163 | } else { // ipv6
164 | ipset.Lock()
165 | ipset.v6 += "add AS_allow_swap6 " + prefix + " comment " + comment_prefix + comment + "\n"
166 | ipset.v6count += 1
167 | ipset.Unlock()
168 | }
169 | } else {
170 | log.Println("not an ip (range): " + prefix + comment_prefix + " " + comment)
171 | }
172 | }
173 | //fmt.Println("starting thread for: "+comment_prefix)
174 | sc <- "done"
175 | }
176 |
177 | func addAllowed(allowed []string) {
178 | for _, el := range allowed {
179 | ip := isIpOrCidr(el)
180 | if ip != nil { //really an IP
181 | if ip.To4() != nil {
182 | ipset.v4 += "add AS_allow_swap " + el + " comment \"read from asallow.conf\"\n"
183 | ipset.v4count += 1
184 | } else {
185 | ipset.v6 += "add AS_allow_swap6 " + el + " comment \"read from asallow.conf\"\n"
186 | ipset.v6count += 1
187 | }
188 | } else {
189 | log.Println("not an ip (range): " + el)
190 | }
191 | }
192 | }
193 |
194 | func main() {
195 | if os.Geteuid() != 0 {
196 | log.Fatal("This needs to be run as root")
197 | }
198 | runtime.GOMAXPROCS(runtime.NumCPU())
199 | cfgfile := flag.String("conf", "asallow.conf", "a valid config file")
200 | flag.Parse()
201 |
202 | counter := 0
203 | sc := make(chan string)
204 |
205 | // parse the config
206 | cfg := readconfig(*cfgfile)
207 | resources := []resourceInfo{
208 | {PREFIX, PREFIX_URI, cfg.Main.ASN},
209 | {COUNTRY, COUNTRY_URI, cfg.Main.Country},
210 | }
211 |
212 | // add always the static entries
213 | addAllowed(cfg.Main.Allow)
214 | doipset(cfg)
215 |
216 | for _, resource := range resources {
217 | for i, uri_id := range resource.cfg {
218 | go func(uri_id string, resource resourceInfo) {
219 | if counter > 0 && i%2 == 0 { // max 2 rqs
220 | time.Sleep(time.Second)
221 | }
222 | counter += 1
223 | body := getURI(resource.uri + uri_id)
224 | go parseBody(body, resource.id, uri_id, sc)
225 | }(uri_id, resource)
226 | }
227 | }
228 |
229 | for range append(cfg.Main.Country, cfg.Main.ASN...) {
230 | <-sc
231 | }
232 |
233 | doipset(cfg)
234 |
235 | fmt.Printf("%v ipv4 / %v ipv6 subnets added\n", ipset.v4count, ipset.v6count)
236 | fmt.Println("AS_allow and AS_allow6 ipset created/modified")
237 | }
238 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/42wim/asallow
2 |
3 | go 1.14
4 |
5 | require (
6 | gopkg.in/gcfg.v1 v1.2.0
7 | gopkg.in/warnings.v0 v0.1.1 // indirect
8 | )
9 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | gopkg.in/gcfg.v1 v1.2.0 h1:0HIbH907iBTAntm+88IJV2qmJALDAh8sPekI9Vc1fm0=
2 | gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
3 | gopkg.in/warnings.v0 v0.1.1 h1:XM28wIgFzaBmeZ5dNHIpWLQpt/9DGKxk+rCg/22nnYE=
4 | gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
5 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/gcfg.v1/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012 Péter Surányi. Portions Copyright (c) 2009 The Go
2 | Authors. All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are
6 | met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above
11 | copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the
13 | distribution.
14 | * Neither the name of Google Inc. nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/gcfg.v1/README:
--------------------------------------------------------------------------------
1 | Gcfg reads INI-style configuration files into Go structs;
2 | supports user-defined types and subsections.
3 |
4 | Package docs: https://godoc.org/gopkg.in/gcfg.v1
5 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/gcfg.v1/doc.go:
--------------------------------------------------------------------------------
1 | // Package gcfg reads "INI-style" text-based configuration files with
2 | // "name=value" pairs grouped into sections (gcfg files).
3 | //
4 | // This package is still a work in progress; see the sections below for planned
5 | // changes.
6 | //
7 | // Syntax
8 | //
9 | // The syntax is based on that used by git config:
10 | // http://git-scm.com/docs/git-config#_syntax .
11 | // There are some (planned) differences compared to the git config format:
12 | // - improve data portability:
13 | // - must be encoded in UTF-8 (for now) and must not contain the 0 byte
14 | // - include and "path" type is not supported
15 | // (path type may be implementable as a user-defined type)
16 | // - internationalization
17 | // - section and variable names can contain unicode letters, unicode digits
18 | // (as defined in http://golang.org/ref/spec#Characters ) and hyphens
19 | // (U+002D), starting with a unicode letter
20 | // - disallow potentially ambiguous or misleading definitions:
21 | // - `[sec.sub]` format is not allowed (deprecated in gitconfig)
22 | // - `[sec ""]` is not allowed
23 | // - use `[sec]` for section name "sec" and empty subsection name
24 | // - (planned) within a single file, definitions must be contiguous for each:
25 | // - section: '[secA]' -> '[secB]' -> '[secA]' is an error
26 | // - subsection: '[sec "A"]' -> '[sec "B"]' -> '[sec "A"]' is an error
27 | // - multivalued variable: 'multi=a' -> 'other=x' -> 'multi=b' is an error
28 | //
29 | // Data structure
30 | //
31 | // The functions in this package read values into a user-defined struct.
32 | // Each section corresponds to a struct field in the config struct, and each
33 | // variable in a section corresponds to a data field in the section struct.
34 | // The mapping of each section or variable name to fields is done either based
35 | // on the "gcfg" struct tag or by matching the name of the section or variable,
36 | // ignoring case. In the latter case, hyphens '-' in section and variable names
37 | // correspond to underscores '_' in field names.
38 | // Fields must be exported; to use a section or variable name starting with a
39 | // letter that is neither upper- or lower-case, prefix the field name with 'X'.
40 | // (See https://code.google.com/p/go/issues/detail?id=5763#c4 .)
41 | //
42 | // For sections with subsections, the corresponding field in config must be a
43 | // map, rather than a struct, with string keys and pointer-to-struct values.
44 | // Values for subsection variables are stored in the map with the subsection
45 | // name used as the map key.
46 | // (Note that unlike section and variable names, subsection names are case
47 | // sensitive.)
48 | // When using a map, and there is a section with the same section name but
49 | // without a subsection name, its values are stored with the empty string used
50 | // as the key.
51 | // It is possible to provide default values for subsections in the section
52 | // "default-" (or by setting values in the corresponding struct
53 | // field "Default_").
54 | //
55 | // The functions in this package panic if config is not a pointer to a struct,
56 | // or when a field is not of a suitable type (either a struct or a map with
57 | // string keys and pointer-to-struct values).
58 | //
59 | // Parsing of values
60 | //
61 | // The section structs in the config struct may contain single-valued or
62 | // multi-valued variables. Variables of unnamed slice type (that is, a type
63 | // starting with `[]`) are treated as multi-value; all others (including named
64 | // slice types) are treated as single-valued variables.
65 | //
66 | // Single-valued variables are handled based on the type as follows.
67 | // Unnamed pointer types (that is, types starting with `*`) are dereferenced,
68 | // and if necessary, a new instance is allocated.
69 | //
70 | // For types implementing the encoding.TextUnmarshaler interface, the
71 | // UnmarshalText method is used to set the value. Implementing this method is
72 | // the recommended way for parsing user-defined types.
73 | //
74 | // For fields of string kind, the value string is assigned to the field, after
75 | // unquoting and unescaping as needed.
76 | // For fields of bool kind, the field is set to true if the value is "true",
77 | // "yes", "on" or "1", and set to false if the value is "false", "no", "off" or
78 | // "0", ignoring case. In addition, single-valued bool fields can be specified
79 | // with a "blank" value (variable name without equals sign and value); in such
80 | // case the value is set to true.
81 | //
82 | // Predefined integer types [u]int(|8|16|32|64) and big.Int are parsed as
83 | // decimal or hexadecimal (if having '0x' prefix). (This is to prevent
84 | // unintuitively handling zero-padded numbers as octal.) Other types having
85 | // [u]int* as the underlying type, such as os.FileMode and uintptr allow
86 | // decimal, hexadecimal, or octal values.
87 | // Parsing mode for integer types can be overridden using the struct tag option
88 | // ",int=mode" where mode is a combination of the 'd', 'h', and 'o' characters
89 | // (each standing for decimal, hexadecimal, and octal, respectively.)
90 | //
91 | // All other types are parsed using fmt.Sscanf with the "%v" verb.
92 | //
93 | // For multi-valued variables, each individual value is parsed as above and
94 | // appended to the slice. If the first value is specified as a "blank" value
95 | // (variable name without equals sign and value), a new slice is allocated;
96 | // that is any values previously set in the slice will be ignored.
97 | //
98 | // The types subpackage for provides helpers for parsing "enum-like" and integer
99 | // types.
100 | //
101 | // Error handling
102 | //
103 | // There are 3 types of errors:
104 | //
105 | // - programmer errors / panics:
106 | // - invalid configuration structure
107 | // - data errors:
108 | // - fatal errors:
109 | // - invalid configuration syntax
110 | // - warnings:
111 | // - data that doesn't belong to any part of the config structure
112 | //
113 | // Programmer errors trigger panics. These are should be fixed by the programmer
114 | // before releasing code that uses gcfg.
115 | //
116 | // Data errors cause gcfg to return a non-nil error value. This includes the
117 | // case when there are extra unknown key-value definitions in the configuration
118 | // data (extra data).
119 | // However, in some occasions it is desirable to be able to proceed in
120 | // situations when the only data error is that of extra data.
121 | // These errors are handled at a different (warning) priority and can be
122 | // filtered out programmatically. To ignore extra data warnings, wrap the
123 | // gcfg.Read*Into invocation into a call to gcfg.FatalOnly.
124 | //
125 | // TODO
126 | //
127 | // The following is a list of changes under consideration:
128 | // - documentation
129 | // - self-contained syntax documentation
130 | // - more practical examples
131 | // - move TODOs to issue tracker (eventually)
132 | // - syntax
133 | // - reconsider valid escape sequences
134 | // (gitconfig doesn't support \r in value, \t in subsection name, etc.)
135 | // - reading / parsing gcfg files
136 | // - define internal representation structure
137 | // - support multiple inputs (readers, strings, files)
138 | // - support declaring encoding (?)
139 | // - support varying fields sets for subsections (?)
140 | // - writing gcfg files
141 | // - error handling
142 | // - make error context accessible programmatically?
143 | // - limit input size?
144 | //
145 | package gcfg // import "gopkg.in/gcfg.v1"
146 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/gcfg.v1/errors.go:
--------------------------------------------------------------------------------
1 | package gcfg
2 |
3 | import (
4 | "gopkg.in/warnings.v0"
5 | )
6 |
7 | // FatalOnly filters the results of a Read*Into invocation and returns only
8 | // fatal errors. That is, errors (warnings) indicating data for unknown
9 | // sections / variables is ignored. Example invocation:
10 | //
11 | // err := gcfg.FatalOnly(gcfg.ReadFileInto(&cfg, configFile))
12 | // if err != nil {
13 | // ...
14 | //
15 | func FatalOnly(err error) error {
16 | return warnings.FatalOnly(err)
17 | }
18 |
19 | func isFatal(err error) bool {
20 | _, ok := err.(extraData)
21 | return !ok
22 | }
23 |
24 | type extraData struct {
25 | section string
26 | subsection *string
27 | variable *string
28 | }
29 |
30 | func (e extraData) Error() string {
31 | s := "can't store data at section \"" + e.section + "\""
32 | if e.subsection != nil {
33 | s += ", subsection \"" + *e.subsection + "\""
34 | }
35 | if e.variable != nil {
36 | s += ", variable \"" + *e.variable + "\""
37 | }
38 | return s
39 | }
40 |
41 | var _ error = extraData{}
42 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/gcfg.v1/go1_0.go:
--------------------------------------------------------------------------------
1 | // +build !go1.2
2 |
3 | package gcfg
4 |
5 | type textUnmarshaler interface {
6 | UnmarshalText(text []byte) error
7 | }
8 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/gcfg.v1/go1_2.go:
--------------------------------------------------------------------------------
1 | // +build go1.2
2 |
3 | package gcfg
4 |
5 | import (
6 | "encoding"
7 | )
8 |
9 | type textUnmarshaler encoding.TextUnmarshaler
10 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/gcfg.v1/read.go:
--------------------------------------------------------------------------------
1 | package gcfg
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "io/ioutil"
7 | "os"
8 | "strings"
9 |
10 | "gopkg.in/gcfg.v1/scanner"
11 | "gopkg.in/gcfg.v1/token"
12 | "gopkg.in/warnings.v0"
13 | )
14 |
15 | var unescape = map[rune]rune{'\\': '\\', '"': '"', 'n': '\n', 't': '\t'}
16 |
17 | // no error: invalid literals should be caught by scanner
18 | func unquote(s string) string {
19 | u, q, esc := make([]rune, 0, len(s)), false, false
20 | for _, c := range s {
21 | if esc {
22 | uc, ok := unescape[c]
23 | switch {
24 | case ok:
25 | u = append(u, uc)
26 | fallthrough
27 | case !q && c == '\n':
28 | esc = false
29 | continue
30 | }
31 | panic("invalid escape sequence")
32 | }
33 | switch c {
34 | case '"':
35 | q = !q
36 | case '\\':
37 | esc = true
38 | default:
39 | u = append(u, c)
40 | }
41 | }
42 | if q {
43 | panic("missing end quote")
44 | }
45 | if esc {
46 | panic("invalid escape sequence")
47 | }
48 | return string(u)
49 | }
50 |
51 | func readIntoPass(c *warnings.Collector, config interface{}, fset *token.FileSet,
52 | file *token.File, src []byte, subsectPass bool) error {
53 | //
54 | var s scanner.Scanner
55 | var errs scanner.ErrorList
56 | s.Init(file, src, func(p token.Position, m string) { errs.Add(p, m) }, 0)
57 | sect, sectsub := "", ""
58 | pos, tok, lit := s.Scan()
59 | errfn := func(msg string) error {
60 | return fmt.Errorf("%s: %s", fset.Position(pos), msg)
61 | }
62 | for {
63 | if errs.Len() > 0 {
64 | if err := c.Collect(errs.Err()); err != nil {
65 | return err
66 | }
67 | }
68 | switch tok {
69 | case token.EOF:
70 | return nil
71 | case token.EOL, token.COMMENT:
72 | pos, tok, lit = s.Scan()
73 | case token.LBRACK:
74 | pos, tok, lit = s.Scan()
75 | if errs.Len() > 0 {
76 | if err := c.Collect(errs.Err()); err != nil {
77 | return err
78 | }
79 | }
80 | if tok != token.IDENT {
81 | if err := c.Collect(errfn("expected section name")); err != nil {
82 | return err
83 | }
84 | }
85 | sect, sectsub = lit, ""
86 | pos, tok, lit = s.Scan()
87 | if errs.Len() > 0 {
88 | if err := c.Collect(errs.Err()); err != nil {
89 | return err
90 | }
91 | }
92 | if tok == token.STRING {
93 | sectsub = unquote(lit)
94 | if sectsub == "" {
95 | if err := c.Collect(errfn("empty subsection name")); err != nil {
96 | return err
97 | }
98 | }
99 | pos, tok, lit = s.Scan()
100 | if errs.Len() > 0 {
101 | if err := c.Collect(errs.Err()); err != nil {
102 | return err
103 | }
104 | }
105 | }
106 | if tok != token.RBRACK {
107 | if sectsub == "" {
108 | if err := c.Collect(errfn("expected subsection name or right bracket")); err != nil {
109 | return err
110 | }
111 | }
112 | if err := c.Collect(errfn("expected right bracket")); err != nil {
113 | return err
114 | }
115 | }
116 | pos, tok, lit = s.Scan()
117 | if tok != token.EOL && tok != token.EOF && tok != token.COMMENT {
118 | if err := c.Collect(errfn("expected EOL, EOF, or comment")); err != nil {
119 | return err
120 | }
121 | }
122 | // If a section/subsection header was found, ensure a
123 | // container object is created, even if there are no
124 | // variables further down.
125 | err := c.Collect(set(c, config, sect, sectsub, "", true, "", subsectPass))
126 | if err != nil {
127 | return err
128 | }
129 | case token.IDENT:
130 | if sect == "" {
131 | if err := c.Collect(errfn("expected section header")); err != nil {
132 | return err
133 | }
134 | }
135 | n := lit
136 | pos, tok, lit = s.Scan()
137 | if errs.Len() > 0 {
138 | return errs.Err()
139 | }
140 | blank, v := tok == token.EOF || tok == token.EOL || tok == token.COMMENT, ""
141 | if !blank {
142 | if tok != token.ASSIGN {
143 | if err := c.Collect(errfn("expected '='")); err != nil {
144 | return err
145 | }
146 | }
147 | pos, tok, lit = s.Scan()
148 | if errs.Len() > 0 {
149 | if err := c.Collect(errs.Err()); err != nil {
150 | return err
151 | }
152 | }
153 | if tok != token.STRING {
154 | if err := c.Collect(errfn("expected value")); err != nil {
155 | return err
156 | }
157 | }
158 | v = unquote(lit)
159 | pos, tok, lit = s.Scan()
160 | if errs.Len() > 0 {
161 | if err := c.Collect(errs.Err()); err != nil {
162 | return err
163 | }
164 | }
165 | if tok != token.EOL && tok != token.EOF && tok != token.COMMENT {
166 | if err := c.Collect(errfn("expected EOL, EOF, or comment")); err != nil {
167 | return err
168 | }
169 | }
170 | }
171 | err := set(c, config, sect, sectsub, n, blank, v, subsectPass)
172 | if err != nil {
173 | return err
174 | }
175 | default:
176 | if sect == "" {
177 | if err := c.Collect(errfn("expected section header")); err != nil {
178 | return err
179 | }
180 | }
181 | if err := c.Collect(errfn("expected section header or variable declaration")); err != nil {
182 | return err
183 | }
184 | }
185 | }
186 | panic("never reached")
187 | }
188 |
189 | func readInto(config interface{}, fset *token.FileSet, file *token.File,
190 | src []byte) error {
191 | //
192 | c := warnings.NewCollector(isFatal)
193 | err := readIntoPass(c, config, fset, file, src, false)
194 | if err != nil {
195 | return err
196 | }
197 | err = readIntoPass(c, config, fset, file, src, true)
198 | if err != nil {
199 | return err
200 | }
201 | return c.Done()
202 | }
203 |
204 | // ReadInto reads gcfg formatted data from reader and sets the values into the
205 | // corresponding fields in config.
206 | func ReadInto(config interface{}, reader io.Reader) error {
207 | src, err := ioutil.ReadAll(reader)
208 | if err != nil {
209 | return err
210 | }
211 | fset := token.NewFileSet()
212 | file := fset.AddFile("", fset.Base(), len(src))
213 | return readInto(config, fset, file, src)
214 | }
215 |
216 | // ReadStringInto reads gcfg formatted data from str and sets the values into
217 | // the corresponding fields in config.
218 | func ReadStringInto(config interface{}, str string) error {
219 | r := strings.NewReader(str)
220 | return ReadInto(config, r)
221 | }
222 |
223 | // ReadFileInto reads gcfg formatted data from the file filename and sets the
224 | // values into the corresponding fields in config.
225 | func ReadFileInto(config interface{}, filename string) error {
226 | f, err := os.Open(filename)
227 | if err != nil {
228 | return err
229 | }
230 | defer f.Close()
231 | src, err := ioutil.ReadAll(f)
232 | if err != nil {
233 | return err
234 | }
235 | fset := token.NewFileSet()
236 | file := fset.AddFile(filename, fset.Base(), len(src))
237 | return readInto(config, fset, file, src)
238 | }
239 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/gcfg.v1/scanner/errors.go:
--------------------------------------------------------------------------------
1 | // Copyright 2009 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package scanner
6 |
7 | import (
8 | "fmt"
9 | "io"
10 | "sort"
11 | )
12 |
13 | import (
14 | "gopkg.in/gcfg.v1/token"
15 | )
16 |
17 | // In an ErrorList, an error is represented by an *Error.
18 | // The position Pos, if valid, points to the beginning of
19 | // the offending token, and the error condition is described
20 | // by Msg.
21 | //
22 | type Error struct {
23 | Pos token.Position
24 | Msg string
25 | }
26 |
27 | // Error implements the error interface.
28 | func (e Error) Error() string {
29 | if e.Pos.Filename != "" || e.Pos.IsValid() {
30 | // don't print ""
31 | // TODO(gri) reconsider the semantics of Position.IsValid
32 | return e.Pos.String() + ": " + e.Msg
33 | }
34 | return e.Msg
35 | }
36 |
37 | // ErrorList is a list of *Errors.
38 | // The zero value for an ErrorList is an empty ErrorList ready to use.
39 | //
40 | type ErrorList []*Error
41 |
42 | // Add adds an Error with given position and error message to an ErrorList.
43 | func (p *ErrorList) Add(pos token.Position, msg string) {
44 | *p = append(*p, &Error{pos, msg})
45 | }
46 |
47 | // Reset resets an ErrorList to no errors.
48 | func (p *ErrorList) Reset() { *p = (*p)[0:0] }
49 |
50 | // ErrorList implements the sort Interface.
51 | func (p ErrorList) Len() int { return len(p) }
52 | func (p ErrorList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
53 |
54 | func (p ErrorList) Less(i, j int) bool {
55 | e := &p[i].Pos
56 | f := &p[j].Pos
57 | if e.Filename < f.Filename {
58 | return true
59 | }
60 | if e.Filename == f.Filename {
61 | return e.Offset < f.Offset
62 | }
63 | return false
64 | }
65 |
66 | // Sort sorts an ErrorList. *Error entries are sorted by position,
67 | // other errors are sorted by error message, and before any *Error
68 | // entry.
69 | //
70 | func (p ErrorList) Sort() {
71 | sort.Sort(p)
72 | }
73 |
74 | // RemoveMultiples sorts an ErrorList and removes all but the first error per line.
75 | func (p *ErrorList) RemoveMultiples() {
76 | sort.Sort(p)
77 | var last token.Position // initial last.Line is != any legal error line
78 | i := 0
79 | for _, e := range *p {
80 | if e.Pos.Filename != last.Filename || e.Pos.Line != last.Line {
81 | last = e.Pos
82 | (*p)[i] = e
83 | i++
84 | }
85 | }
86 | (*p) = (*p)[0:i]
87 | }
88 |
89 | // An ErrorList implements the error interface.
90 | func (p ErrorList) Error() string {
91 | switch len(p) {
92 | case 0:
93 | return "no errors"
94 | case 1:
95 | return p[0].Error()
96 | }
97 | return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1)
98 | }
99 |
100 | // Err returns an error equivalent to this error list.
101 | // If the list is empty, Err returns nil.
102 | func (p ErrorList) Err() error {
103 | if len(p) == 0 {
104 | return nil
105 | }
106 | return p
107 | }
108 |
109 | // PrintError is a utility function that prints a list of errors to w,
110 | // one error per line, if the err parameter is an ErrorList. Otherwise
111 | // it prints the err string.
112 | //
113 | func PrintError(w io.Writer, err error) {
114 | if list, ok := err.(ErrorList); ok {
115 | for _, e := range list {
116 | fmt.Fprintf(w, "%s\n", e)
117 | }
118 | } else if err != nil {
119 | fmt.Fprintf(w, "%s\n", err)
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/gcfg.v1/scanner/scanner.go:
--------------------------------------------------------------------------------
1 | // Copyright 2009 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Package scanner implements a scanner for gcfg configuration text.
6 | // It takes a []byte as source which can then be tokenized
7 | // through repeated calls to the Scan method.
8 | //
9 | // Note that the API for the scanner package may change to accommodate new
10 | // features or implementation changes in gcfg.
11 | //
12 | package scanner
13 |
14 | import (
15 | "fmt"
16 | "path/filepath"
17 | "unicode"
18 | "unicode/utf8"
19 | )
20 |
21 | import (
22 | "gopkg.in/gcfg.v1/token"
23 | )
24 |
25 | // An ErrorHandler may be provided to Scanner.Init. If a syntax error is
26 | // encountered and a handler was installed, the handler is called with a
27 | // position and an error message. The position points to the beginning of
28 | // the offending token.
29 | //
30 | type ErrorHandler func(pos token.Position, msg string)
31 |
32 | // A Scanner holds the scanner's internal state while processing
33 | // a given text. It can be allocated as part of another data
34 | // structure but must be initialized via Init before use.
35 | //
36 | type Scanner struct {
37 | // immutable state
38 | file *token.File // source file handle
39 | dir string // directory portion of file.Name()
40 | src []byte // source
41 | err ErrorHandler // error reporting; or nil
42 | mode Mode // scanning mode
43 |
44 | // scanning state
45 | ch rune // current character
46 | offset int // character offset
47 | rdOffset int // reading offset (position after current character)
48 | lineOffset int // current line offset
49 | nextVal bool // next token is expected to be a value
50 |
51 | // public state - ok to modify
52 | ErrorCount int // number of errors encountered
53 | }
54 |
55 | // Read the next Unicode char into s.ch.
56 | // s.ch < 0 means end-of-file.
57 | //
58 | func (s *Scanner) next() {
59 | if s.rdOffset < len(s.src) {
60 | s.offset = s.rdOffset
61 | if s.ch == '\n' {
62 | s.lineOffset = s.offset
63 | s.file.AddLine(s.offset)
64 | }
65 | r, w := rune(s.src[s.rdOffset]), 1
66 | switch {
67 | case r == 0:
68 | s.error(s.offset, "illegal character NUL")
69 | case r >= 0x80:
70 | // not ASCII
71 | r, w = utf8.DecodeRune(s.src[s.rdOffset:])
72 | if r == utf8.RuneError && w == 1 {
73 | s.error(s.offset, "illegal UTF-8 encoding")
74 | }
75 | }
76 | s.rdOffset += w
77 | s.ch = r
78 | } else {
79 | s.offset = len(s.src)
80 | if s.ch == '\n' {
81 | s.lineOffset = s.offset
82 | s.file.AddLine(s.offset)
83 | }
84 | s.ch = -1 // eof
85 | }
86 | }
87 |
88 | // A mode value is a set of flags (or 0).
89 | // They control scanner behavior.
90 | //
91 | type Mode uint
92 |
93 | const (
94 | ScanComments Mode = 1 << iota // return comments as COMMENT tokens
95 | )
96 |
97 | // Init prepares the scanner s to tokenize the text src by setting the
98 | // scanner at the beginning of src. The scanner uses the file set file
99 | // for position information and it adds line information for each line.
100 | // It is ok to re-use the same file when re-scanning the same file as
101 | // line information which is already present is ignored. Init causes a
102 | // panic if the file size does not match the src size.
103 | //
104 | // Calls to Scan will invoke the error handler err if they encounter a
105 | // syntax error and err is not nil. Also, for each error encountered,
106 | // the Scanner field ErrorCount is incremented by one. The mode parameter
107 | // determines how comments are handled.
108 | //
109 | // Note that Init may call err if there is an error in the first character
110 | // of the file.
111 | //
112 | func (s *Scanner) Init(file *token.File, src []byte, err ErrorHandler, mode Mode) {
113 | // Explicitly initialize all fields since a scanner may be reused.
114 | if file.Size() != len(src) {
115 | panic(fmt.Sprintf("file size (%d) does not match src len (%d)", file.Size(), len(src)))
116 | }
117 | s.file = file
118 | s.dir, _ = filepath.Split(file.Name())
119 | s.src = src
120 | s.err = err
121 | s.mode = mode
122 |
123 | s.ch = ' '
124 | s.offset = 0
125 | s.rdOffset = 0
126 | s.lineOffset = 0
127 | s.ErrorCount = 0
128 | s.nextVal = false
129 |
130 | s.next()
131 | }
132 |
133 | func (s *Scanner) error(offs int, msg string) {
134 | if s.err != nil {
135 | s.err(s.file.Position(s.file.Pos(offs)), msg)
136 | }
137 | s.ErrorCount++
138 | }
139 |
140 | func (s *Scanner) scanComment() string {
141 | // initial [;#] already consumed
142 | offs := s.offset - 1 // position of initial [;#]
143 |
144 | for s.ch != '\n' && s.ch >= 0 {
145 | s.next()
146 | }
147 | return string(s.src[offs:s.offset])
148 | }
149 |
150 | func isLetter(ch rune) bool {
151 | return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch >= 0x80 && unicode.IsLetter(ch)
152 | }
153 |
154 | func isDigit(ch rune) bool {
155 | return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch)
156 | }
157 |
158 | func (s *Scanner) scanIdentifier() string {
159 | offs := s.offset
160 | for isLetter(s.ch) || isDigit(s.ch) || s.ch == '-' {
161 | s.next()
162 | }
163 | return string(s.src[offs:s.offset])
164 | }
165 |
166 | func (s *Scanner) scanEscape(val bool) {
167 | offs := s.offset
168 | ch := s.ch
169 | s.next() // always make progress
170 | switch ch {
171 | case '\\', '"':
172 | // ok
173 | case 'n', 't':
174 | if val {
175 | break // ok
176 | }
177 | fallthrough
178 | default:
179 | s.error(offs, "unknown escape sequence")
180 | }
181 | }
182 |
183 | func (s *Scanner) scanString() string {
184 | // '"' opening already consumed
185 | offs := s.offset - 1
186 |
187 | for s.ch != '"' {
188 | ch := s.ch
189 | s.next()
190 | if ch == '\n' || ch < 0 {
191 | s.error(offs, "string not terminated")
192 | break
193 | }
194 | if ch == '\\' {
195 | s.scanEscape(false)
196 | }
197 | }
198 |
199 | s.next()
200 |
201 | return string(s.src[offs:s.offset])
202 | }
203 |
204 | func stripCR(b []byte) []byte {
205 | c := make([]byte, len(b))
206 | i := 0
207 | for _, ch := range b {
208 | if ch != '\r' {
209 | c[i] = ch
210 | i++
211 | }
212 | }
213 | return c[:i]
214 | }
215 |
216 | func (s *Scanner) scanValString() string {
217 | offs := s.offset
218 |
219 | hasCR := false
220 | end := offs
221 | inQuote := false
222 | loop:
223 | for inQuote || s.ch >= 0 && s.ch != '\n' && s.ch != ';' && s.ch != '#' {
224 | ch := s.ch
225 | s.next()
226 | switch {
227 | case inQuote && ch == '\\':
228 | s.scanEscape(true)
229 | case !inQuote && ch == '\\':
230 | if s.ch == '\r' {
231 | hasCR = true
232 | s.next()
233 | }
234 | if s.ch != '\n' {
235 | s.error(offs, "unquoted '\\' must be followed by new line")
236 | break loop
237 | }
238 | s.next()
239 | case ch == '"':
240 | inQuote = !inQuote
241 | case ch == '\r':
242 | hasCR = true
243 | case ch < 0 || inQuote && ch == '\n':
244 | s.error(offs, "string not terminated")
245 | break loop
246 | }
247 | if inQuote || !isWhiteSpace(ch) {
248 | end = s.offset
249 | }
250 | }
251 |
252 | lit := s.src[offs:end]
253 | if hasCR {
254 | lit = stripCR(lit)
255 | }
256 |
257 | return string(lit)
258 | }
259 |
260 | func isWhiteSpace(ch rune) bool {
261 | return ch == ' ' || ch == '\t' || ch == '\r'
262 | }
263 |
264 | func (s *Scanner) skipWhitespace() {
265 | for isWhiteSpace(s.ch) {
266 | s.next()
267 | }
268 | }
269 |
270 | // Scan scans the next token and returns the token position, the token,
271 | // and its literal string if applicable. The source end is indicated by
272 | // token.EOF.
273 | //
274 | // If the returned token is a literal (token.IDENT, token.STRING) or
275 | // token.COMMENT, the literal string has the corresponding value.
276 | //
277 | // If the returned token is token.ILLEGAL, the literal string is the
278 | // offending character.
279 | //
280 | // In all other cases, Scan returns an empty literal string.
281 | //
282 | // For more tolerant parsing, Scan will return a valid token if
283 | // possible even if a syntax error was encountered. Thus, even
284 | // if the resulting token sequence contains no illegal tokens,
285 | // a client may not assume that no error occurred. Instead it
286 | // must check the scanner's ErrorCount or the number of calls
287 | // of the error handler, if there was one installed.
288 | //
289 | // Scan adds line information to the file added to the file
290 | // set with Init. Token positions are relative to that file
291 | // and thus relative to the file set.
292 | //
293 | func (s *Scanner) Scan() (pos token.Pos, tok token.Token, lit string) {
294 | scanAgain:
295 | s.skipWhitespace()
296 |
297 | // current token start
298 | pos = s.file.Pos(s.offset)
299 |
300 | // determine token value
301 | switch ch := s.ch; {
302 | case s.nextVal:
303 | lit = s.scanValString()
304 | tok = token.STRING
305 | s.nextVal = false
306 | case isLetter(ch):
307 | lit = s.scanIdentifier()
308 | tok = token.IDENT
309 | default:
310 | s.next() // always make progress
311 | switch ch {
312 | case -1:
313 | tok = token.EOF
314 | case '\n':
315 | tok = token.EOL
316 | case '"':
317 | tok = token.STRING
318 | lit = s.scanString()
319 | case '[':
320 | tok = token.LBRACK
321 | case ']':
322 | tok = token.RBRACK
323 | case ';', '#':
324 | // comment
325 | lit = s.scanComment()
326 | if s.mode&ScanComments == 0 {
327 | // skip comment
328 | goto scanAgain
329 | }
330 | tok = token.COMMENT
331 | case '=':
332 | tok = token.ASSIGN
333 | s.nextVal = true
334 | default:
335 | s.error(s.file.Offset(pos), fmt.Sprintf("illegal character %#U", ch))
336 | tok = token.ILLEGAL
337 | lit = string(ch)
338 | }
339 | }
340 |
341 | return
342 | }
343 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/gcfg.v1/set.go:
--------------------------------------------------------------------------------
1 | package gcfg
2 |
3 | import (
4 | "bytes"
5 | "encoding/gob"
6 | "fmt"
7 | "math/big"
8 | "reflect"
9 | "strings"
10 | "unicode"
11 | "unicode/utf8"
12 |
13 | "gopkg.in/gcfg.v1/types"
14 | "gopkg.in/warnings.v0"
15 | )
16 |
17 | type tag struct {
18 | ident string
19 | intMode string
20 | }
21 |
22 | func newTag(ts string) tag {
23 | t := tag{}
24 | s := strings.Split(ts, ",")
25 | t.ident = s[0]
26 | for _, tse := range s[1:] {
27 | if strings.HasPrefix(tse, "int=") {
28 | t.intMode = tse[len("int="):]
29 | }
30 | }
31 | return t
32 | }
33 |
34 | func fieldFold(v reflect.Value, name string) (reflect.Value, tag) {
35 | var n string
36 | r0, _ := utf8.DecodeRuneInString(name)
37 | if unicode.IsLetter(r0) && !unicode.IsLower(r0) && !unicode.IsUpper(r0) {
38 | n = "X"
39 | }
40 | n += strings.Replace(name, "-", "_", -1)
41 | f, ok := v.Type().FieldByNameFunc(func(fieldName string) bool {
42 | if !v.FieldByName(fieldName).CanSet() {
43 | return false
44 | }
45 | f, _ := v.Type().FieldByName(fieldName)
46 | t := newTag(f.Tag.Get("gcfg"))
47 | if t.ident != "" {
48 | return strings.EqualFold(t.ident, name)
49 | }
50 | return strings.EqualFold(n, fieldName)
51 | })
52 | if !ok {
53 | return reflect.Value{}, tag{}
54 | }
55 | return v.FieldByName(f.Name), newTag(f.Tag.Get("gcfg"))
56 | }
57 |
58 | type setter func(destp interface{}, blank bool, val string, t tag) error
59 |
60 | var errUnsupportedType = fmt.Errorf("unsupported type")
61 | var errBlankUnsupported = fmt.Errorf("blank value not supported for type")
62 |
63 | var setters = []setter{
64 | typeSetter, textUnmarshalerSetter, kindSetter, scanSetter,
65 | }
66 |
67 | func textUnmarshalerSetter(d interface{}, blank bool, val string, t tag) error {
68 | dtu, ok := d.(textUnmarshaler)
69 | if !ok {
70 | return errUnsupportedType
71 | }
72 | if blank {
73 | return errBlankUnsupported
74 | }
75 | return dtu.UnmarshalText([]byte(val))
76 | }
77 |
78 | func boolSetter(d interface{}, blank bool, val string, t tag) error {
79 | if blank {
80 | reflect.ValueOf(d).Elem().Set(reflect.ValueOf(true))
81 | return nil
82 | }
83 | b, err := types.ParseBool(val)
84 | if err == nil {
85 | reflect.ValueOf(d).Elem().Set(reflect.ValueOf(b))
86 | }
87 | return err
88 | }
89 |
90 | func intMode(mode string) types.IntMode {
91 | var m types.IntMode
92 | if strings.ContainsAny(mode, "dD") {
93 | m |= types.Dec
94 | }
95 | if strings.ContainsAny(mode, "hH") {
96 | m |= types.Hex
97 | }
98 | if strings.ContainsAny(mode, "oO") {
99 | m |= types.Oct
100 | }
101 | return m
102 | }
103 |
104 | var typeModes = map[reflect.Type]types.IntMode{
105 | reflect.TypeOf(int(0)): types.Dec | types.Hex,
106 | reflect.TypeOf(int8(0)): types.Dec | types.Hex,
107 | reflect.TypeOf(int16(0)): types.Dec | types.Hex,
108 | reflect.TypeOf(int32(0)): types.Dec | types.Hex,
109 | reflect.TypeOf(int64(0)): types.Dec | types.Hex,
110 | reflect.TypeOf(uint(0)): types.Dec | types.Hex,
111 | reflect.TypeOf(uint8(0)): types.Dec | types.Hex,
112 | reflect.TypeOf(uint16(0)): types.Dec | types.Hex,
113 | reflect.TypeOf(uint32(0)): types.Dec | types.Hex,
114 | reflect.TypeOf(uint64(0)): types.Dec | types.Hex,
115 | // use default mode (allow dec/hex/oct) for uintptr type
116 | reflect.TypeOf(big.Int{}): types.Dec | types.Hex,
117 | }
118 |
119 | func intModeDefault(t reflect.Type) types.IntMode {
120 | m, ok := typeModes[t]
121 | if !ok {
122 | m = types.Dec | types.Hex | types.Oct
123 | }
124 | return m
125 | }
126 |
127 | func intSetter(d interface{}, blank bool, val string, t tag) error {
128 | if blank {
129 | return errBlankUnsupported
130 | }
131 | mode := intMode(t.intMode)
132 | if mode == 0 {
133 | mode = intModeDefault(reflect.TypeOf(d).Elem())
134 | }
135 | return types.ParseInt(d, val, mode)
136 | }
137 |
138 | func stringSetter(d interface{}, blank bool, val string, t tag) error {
139 | if blank {
140 | return errBlankUnsupported
141 | }
142 | dsp, ok := d.(*string)
143 | if !ok {
144 | return errUnsupportedType
145 | }
146 | *dsp = val
147 | return nil
148 | }
149 |
150 | var kindSetters = map[reflect.Kind]setter{
151 | reflect.String: stringSetter,
152 | reflect.Bool: boolSetter,
153 | reflect.Int: intSetter,
154 | reflect.Int8: intSetter,
155 | reflect.Int16: intSetter,
156 | reflect.Int32: intSetter,
157 | reflect.Int64: intSetter,
158 | reflect.Uint: intSetter,
159 | reflect.Uint8: intSetter,
160 | reflect.Uint16: intSetter,
161 | reflect.Uint32: intSetter,
162 | reflect.Uint64: intSetter,
163 | reflect.Uintptr: intSetter,
164 | }
165 |
166 | var typeSetters = map[reflect.Type]setter{
167 | reflect.TypeOf(big.Int{}): intSetter,
168 | }
169 |
170 | func typeSetter(d interface{}, blank bool, val string, tt tag) error {
171 | t := reflect.ValueOf(d).Type().Elem()
172 | setter, ok := typeSetters[t]
173 | if !ok {
174 | return errUnsupportedType
175 | }
176 | return setter(d, blank, val, tt)
177 | }
178 |
179 | func kindSetter(d interface{}, blank bool, val string, tt tag) error {
180 | k := reflect.ValueOf(d).Type().Elem().Kind()
181 | setter, ok := kindSetters[k]
182 | if !ok {
183 | return errUnsupportedType
184 | }
185 | return setter(d, blank, val, tt)
186 | }
187 |
188 | func scanSetter(d interface{}, blank bool, val string, tt tag) error {
189 | if blank {
190 | return errBlankUnsupported
191 | }
192 | return types.ScanFully(d, val, 'v')
193 | }
194 |
195 | func newValue(sect string, vCfg reflect.Value, vType reflect.Type) (reflect.Value, error) {
196 | pv := reflect.New(vType)
197 | dfltName := "default-" + sect
198 | dfltField, _ := fieldFold(vCfg, dfltName)
199 | var err error
200 | if dfltField.IsValid() {
201 | b := bytes.NewBuffer(nil)
202 | ge := gob.NewEncoder(b)
203 | err = ge.EncodeValue(dfltField)
204 | if err != nil {
205 | return pv, err
206 | }
207 | gd := gob.NewDecoder(bytes.NewReader(b.Bytes()))
208 | err = gd.DecodeValue(pv.Elem())
209 | if err != nil {
210 | return pv, err
211 | }
212 | }
213 | return pv, nil
214 | }
215 |
216 | func set(c *warnings.Collector, cfg interface{}, sect, sub, name string,
217 | blank bool, value string, subsectPass bool) error {
218 | //
219 | vPCfg := reflect.ValueOf(cfg)
220 | if vPCfg.Kind() != reflect.Ptr || vPCfg.Elem().Kind() != reflect.Struct {
221 | panic(fmt.Errorf("config must be a pointer to a struct"))
222 | }
223 | vCfg := vPCfg.Elem()
224 | vSect, _ := fieldFold(vCfg, sect)
225 | if !vSect.IsValid() {
226 | err := extraData{section: sect}
227 | return c.Collect(err)
228 | }
229 | isSubsect := vSect.Kind() == reflect.Map
230 | if subsectPass != isSubsect {
231 | return nil
232 | }
233 | if isSubsect {
234 | vst := vSect.Type()
235 | if vst.Key().Kind() != reflect.String ||
236 | vst.Elem().Kind() != reflect.Ptr ||
237 | vst.Elem().Elem().Kind() != reflect.Struct {
238 | panic(fmt.Errorf("map field for section must have string keys and "+
239 | " pointer-to-struct values: section %q", sect))
240 | }
241 | if vSect.IsNil() {
242 | vSect.Set(reflect.MakeMap(vst))
243 | }
244 | k := reflect.ValueOf(sub)
245 | pv := vSect.MapIndex(k)
246 | if !pv.IsValid() {
247 | vType := vSect.Type().Elem().Elem()
248 | var err error
249 | pv, err = newValue(sect, vCfg, vType)
250 | if err != nil {
251 | return err
252 | }
253 | vSect.SetMapIndex(k, pv)
254 | }
255 | vSect = pv.Elem()
256 | } else if vSect.Kind() != reflect.Struct {
257 | panic(fmt.Errorf("field for section must be a map or a struct: "+
258 | "section %q", sect))
259 | } else if sub != "" {
260 | err := extraData{section: sect, subsection: &sub}
261 | return c.Collect(err)
262 | }
263 | // Empty name is a special value, meaning that only the
264 | // section/subsection object is to be created, with no values set.
265 | if name == "" {
266 | return nil
267 | }
268 | vVar, t := fieldFold(vSect, name)
269 | if !vVar.IsValid() {
270 | var err error
271 | if isSubsect {
272 | err = extraData{section: sect, subsection: &sub, variable: &name}
273 | } else {
274 | err = extraData{section: sect, variable: &name}
275 | }
276 | return c.Collect(err)
277 | }
278 | // vVal is either single-valued var, or newly allocated value within multi-valued var
279 | var vVal reflect.Value
280 | // multi-value if unnamed slice type
281 | isMulti := vVar.Type().Name() == "" && vVar.Kind() == reflect.Slice ||
282 | vVar.Type().Name() == "" && vVar.Kind() == reflect.Ptr && vVar.Type().Elem().Name() == "" && vVar.Type().Elem().Kind() == reflect.Slice
283 | if isMulti && vVar.Kind() == reflect.Ptr {
284 | if vVar.IsNil() {
285 | vVar.Set(reflect.New(vVar.Type().Elem()))
286 | }
287 | vVar = vVar.Elem()
288 | }
289 | if isMulti && blank {
290 | vVar.Set(reflect.Zero(vVar.Type()))
291 | return nil
292 | }
293 | if isMulti {
294 | vVal = reflect.New(vVar.Type().Elem()).Elem()
295 | } else {
296 | vVal = vVar
297 | }
298 | isDeref := vVal.Type().Name() == "" && vVal.Type().Kind() == reflect.Ptr
299 | isNew := isDeref && vVal.IsNil()
300 | // vAddr is address of value to set (dereferenced & allocated as needed)
301 | var vAddr reflect.Value
302 | switch {
303 | case isNew:
304 | vAddr = reflect.New(vVal.Type().Elem())
305 | case isDeref && !isNew:
306 | vAddr = vVal
307 | default:
308 | vAddr = vVal.Addr()
309 | }
310 | vAddrI := vAddr.Interface()
311 | err, ok := error(nil), false
312 | for _, s := range setters {
313 | err = s(vAddrI, blank, value, t)
314 | if err == nil {
315 | ok = true
316 | break
317 | }
318 | if err != errUnsupportedType {
319 | return err
320 | }
321 | }
322 | if !ok {
323 | // in case all setters returned errUnsupportedType
324 | return err
325 | }
326 | if isNew { // set reference if it was dereferenced and newly allocated
327 | vVal.Set(vAddr)
328 | }
329 | if isMulti { // append if multi-valued
330 | vVar.Set(reflect.Append(vVar, vVal))
331 | }
332 | return nil
333 | }
334 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/gcfg.v1/token/position.go:
--------------------------------------------------------------------------------
1 | // Copyright 2010 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // TODO(gri) consider making this a separate package outside the go directory.
6 |
7 | package token
8 |
9 | import (
10 | "fmt"
11 | "sort"
12 | "sync"
13 | )
14 |
15 | // -----------------------------------------------------------------------------
16 | // Positions
17 |
18 | // Position describes an arbitrary source position
19 | // including the file, line, and column location.
20 | // A Position is valid if the line number is > 0.
21 | //
22 | type Position struct {
23 | Filename string // filename, if any
24 | Offset int // offset, starting at 0
25 | Line int // line number, starting at 1
26 | Column int // column number, starting at 1 (character count)
27 | }
28 |
29 | // IsValid returns true if the position is valid.
30 | func (pos *Position) IsValid() bool { return pos.Line > 0 }
31 |
32 | // String returns a string in one of several forms:
33 | //
34 | // file:line:column valid position with file name
35 | // line:column valid position without file name
36 | // file invalid position with file name
37 | // - invalid position without file name
38 | //
39 | func (pos Position) String() string {
40 | s := pos.Filename
41 | if pos.IsValid() {
42 | if s != "" {
43 | s += ":"
44 | }
45 | s += fmt.Sprintf("%d:%d", pos.Line, pos.Column)
46 | }
47 | if s == "" {
48 | s = "-"
49 | }
50 | return s
51 | }
52 |
53 | // Pos is a compact encoding of a source position within a file set.
54 | // It can be converted into a Position for a more convenient, but much
55 | // larger, representation.
56 | //
57 | // The Pos value for a given file is a number in the range [base, base+size],
58 | // where base and size are specified when adding the file to the file set via
59 | // AddFile.
60 | //
61 | // To create the Pos value for a specific source offset, first add
62 | // the respective file to the current file set (via FileSet.AddFile)
63 | // and then call File.Pos(offset) for that file. Given a Pos value p
64 | // for a specific file set fset, the corresponding Position value is
65 | // obtained by calling fset.Position(p).
66 | //
67 | // Pos values can be compared directly with the usual comparison operators:
68 | // If two Pos values p and q are in the same file, comparing p and q is
69 | // equivalent to comparing the respective source file offsets. If p and q
70 | // are in different files, p < q is true if the file implied by p was added
71 | // to the respective file set before the file implied by q.
72 | //
73 | type Pos int
74 |
75 | // The zero value for Pos is NoPos; there is no file and line information
76 | // associated with it, and NoPos().IsValid() is false. NoPos is always
77 | // smaller than any other Pos value. The corresponding Position value
78 | // for NoPos is the zero value for Position.
79 | //
80 | const NoPos Pos = 0
81 |
82 | // IsValid returns true if the position is valid.
83 | func (p Pos) IsValid() bool {
84 | return p != NoPos
85 | }
86 |
87 | // -----------------------------------------------------------------------------
88 | // File
89 |
90 | // A File is a handle for a file belonging to a FileSet.
91 | // A File has a name, size, and line offset table.
92 | //
93 | type File struct {
94 | set *FileSet
95 | name string // file name as provided to AddFile
96 | base int // Pos value range for this file is [base...base+size]
97 | size int // file size as provided to AddFile
98 |
99 | // lines and infos are protected by set.mutex
100 | lines []int
101 | infos []lineInfo
102 | }
103 |
104 | // Name returns the file name of file f as registered with AddFile.
105 | func (f *File) Name() string {
106 | return f.name
107 | }
108 |
109 | // Base returns the base offset of file f as registered with AddFile.
110 | func (f *File) Base() int {
111 | return f.base
112 | }
113 |
114 | // Size returns the size of file f as registered with AddFile.
115 | func (f *File) Size() int {
116 | return f.size
117 | }
118 |
119 | // LineCount returns the number of lines in file f.
120 | func (f *File) LineCount() int {
121 | f.set.mutex.RLock()
122 | n := len(f.lines)
123 | f.set.mutex.RUnlock()
124 | return n
125 | }
126 |
127 | // AddLine adds the line offset for a new line.
128 | // The line offset must be larger than the offset for the previous line
129 | // and smaller than the file size; otherwise the line offset is ignored.
130 | //
131 | func (f *File) AddLine(offset int) {
132 | f.set.mutex.Lock()
133 | if i := len(f.lines); (i == 0 || f.lines[i-1] < offset) && offset < f.size {
134 | f.lines = append(f.lines, offset)
135 | }
136 | f.set.mutex.Unlock()
137 | }
138 |
139 | // SetLines sets the line offsets for a file and returns true if successful.
140 | // The line offsets are the offsets of the first character of each line;
141 | // for instance for the content "ab\nc\n" the line offsets are {0, 3}.
142 | // An empty file has an empty line offset table.
143 | // Each line offset must be larger than the offset for the previous line
144 | // and smaller than the file size; otherwise SetLines fails and returns
145 | // false.
146 | //
147 | func (f *File) SetLines(lines []int) bool {
148 | // verify validity of lines table
149 | size := f.size
150 | for i, offset := range lines {
151 | if i > 0 && offset <= lines[i-1] || size <= offset {
152 | return false
153 | }
154 | }
155 |
156 | // set lines table
157 | f.set.mutex.Lock()
158 | f.lines = lines
159 | f.set.mutex.Unlock()
160 | return true
161 | }
162 |
163 | // SetLinesForContent sets the line offsets for the given file content.
164 | func (f *File) SetLinesForContent(content []byte) {
165 | var lines []int
166 | line := 0
167 | for offset, b := range content {
168 | if line >= 0 {
169 | lines = append(lines, line)
170 | }
171 | line = -1
172 | if b == '\n' {
173 | line = offset + 1
174 | }
175 | }
176 |
177 | // set lines table
178 | f.set.mutex.Lock()
179 | f.lines = lines
180 | f.set.mutex.Unlock()
181 | }
182 |
183 | // A lineInfo object describes alternative file and line number
184 | // information (such as provided via a //line comment in a .go
185 | // file) for a given file offset.
186 | type lineInfo struct {
187 | // fields are exported to make them accessible to gob
188 | Offset int
189 | Filename string
190 | Line int
191 | }
192 |
193 | // AddLineInfo adds alternative file and line number information for
194 | // a given file offset. The offset must be larger than the offset for
195 | // the previously added alternative line info and smaller than the
196 | // file size; otherwise the information is ignored.
197 | //
198 | // AddLineInfo is typically used to register alternative position
199 | // information for //line filename:line comments in source files.
200 | //
201 | func (f *File) AddLineInfo(offset int, filename string, line int) {
202 | f.set.mutex.Lock()
203 | if i := len(f.infos); i == 0 || f.infos[i-1].Offset < offset && offset < f.size {
204 | f.infos = append(f.infos, lineInfo{offset, filename, line})
205 | }
206 | f.set.mutex.Unlock()
207 | }
208 |
209 | // Pos returns the Pos value for the given file offset;
210 | // the offset must be <= f.Size().
211 | // f.Pos(f.Offset(p)) == p.
212 | //
213 | func (f *File) Pos(offset int) Pos {
214 | if offset > f.size {
215 | panic("illegal file offset")
216 | }
217 | return Pos(f.base + offset)
218 | }
219 |
220 | // Offset returns the offset for the given file position p;
221 | // p must be a valid Pos value in that file.
222 | // f.Offset(f.Pos(offset)) == offset.
223 | //
224 | func (f *File) Offset(p Pos) int {
225 | if int(p) < f.base || int(p) > f.base+f.size {
226 | panic("illegal Pos value")
227 | }
228 | return int(p) - f.base
229 | }
230 |
231 | // Line returns the line number for the given file position p;
232 | // p must be a Pos value in that file or NoPos.
233 | //
234 | func (f *File) Line(p Pos) int {
235 | // TODO(gri) this can be implemented much more efficiently
236 | return f.Position(p).Line
237 | }
238 |
239 | func searchLineInfos(a []lineInfo, x int) int {
240 | return sort.Search(len(a), func(i int) bool { return a[i].Offset > x }) - 1
241 | }
242 |
243 | // info returns the file name, line, and column number for a file offset.
244 | func (f *File) info(offset int) (filename string, line, column int) {
245 | filename = f.name
246 | if i := searchInts(f.lines, offset); i >= 0 {
247 | line, column = i+1, offset-f.lines[i]+1
248 | }
249 | if len(f.infos) > 0 {
250 | // almost no files have extra line infos
251 | if i := searchLineInfos(f.infos, offset); i >= 0 {
252 | alt := &f.infos[i]
253 | filename = alt.Filename
254 | if i := searchInts(f.lines, alt.Offset); i >= 0 {
255 | line += alt.Line - i - 1
256 | }
257 | }
258 | }
259 | return
260 | }
261 |
262 | func (f *File) position(p Pos) (pos Position) {
263 | offset := int(p) - f.base
264 | pos.Offset = offset
265 | pos.Filename, pos.Line, pos.Column = f.info(offset)
266 | return
267 | }
268 |
269 | // Position returns the Position value for the given file position p;
270 | // p must be a Pos value in that file or NoPos.
271 | //
272 | func (f *File) Position(p Pos) (pos Position) {
273 | if p != NoPos {
274 | if int(p) < f.base || int(p) > f.base+f.size {
275 | panic("illegal Pos value")
276 | }
277 | pos = f.position(p)
278 | }
279 | return
280 | }
281 |
282 | // -----------------------------------------------------------------------------
283 | // FileSet
284 |
285 | // A FileSet represents a set of source files.
286 | // Methods of file sets are synchronized; multiple goroutines
287 | // may invoke them concurrently.
288 | //
289 | type FileSet struct {
290 | mutex sync.RWMutex // protects the file set
291 | base int // base offset for the next file
292 | files []*File // list of files in the order added to the set
293 | last *File // cache of last file looked up
294 | }
295 |
296 | // NewFileSet creates a new file set.
297 | func NewFileSet() *FileSet {
298 | s := new(FileSet)
299 | s.base = 1 // 0 == NoPos
300 | return s
301 | }
302 |
303 | // Base returns the minimum base offset that must be provided to
304 | // AddFile when adding the next file.
305 | //
306 | func (s *FileSet) Base() int {
307 | s.mutex.RLock()
308 | b := s.base
309 | s.mutex.RUnlock()
310 | return b
311 |
312 | }
313 |
314 | // AddFile adds a new file with a given filename, base offset, and file size
315 | // to the file set s and returns the file. Multiple files may have the same
316 | // name. The base offset must not be smaller than the FileSet's Base(), and
317 | // size must not be negative.
318 | //
319 | // Adding the file will set the file set's Base() value to base + size + 1
320 | // as the minimum base value for the next file. The following relationship
321 | // exists between a Pos value p for a given file offset offs:
322 | //
323 | // int(p) = base + offs
324 | //
325 | // with offs in the range [0, size] and thus p in the range [base, base+size].
326 | // For convenience, File.Pos may be used to create file-specific position
327 | // values from a file offset.
328 | //
329 | func (s *FileSet) AddFile(filename string, base, size int) *File {
330 | s.mutex.Lock()
331 | defer s.mutex.Unlock()
332 | if base < s.base || size < 0 {
333 | panic("illegal base or size")
334 | }
335 | // base >= s.base && size >= 0
336 | f := &File{s, filename, base, size, []int{0}, nil}
337 | base += size + 1 // +1 because EOF also has a position
338 | if base < 0 {
339 | panic("token.Pos offset overflow (> 2G of source code in file set)")
340 | }
341 | // add the file to the file set
342 | s.base = base
343 | s.files = append(s.files, f)
344 | s.last = f
345 | return f
346 | }
347 |
348 | // Iterate calls f for the files in the file set in the order they were added
349 | // until f returns false.
350 | //
351 | func (s *FileSet) Iterate(f func(*File) bool) {
352 | for i := 0; ; i++ {
353 | var file *File
354 | s.mutex.RLock()
355 | if i < len(s.files) {
356 | file = s.files[i]
357 | }
358 | s.mutex.RUnlock()
359 | if file == nil || !f(file) {
360 | break
361 | }
362 | }
363 | }
364 |
365 | func searchFiles(a []*File, x int) int {
366 | return sort.Search(len(a), func(i int) bool { return a[i].base > x }) - 1
367 | }
368 |
369 | func (s *FileSet) file(p Pos) *File {
370 | // common case: p is in last file
371 | if f := s.last; f != nil && f.base <= int(p) && int(p) <= f.base+f.size {
372 | return f
373 | }
374 | // p is not in last file - search all files
375 | if i := searchFiles(s.files, int(p)); i >= 0 {
376 | f := s.files[i]
377 | // f.base <= int(p) by definition of searchFiles
378 | if int(p) <= f.base+f.size {
379 | s.last = f
380 | return f
381 | }
382 | }
383 | return nil
384 | }
385 |
386 | // File returns the file that contains the position p.
387 | // If no such file is found (for instance for p == NoPos),
388 | // the result is nil.
389 | //
390 | func (s *FileSet) File(p Pos) (f *File) {
391 | if p != NoPos {
392 | s.mutex.RLock()
393 | f = s.file(p)
394 | s.mutex.RUnlock()
395 | }
396 | return
397 | }
398 |
399 | // Position converts a Pos in the fileset into a general Position.
400 | func (s *FileSet) Position(p Pos) (pos Position) {
401 | if p != NoPos {
402 | s.mutex.RLock()
403 | if f := s.file(p); f != nil {
404 | pos = f.position(p)
405 | }
406 | s.mutex.RUnlock()
407 | }
408 | return
409 | }
410 |
411 | // -----------------------------------------------------------------------------
412 | // Helper functions
413 |
414 | func searchInts(a []int, x int) int {
415 | // This function body is a manually inlined version of:
416 | //
417 | // return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1
418 | //
419 | // With better compiler optimizations, this may not be needed in the
420 | // future, but at the moment this change improves the go/printer
421 | // benchmark performance by ~30%. This has a direct impact on the
422 | // speed of gofmt and thus seems worthwhile (2011-04-29).
423 | // TODO(gri): Remove this when compilers have caught up.
424 | i, j := 0, len(a)
425 | for i < j {
426 | h := i + (j-i)/2 // avoid overflow when computing h
427 | // i ≤ h < j
428 | if a[h] <= x {
429 | i = h + 1
430 | } else {
431 | j = h
432 | }
433 | }
434 | return i - 1
435 | }
436 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/gcfg.v1/token/serialize.go:
--------------------------------------------------------------------------------
1 | // Copyright 2011 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package token
6 |
7 | type serializedFile struct {
8 | // fields correspond 1:1 to fields with same (lower-case) name in File
9 | Name string
10 | Base int
11 | Size int
12 | Lines []int
13 | Infos []lineInfo
14 | }
15 |
16 | type serializedFileSet struct {
17 | Base int
18 | Files []serializedFile
19 | }
20 |
21 | // Read calls decode to deserialize a file set into s; s must not be nil.
22 | func (s *FileSet) Read(decode func(interface{}) error) error {
23 | var ss serializedFileSet
24 | if err := decode(&ss); err != nil {
25 | return err
26 | }
27 |
28 | s.mutex.Lock()
29 | s.base = ss.Base
30 | files := make([]*File, len(ss.Files))
31 | for i := 0; i < len(ss.Files); i++ {
32 | f := &ss.Files[i]
33 | files[i] = &File{s, f.Name, f.Base, f.Size, f.Lines, f.Infos}
34 | }
35 | s.files = files
36 | s.last = nil
37 | s.mutex.Unlock()
38 |
39 | return nil
40 | }
41 |
42 | // Write calls encode to serialize the file set s.
43 | func (s *FileSet) Write(encode func(interface{}) error) error {
44 | var ss serializedFileSet
45 |
46 | s.mutex.Lock()
47 | ss.Base = s.base
48 | files := make([]serializedFile, len(s.files))
49 | for i, f := range s.files {
50 | files[i] = serializedFile{f.name, f.base, f.size, f.lines, f.infos}
51 | }
52 | ss.Files = files
53 | s.mutex.Unlock()
54 |
55 | return encode(ss)
56 | }
57 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/gcfg.v1/token/token.go:
--------------------------------------------------------------------------------
1 | // Copyright 2009 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Package token defines constants representing the lexical tokens of the gcfg
6 | // configuration syntax and basic operations on tokens (printing, predicates).
7 | //
8 | // Note that the API for the token package may change to accommodate new
9 | // features or implementation changes in gcfg.
10 | //
11 | package token
12 |
13 | import "strconv"
14 |
15 | // Token is the set of lexical tokens of the gcfg configuration syntax.
16 | type Token int
17 |
18 | // The list of tokens.
19 | const (
20 | // Special tokens
21 | ILLEGAL Token = iota
22 | EOF
23 | COMMENT
24 |
25 | literal_beg
26 | // Identifiers and basic type literals
27 | // (these tokens stand for classes of literals)
28 | IDENT // section-name, variable-name
29 | STRING // "subsection-name", variable value
30 | literal_end
31 |
32 | operator_beg
33 | // Operators and delimiters
34 | ASSIGN // =
35 | LBRACK // [
36 | RBRACK // ]
37 | EOL // \n
38 | operator_end
39 | )
40 |
41 | var tokens = [...]string{
42 | ILLEGAL: "ILLEGAL",
43 |
44 | EOF: "EOF",
45 | COMMENT: "COMMENT",
46 |
47 | IDENT: "IDENT",
48 | STRING: "STRING",
49 |
50 | ASSIGN: "=",
51 | LBRACK: "[",
52 | RBRACK: "]",
53 | EOL: "\n",
54 | }
55 |
56 | // String returns the string corresponding to the token tok.
57 | // For operators and delimiters, the string is the actual token character
58 | // sequence (e.g., for the token ASSIGN, the string is "="). For all other
59 | // tokens the string corresponds to the token constant name (e.g. for the
60 | // token IDENT, the string is "IDENT").
61 | //
62 | func (tok Token) String() string {
63 | s := ""
64 | if 0 <= tok && tok < Token(len(tokens)) {
65 | s = tokens[tok]
66 | }
67 | if s == "" {
68 | s = "token(" + strconv.Itoa(int(tok)) + ")"
69 | }
70 | return s
71 | }
72 |
73 | // Predicates
74 |
75 | // IsLiteral returns true for tokens corresponding to identifiers
76 | // and basic type literals; it returns false otherwise.
77 | //
78 | func (tok Token) IsLiteral() bool { return literal_beg < tok && tok < literal_end }
79 |
80 | // IsOperator returns true for tokens corresponding to operators and
81 | // delimiters; it returns false otherwise.
82 | //
83 | func (tok Token) IsOperator() bool { return operator_beg < tok && tok < operator_end }
84 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/gcfg.v1/types/bool.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | // BoolValues defines the name and value mappings for ParseBool.
4 | var BoolValues = map[string]interface{}{
5 | "true": true, "yes": true, "on": true, "1": true,
6 | "false": false, "no": false, "off": false, "0": false,
7 | }
8 |
9 | var boolParser = func() *EnumParser {
10 | ep := &EnumParser{}
11 | ep.AddVals(BoolValues)
12 | return ep
13 | }()
14 |
15 | // ParseBool parses bool values according to the definitions in BoolValues.
16 | // Parsing is case-insensitive.
17 | func ParseBool(s string) (bool, error) {
18 | v, err := boolParser.Parse(s)
19 | if err != nil {
20 | return false, err
21 | }
22 | return v.(bool), nil
23 | }
24 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/gcfg.v1/types/doc.go:
--------------------------------------------------------------------------------
1 | // Package types defines helpers for type conversions.
2 | //
3 | // The API for this package is not finalized yet.
4 | package types
5 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/gcfg.v1/types/enum.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | "strings"
7 | )
8 |
9 | // EnumParser parses "enum" values; i.e. a predefined set of strings to
10 | // predefined values.
11 | type EnumParser struct {
12 | Type string // type name; if not set, use type of first value added
13 | CaseMatch bool // if true, matching of strings is case-sensitive
14 | // PrefixMatch bool
15 | vals map[string]interface{}
16 | }
17 |
18 | // AddVals adds strings and values to an EnumParser.
19 | func (ep *EnumParser) AddVals(vals map[string]interface{}) {
20 | if ep.vals == nil {
21 | ep.vals = make(map[string]interface{})
22 | }
23 | for k, v := range vals {
24 | if ep.Type == "" {
25 | ep.Type = reflect.TypeOf(v).Name()
26 | }
27 | if !ep.CaseMatch {
28 | k = strings.ToLower(k)
29 | }
30 | ep.vals[k] = v
31 | }
32 | }
33 |
34 | // Parse parses the string and returns the value or an error.
35 | func (ep EnumParser) Parse(s string) (interface{}, error) {
36 | if !ep.CaseMatch {
37 | s = strings.ToLower(s)
38 | }
39 | v, ok := ep.vals[s]
40 | if !ok {
41 | return false, fmt.Errorf("failed to parse %s %#q", ep.Type, s)
42 | }
43 | return v, nil
44 | }
45 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/gcfg.v1/types/int.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | // An IntMode is a mode for parsing integer values, representing a set of
9 | // accepted bases.
10 | type IntMode uint8
11 |
12 | // IntMode values for ParseInt; can be combined using binary or.
13 | const (
14 | Dec IntMode = 1 << iota
15 | Hex
16 | Oct
17 | )
18 |
19 | // String returns a string representation of IntMode; e.g. `IntMode(Dec|Hex)`.
20 | func (m IntMode) String() string {
21 | var modes []string
22 | if m&Dec != 0 {
23 | modes = append(modes, "Dec")
24 | }
25 | if m&Hex != 0 {
26 | modes = append(modes, "Hex")
27 | }
28 | if m&Oct != 0 {
29 | modes = append(modes, "Oct")
30 | }
31 | return "IntMode(" + strings.Join(modes, "|") + ")"
32 | }
33 |
34 | var errIntAmbig = fmt.Errorf("ambiguous integer value; must include '0' prefix")
35 |
36 | func prefix0(val string) bool {
37 | return strings.HasPrefix(val, "0") || strings.HasPrefix(val, "-0")
38 | }
39 |
40 | func prefix0x(val string) bool {
41 | return strings.HasPrefix(val, "0x") || strings.HasPrefix(val, "-0x")
42 | }
43 |
44 | // ParseInt parses val using mode into intptr, which must be a pointer to an
45 | // integer kind type. Non-decimal value require prefix `0` or `0x` in the cases
46 | // when mode permits ambiguity of base; otherwise the prefix can be omitted.
47 | func ParseInt(intptr interface{}, val string, mode IntMode) error {
48 | val = strings.TrimSpace(val)
49 | verb := byte(0)
50 | switch mode {
51 | case Dec:
52 | verb = 'd'
53 | case Dec + Hex:
54 | if prefix0x(val) {
55 | verb = 'v'
56 | } else {
57 | verb = 'd'
58 | }
59 | case Dec + Oct:
60 | if prefix0(val) && !prefix0x(val) {
61 | verb = 'v'
62 | } else {
63 | verb = 'd'
64 | }
65 | case Dec + Hex + Oct:
66 | verb = 'v'
67 | case Hex:
68 | if prefix0x(val) {
69 | verb = 'v'
70 | } else {
71 | verb = 'x'
72 | }
73 | case Oct:
74 | verb = 'o'
75 | case Hex + Oct:
76 | if prefix0(val) {
77 | verb = 'v'
78 | } else {
79 | return errIntAmbig
80 | }
81 | }
82 | if verb == 0 {
83 | panic("unsupported mode")
84 | }
85 | return ScanFully(intptr, val, verb)
86 | }
87 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/gcfg.v1/types/scan.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "reflect"
7 | )
8 |
9 | // ScanFully uses fmt.Sscanf with verb to fully scan val into ptr.
10 | func ScanFully(ptr interface{}, val string, verb byte) error {
11 | t := reflect.ValueOf(ptr).Elem().Type()
12 | // attempt to read extra bytes to make sure the value is consumed
13 | var b []byte
14 | n, err := fmt.Sscanf(val, "%"+string(verb)+"%s", ptr, &b)
15 | switch {
16 | case n < 1 || n == 1 && err != io.EOF:
17 | return fmt.Errorf("failed to parse %q as %v: %v", val, t, err)
18 | case n > 1:
19 | return fmt.Errorf("failed to parse %q as %v: extra characters %q", val, t, string(b))
20 | }
21 | // n == 1 && err == io.EOF
22 | return nil
23 | }
24 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/warnings.v0/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016 Péter Surányi.
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions are
5 | met:
6 |
7 | * Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 |
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/warnings.v0/README:
--------------------------------------------------------------------------------
1 | Package warnings implements error handling with non-fatal errors (warnings).
2 |
3 | import path: "gopkg.in/warnings.v0"
4 | package docs: https://godoc.org/gopkg.in/warnings.v0
5 | issues: https://github.com/go-warnings/warnings/issues
6 | pull requests: https://github.com/go-warnings/warnings/pulls
7 |
8 | A recurring pattern in Go programming is the following:
9 |
10 | func myfunc(params) error {
11 | if err := doSomething(...); err != nil {
12 | return err
13 | }
14 | if err := doSomethingElse(...); err != nil {
15 | return err
16 | }
17 | if ok := doAnotherThing(...); !ok {
18 | return errors.New("my error")
19 | }
20 | ...
21 | return nil
22 | }
23 |
24 | This pattern allows interrupting the flow on any received error. But what if
25 | there are errors that should be noted but still not fatal, for which the flow
26 | should not be interrupted? Implementing such logic at each if statement would
27 | make the code complex and the flow much harder to follow.
28 |
29 | Package warnings provides the Collector type and a clean and simple pattern
30 | for achieving such logic. The Collector takes care of deciding when to break
31 | the flow and when to continue, collecting any non-fatal errors (warnings)
32 | along the way. The only requirement is that fatal and non-fatal errors can be
33 | distinguished programmatically; that is a function such as
34 |
35 | IsFatal(error) bool
36 |
37 | must be implemented. The following is an example of what the above snippet
38 | could look like using the warnings package:
39 |
40 | import "gopkg.in/warnings.v0"
41 |
42 | func isFatal(err error) bool {
43 | _, ok := err.(WarningType)
44 | return !ok
45 | }
46 |
47 | func myfunc(params) error {
48 | c := warnings.NewCollector(isFatal)
49 | c.FatalWithWarnings = true
50 | if err := c.Collect(doSomething()); err != nil {
51 | return err
52 | }
53 | if err := c.Collect(doSomethingElse(...)); err != nil {
54 | return err
55 | }
56 | if ok := doAnotherThing(...); !ok {
57 | if err := c.Collect(errors.New("my error")); err != nil {
58 | return err
59 | }
60 | }
61 | ...
62 | return c.Done()
63 | }
64 |
65 | Rules for using warnings
66 |
67 | - ensure that warnings are programmatically distinguishable from fatal
68 | errors (i.e. implement an isFatal function and any necessary error types)
69 | - ensure that there is a single Collector instance for a call of each
70 | exported function
71 | - ensure that all errors (fatal or warning) are fed through Collect
72 | - ensure that every time an error is returned, it is one returned by a
73 | Collector (from Collect or Done)
74 | - ensure that Collect is never called after Done
75 |
--------------------------------------------------------------------------------
/vendor/gopkg.in/warnings.v0/warnings.go:
--------------------------------------------------------------------------------
1 | // Package warnings implements error handling with non-fatal errors (warnings).
2 | //
3 | // A recurring pattern in Go programming is the following:
4 | //
5 | // func myfunc(params) error {
6 | // if err := doSomething(...); err != nil {
7 | // return err
8 | // }
9 | // if err := doSomethingElse(...); err != nil {
10 | // return err
11 | // }
12 | // if ok := doAnotherThing(...); !ok {
13 | // return errors.New("my error")
14 | // }
15 | // ...
16 | // return nil
17 | // }
18 | //
19 | // This pattern allows interrupting the flow on any received error. But what if
20 | // there are errors that should be noted but still not fatal, for which the flow
21 | // should not be interrupted? Implementing such logic at each if statement would
22 | // make the code complex and the flow much harder to follow.
23 | //
24 | // Package warnings provides the Collector type and a clean and simple pattern
25 | // for achieving such logic. The Collector takes care of deciding when to break
26 | // the flow and when to continue, collecting any non-fatal errors (warnings)
27 | // along the way. The only requirement is that fatal and non-fatal errors can be
28 | // distinguished programmatically; that is a function such as
29 | //
30 | // IsFatal(error) bool
31 | //
32 | // must be implemented. The following is an example of what the above snippet
33 | // could look like using the warnings package:
34 | //
35 | // import "gopkg.in/warnings.v0"
36 | //
37 | // func isFatal(err error) bool {
38 | // _, ok := err.(WarningType)
39 | // return !ok
40 | // }
41 | //
42 | // func myfunc(params) error {
43 | // c := warnings.NewCollector(isFatal)
44 | // c.FatalWithWarnings = true
45 | // if err := c.Collect(doSomething()); err != nil {
46 | // return err
47 | // }
48 | // if err := c.Collect(doSomethingElse(...)); err != nil {
49 | // return err
50 | // }
51 | // if ok := doAnotherThing(...); !ok {
52 | // if err := c.Collect(errors.New("my error")); err != nil {
53 | // return err
54 | // }
55 | // }
56 | // ...
57 | // return c.Done()
58 | // }
59 | //
60 | // Rules for using warnings
61 | //
62 | // - ensure that warnings are programmatically distinguishable from fatal
63 | // errors (i.e. implement an isFatal function and any necessary error types)
64 | // - ensure that there is a single Collector instance for a call of each
65 | // exported function
66 | // - ensure that all errors (fatal or warning) are fed through Collect
67 | // - ensure that every time an error is returned, it is one returned by a
68 | // Collector (from Collect or Done)
69 | // - ensure that Collect is never called after Done
70 | //
71 | // TODO
72 | //
73 | // - optionally limit the number of warnings (e.g. stop after 20 warnings) (?)
74 | // - consider interaction with contexts
75 | // - go vet-style invocations verifier
76 | // - semi-automatic code converter
77 | //
78 | package warnings // import "gopkg.in/warnings.v0"
79 |
80 | import (
81 | "bytes"
82 | "fmt"
83 | )
84 |
85 | // List holds a collection of warnings and optionally one fatal error.
86 | type List struct {
87 | Warnings []error
88 | Fatal error
89 | }
90 |
91 | // Error implements the error interface.
92 | func (l List) Error() string {
93 | b := bytes.NewBuffer(nil)
94 | if l.Fatal != nil {
95 | fmt.Fprintln(b, "fatal:")
96 | fmt.Fprintln(b, l.Fatal)
97 | }
98 | switch len(l.Warnings) {
99 | case 0:
100 | // nop
101 | case 1:
102 | fmt.Fprintln(b, "warning:")
103 | default:
104 | fmt.Fprintln(b, "warnings:")
105 | }
106 | for _, err := range l.Warnings {
107 | fmt.Fprintln(b, err)
108 | }
109 | return b.String()
110 | }
111 |
112 | // A Collector collects errors up to the first fatal error.
113 | type Collector struct {
114 | // IsFatal distinguishes between warnings and fatal errors.
115 | IsFatal func(error) bool
116 | // FatalWithWarnings set to true means that a fatal error is returned as
117 | // a List together with all warnings so far. The default behavior is to
118 | // only return the fatal error and discard any warnings that have been
119 | // collected.
120 | FatalWithWarnings bool
121 |
122 | l List
123 | done bool
124 | }
125 |
126 | // NewCollector returns a new Collector; it uses isFatal to distinguish between
127 | // warnings and fatal errors.
128 | func NewCollector(isFatal func(error) bool) *Collector {
129 | return &Collector{IsFatal: isFatal}
130 | }
131 |
132 | // Collect collects a single error (warning or fatal). It returns nil if
133 | // collection can continue (only warnings so far), or otherwise the errors
134 | // collected. Collect mustn't be called after the first fatal error or after
135 | // Done has been called.
136 | func (c *Collector) Collect(err error) error {
137 | if c.done {
138 | panic("warnings.Collector already done")
139 | }
140 | if err == nil {
141 | return nil
142 | }
143 | if c.IsFatal(err) {
144 | c.done = true
145 | c.l.Fatal = err
146 | } else {
147 | c.l.Warnings = append(c.l.Warnings, err)
148 | }
149 | if c.l.Fatal != nil {
150 | return c.erorr()
151 | }
152 | return nil
153 | }
154 |
155 | // Done ends collection and returns the collected error(s).
156 | func (c *Collector) Done() error {
157 | c.done = true
158 | return c.erorr()
159 | }
160 |
161 | func (c *Collector) erorr() error {
162 | if !c.FatalWithWarnings && c.l.Fatal != nil {
163 | return c.l.Fatal
164 | }
165 | if c.l.Fatal == nil && len(c.l.Warnings) == 0 {
166 | return nil
167 | }
168 | // Note that a single warning is also returned as a List. This is to make it
169 | // easier to determine fatal-ness of the returned error.
170 | return c.l
171 | }
172 |
173 | // FatalOnly returns the fatal error, if any, **in an error returned by a
174 | // Collector**. It returns nil if and only if err is nil or err is a List
175 | // with err.Fatal == nil.
176 | func FatalOnly(err error) error {
177 | l, ok := err.(List)
178 | if !ok {
179 | return err
180 | }
181 | return l.Fatal
182 | }
183 |
184 | // WarningsOnly returns the warnings **in an error returned by a Collector**.
185 | func WarningsOnly(err error) []error {
186 | l, ok := err.(List)
187 | if !ok {
188 | return nil
189 | }
190 | return l.Warnings
191 | }
192 |
--------------------------------------------------------------------------------
/vendor/modules.txt:
--------------------------------------------------------------------------------
1 | # gopkg.in/gcfg.v1 v1.2.0
2 | ## explicit
3 | gopkg.in/gcfg.v1
4 | gopkg.in/gcfg.v1/scanner
5 | gopkg.in/gcfg.v1/token
6 | gopkg.in/gcfg.v1/types
7 | # gopkg.in/warnings.v0 v0.1.1
8 | ## explicit
9 | gopkg.in/warnings.v0
10 |
--------------------------------------------------------------------------------