├── 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 | --------------------------------------------------------------------------------