From 7ed961f871f4a4be6b681f6fa1d29212560e443d Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 31 Jul 2015 01:41:25 -0700 Subject: [PATCH] more dependencies --- vendor/manifest | 24 +- vendor/src/github.com/Xe/uuid/CONTRIBUTORS | 2 + vendor/src/github.com/Xe/uuid/LICENSE | 27 ++ vendor/src/github.com/Xe/uuid/README.md | 5 + vendor/src/github.com/Xe/uuid/dce.go | 84 ++++ vendor/src/github.com/Xe/uuid/doc.go | 8 + vendor/src/github.com/Xe/uuid/hash.go | 53 +++ vendor/src/github.com/Xe/uuid/node.go | 101 ++++ vendor/src/github.com/Xe/uuid/time.go | 132 ++++++ vendor/src/github.com/Xe/uuid/util.go | 43 ++ vendor/src/github.com/Xe/uuid/uuid.go | 163 +++++++ vendor/src/github.com/Xe/uuid/uuid_test.go | 390 ++++++++++++++++ vendor/src/github.com/Xe/uuid/version1.go | 41 ++ vendor/src/github.com/Xe/uuid/version4.go | 25 + .../src/github.com/scalingdata/gcfg/LICENSE | 57 +++ vendor/src/github.com/scalingdata/gcfg/README | 7 + vendor/src/github.com/scalingdata/gcfg/doc.go | 118 +++++ .../scalingdata/gcfg/example_test.go | 132 ++++++ .../src/github.com/scalingdata/gcfg/go1_0.go | 7 + .../src/github.com/scalingdata/gcfg/go1_2.go | 9 + .../scalingdata/gcfg/issues_test.go | 63 +++ .../src/github.com/scalingdata/gcfg/read.go | 181 ++++++++ .../github.com/scalingdata/gcfg/read_test.go | 333 ++++++++++++++ .../scalingdata/gcfg/scanner/errors.go | 121 +++++ .../scalingdata/gcfg/scanner/example_test.go | 46 ++ .../scalingdata/gcfg/scanner/scanner.go | 342 ++++++++++++++ .../scalingdata/gcfg/scanner/scanner_test.go | 417 +++++++++++++++++ vendor/src/github.com/scalingdata/gcfg/set.go | 281 +++++++++++ .../scalingdata/gcfg/testdata/gcfg_test.gcfg | 3 + .../gcfg/testdata/gcfg_unicode_test.gcfg | 3 + .../scalingdata/gcfg/token/position.go | 435 ++++++++++++++++++ .../scalingdata/gcfg/token/position_test.go | 181 ++++++++ .../scalingdata/gcfg/token/serialize.go | 56 +++ .../scalingdata/gcfg/token/serialize_test.go | 111 +++++ .../scalingdata/gcfg/token/token.go | 83 ++++ .../github.com/scalingdata/gcfg/types/bool.go | 23 + .../github.com/scalingdata/gcfg/types/doc.go | 4 + .../github.com/scalingdata/gcfg/types/enum.go | 44 ++ .../scalingdata/gcfg/types/enum_test.go | 29 ++ .../github.com/scalingdata/gcfg/types/int.go | 86 ++++ .../scalingdata/gcfg/types/int_test.go | 67 +++ .../github.com/scalingdata/gcfg/types/scan.go | 23 + .../scalingdata/gcfg/types/scan_test.go | 36 ++ 43 files changed, 4390 insertions(+), 6 deletions(-) create mode 100644 vendor/src/github.com/Xe/uuid/CONTRIBUTORS create mode 100644 vendor/src/github.com/Xe/uuid/LICENSE create mode 100644 vendor/src/github.com/Xe/uuid/README.md create mode 100644 vendor/src/github.com/Xe/uuid/dce.go create mode 100644 vendor/src/github.com/Xe/uuid/doc.go create mode 100644 vendor/src/github.com/Xe/uuid/hash.go create mode 100644 vendor/src/github.com/Xe/uuid/node.go create mode 100644 vendor/src/github.com/Xe/uuid/time.go create mode 100644 vendor/src/github.com/Xe/uuid/util.go create mode 100644 vendor/src/github.com/Xe/uuid/uuid.go create mode 100644 vendor/src/github.com/Xe/uuid/uuid_test.go create mode 100644 vendor/src/github.com/Xe/uuid/version1.go create mode 100644 vendor/src/github.com/Xe/uuid/version4.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/LICENSE create mode 100644 vendor/src/github.com/scalingdata/gcfg/README create mode 100644 vendor/src/github.com/scalingdata/gcfg/doc.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/example_test.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/go1_0.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/go1_2.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/issues_test.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/read.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/read_test.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/scanner/errors.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/scanner/example_test.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/scanner/scanner.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/scanner/scanner_test.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/set.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/testdata/gcfg_test.gcfg create mode 100644 vendor/src/github.com/scalingdata/gcfg/testdata/gcfg_unicode_test.gcfg create mode 100644 vendor/src/github.com/scalingdata/gcfg/token/position.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/token/position_test.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/token/serialize.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/token/serialize_test.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/token/token.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/types/bool.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/types/doc.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/types/enum.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/types/enum_test.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/types/int.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/types/int_test.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/types/scan.go create mode 100644 vendor/src/github.com/scalingdata/gcfg/types/scan_test.go diff --git a/vendor/manifest b/vendor/manifest index 064f43f..d67663b 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -7,6 +7,12 @@ "revision": "7d23200fbed9e7f3be4ac76b4f7f6bd19cc4aba0", "branch": "master" }, + { + "importpath": "github.com/Xe/uuid", + "repository": "https://github.com/Xe/uuid", + "revision": "62b230097e9c9534ca2074782b25d738c4b68964", + "branch": "master" + }, { "importpath": "github.com/codegangsta/negroni", "repository": "https://github.com/codegangsta/negroni", @@ -61,6 +67,12 @@ "revision": "0dad96c0b94f8dee039aa40467f767467392a0af", "branch": "master" }, + { + "importpath": "github.com/scalingdata/gcfg", + "repository": "https://github.com/scalingdata/gcfg", + "revision": "37aabad69cfd3d20b8390d902a8b10e245c615ff", + "branch": "master" + }, { "importpath": "github.com/sebest/xff", "repository": "https://github.com/sebest/xff", @@ -79,6 +91,12 @@ "revision": "aa61028b1d32873eaa3e261a3ef0e892a153107b", "branch": "v1" }, + { + "importpath": "github.com/yosssi/ace", + "repository": "https://github.com/yosssi/ace", + "revision": "78e48a2f0ac5fb5a642585f96b03a5f47f7775f5", + "branch": "master" + }, { "importpath": "github.com/yosssi/ace-proxy", "repository": "https://github.com/yosssi/ace-proxy", @@ -98,12 +116,6 @@ "revision": "5ec5e003b21ac1f06e175898413ada23a6797fc0", "branch": "master", "path": "/tiff" - }, - { - "importpath": "github.com/yosssi/ace", - "repository": "https://github.com/yosssi/ace", - "revision": "78e48a2f0ac5fb5a642585f96b03a5f47f7775f5", - "branch": "master" } ] } \ No newline at end of file diff --git a/vendor/src/github.com/Xe/uuid/CONTRIBUTORS b/vendor/src/github.com/Xe/uuid/CONTRIBUTORS new file mode 100644 index 0000000..2703c03 --- /dev/null +++ b/vendor/src/github.com/Xe/uuid/CONTRIBUTORS @@ -0,0 +1,2 @@ +Paul Borman +Christine Dodrill diff --git a/vendor/src/github.com/Xe/uuid/LICENSE b/vendor/src/github.com/Xe/uuid/LICENSE new file mode 100644 index 0000000..ab6b011 --- /dev/null +++ b/vendor/src/github.com/Xe/uuid/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/src/github.com/Xe/uuid/README.md b/vendor/src/github.com/Xe/uuid/README.md new file mode 100644 index 0000000..ccefbf1 --- /dev/null +++ b/vendor/src/github.com/Xe/uuid/README.md @@ -0,0 +1,5 @@ +go-uuid +======= + +code.google.com is going away and I use this library a lot. It used to live at +https://code.google.com/p/go-uuid/ but now I take care of it. diff --git a/vendor/src/github.com/Xe/uuid/dce.go b/vendor/src/github.com/Xe/uuid/dce.go new file mode 100644 index 0000000..50a0f2d --- /dev/null +++ b/vendor/src/github.com/Xe/uuid/dce.go @@ -0,0 +1,84 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "fmt" + "os" +) + +// A Domain represents a Version 2 domain +type Domain byte + +// Domain constants for DCE Security (Version 2) UUIDs. +const ( + Person = Domain(0) + Group = Domain(1) + Org = Domain(2) +) + +// NewDCESecurity returns a DCE Security (Version 2) UUID. +// +// The domain should be one of Person, Group or Org. +// On a POSIX system the id should be the users UID for the Person +// domain and the users GID for the Group. The meaning of id for +// the domain Org or on non-POSIX systems is site defined. +// +// For a given domain/id pair the same token may be returned for up to +// 7 minutes and 10 seconds. +func NewDCESecurity(domain Domain, id uint32) UUID { + uuid := NewUUID() + if uuid != nil { + uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2 + uuid[9] = byte(domain) + binary.BigEndian.PutUint32(uuid[0:], id) + } + return uuid +} + +// NewDCEPerson returns a DCE Security (Version 2) UUID in the person +// domain with the id returned by os.Getuid. +// +// NewDCEPerson(Person, uint32(os.Getuid())) +func NewDCEPerson() UUID { + return NewDCESecurity(Person, uint32(os.Getuid())) +} + +// NewDCEGroup returns a DCE Security (Version 2) UUID in the group +// domain with the id returned by os.Getgid. +// +// NewDCEGroup(Group, uint32(os.Getgid())) +func NewDCEGroup() UUID { + return NewDCESecurity(Group, uint32(os.Getgid())) +} + +// Domain returns the domain for a Version 2 UUID or false. +func (uuid UUID) Domain() (Domain, bool) { + if v, _ := uuid.Version(); v != 2 { + return 0, false + } + return Domain(uuid[9]), true +} + +// Id returns the id for a Version 2 UUID or false. +func (uuid UUID) Id() (uint32, bool) { + if v, _ := uuid.Version(); v != 2 { + return 0, false + } + return binary.BigEndian.Uint32(uuid[0:4]), true +} + +func (d Domain) String() string { + switch d { + case Person: + return "Person" + case Group: + return "Group" + case Org: + return "Org" + } + return fmt.Sprintf("Domain%d", int(d)) +} diff --git a/vendor/src/github.com/Xe/uuid/doc.go b/vendor/src/github.com/Xe/uuid/doc.go new file mode 100644 index 0000000..d8bd013 --- /dev/null +++ b/vendor/src/github.com/Xe/uuid/doc.go @@ -0,0 +1,8 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The uuid package generates and inspects UUIDs. +// +// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security Services. +package uuid diff --git a/vendor/src/github.com/Xe/uuid/hash.go b/vendor/src/github.com/Xe/uuid/hash.go new file mode 100644 index 0000000..cdd4192 --- /dev/null +++ b/vendor/src/github.com/Xe/uuid/hash.go @@ -0,0 +1,53 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "crypto/md5" + "crypto/sha1" + "hash" +) + +// Well known Name Space IDs and UUIDs +var ( + NameSpace_DNS = Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + NameSpace_URL = Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8") + NameSpace_OID = Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8") + NameSpace_X500 = Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8") + NIL = Parse("00000000-0000-0000-0000-000000000000") +) + +// NewHash returns a new UUID dervied from the hash of space concatenated with +// data generated by h. The hash should be at least 16 byte in length. The +// first 16 bytes of the hash are used to form the UUID. The version of the +// UUID will be the lower 4 bits of version. NewHash is used to implement +// NewMD5 and NewSHA1. +func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { + h.Reset() + h.Write(space) + h.Write([]byte(data)) + s := h.Sum(nil) + uuid := make([]byte, 16) + copy(uuid, s) + uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4) + uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant + return uuid +} + +// NewMD5 returns a new MD5 (Version 3) UUID based on the +// supplied name space and data. +// +// NewHash(md5.New(), space, data, 3) +func NewMD5(space UUID, data []byte) UUID { + return NewHash(md5.New(), space, data, 3) +} + +// NewSHA1 returns a new SHA1 (Version 5) UUID based on the +// supplied name space and data. +// +// NewHash(sha1.New(), space, data, 5) +func NewSHA1(space UUID, data []byte) UUID { + return NewHash(sha1.New(), space, data, 5) +} diff --git a/vendor/src/github.com/Xe/uuid/node.go b/vendor/src/github.com/Xe/uuid/node.go new file mode 100644 index 0000000..dd0a8ac --- /dev/null +++ b/vendor/src/github.com/Xe/uuid/node.go @@ -0,0 +1,101 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "net" + +var ( + interfaces []net.Interface // cached list of interfaces + ifname string // name of interface being used + nodeID []byte // hardware for version 1 UUIDs +) + +// NodeInterface returns the name of the interface from which the NodeID was +// derived. The interface "user" is returned if the NodeID was set by +// SetNodeID. +func NodeInterface() string { + return ifname +} + +// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs. +// If name is "" then the first usable interface found will be used or a random +// Node ID will be generated. If a named interface cannot be found then false +// is returned. +// +// SetNodeInterface never fails when name is "". +func SetNodeInterface(name string) bool { + if interfaces == nil { + var err error + interfaces, err = net.Interfaces() + if err != nil && name != "" { + return false + } + } + + for _, ifs := range interfaces { + if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { + if setNodeID(ifs.HardwareAddr) { + ifname = ifs.Name + return true + } + } + } + + // We found no interfaces with a valid hardware address. If name + // does not specify a specific interface generate a random Node ID + // (section 4.1.6) + if name == "" { + if nodeID == nil { + nodeID = make([]byte, 6) + } + randomBits(nodeID) + return true + } + return false +} + +// NodeID returns a slice of a copy of the current Node ID, setting the Node ID +// if not already set. +func NodeID() []byte { + if nodeID == nil { + SetNodeInterface("") + } + nid := make([]byte, 6) + copy(nid, nodeID) + return nid +} + +// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes +// of id are used. If id is less than 6 bytes then false is returned and the +// Node ID is not set. +func SetNodeID(id []byte) bool { + if setNodeID(id) { + ifname = "user" + return true + } + return false +} + +func setNodeID(id []byte) bool { + if len(id) < 6 { + return false + } + if nodeID == nil { + nodeID = make([]byte, 6) + } + copy(nodeID, id) + return true +} + +// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is +// not valid. The NodeID is only well defined for version 1 and 2 UUIDs. +func (uuid UUID) NodeID() []byte { + if len(uuid) != 16 { + return nil + } + node := make([]byte, 6) + copy(node, uuid[10:]) + return node +} diff --git a/vendor/src/github.com/Xe/uuid/time.go b/vendor/src/github.com/Xe/uuid/time.go new file mode 100644 index 0000000..b9369c2 --- /dev/null +++ b/vendor/src/github.com/Xe/uuid/time.go @@ -0,0 +1,132 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "sync" + "time" +) + +// A Time represents a time as the number of 100's of nanoseconds since 15 Oct +// 1582. +type Time int64 + +const ( + lillian = 2299160 // Julian day of 15 Oct 1582 + unix = 2440587 // Julian day of 1 Jan 1970 + epoch = unix - lillian // Days between epochs + g1582 = epoch * 86400 // seconds between epochs + g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs +) + +var ( + mu sync.Mutex + lasttime uint64 // last time we returned + clock_seq uint16 // clock sequence for this run + + timeNow = time.Now // for testing +) + +// UnixTime converts t the number of seconds and nanoseconds using the Unix +// epoch of 1 Jan 1970. +func (t Time) UnixTime() (sec, nsec int64) { + sec = int64(t - g1582ns100) + nsec = (sec % 10000000) * 100 + sec /= 10000000 + return sec, nsec +} + +// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and +// adjusts the clock sequence as needed. An error is returned if the current +// time cannot be determined. +func GetTime() (Time, error) { + defer mu.Unlock() + mu.Lock() + return getTime() +} + +func getTime() (Time, error) { + t := timeNow() + + // If we don't have a clock sequence already, set one. + if clock_seq == 0 { + setClockSequence(-1) + } + now := uint64(t.UnixNano()/100) + g1582ns100 + + // If time has gone backwards with this clock sequence then we + // increment the clock sequence + if now <= lasttime { + clock_seq = ((clock_seq + 1) & 0x3fff) | 0x8000 + } + lasttime = now + return Time(now), nil +} + +// ClockSequence returns the current clock sequence, generating one if not +// already set. The clock sequence is only used for Version 1 UUIDs. +// +// The uuid package does not use global static storage for the clock sequence or +// the last time a UUID was generated. Unless SetClockSequence a new random +// clock sequence is generated the first time a clock sequence is requested by +// ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) sequence is generated +// for +func ClockSequence() int { + defer mu.Unlock() + mu.Lock() + return clockSequence() +} + +func clockSequence() int { + if clock_seq == 0 { + setClockSequence(-1) + } + return int(clock_seq & 0x3fff) +} + +// SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to +// -1 causes a new sequence to be generated. +func SetClockSequence(seq int) { + defer mu.Unlock() + mu.Lock() + setClockSequence(seq) +} + +func setClockSequence(seq int) { + if seq == -1 { + var b [2]byte + randomBits(b[:]) // clock sequence + seq = int(b[0])<<8 | int(b[1]) + } + old_seq := clock_seq + clock_seq = uint16(seq&0x3fff) | 0x8000 // Set our variant + if old_seq != clock_seq { + lasttime = 0 + } +} + +// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in +// uuid. It returns false if uuid is not valid. The time is only well defined +// for version 1 and 2 UUIDs. +func (uuid UUID) Time() (Time, bool) { + if len(uuid) != 16 { + return 0, false + } + time := int64(binary.BigEndian.Uint32(uuid[0:4])) + time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 + time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 + return Time(time), true +} + +// ClockSequence returns the clock sequence encoded in uuid. It returns false +// if uuid is not valid. The clock sequence is only well defined for version 1 +// and 2 UUIDs. +func (uuid UUID) ClockSequence() (int, bool) { + if len(uuid) != 16 { + return 0, false + } + return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff, true +} diff --git a/vendor/src/github.com/Xe/uuid/util.go b/vendor/src/github.com/Xe/uuid/util.go new file mode 100644 index 0000000..de40b10 --- /dev/null +++ b/vendor/src/github.com/Xe/uuid/util.go @@ -0,0 +1,43 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "io" +) + +// randomBits completely fills slice b with random data. +func randomBits(b []byte) { + if _, err := io.ReadFull(rander, b); err != nil { + panic(err.Error()) // rand should never fail + } +} + +// xvalues returns the value of a byte as a hexadecimal digit or 255. +var xvalues = []byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +} + +// xtob converts the the first two hex bytes of x into a byte. +func xtob(x string) (byte, bool) { + b1 := xvalues[x[0]] + b2 := xvalues[x[1]] + return (b1 << 4) | b2, b1 != 255 && b2 != 255 +} diff --git a/vendor/src/github.com/Xe/uuid/uuid.go b/vendor/src/github.com/Xe/uuid/uuid.go new file mode 100644 index 0000000..2920fae --- /dev/null +++ b/vendor/src/github.com/Xe/uuid/uuid.go @@ -0,0 +1,163 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "crypto/rand" + "fmt" + "io" + "strings" +) + +// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC +// 4122. +type UUID []byte + +// A Version represents a UUIDs version. +type Version byte + +// A Variant represents a UUIDs variant. +type Variant byte + +// Constants returned by Variant. +const ( + Invalid = Variant(iota) // Invalid UUID + RFC4122 // The variant specified in RFC4122 + Reserved // Reserved, NCS backward compatibility. + Microsoft // Reserved, Microsoft Corporation backward compatibility. + Future // Reserved for future definition. +) + +var rander = rand.Reader // random function + +// New returns a new random (version 4) UUID as a string. It is a convenience +// function for NewRandom().String(). +func New() string { + return NewRandom().String() +} + +// Parse decodes s into a UUID or returns nil. Both the UUID form of +// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded. +func Parse(s string) UUID { + if len(s) == 36+9 { + if strings.ToLower(s[:9]) != "urn:uuid:" { + return nil + } + s = s[9:] + } else if len(s) != 36 { + return nil + } + if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { + return nil + } + uuid := make([]byte, 16) + for i, x := range []int{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34} { + if v, ok := xtob(s[x:]); !ok { + return nil + } else { + uuid[i] = v + } + } + return uuid +} + +// Equal returns true if uuid1 and uuid2 are equal. +func Equal(uuid1, uuid2 UUID) bool { + return bytes.Equal(uuid1, uuid2) +} + +// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +// , or "" if uuid is invalid. +func (uuid UUID) String() string { + if uuid == nil || len(uuid) != 16 { + return "" + } + b := []byte(uuid) + return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x", + b[:4], b[4:6], b[6:8], b[8:10], b[10:]) +} + +// URN returns the RFC 2141 URN form of uuid, +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid. +func (uuid UUID) URN() string { + if uuid == nil || len(uuid) != 16 { + return "" + } + b := []byte(uuid) + return fmt.Sprintf("urn:uuid:%08x-%04x-%04x-%04x-%012x", + b[:4], b[4:6], b[6:8], b[8:10], b[10:]) +} + +// Variant returns the variant encoded in uuid. It returns Invalid if +// uuid is invalid. +func (uuid UUID) Variant() Variant { + if len(uuid) != 16 { + return Invalid + } + switch { + case (uuid[8] & 0xc0) == 0x80: + return RFC4122 + case (uuid[8] & 0xe0) == 0xc0: + return Microsoft + case (uuid[8] & 0xe0) == 0xe0: + return Future + default: + return Reserved + } + panic("unreachable") +} + +// Version returns the verison of uuid. It returns false if uuid is not +// valid. +func (uuid UUID) Version() (Version, bool) { + if len(uuid) != 16 { + return 0, false + } + return Version(uuid[6] >> 4), true +} + +func (v Version) String() string { + if v > 15 { + return fmt.Sprintf("BAD_VERSION_%d", v) + } + return fmt.Sprintf("VERSION_%d", v) +} + +func (v Variant) String() string { + switch v { + case RFC4122: + return "RFC4122" + case Reserved: + return "Reserved" + case Microsoft: + return "Microsoft" + case Future: + return "Future" + case Invalid: + return "Invalid" + } + return fmt.Sprintf("BadVariant%d", int(v)) +} + +// SetRand sets the random number generator to r, which implents io.Reader. +// If r.Read returns an error when the package requests random data then +// a panic will be issued. +// +// Calling SetRand with nil sets the random number generator to the default +// generator. +func SetRand(r io.Reader) { + if r == nil { + rander = rand.Reader + return + } + rander = r +} diff --git a/vendor/src/github.com/Xe/uuid/uuid_test.go b/vendor/src/github.com/Xe/uuid/uuid_test.go new file mode 100644 index 0000000..417ebeb --- /dev/null +++ b/vendor/src/github.com/Xe/uuid/uuid_test.go @@ -0,0 +1,390 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "fmt" + "os" + "strings" + "testing" + "time" +) + +type test struct { + in string + version Version + variant Variant + isuuid bool +} + +var tests = []test{ + {"f47ac10b-58cc-0372-8567-0e02b2c3d479", 0, RFC4122, true}, + {"f47ac10b-58cc-1372-8567-0e02b2c3d479", 1, RFC4122, true}, + {"f47ac10b-58cc-2372-8567-0e02b2c3d479", 2, RFC4122, true}, + {"f47ac10b-58cc-3372-8567-0e02b2c3d479", 3, RFC4122, true}, + {"f47ac10b-58cc-4372-8567-0e02b2c3d479", 4, RFC4122, true}, + {"f47ac10b-58cc-5372-8567-0e02b2c3d479", 5, RFC4122, true}, + {"f47ac10b-58cc-6372-8567-0e02b2c3d479", 6, RFC4122, true}, + {"f47ac10b-58cc-7372-8567-0e02b2c3d479", 7, RFC4122, true}, + {"f47ac10b-58cc-8372-8567-0e02b2c3d479", 8, RFC4122, true}, + {"f47ac10b-58cc-9372-8567-0e02b2c3d479", 9, RFC4122, true}, + {"f47ac10b-58cc-a372-8567-0e02b2c3d479", 10, RFC4122, true}, + {"f47ac10b-58cc-b372-8567-0e02b2c3d479", 11, RFC4122, true}, + {"f47ac10b-58cc-c372-8567-0e02b2c3d479", 12, RFC4122, true}, + {"f47ac10b-58cc-d372-8567-0e02b2c3d479", 13, RFC4122, true}, + {"f47ac10b-58cc-e372-8567-0e02b2c3d479", 14, RFC4122, true}, + {"f47ac10b-58cc-f372-8567-0e02b2c3d479", 15, RFC4122, true}, + + {"urn:uuid:f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true}, + {"URN:UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-1567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-2567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-3567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-4567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-5567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-6567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-7567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-8567-0e02b2c3d479", 4, RFC4122, true}, + {"f47ac10b-58cc-4372-9567-0e02b2c3d479", 4, RFC4122, true}, + {"f47ac10b-58cc-4372-a567-0e02b2c3d479", 4, RFC4122, true}, + {"f47ac10b-58cc-4372-b567-0e02b2c3d479", 4, RFC4122, true}, + {"f47ac10b-58cc-4372-c567-0e02b2c3d479", 4, Microsoft, true}, + {"f47ac10b-58cc-4372-d567-0e02b2c3d479", 4, Microsoft, true}, + {"f47ac10b-58cc-4372-e567-0e02b2c3d479", 4, Future, true}, + {"f47ac10b-58cc-4372-f567-0e02b2c3d479", 4, Future, true}, + + {"f47ac10b158cc-5372-a567-0e02b2c3d479", 0, Invalid, false}, + {"f47ac10b-58cc25372-a567-0e02b2c3d479", 0, Invalid, false}, + {"f47ac10b-58cc-53723a567-0e02b2c3d479", 0, Invalid, false}, + {"f47ac10b-58cc-5372-a56740e02b2c3d479", 0, Invalid, false}, + {"f47ac10b-58cc-5372-a567-0e02-2c3d479", 0, Invalid, false}, + {"g47ac10b-58cc-4372-a567-0e02b2c3d479", 0, Invalid, false}, +} + +var constants = []struct { + c interface{} + name string +}{ + {Person, "Person"}, + {Group, "Group"}, + {Org, "Org"}, + {Invalid, "Invalid"}, + {RFC4122, "RFC4122"}, + {Reserved, "Reserved"}, + {Microsoft, "Microsoft"}, + {Future, "Future"}, + {Domain(17), "Domain17"}, + {Variant(42), "BadVariant42"}, +} + +func testTest(t *testing.T, in string, tt test) { + uuid := Parse(in) + if ok := (uuid != nil); ok != tt.isuuid { + t.Errorf("Parse(%s) got %v expected %v\b", in, ok, tt.isuuid) + } + if uuid == nil { + return + } + + if v := uuid.Variant(); v != tt.variant { + t.Errorf("Variant(%s) got %d expected %d\b", in, v, tt.variant) + } + if v, _ := uuid.Version(); v != tt.version { + t.Errorf("Version(%s) got %d expected %d\b", in, v, tt.version) + } +} + +func TestUUID(t *testing.T) { + for _, tt := range tests { + testTest(t, tt.in, tt) + testTest(t, strings.ToUpper(tt.in), tt) + } +} + +func TestConstants(t *testing.T) { + for x, tt := range constants { + v, ok := tt.c.(fmt.Stringer) + if !ok { + t.Errorf("%x: %v: not a stringer", x, v) + } else if s := v.String(); s != tt.name { + v, _ := tt.c.(int) + t.Errorf("%x: Constant %T:%d gives %q, expected %q\n", x, tt.c, v, s, tt.name) + } + } +} + +func TestRandomUUID(t *testing.T) { + m := make(map[string]bool) + for x := 1; x < 32; x++ { + uuid := NewRandom() + s := uuid.String() + if m[s] { + t.Errorf("NewRandom returned duplicated UUID %s\n", s) + } + m[s] = true + if v, _ := uuid.Version(); v != 4 { + t.Errorf("Random UUID of version %s\n", v) + } + if uuid.Variant() != RFC4122 { + t.Errorf("Random UUID is variant %d\n", uuid.Variant()) + } + } +} + +func TestNew(t *testing.T) { + m := make(map[string]bool) + for x := 1; x < 32; x++ { + s := New() + if m[s] { + t.Errorf("New returned duplicated UUID %s\n", s) + } + m[s] = true + uuid := Parse(s) + if uuid == nil { + t.Errorf("New returned %q which does not decode\n", s) + continue + } + if v, _ := uuid.Version(); v != 4 { + t.Errorf("Random UUID of version %s\n", v) + } + if uuid.Variant() != RFC4122 { + t.Errorf("Random UUID is variant %d\n", uuid.Variant()) + } + } +} + +func clockSeq(t *testing.T, uuid UUID) int { + seq, ok := uuid.ClockSequence() + if !ok { + t.Fatalf("%s: invalid clock sequence\n", uuid) + } + return seq +} + +func TestClockSeq(t *testing.T) { + // Fake time.Now for this test to return a monotonically advancing time; restore it at end. + defer func(orig func() time.Time) { timeNow = orig }(timeNow) + monTime := time.Now() + timeNow = func() time.Time { + monTime = monTime.Add(1 * time.Second) + return monTime + } + + SetClockSequence(-1) + uuid1 := NewUUID() + uuid2 := NewUUID() + + if clockSeq(t, uuid1) != clockSeq(t, uuid2) { + t.Errorf("clock sequence %d != %d\n", clockSeq(t, uuid1), clockSeq(t, uuid2)) + } + + SetClockSequence(-1) + uuid2 = NewUUID() + + // Just on the very off chance we generated the same sequence + // two times we try again. + if clockSeq(t, uuid1) == clockSeq(t, uuid2) { + SetClockSequence(-1) + uuid2 = NewUUID() + } + if clockSeq(t, uuid1) == clockSeq(t, uuid2) { + t.Errorf("Duplicate clock sequence %d\n", clockSeq(t, uuid1)) + } + + SetClockSequence(0x1234) + uuid1 = NewUUID() + if seq := clockSeq(t, uuid1); seq != 0x1234 { + t.Errorf("%s: expected seq 0x1234 got 0x%04x\n", uuid1, seq) + } +} + +func TestCoding(t *testing.T) { + text := "7d444840-9dc0-11d1-b245-5ffdce74fad2" + urn := "urn:uuid:7d444840-9dc0-11d1-b245-5ffdce74fad2" + data := UUID{ + 0x7d, 0x44, 0x48, 0x40, + 0x9d, 0xc0, + 0x11, 0xd1, + 0xb2, 0x45, + 0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2, + } + if v := data.String(); v != text { + t.Errorf("%x: encoded to %s, expected %s\n", data, v, text) + } + if v := data.URN(); v != urn { + t.Errorf("%x: urn is %s, expected %s\n", data, v, urn) + } + + uuid := Parse(text) + if !Equal(uuid, data) { + t.Errorf("%s: decoded to %s, expected %s\n", text, uuid, data) + } +} + +func TestVersion1(t *testing.T) { + uuid1 := NewUUID() + uuid2 := NewUUID() + + if Equal(uuid1, uuid2) { + t.Errorf("%s:duplicate uuid\n", uuid1) + } + if v, _ := uuid1.Version(); v != 1 { + t.Errorf("%s: version %s expected 1\n", uuid1, v) + } + if v, _ := uuid2.Version(); v != 1 { + t.Errorf("%s: version %s expected 1\n", uuid2, v) + } + n1 := uuid1.NodeID() + n2 := uuid2.NodeID() + if !bytes.Equal(n1, n2) { + t.Errorf("Different nodes %x != %x\n", n1, n2) + } + t1, ok := uuid1.Time() + if !ok { + t.Errorf("%s: invalid time\n", uuid1) + } + t2, ok := uuid2.Time() + if !ok { + t.Errorf("%s: invalid time\n", uuid2) + } + q1, ok := uuid1.ClockSequence() + if !ok { + t.Errorf("%s: invalid clock sequence\n", uuid1) + } + q2, ok := uuid2.ClockSequence() + if !ok { + t.Errorf("%s: invalid clock sequence", uuid2) + } + + switch { + case t1 == t2 && q1 == q2: + t.Errorf("time stopped\n") + case t1 > t2 && q1 == q2: + t.Errorf("time reversed\n") + case t1 < t2 && q1 != q2: + t.Errorf("clock sequence chaned unexpectedly\n") + } +} + +func TestNodeAndTime(t *testing.T) { + // Time is February 5, 1998 12:30:23.136364800 AM GMT + + uuid := Parse("7d444840-9dc0-11d1-b245-5ffdce74fad2") + node := []byte{0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2} + + ts, ok := uuid.Time() + if ok { + c := time.Unix(ts.UnixTime()) + want := time.Date(1998, 2, 5, 0, 30, 23, 136364800, time.UTC) + if !c.Equal(want) { + t.Errorf("Got time %v, want %v", c, want) + } + } else { + t.Errorf("%s: bad time\n", uuid) + } + if !bytes.Equal(node, uuid.NodeID()) { + t.Errorf("Expected node %v got %v\n", node, uuid.NodeID()) + } +} + +func TestMD5(t *testing.T) { + uuid := NewMD5(NameSpace_DNS, []byte("python.org")).String() + want := "6fa459ea-ee8a-3ca4-894e-db77e160355e" + if uuid != want { + t.Errorf("MD5: got %q expected %q\n", uuid, want) + } +} + +func TestSHA1(t *testing.T) { + uuid := NewSHA1(NameSpace_DNS, []byte("python.org")).String() + want := "886313e1-3b8a-5372-9b90-0c9aee199e5d" + if uuid != want { + t.Errorf("SHA1: got %q expected %q\n", uuid, want) + } +} + +func TestNodeID(t *testing.T) { + nid := []byte{1, 2, 3, 4, 5, 6} + SetNodeInterface("") + s := NodeInterface() + if s == "" || s == "user" { + t.Errorf("NodeInterface %q after SetInteface\n", s) + } + node1 := NodeID() + if node1 == nil { + t.Errorf("NodeID nil after SetNodeInterface\n", s) + } + SetNodeID(nid) + s = NodeInterface() + if s != "user" { + t.Errorf("Expected NodeInterface %q got %q\n", "user", s) + } + node2 := NodeID() + if node2 == nil { + t.Errorf("NodeID nil after SetNodeID\n", s) + } + if bytes.Equal(node1, node2) { + t.Errorf("NodeID not changed after SetNodeID\n", s) + } else if !bytes.Equal(nid, node2) { + t.Errorf("NodeID is %x, expected %x\n", node2, nid) + } +} + +func testDCE(t *testing.T, name string, uuid UUID, domain Domain, id uint32) { + if uuid == nil { + t.Errorf("%s failed\n", name) + return + } + if v, _ := uuid.Version(); v != 2 { + t.Errorf("%s: %s: expected version 2, got %s\n", name, uuid, v) + return + } + if v, ok := uuid.Domain(); !ok || v != domain { + if !ok { + t.Errorf("%s: %d: Domain failed\n", name, uuid) + } else { + t.Errorf("%s: %s: expected domain %d, got %d\n", name, uuid, domain, v) + } + } + if v, ok := uuid.Id(); !ok || v != id { + if !ok { + t.Errorf("%s: %d: Id failed\n", name, uuid) + } else { + t.Errorf("%s: %s: expected id %d, got %d\n", name, uuid, id, v) + } + } +} + +func TestDCE(t *testing.T) { + testDCE(t, "NewDCESecurity", NewDCESecurity(42, 12345678), 42, 12345678) + testDCE(t, "NewDCEPerson", NewDCEPerson(), Person, uint32(os.Getuid())) + testDCE(t, "NewDCEGroup", NewDCEGroup(), Group, uint32(os.Getgid())) +} + +type badRand struct{} + +func (r badRand) Read(buf []byte) (int, error) { + for i, _ := range buf { + buf[i] = byte(i) + } + return len(buf), nil +} + +func TestBadRand(t *testing.T) { + SetRand(badRand{}) + uuid1 := New() + uuid2 := New() + if uuid1 != uuid2 { + t.Errorf("execpted duplicates, got %q and %q\n", uuid1, uuid2) + } + SetRand(nil) + uuid1 = New() + uuid2 = New() + if uuid1 == uuid2 { + t.Errorf("unexecpted duplicates, got %q\n", uuid1) + } +} diff --git a/vendor/src/github.com/Xe/uuid/version1.go b/vendor/src/github.com/Xe/uuid/version1.go new file mode 100644 index 0000000..6358004 --- /dev/null +++ b/vendor/src/github.com/Xe/uuid/version1.go @@ -0,0 +1,41 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" +) + +// NewUUID returns a Version 1 UUID based on the current NodeID and clock +// sequence, and the current time. If the NodeID has not been set by SetNodeID +// or SetNodeInterface then it will be set automatically. If the NodeID cannot +// be set NewUUID returns nil. If clock sequence has not been set by +// SetClockSequence then it will be set automatically. If GetTime fails to +// return the current NewUUID returns nil. +func NewUUID() UUID { + if nodeID == nil { + SetNodeInterface("") + } + + now, err := GetTime() + if err != nil { + return nil + } + + uuid := make([]byte, 16) + + time_low := uint32(now & 0xffffffff) + time_mid := uint16((now >> 32) & 0xffff) + time_hi := uint16((now >> 48) & 0x0fff) + time_hi |= 0x1000 // Version 1 + + binary.BigEndian.PutUint32(uuid[0:], time_low) + binary.BigEndian.PutUint16(uuid[4:], time_mid) + binary.BigEndian.PutUint16(uuid[6:], time_hi) + binary.BigEndian.PutUint16(uuid[8:], clock_seq) + copy(uuid[10:], nodeID) + + return uuid +} diff --git a/vendor/src/github.com/Xe/uuid/version4.go b/vendor/src/github.com/Xe/uuid/version4.go new file mode 100644 index 0000000..b3d4a36 --- /dev/null +++ b/vendor/src/github.com/Xe/uuid/version4.go @@ -0,0 +1,25 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +// Random returns a Random (Version 4) UUID or panics. +// +// The strength of the UUIDs is based on the strength of the crypto/rand +// package. +// +// A note about uniqueness derived from from the UUID Wikipedia entry: +// +// Randomly generated UUIDs have 122 random bits. One's annual risk of being +// hit by a meteorite is estimated to be one chance in 17 billion, that +// means the probability is about 0.00000000006 (6 × 10−11), +// equivalent to the odds of creating a few tens of trillions of UUIDs in a +// year and having one duplicate. +func NewRandom() UUID { + uuid := make([]byte, 16) + randomBits([]byte(uuid)) + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 + return uuid +} diff --git a/vendor/src/github.com/scalingdata/gcfg/LICENSE b/vendor/src/github.com/scalingdata/gcfg/LICENSE new file mode 100644 index 0000000..b0a9e76 --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/LICENSE @@ -0,0 +1,57 @@ +Copyright (c) 2012 Péter Surányi. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +Portions of gcfg's source code have been derived from Go, and are +covered by the following license: +---------------------------------------------------------------------- + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/src/github.com/scalingdata/gcfg/README b/vendor/src/github.com/scalingdata/gcfg/README new file mode 100644 index 0000000..8f621c3 --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/README @@ -0,0 +1,7 @@ +Gcfg reads INI-style configuration files into Go structs; +supports user-defined types and subsections. + +Project page: https://code.google.com/p/gcfg +Package docs: http://godoc.org/code.google.com/p/gcfg + +My other projects: https://speter.net diff --git a/vendor/src/github.com/scalingdata/gcfg/doc.go b/vendor/src/github.com/scalingdata/gcfg/doc.go new file mode 100644 index 0000000..99687b4 --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/doc.go @@ -0,0 +1,118 @@ +// Package gcfg reads "INI-style" text-based configuration files with +// "name=value" pairs grouped into sections (gcfg files). +// +// This package is still a work in progress; see the sections below for planned +// changes. +// +// Syntax +// +// The syntax is based on that used by git config: +// http://git-scm.com/docs/git-config#_syntax . +// There are some (planned) differences compared to the git config format: +// - improve data portability: +// - must be encoded in UTF-8 (for now) and must not contain the 0 byte +// - include and "path" type is not supported +// (path type may be implementable as a user-defined type) +// - internationalization +// - section and variable names can contain unicode letters, unicode digits +// (as defined in http://golang.org/ref/spec#Characters ) and hyphens +// (U+002D), starting with a unicode letter +// - disallow potentially ambiguous or misleading definitions: +// - `[sec.sub]` format is not allowed (deprecated in gitconfig) +// - `[sec ""]` is not allowed +// - use `[sec]` for section name "sec" and empty subsection name +// - (planned) within a single file, definitions must be contiguous for each: +// - section: '[secA]' -> '[secB]' -> '[secA]' is an error +// - subsection: '[sec "A"]' -> '[sec "B"]' -> '[sec "A"]' is an error +// - multivalued variable: 'multi=a' -> 'other=x' -> 'multi=b' is an error +// +// Data structure +// +// The functions in this package read values into a user-defined struct. +// Each section corresponds to a struct field in the config struct, and each +// variable in a section corresponds to a data field in the section struct. +// The mapping of each section or variable name to fields is done either based +// on the "gcfg" struct tag or by matching the name of the section or variable, +// ignoring case. In the latter case, hyphens '-' in section and variable names +// correspond to underscores '_' in field names. +// Fields must be exported; to use a section or variable name starting with a +// letter that is neither upper- or lower-case, prefix the field name with 'X'. +// (See https://code.google.com/p/go/issues/detail?id=5763#c4 .) +// +// For sections with subsections, the corresponding field in config must be a +// map, rather than a struct, with string keys and pointer-to-struct values. +// Values for subsection variables are stored in the map with the subsection +// name used as the map key. +// (Note that unlike section and variable names, subsection names are case +// sensitive.) +// When using a map, and there is a section with the same section name but +// without a subsection name, its values are stored with the empty string used +// as the key. +// +// The functions in this package panic if config is not a pointer to a struct, +// or when a field is not of a suitable type (either a struct or a map with +// string keys and pointer-to-struct values). +// +// Parsing of values +// +// The section structs in the config struct may contain single-valued or +// multi-valued variables. Variables of unnamed slice type (that is, a type +// starting with `[]`) are treated as multi-value; all others (including named +// slice types) are treated as single-valued variables. +// +// Single-valued variables are handled based on the type as follows. +// Unnamed pointer types (that is, types starting with `*`) are dereferenced, +// and if necessary, a new instance is allocated. +// +// For types implementing the encoding.TextUnmarshaler interface, the +// UnmarshalText method is used to set the value. Implementing this method is +// the recommended way for parsing user-defined types. +// +// For fields of string kind, the value string is assigned to the field, after +// unquoting and unescaping as needed. +// For fields of bool kind, the field is set to true if the value is "true", +// "yes", "on" or "1", and set to false if the value is "false", "no", "off" or +// "0", ignoring case. In addition, single-valued bool fields can be specified +// with a "blank" value (variable name without equals sign and value); in such +// case the value is set to true. +// +// Predefined integer types [u]int(|8|16|32|64) and big.Int are parsed as +// decimal or hexadecimal (if having '0x' prefix). (This is to prevent +// unintuitively handling zero-padded numbers as octal.) Other types having +// [u]int* as the underlying type, such as os.FileMode and uintptr allow +// decimal, hexadecimal, or octal values. +// Parsing mode for integer types can be overridden using the struct tag option +// ",int=mode" where mode is a combination of the 'd', 'h', and 'o' characters +// (each standing for decimal, hexadecimal, and octal, respectively.) +// +// All other types are parsed using fmt.Sscanf with the "%v" verb. +// +// For multi-valued variables, each individual value is parsed as above and +// appended to the slice. If the first value is specified as a "blank" value +// (variable name without equals sign and value), a new slice is allocated; +// that is any values previously set in the slice will be ignored. +// +// The types subpackage for provides helpers for parsing "enum-like" and integer +// types. +// +// TODO +// +// The following is a list of changes under consideration: +// - documentation +// - self-contained syntax documentation +// - more practical examples +// - move TODOs to issue tracker (eventually) +// - syntax +// - reconsider valid escape sequences +// (gitconfig doesn't support \r in value, \t in subsection name, etc.) +// - reading / parsing gcfg files +// - define internal representation structure +// - support multiple inputs (readers, strings, files) +// - support declaring encoding (?) +// - support varying fields sets for subsections (?) +// - writing gcfg files +// - error handling +// - make error context accessible programmatically? +// - limit input size? +// +package gcfg diff --git a/vendor/src/github.com/scalingdata/gcfg/example_test.go b/vendor/src/github.com/scalingdata/gcfg/example_test.go new file mode 100644 index 0000000..7fa554c --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/example_test.go @@ -0,0 +1,132 @@ +package gcfg_test + +import ( + "fmt" + "log" +) + +import "github.com/scalingdata/gcfg" + +func ExampleReadStringInto() { + cfgStr := `; Comment line +[section] +name=value # comment` + cfg := struct { + Section struct { + Name string + } + }{} + err := gcfg.ReadStringInto(&cfg, cfgStr) + if err != nil { + log.Fatalf("Failed to parse gcfg data: %s", err) + } + fmt.Println(cfg.Section.Name) + // Output: value +} + +func ExampleReadStringInto_bool() { + cfgStr := `; Comment line +[section] +switch=on` + cfg := struct { + Section struct { + Switch bool + } + }{} + err := gcfg.ReadStringInto(&cfg, cfgStr) + if err != nil { + log.Fatalf("Failed to parse gcfg data: %s", err) + } + fmt.Println(cfg.Section.Switch) + // Output: true +} + +func ExampleReadStringInto_hyphens() { + cfgStr := `; Comment line +[section-name] +variable-name=value # comment` + cfg := struct { + Section_Name struct { + Variable_Name string + } + }{} + err := gcfg.ReadStringInto(&cfg, cfgStr) + if err != nil { + log.Fatalf("Failed to parse gcfg data: %s", err) + } + fmt.Println(cfg.Section_Name.Variable_Name) + // Output: value +} + +func ExampleReadStringInto_tags() { + cfgStr := `; Comment line +[section] +var-name=value # comment` + cfg := struct { + Section struct { + FieldName string `gcfg:"var-name"` + } + }{} + err := gcfg.ReadStringInto(&cfg, cfgStr) + if err != nil { + log.Fatalf("Failed to parse gcfg data: %s", err) + } + fmt.Println(cfg.Section.FieldName) + // Output: value +} + +func ExampleReadStringInto_subsections() { + cfgStr := `; Comment line +[profile "A"] +color = white + +[profile "B"] +color = black +` + cfg := struct { + Profile map[string]*struct { + Color string + } + }{} + err := gcfg.ReadStringInto(&cfg, cfgStr) + if err != nil { + log.Fatalf("Failed to parse gcfg data: %s", err) + } + fmt.Printf("%s %s\n", cfg.Profile["A"].Color, cfg.Profile["B"].Color) + // Output: white black +} + +func ExampleReadStringInto_multivalue() { + cfgStr := `; Comment line +[section] +multi=value1 +multi=value2` + cfg := struct { + Section struct { + Multi []string + } + }{} + err := gcfg.ReadStringInto(&cfg, cfgStr) + if err != nil { + log.Fatalf("Failed to parse gcfg data: %s", err) + } + fmt.Println(cfg.Section.Multi) + // Output: [value1 value2] +} + +func ExampleReadStringInto_unicode() { + cfgStr := `; Comment line +[甲] +乙=丙 # comment` + cfg := struct { + X甲 struct { + X乙 string + } + }{} + err := gcfg.ReadStringInto(&cfg, cfgStr) + if err != nil { + log.Fatalf("Failed to parse gcfg data: %s", err) + } + fmt.Println(cfg.X甲.X乙) + // Output: 丙 +} diff --git a/vendor/src/github.com/scalingdata/gcfg/go1_0.go b/vendor/src/github.com/scalingdata/gcfg/go1_0.go new file mode 100644 index 0000000..6670210 --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/go1_0.go @@ -0,0 +1,7 @@ +// +build !go1.2 + +package gcfg + +type textUnmarshaler interface { + UnmarshalText(text []byte) error +} diff --git a/vendor/src/github.com/scalingdata/gcfg/go1_2.go b/vendor/src/github.com/scalingdata/gcfg/go1_2.go new file mode 100644 index 0000000..6f5843b --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/go1_2.go @@ -0,0 +1,9 @@ +// +build go1.2 + +package gcfg + +import ( + "encoding" +) + +type textUnmarshaler encoding.TextUnmarshaler diff --git a/vendor/src/github.com/scalingdata/gcfg/issues_test.go b/vendor/src/github.com/scalingdata/gcfg/issues_test.go new file mode 100644 index 0000000..796dd10 --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/issues_test.go @@ -0,0 +1,63 @@ +package gcfg + +import ( + "fmt" + "math/big" + "strings" + "testing" +) + +type Config1 struct { + Section struct { + Int int + BigInt big.Int + } +} + +var testsIssue1 = []struct { + cfg string + typename string +}{ + {"[section]\nint=X", "int"}, + {"[section]\nint=", "int"}, + {"[section]\nint=1A", "int"}, + {"[section]\nbigint=X", "big.Int"}, + {"[section]\nbigint=", "big.Int"}, + {"[section]\nbigint=1A", "big.Int"}, +} + +// Value parse error should: +// - include plain type name +// - not include reflect internals +func TestIssue1(t *testing.T) { + for i, tt := range testsIssue1 { + var c Config1 + err := ReadStringInto(&c, tt.cfg) + switch { + case err == nil: + t.Errorf("%d fail: got ok; wanted error", i) + case !strings.Contains(err.Error(), tt.typename): + t.Errorf("%d fail: error message doesn't contain type name %q: %v", + i, tt.typename, err) + case strings.Contains(err.Error(), "reflect"): + t.Errorf("%d fail: error message includes reflect internals: %v", + i, err) + default: + t.Logf("%d pass: %v", i, err) + } + } +} + +type confIssue2 struct{ Main struct{ Foo string } } + +var testsIssue2 = []readtest{ + {"[main]\n;\nfoo = bar\n", &confIssue2{struct{ Foo string }{"bar"}}, true}, + {"[main]\r\n;\r\nfoo = bar\r\n", &confIssue2{struct{ Foo string }{"bar"}}, true}, +} + +func TestIssue2(t *testing.T) { + for i, tt := range testsIssue2 { + id := fmt.Sprintf("issue2:%d", i) + testRead(t, id, tt) + } +} diff --git a/vendor/src/github.com/scalingdata/gcfg/read.go b/vendor/src/github.com/scalingdata/gcfg/read.go new file mode 100644 index 0000000..280ec77 --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/read.go @@ -0,0 +1,181 @@ +package gcfg + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "strings" +) + +import ( + "github.com/scalingdata/gcfg/scanner" + "github.com/scalingdata/gcfg/token" +) + +var unescape = map[rune]rune{'\\': '\\', '"': '"', 'n': '\n', 't': '\t'} + +// no error: invalid literals should be caught by scanner +func unquote(s string) string { + u, q, esc := make([]rune, 0, len(s)), false, false + for _, c := range s { + if esc { + uc, ok := unescape[c] + switch { + case ok: + u = append(u, uc) + fallthrough + case !q && c == '\n': + esc = false + continue + } + panic("invalid escape sequence") + } + switch c { + case '"': + q = !q + case '\\': + esc = true + default: + u = append(u, c) + } + } + if q { + panic("missing end quote") + } + if esc { + panic("invalid escape sequence") + } + return string(u) +} + +func readInto(config interface{}, fset *token.FileSet, file *token.File, src []byte) error { + var s scanner.Scanner + var errs scanner.ErrorList + s.Init(file, src, func(p token.Position, m string) { errs.Add(p, m) }, 0) + sect, sectsub := "", "" + pos, tok, lit := s.Scan() + errfn := func(msg string) error { + return fmt.Errorf("%s: %s", fset.Position(pos), msg) + } + for { + if errs.Len() > 0 { + return errs.Err() + } + switch tok { + case token.EOF: + return nil + case token.EOL, token.COMMENT: + pos, tok, lit = s.Scan() + case token.LBRACK: + pos, tok, lit = s.Scan() + if errs.Len() > 0 { + return errs.Err() + } + if tok != token.IDENT { + return errfn("expected section name") + } + sect, sectsub = lit, "" + pos, tok, lit = s.Scan() + if errs.Len() > 0 { + return errs.Err() + } + if tok == token.STRING { + sectsub = unquote(lit) + if sectsub == "" { + return errfn("empty subsection name") + } + pos, tok, lit = s.Scan() + if errs.Len() > 0 { + return errs.Err() + } + } + if tok != token.RBRACK { + if sectsub == "" { + return errfn("expected subsection name or right bracket") + } + return errfn("expected right bracket") + } + pos, tok, lit = s.Scan() + if tok != token.EOL && tok != token.EOF && tok != token.COMMENT { + return errfn("expected EOL, EOF, or comment") + } + case token.IDENT: + if sect == "" { + return errfn("expected section header") + } + n := lit + pos, tok, lit = s.Scan() + if errs.Len() > 0 { + return errs.Err() + } + blank, v := tok == token.EOF || tok == token.EOL || tok == token.COMMENT, "" + if !blank { + if tok != token.ASSIGN { + return errfn("expected '='") + } + pos, tok, lit = s.Scan() + if errs.Len() > 0 { + return errs.Err() + } + if tok != token.STRING { + return errfn("expected value") + } + v = unquote(lit) + pos, tok, lit = s.Scan() + if errs.Len() > 0 { + return errs.Err() + } + if tok != token.EOL && tok != token.EOF && tok != token.COMMENT { + return errfn("expected EOL, EOF, or comment") + } + } + err := set(config, sect, sectsub, n, blank, v) + if err != nil { + return err + } + default: + if sect == "" { + return errfn("expected section header") + } + return errfn("expected section header or variable declaration") + } + } + panic("never reached") +} + +// ReadInto reads gcfg formatted data from reader and sets the values into the +// corresponding fields in config. +func ReadInto(config interface{}, reader io.Reader) error { + src, err := ioutil.ReadAll(reader) + if err != nil { + return err + } + fset := token.NewFileSet() + file := fset.AddFile("", fset.Base(), len(src)) + return readInto(config, fset, file, src) +} + +// ReadStringInto reads gcfg formatted data from str and sets the values into +// the corresponding fields in config. +func ReadStringInto(config interface{}, str string) error { + r := strings.NewReader(str) + return ReadInto(config, r) +} + +// ReadFileInto reads gcfg formatted data from the file filename and sets the +// values into the corresponding fields in config. +func ReadFileInto(config interface{}, filename string) error { + f, err := os.Open(filename) + if err != nil { + return err + } + defer f.Close() + src, err := ioutil.ReadAll(f) + if err != nil { + return err + } + fset := token.NewFileSet() + file := fset.AddFile(filename, fset.Base(), len(src)) + return readInto(config, fset, file, src) +} diff --git a/vendor/src/github.com/scalingdata/gcfg/read_test.go b/vendor/src/github.com/scalingdata/gcfg/read_test.go new file mode 100644 index 0000000..4a7d8e1 --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/read_test.go @@ -0,0 +1,333 @@ +package gcfg + +import ( + "fmt" + "math/big" + "os" + "reflect" + "testing" +) + +const ( + // 64 spaces + sp64 = " " + // 512 spaces + sp512 = sp64 + sp64 + sp64 + sp64 + sp64 + sp64 + sp64 + sp64 + // 4096 spaces + sp4096 = sp512 + sp512 + sp512 + sp512 + sp512 + sp512 + sp512 + sp512 +) + +type cBasic struct { + Section cBasicS1 + Hyphen_In_Section cBasicS2 + unexported cBasicS1 + Exported cBasicS3 + TagName cBasicS1 `gcfg:"tag-name"` +} +type cBasicS1 struct { + Name string + Int int + PName *string +} +type cBasicS2 struct { + Hyphen_In_Name string +} +type cBasicS3 struct { + unexported string +} + +type nonMulti []string + +type unmarshalable string + +func (u *unmarshalable) UnmarshalText(text []byte) error { + s := string(text) + if s == "error" { + return fmt.Errorf("%s", s) + } + *u = unmarshalable(s) + return nil +} + +var _ textUnmarshaler = new(unmarshalable) + +type cUni struct { + X甲 cUniS1 + XSection cUniS2 +} +type cUniS1 struct { + X乙 string +} +type cUniS2 struct { + XName string +} + +type cMulti struct { + M1 cMultiS1 + M2 cMultiS2 + M3 cMultiS3 +} +type cMultiS1 struct{ Multi []string } +type cMultiS2 struct{ NonMulti nonMulti } +type cMultiS3 struct{ MultiInt []int } + +type cSubs struct{ Sub map[string]*cSubsS1 } +type cSubsS1 struct{ Name string } + +type cBool struct{ Section cBoolS1 } +type cBoolS1 struct{ Bool bool } + +type cTxUnm struct{ Section cTxUnmS1 } +type cTxUnmS1 struct{ Name unmarshalable } + +type cNum struct { + N1 cNumS1 + N2 cNumS2 + N3 cNumS3 +} +type cNumS1 struct { + Int int + IntDHO int `gcfg:",int=dho"` + Big *big.Int +} +type cNumS2 struct { + MultiInt []int + MultiBig []*big.Int +} +type cNumS3 struct{ FileMode os.FileMode } +type readtest struct { + gcfg string + exp interface{} + ok bool +} + +func newString(s string) *string { + return &s +} + +var readtests = []struct { + group string + tests []readtest +}{{"scanning", []readtest{ + {"[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + // hyphen in name + {"[hyphen-in-section]\nhyphen-in-name=value", &cBasic{Hyphen_In_Section: cBasicS2{Hyphen_In_Name: "value"}}, true}, + // quoted string value + {"[section]\nname=\"\"", &cBasic{Section: cBasicS1{Name: ""}}, true}, + {"[section]\nname=\" \"", &cBasic{Section: cBasicS1{Name: " "}}, true}, + {"[section]\nname=\"value\"", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"[section]\nname=\" value \"", &cBasic{Section: cBasicS1{Name: " value "}}, true}, + {"\n[section]\nname=\"va ; lue\"", &cBasic{Section: cBasicS1{Name: "va ; lue"}}, true}, + {"[section]\nname=\"val\" \"ue\"", &cBasic{Section: cBasicS1{Name: "val ue"}}, true}, + {"[section]\nname=\"value", &cBasic{}, false}, + // escape sequences + {"[section]\nname=\"va\\\\lue\"", &cBasic{Section: cBasicS1{Name: "va\\lue"}}, true}, + {"[section]\nname=\"va\\\"lue\"", &cBasic{Section: cBasicS1{Name: "va\"lue"}}, true}, + {"[section]\nname=\"va\\nlue\"", &cBasic{Section: cBasicS1{Name: "va\nlue"}}, true}, + {"[section]\nname=\"va\\tlue\"", &cBasic{Section: cBasicS1{Name: "va\tlue"}}, true}, + {"\n[section]\nname=\\", &cBasic{}, false}, + {"\n[section]\nname=\\a", &cBasic{}, false}, + {"\n[section]\nname=\"val\\a\"", &cBasic{}, false}, + {"\n[section]\nname=val\\", &cBasic{}, false}, + {"\n[sub \"A\\\n\"]\nname=value", &cSubs{}, false}, + {"\n[sub \"A\\\t\"]\nname=value", &cSubs{}, false}, + // broken line + {"[section]\nname=value \\\n value", &cBasic{Section: cBasicS1{Name: "value value"}}, true}, + {"[section]\nname=\"value \\\n value\"", &cBasic{}, false}, +}}, {"scanning:whitespace", []readtest{ + {" \n[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {" [section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"\t[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"[ section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"[section ]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"[section]\n name=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"[section]\nname =value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"[section]\nname= value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"[section]\nname=value ", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"[section]\r\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"[section]\r\nname=value\r\n", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {";cmnt\r\n[section]\r\nname=value\r\n", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + // long lines + {sp4096 + "[section]\nname=value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"[" + sp4096 + "section]\nname=value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"[section" + sp4096 + "]\nname=value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"[section]" + sp4096 + "\nname=value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"[section]\n" + sp4096 + "name=value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"[section]\nname" + sp4096 + "=value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"[section]\nname=" + sp4096 + "value\n", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"[section]\nname=value\n" + sp4096, &cBasic{Section: cBasicS1{Name: "value"}}, true}, +}}, {"scanning:comments", []readtest{ + {"; cmnt\n[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"# cmnt\n[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {" ; cmnt\n[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"\t; cmnt\n[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"\n[section]; cmnt\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"\n[section] ; cmnt\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"\n[section]\nname=value; cmnt", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"\n[section]\nname=value ; cmnt", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"\n[section]\nname=\"value\" ; cmnt", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"\n[section]\nname=value ; \"cmnt", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"\n[section]\nname=\"va ; lue\" ; cmnt", &cBasic{Section: cBasicS1{Name: "va ; lue"}}, true}, + {"\n[section]\nname=; cmnt", &cBasic{Section: cBasicS1{Name: ""}}, true}, +}}, {"scanning:subsections", []readtest{ + {"\n[sub \"A\"]\nname=value", &cSubs{map[string]*cSubsS1{"A": &cSubsS1{"value"}}}, true}, + {"\n[sub \"b\"]\nname=value", &cSubs{map[string]*cSubsS1{"b": &cSubsS1{"value"}}}, true}, + {"\n[sub \"A\\\\\"]\nname=value", &cSubs{map[string]*cSubsS1{"A\\": &cSubsS1{"value"}}}, true}, + {"\n[sub \"A\\\"\"]\nname=value", &cSubs{map[string]*cSubsS1{"A\"": &cSubsS1{"value"}}}, true}, +}}, {"syntax", []readtest{ + // invalid line + {"\n[section]\n=", &cBasic{}, false}, + // no section + {"name=value", &cBasic{}, false}, + // empty section + {"\n[]\nname=value", &cBasic{}, false}, + // empty subsection + {"\n[sub \"\"]\nname=value", &cSubs{}, false}, +}}, {"setting", []readtest{ + {"[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + // pointer + {"[section]", &cBasic{Section: cBasicS1{PName: nil}}, true}, + {"[section]\npname=value", &cBasic{Section: cBasicS1{PName: newString("value")}}, true}, + // section name not matched + {"\n[nonexistent]\nname=value", &cBasic{}, false}, + // subsection name not matched + {"\n[section \"nonexistent\"]\nname=value", &cBasic{}, false}, + // variable name not matched + {"\n[section]\nnonexistent=value", &cBasic{}, false}, + // hyphen in name + {"[hyphen-in-section]\nhyphen-in-name=value", &cBasic{Hyphen_In_Section: cBasicS2{Hyphen_In_Name: "value"}}, true}, + // ignore unexported fields + {"[unexported]\nname=value", &cBasic{}, false}, + {"[exported]\nunexported=value", &cBasic{}, false}, + // 'X' prefix for non-upper/lower-case letters + {"[甲]\n乙=丙", &cUni{X甲: cUniS1{X乙: "丙"}}, true}, + //{"[section]\nxname=value", &cBasic{XSection: cBasicS4{XName: "value"}}, false}, + //{"[xsection]\nname=value", &cBasic{XSection: cBasicS4{XName: "value"}}, false}, + // name specified as struct tag + {"[tag-name]\nname=value", &cBasic{TagName: cBasicS1{Name: "value"}}, true}, +}}, {"multivalue", []readtest{ + // unnamed slice type: treat as multi-value + {"\n[m1]", &cMulti{M1: cMultiS1{}}, true}, + {"\n[m1]\nmulti=value", &cMulti{M1: cMultiS1{[]string{"value"}}}, true}, + {"\n[m1]\nmulti=value1\nmulti=value2", &cMulti{M1: cMultiS1{[]string{"value1", "value2"}}}, true}, + // "blank" empties multi-valued slice -- here same result as above + {"\n[m1]\nmulti\nmulti=value1\nmulti=value2", &cMulti{M1: cMultiS1{[]string{"value1", "value2"}}}, true}, + // named slice type: do not treat as multi-value + {"\n[m2]", &cMulti{}, true}, + {"\n[m2]\nmulti=value", &cMulti{}, false}, + {"\n[m2]\nmulti=value1\nmulti=value2", &cMulti{}, false}, +}}, {"type:string", []readtest{ + {"[section]\nname=value", &cBasic{Section: cBasicS1{Name: "value"}}, true}, + {"[section]\nname=", &cBasic{Section: cBasicS1{Name: ""}}, true}, +}}, {"type:bool", []readtest{ + // explicit values + {"[section]\nbool=true", &cBool{cBoolS1{true}}, true}, + {"[section]\nbool=yes", &cBool{cBoolS1{true}}, true}, + {"[section]\nbool=on", &cBool{cBoolS1{true}}, true}, + {"[section]\nbool=1", &cBool{cBoolS1{true}}, true}, + {"[section]\nbool=tRuE", &cBool{cBoolS1{true}}, true}, + {"[section]\nbool=false", &cBool{cBoolS1{false}}, true}, + {"[section]\nbool=no", &cBool{cBoolS1{false}}, true}, + {"[section]\nbool=off", &cBool{cBoolS1{false}}, true}, + {"[section]\nbool=0", &cBool{cBoolS1{false}}, true}, + {"[section]\nbool=NO", &cBool{cBoolS1{false}}, true}, + // "blank" value handled as true + {"[section]\nbool", &cBool{cBoolS1{true}}, true}, + // bool parse errors + {"[section]\nbool=maybe", &cBool{}, false}, + {"[section]\nbool=t", &cBool{}, false}, + {"[section]\nbool=truer", &cBool{}, false}, + {"[section]\nbool=2", &cBool{}, false}, + {"[section]\nbool=-1", &cBool{}, false}, +}}, {"type:numeric", []readtest{ + {"[section]\nint=0", &cBasic{Section: cBasicS1{Int: 0}}, true}, + {"[section]\nint=1", &cBasic{Section: cBasicS1{Int: 1}}, true}, + {"[section]\nint=-1", &cBasic{Section: cBasicS1{Int: -1}}, true}, + {"[section]\nint=0.2", &cBasic{}, false}, + {"[section]\nint=1e3", &cBasic{}, false}, + // primitive [u]int(|8|16|32|64) and big.Int is parsed as dec or hex (not octal) + {"[n1]\nint=010", &cNum{N1: cNumS1{Int: 10}}, true}, + {"[n1]\nint=0x10", &cNum{N1: cNumS1{Int: 0x10}}, true}, + {"[n1]\nbig=1", &cNum{N1: cNumS1{Big: big.NewInt(1)}}, true}, + {"[n1]\nbig=0x10", &cNum{N1: cNumS1{Big: big.NewInt(0x10)}}, true}, + {"[n1]\nbig=010", &cNum{N1: cNumS1{Big: big.NewInt(10)}}, true}, + {"[n2]\nmultiint=010", &cNum{N2: cNumS2{MultiInt: []int{10}}}, true}, + {"[n2]\nmultibig=010", &cNum{N2: cNumS2{MultiBig: []*big.Int{big.NewInt(10)}}}, true}, + // set parse mode for int types via struct tag + {"[n1]\nintdho=010", &cNum{N1: cNumS1{IntDHO: 010}}, true}, + // octal allowed for named type + {"[n3]\nfilemode=0777", &cNum{N3: cNumS3{FileMode: 0777}}, true}, +}}, {"type:textUnmarshaler", []readtest{ + {"[section]\nname=value", &cTxUnm{Section: cTxUnmS1{Name: "value"}}, true}, + {"[section]\nname=error", &cTxUnm{}, false}, +}}, +} + +func TestReadStringInto(t *testing.T) { + for _, tg := range readtests { + for i, tt := range tg.tests { + id := fmt.Sprintf("%s:%d", tg.group, i) + testRead(t, id, tt) + } + } +} + +func TestReadStringIntoMultiBlankPreset(t *testing.T) { + tt := readtest{"\n[m1]\nmulti\nmulti=value1\nmulti=value2", &cMulti{M1: cMultiS1{[]string{"value1", "value2"}}}, true} + cfg := &cMulti{M1: cMultiS1{[]string{"preset1", "preset2"}}} + testReadInto(t, "multi:blank", tt, cfg) +} + +func testRead(t *testing.T, id string, tt readtest) { + // get the type of the expected result + restyp := reflect.TypeOf(tt.exp).Elem() + // create a new instance to hold the actual result + res := reflect.New(restyp).Interface() + testReadInto(t, id, tt, res) +} + +func testReadInto(t *testing.T, id string, tt readtest, res interface{}) { + err := ReadStringInto(res, tt.gcfg) + if tt.ok { + if err != nil { + t.Errorf("%s fail: got error %v, wanted ok", id, err) + return + } else if !reflect.DeepEqual(res, tt.exp) { + t.Errorf("%s fail: got value %#v, wanted value %#v", id, res, tt.exp) + return + } + if !testing.Short() { + t.Logf("%s pass: got value %#v", id, res) + } + } else { // !tt.ok + if err == nil { + t.Errorf("%s fail: got value %#v, wanted error", id, res) + return + } + if !testing.Short() { + t.Logf("%s pass: got error %v", id, err) + } + } +} + +func TestReadFileInto(t *testing.T) { + res := &struct{ Section struct{ Name string } }{} + err := ReadFileInto(res, "testdata/gcfg_test.gcfg") + if err != nil { + t.Errorf(err.Error()) + } + if "value" != res.Section.Name { + t.Errorf("got %q, wanted %q", res.Section.Name, "value") + } +} + +func TestReadFileIntoUnicode(t *testing.T) { + res := &struct{ X甲 struct{ X乙 string } }{} + err := ReadFileInto(res, "testdata/gcfg_unicode_test.gcfg") + if err != nil { + t.Errorf(err.Error()) + } + if "丙" != res.X甲.X乙 { + t.Errorf("got %q, wanted %q", res.X甲.X乙, "丙") + } +} diff --git a/vendor/src/github.com/scalingdata/gcfg/scanner/errors.go b/vendor/src/github.com/scalingdata/gcfg/scanner/errors.go new file mode 100644 index 0000000..e33d656 --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/scanner/errors.go @@ -0,0 +1,121 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package scanner + +import ( + "fmt" + "io" + "sort" +) + +import ( + "github.com/scalingdata/gcfg/token" +) + +// In an ErrorList, an error is represented by an *Error. +// The position Pos, if valid, points to the beginning of +// the offending token, and the error condition is described +// by Msg. +// +type Error struct { + Pos token.Position + Msg string +} + +// Error implements the error interface. +func (e Error) Error() string { + if e.Pos.Filename != "" || e.Pos.IsValid() { + // don't print "" + // TODO(gri) reconsider the semantics of Position.IsValid + return e.Pos.String() + ": " + e.Msg + } + return e.Msg +} + +// ErrorList is a list of *Errors. +// The zero value for an ErrorList is an empty ErrorList ready to use. +// +type ErrorList []*Error + +// Add adds an Error with given position and error message to an ErrorList. +func (p *ErrorList) Add(pos token.Position, msg string) { + *p = append(*p, &Error{pos, msg}) +} + +// Reset resets an ErrorList to no errors. +func (p *ErrorList) Reset() { *p = (*p)[0:0] } + +// ErrorList implements the sort Interface. +func (p ErrorList) Len() int { return len(p) } +func (p ErrorList) Swap(i, j int) { p[i], p[j] = p[j], p[i] } + +func (p ErrorList) Less(i, j int) bool { + e := &p[i].Pos + f := &p[j].Pos + if e.Filename < f.Filename { + return true + } + if e.Filename == f.Filename { + return e.Offset < f.Offset + } + return false +} + +// Sort sorts an ErrorList. *Error entries are sorted by position, +// other errors are sorted by error message, and before any *Error +// entry. +// +func (p ErrorList) Sort() { + sort.Sort(p) +} + +// RemoveMultiples sorts an ErrorList and removes all but the first error per line. +func (p *ErrorList) RemoveMultiples() { + sort.Sort(p) + var last token.Position // initial last.Line is != any legal error line + i := 0 + for _, e := range *p { + if e.Pos.Filename != last.Filename || e.Pos.Line != last.Line { + last = e.Pos + (*p)[i] = e + i++ + } + } + (*p) = (*p)[0:i] +} + +// An ErrorList implements the error interface. +func (p ErrorList) Error() string { + switch len(p) { + case 0: + return "no errors" + case 1: + return p[0].Error() + } + return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1) +} + +// Err returns an error equivalent to this error list. +// If the list is empty, Err returns nil. +func (p ErrorList) Err() error { + if len(p) == 0 { + return nil + } + return p +} + +// PrintError is a utility function that prints a list of errors to w, +// one error per line, if the err parameter is an ErrorList. Otherwise +// it prints the err string. +// +func PrintError(w io.Writer, err error) { + if list, ok := err.(ErrorList); ok { + for _, e := range list { + fmt.Fprintf(w, "%s\n", e) + } + } else if err != nil { + fmt.Fprintf(w, "%s\n", err) + } +} diff --git a/vendor/src/github.com/scalingdata/gcfg/scanner/example_test.go b/vendor/src/github.com/scalingdata/gcfg/scanner/example_test.go new file mode 100644 index 0000000..1a15f6b --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/scanner/example_test.go @@ -0,0 +1,46 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package scanner_test + +import ( + "fmt" +) + +import ( + "github.com/scalingdata/gcfg/scanner" + "github.com/scalingdata/gcfg/token" +) + +func ExampleScanner_Scan() { + // src is the input that we want to tokenize. + src := []byte(`[profile "A"] +color = blue ; Comment`) + + // Initialize the scanner. + var s scanner.Scanner + fset := token.NewFileSet() // positions are relative to fset + file := fset.AddFile("", fset.Base(), len(src)) // register input "file" + s.Init(file, src, nil /* no error handler */, scanner.ScanComments) + + // Repeated calls to Scan yield the token sequence found in the input. + for { + pos, tok, lit := s.Scan() + if tok == token.EOF { + break + } + fmt.Printf("%s\t%q\t%q\n", fset.Position(pos), tok, lit) + } + + // output: + // 1:1 "[" "" + // 1:2 "IDENT" "profile" + // 1:10 "STRING" "\"A\"" + // 1:13 "]" "" + // 1:14 "\n" "" + // 2:1 "IDENT" "color" + // 2:7 "=" "" + // 2:9 "STRING" "blue" + // 2:14 "COMMENT" "; Comment" +} diff --git a/vendor/src/github.com/scalingdata/gcfg/scanner/scanner.go b/vendor/src/github.com/scalingdata/gcfg/scanner/scanner.go new file mode 100644 index 0000000..462c87e --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/scanner/scanner.go @@ -0,0 +1,342 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package scanner implements a scanner for gcfg configuration text. +// It takes a []byte as source which can then be tokenized +// through repeated calls to the Scan method. +// +// Note that the API for the scanner package may change to accommodate new +// features or implementation changes in gcfg. +// +package scanner + +import ( + "fmt" + "path/filepath" + "unicode" + "unicode/utf8" +) + +import ( + "github.com/scalingdata/gcfg/token" +) + +// An ErrorHandler may be provided to Scanner.Init. If a syntax error is +// encountered and a handler was installed, the handler is called with a +// position and an error message. The position points to the beginning of +// the offending token. +// +type ErrorHandler func(pos token.Position, msg string) + +// A Scanner holds the scanner's internal state while processing +// a given text. It can be allocated as part of another data +// structure but must be initialized via Init before use. +// +type Scanner struct { + // immutable state + file *token.File // source file handle + dir string // directory portion of file.Name() + src []byte // source + err ErrorHandler // error reporting; or nil + mode Mode // scanning mode + + // scanning state + ch rune // current character + offset int // character offset + rdOffset int // reading offset (position after current character) + lineOffset int // current line offset + nextVal bool // next token is expected to be a value + + // public state - ok to modify + ErrorCount int // number of errors encountered +} + +// Read the next Unicode char into s.ch. +// s.ch < 0 means end-of-file. +// +func (s *Scanner) next() { + if s.rdOffset < len(s.src) { + s.offset = s.rdOffset + if s.ch == '\n' { + s.lineOffset = s.offset + s.file.AddLine(s.offset) + } + r, w := rune(s.src[s.rdOffset]), 1 + switch { + case r == 0: + s.error(s.offset, "illegal character NUL") + case r >= 0x80: + // not ASCII + r, w = utf8.DecodeRune(s.src[s.rdOffset:]) + if r == utf8.RuneError && w == 1 { + s.error(s.offset, "illegal UTF-8 encoding") + } + } + s.rdOffset += w + s.ch = r + } else { + s.offset = len(s.src) + if s.ch == '\n' { + s.lineOffset = s.offset + s.file.AddLine(s.offset) + } + s.ch = -1 // eof + } +} + +// A mode value is a set of flags (or 0). +// They control scanner behavior. +// +type Mode uint + +const ( + ScanComments Mode = 1 << iota // return comments as COMMENT tokens +) + +// Init prepares the scanner s to tokenize the text src by setting the +// scanner at the beginning of src. The scanner uses the file set file +// for position information and it adds line information for each line. +// It is ok to re-use the same file when re-scanning the same file as +// line information which is already present is ignored. Init causes a +// panic if the file size does not match the src size. +// +// Calls to Scan will invoke the error handler err if they encounter a +// syntax error and err is not nil. Also, for each error encountered, +// the Scanner field ErrorCount is incremented by one. The mode parameter +// determines how comments are handled. +// +// Note that Init may call err if there is an error in the first character +// of the file. +// +func (s *Scanner) Init(file *token.File, src []byte, err ErrorHandler, mode Mode) { + // Explicitly initialize all fields since a scanner may be reused. + if file.Size() != len(src) { + panic(fmt.Sprintf("file size (%d) does not match src len (%d)", file.Size(), len(src))) + } + s.file = file + s.dir, _ = filepath.Split(file.Name()) + s.src = src + s.err = err + s.mode = mode + + s.ch = ' ' + s.offset = 0 + s.rdOffset = 0 + s.lineOffset = 0 + s.ErrorCount = 0 + s.nextVal = false + + s.next() +} + +func (s *Scanner) error(offs int, msg string) { + if s.err != nil { + s.err(s.file.Position(s.file.Pos(offs)), msg) + } + s.ErrorCount++ +} + +func (s *Scanner) scanComment() string { + // initial [;#] already consumed + offs := s.offset - 1 // position of initial [;#] + + for s.ch != '\n' && s.ch >= 0 { + s.next() + } + return string(s.src[offs:s.offset]) +} + +func isLetter(ch rune) bool { + return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch >= 0x80 && unicode.IsLetter(ch) +} + +func isDigit(ch rune) bool { + return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch) +} + +func (s *Scanner) scanIdentifier() string { + offs := s.offset + for isLetter(s.ch) || isDigit(s.ch) || s.ch == '-' { + s.next() + } + return string(s.src[offs:s.offset]) +} + +func (s *Scanner) scanEscape(val bool) { + offs := s.offset + ch := s.ch + s.next() // always make progress + switch ch { + case '\\', '"': + // ok + case 'n', 't': + if val { + break // ok + } + fallthrough + default: + s.error(offs, "unknown escape sequence") + } +} + +func (s *Scanner) scanString() string { + // '"' opening already consumed + offs := s.offset - 1 + + for s.ch != '"' { + ch := s.ch + s.next() + if ch == '\n' || ch < 0 { + s.error(offs, "string not terminated") + break + } + if ch == '\\' { + s.scanEscape(false) + } + } + + s.next() + + return string(s.src[offs:s.offset]) +} + +func stripCR(b []byte) []byte { + c := make([]byte, len(b)) + i := 0 + for _, ch := range b { + if ch != '\r' { + c[i] = ch + i++ + } + } + return c[:i] +} + +func (s *Scanner) scanValString() string { + offs := s.offset + + hasCR := false + end := offs + inQuote := false +loop: + for inQuote || s.ch >= 0 && s.ch != '\n' && s.ch != ';' && s.ch != '#' { + ch := s.ch + s.next() + switch { + case inQuote && ch == '\\': + s.scanEscape(true) + case !inQuote && ch == '\\': + if s.ch == '\r' { + hasCR = true + s.next() + } + if s.ch != '\n' { + s.error(offs, "unquoted '\\' must be followed by new line") + break loop + } + s.next() + case ch == '"': + inQuote = !inQuote + case ch == '\r': + hasCR = true + case ch < 0 || inQuote && ch == '\n': + s.error(offs, "string not terminated") + break loop + } + if inQuote || !isWhiteSpace(ch) { + end = s.offset + } + } + + lit := s.src[offs:end] + if hasCR { + lit = stripCR(lit) + } + + return string(lit) +} + +func isWhiteSpace(ch rune) bool { + return ch == ' ' || ch == '\t' || ch == '\r' +} + +func (s *Scanner) skipWhitespace() { + for isWhiteSpace(s.ch) { + s.next() + } +} + +// Scan scans the next token and returns the token position, the token, +// and its literal string if applicable. The source end is indicated by +// token.EOF. +// +// If the returned token is a literal (token.IDENT, token.STRING) or +// token.COMMENT, the literal string has the corresponding value. +// +// If the returned token is token.ILLEGAL, the literal string is the +// offending character. +// +// In all other cases, Scan returns an empty literal string. +// +// For more tolerant parsing, Scan will return a valid token if +// possible even if a syntax error was encountered. Thus, even +// if the resulting token sequence contains no illegal tokens, +// a client may not assume that no error occurred. Instead it +// must check the scanner's ErrorCount or the number of calls +// of the error handler, if there was one installed. +// +// Scan adds line information to the file added to the file +// set with Init. Token positions are relative to that file +// and thus relative to the file set. +// +func (s *Scanner) Scan() (pos token.Pos, tok token.Token, lit string) { +scanAgain: + s.skipWhitespace() + + // current token start + pos = s.file.Pos(s.offset) + + // determine token value + switch ch := s.ch; { + case s.nextVal: + lit = s.scanValString() + tok = token.STRING + s.nextVal = false + case isLetter(ch): + lit = s.scanIdentifier() + tok = token.IDENT + default: + s.next() // always make progress + switch ch { + case -1: + tok = token.EOF + case '\n': + tok = token.EOL + case '"': + tok = token.STRING + lit = s.scanString() + case '[': + tok = token.LBRACK + case ']': + tok = token.RBRACK + case ';', '#': + // comment + lit = s.scanComment() + if s.mode&ScanComments == 0 { + // skip comment + goto scanAgain + } + tok = token.COMMENT + case '=': + tok = token.ASSIGN + s.nextVal = true + default: + s.error(s.file.Offset(pos), fmt.Sprintf("illegal character %#U", ch)) + tok = token.ILLEGAL + lit = string(ch) + } + } + + return +} diff --git a/vendor/src/github.com/scalingdata/gcfg/scanner/scanner_test.go b/vendor/src/github.com/scalingdata/gcfg/scanner/scanner_test.go new file mode 100644 index 0000000..e401573 --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/scanner/scanner_test.go @@ -0,0 +1,417 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package scanner + +import ( + "os" + "strings" + "testing" +) + +import ( + "github.com/scalingdata/gcfg/token" +) + +var fset = token.NewFileSet() + +const /* class */ ( + special = iota + literal + operator +) + +func tokenclass(tok token.Token) int { + switch { + case tok.IsLiteral(): + return literal + case tok.IsOperator(): + return operator + } + return special +} + +type elt struct { + tok token.Token + lit string + class int + pre string + suf string +} + +var tokens = [...]elt{ + // Special tokens + {token.COMMENT, "; a comment", special, "", "\n"}, + {token.COMMENT, "# a comment", special, "", "\n"}, + + // Operators and delimiters + {token.ASSIGN, "=", operator, "", "value"}, + {token.LBRACK, "[", operator, "", ""}, + {token.RBRACK, "]", operator, "", ""}, + {token.EOL, "\n", operator, "", ""}, + + // Identifiers + {token.IDENT, "foobar", literal, "", ""}, + {token.IDENT, "a۰۱۸", literal, "", ""}, + {token.IDENT, "foo६४", literal, "", ""}, + {token.IDENT, "bar9876", literal, "", ""}, + {token.IDENT, "foo-bar", literal, "", ""}, + {token.IDENT, "foo", literal, ";\n", ""}, + // String literals (subsection names) + {token.STRING, `"foobar"`, literal, "", ""}, + {token.STRING, `"\""`, literal, "", ""}, + // String literals (values) + {token.STRING, `"\n"`, literal, "=", ""}, + {token.STRING, `"foobar"`, literal, "=", ""}, + {token.STRING, `"foo\nbar"`, literal, "=", ""}, + {token.STRING, `"foo\"bar"`, literal, "=", ""}, + {token.STRING, `"foo\\bar"`, literal, "=", ""}, + {token.STRING, `"foobar"`, literal, "=", ""}, + {token.STRING, `"foobar"`, literal, "= ", ""}, + {token.STRING, `"foobar"`, literal, "=", "\n"}, + {token.STRING, `"foobar"`, literal, "=", ";"}, + {token.STRING, `"foobar"`, literal, "=", " ;"}, + {token.STRING, `"foobar"`, literal, "=", "#"}, + {token.STRING, `"foobar"`, literal, "=", " #"}, + {token.STRING, "foobar", literal, "=", ""}, + {token.STRING, "foobar", literal, "= ", ""}, + {token.STRING, "foobar", literal, "=", " "}, + {token.STRING, `"foo" "bar"`, literal, "=", " "}, + {token.STRING, "foo\\\nbar", literal, "=", ""}, + {token.STRING, "foo\\\r\nbar", literal, "=", ""}, +} + +const whitespace = " \t \n\n\n" // to separate tokens + +var source = func() []byte { + var src []byte + for _, t := range tokens { + src = append(src, t.pre...) + src = append(src, t.lit...) + src = append(src, t.suf...) + src = append(src, whitespace...) + } + return src +}() + +func newlineCount(s string) int { + n := 0 + for i := 0; i < len(s); i++ { + if s[i] == '\n' { + n++ + } + } + return n +} + +func checkPos(t *testing.T, lit string, p token.Pos, expected token.Position) { + pos := fset.Position(p) + if pos.Filename != expected.Filename { + t.Errorf("bad filename for %q: got %s, expected %s", lit, pos.Filename, expected.Filename) + } + if pos.Offset != expected.Offset { + t.Errorf("bad position for %q: got %d, expected %d", lit, pos.Offset, expected.Offset) + } + if pos.Line != expected.Line { + t.Errorf("bad line for %q: got %d, expected %d", lit, pos.Line, expected.Line) + } + if pos.Column != expected.Column { + t.Errorf("bad column for %q: got %d, expected %d", lit, pos.Column, expected.Column) + } +} + +// Verify that calling Scan() provides the correct results. +func TestScan(t *testing.T) { + // make source + src_linecount := newlineCount(string(source)) + whitespace_linecount := newlineCount(whitespace) + + index := 0 + + // error handler + eh := func(_ token.Position, msg string) { + t.Errorf("%d: error handler called (msg = %s)", index, msg) + } + + // verify scan + var s Scanner + s.Init(fset.AddFile("", fset.Base(), len(source)), source, eh, ScanComments) + // epos is the expected position + epos := token.Position{ + Filename: "", + Offset: 0, + Line: 1, + Column: 1, + } + for { + pos, tok, lit := s.Scan() + if lit == "" { + // no literal value for non-literal tokens + lit = tok.String() + } + e := elt{token.EOF, "", special, "", ""} + if index < len(tokens) { + e = tokens[index] + } + if tok == token.EOF { + lit = "" + epos.Line = src_linecount + epos.Column = 2 + } + if e.pre != "" && strings.ContainsRune("=;#", rune(e.pre[0])) { + epos.Column = 1 + checkPos(t, lit, pos, epos) + var etok token.Token + if e.pre[0] == '=' { + etok = token.ASSIGN + } else { + etok = token.COMMENT + } + if tok != etok { + t.Errorf("bad token for %q: got %q, expected %q", lit, tok, etok) + } + pos, tok, lit = s.Scan() + } + epos.Offset += len(e.pre) + if tok != token.EOF { + epos.Column = 1 + len(e.pre) + } + if e.pre != "" && e.pre[len(e.pre)-1] == '\n' { + epos.Offset-- + epos.Column-- + checkPos(t, lit, pos, epos) + if tok != token.EOL { + t.Errorf("bad token for %q: got %q, expected %q", lit, tok, token.EOL) + } + epos.Line++ + epos.Offset++ + epos.Column = 1 + pos, tok, lit = s.Scan() + } + checkPos(t, lit, pos, epos) + if tok != e.tok { + t.Errorf("bad token for %q: got %q, expected %q", lit, tok, e.tok) + } + if e.tok.IsLiteral() { + // no CRs in value string literals + elit := e.lit + if strings.ContainsRune(e.pre, '=') { + elit = string(stripCR([]byte(elit))) + epos.Offset += len(e.lit) - len(lit) // correct position + } + if lit != elit { + t.Errorf("bad literal for %q: got %q, expected %q", lit, lit, elit) + } + } + if tokenclass(tok) != e.class { + t.Errorf("bad class for %q: got %d, expected %d", lit, tokenclass(tok), e.class) + } + epos.Offset += len(lit) + len(e.suf) + len(whitespace) + epos.Line += newlineCount(lit) + newlineCount(e.suf) + whitespace_linecount + index++ + if tok == token.EOF { + break + } + if e.suf == "value" { + pos, tok, lit = s.Scan() + if tok != token.STRING { + t.Errorf("bad token for %q: got %q, expected %q", lit, tok, token.STRING) + } + } else if strings.ContainsRune(e.suf, ';') || strings.ContainsRune(e.suf, '#') { + pos, tok, lit = s.Scan() + if tok != token.COMMENT { + t.Errorf("bad token for %q: got %q, expected %q", lit, tok, token.COMMENT) + } + } + // skip EOLs + for i := 0; i < whitespace_linecount+newlineCount(e.suf); i++ { + pos, tok, lit = s.Scan() + if tok != token.EOL { + t.Errorf("bad token for %q: got %q, expected %q", lit, tok, token.EOL) + } + } + } + if s.ErrorCount != 0 { + t.Errorf("found %d errors", s.ErrorCount) + } +} + +func TestScanValStringEOF(t *testing.T) { + var s Scanner + src := "= value" + f := fset.AddFile("src", fset.Base(), len(src)) + s.Init(f, []byte(src), nil, 0) + s.Scan() // = + s.Scan() // value + _, tok, _ := s.Scan() // EOF + if tok != token.EOF { + t.Errorf("bad token: got %s, expected %s", tok, token.EOF) + } + if s.ErrorCount > 0 { + t.Error("scanning error") + } +} + +// Verify that initializing the same scanner more then once works correctly. +func TestInit(t *testing.T) { + var s Scanner + + // 1st init + src1 := "\nname = value" + f1 := fset.AddFile("src1", fset.Base(), len(src1)) + s.Init(f1, []byte(src1), nil, 0) + if f1.Size() != len(src1) { + t.Errorf("bad file size: got %d, expected %d", f1.Size(), len(src1)) + } + s.Scan() // \n + s.Scan() // name + _, tok, _ := s.Scan() // = + if tok != token.ASSIGN { + t.Errorf("bad token: got %s, expected %s", tok, token.ASSIGN) + } + + // 2nd init + src2 := "[section]" + f2 := fset.AddFile("src2", fset.Base(), len(src2)) + s.Init(f2, []byte(src2), nil, 0) + if f2.Size() != len(src2) { + t.Errorf("bad file size: got %d, expected %d", f2.Size(), len(src2)) + } + _, tok, _ = s.Scan() // [ + if tok != token.LBRACK { + t.Errorf("bad token: got %s, expected %s", tok, token.LBRACK) + } + + if s.ErrorCount != 0 { + t.Errorf("found %d errors", s.ErrorCount) + } +} + +func TestStdErrorHandler(t *testing.T) { + const src = "@\n" + // illegal character, cause an error + "@ @\n" // two errors on the same line + + var list ErrorList + eh := func(pos token.Position, msg string) { list.Add(pos, msg) } + + var s Scanner + s.Init(fset.AddFile("File1", fset.Base(), len(src)), []byte(src), eh, 0) + for { + if _, tok, _ := s.Scan(); tok == token.EOF { + break + } + } + + if len(list) != s.ErrorCount { + t.Errorf("found %d errors, expected %d", len(list), s.ErrorCount) + } + + if len(list) != 3 { + t.Errorf("found %d raw errors, expected 3", len(list)) + PrintError(os.Stderr, list) + } + + list.Sort() + if len(list) != 3 { + t.Errorf("found %d sorted errors, expected 3", len(list)) + PrintError(os.Stderr, list) + } + + list.RemoveMultiples() + if len(list) != 2 { + t.Errorf("found %d one-per-line errors, expected 2", len(list)) + PrintError(os.Stderr, list) + } +} + +type errorCollector struct { + cnt int // number of errors encountered + msg string // last error message encountered + pos token.Position // last error position encountered +} + +func checkError(t *testing.T, src string, tok token.Token, pos int, err string) { + var s Scanner + var h errorCollector + eh := func(pos token.Position, msg string) { + h.cnt++ + h.msg = msg + h.pos = pos + } + s.Init(fset.AddFile("", fset.Base(), len(src)), []byte(src), eh, ScanComments) + if src[0] == '=' { + _, _, _ = s.Scan() + } + _, tok0, _ := s.Scan() + _, tok1, _ := s.Scan() + if tok0 != tok { + t.Errorf("%q: got %s, expected %s", src, tok0, tok) + } + if tok1 != token.EOF { + t.Errorf("%q: got %s, expected EOF", src, tok1) + } + cnt := 0 + if err != "" { + cnt = 1 + } + if h.cnt != cnt { + t.Errorf("%q: got cnt %d, expected %d", src, h.cnt, cnt) + } + if h.msg != err { + t.Errorf("%q: got msg %q, expected %q", src, h.msg, err) + } + if h.pos.Offset != pos { + t.Errorf("%q: got offset %d, expected %d", src, h.pos.Offset, pos) + } +} + +var errors = []struct { + src string + tok token.Token + pos int + err string +}{ + {"\a", token.ILLEGAL, 0, "illegal character U+0007"}, + {"/", token.ILLEGAL, 0, "illegal character U+002F '/'"}, + {"_", token.ILLEGAL, 0, "illegal character U+005F '_'"}, + {`…`, token.ILLEGAL, 0, "illegal character U+2026 '…'"}, + {`""`, token.STRING, 0, ""}, + {`"`, token.STRING, 0, "string not terminated"}, + {"\"\n", token.STRING, 0, "string not terminated"}, + {`="`, token.STRING, 1, "string not terminated"}, + {"=\"\n", token.STRING, 1, "string not terminated"}, + {"=\\", token.STRING, 1, "unquoted '\\' must be followed by new line"}, + {"=\\\r", token.STRING, 1, "unquoted '\\' must be followed by new line"}, + {`"\z"`, token.STRING, 2, "unknown escape sequence"}, + {`"\a"`, token.STRING, 2, "unknown escape sequence"}, + {`"\b"`, token.STRING, 2, "unknown escape sequence"}, + {`"\f"`, token.STRING, 2, "unknown escape sequence"}, + {`"\r"`, token.STRING, 2, "unknown escape sequence"}, + {`"\t"`, token.STRING, 2, "unknown escape sequence"}, + {`"\v"`, token.STRING, 2, "unknown escape sequence"}, + {`"\0"`, token.STRING, 2, "unknown escape sequence"}, +} + +func TestScanErrors(t *testing.T) { + for _, e := range errors { + checkError(t, e.src, e.tok, e.pos, e.err) + } +} + +func BenchmarkScan(b *testing.B) { + b.StopTimer() + fset := token.NewFileSet() + file := fset.AddFile("", fset.Base(), len(source)) + var s Scanner + b.StartTimer() + for i := b.N - 1; i >= 0; i-- { + s.Init(file, source, nil, ScanComments) + for { + _, tok, _ := s.Scan() + if tok == token.EOF { + break + } + } + } +} diff --git a/vendor/src/github.com/scalingdata/gcfg/set.go b/vendor/src/github.com/scalingdata/gcfg/set.go new file mode 100644 index 0000000..5fa8b7f --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/set.go @@ -0,0 +1,281 @@ +package gcfg + +import ( + "fmt" + "math/big" + "reflect" + "strings" + "unicode" + "unicode/utf8" + + "github.com/scalingdata/gcfg/types" +) + +type tag struct { + ident string + intMode string +} + +func newTag(ts string) tag { + t := tag{} + s := strings.Split(ts, ",") + t.ident = s[0] + for _, tse := range s[1:] { + if strings.HasPrefix(tse, "int=") { + t.intMode = tse[len("int="):] + } + } + return t +} + +func fieldFold(v reflect.Value, name string) (reflect.Value, tag) { + var n string + r0, _ := utf8.DecodeRuneInString(name) + if unicode.IsLetter(r0) && !unicode.IsLower(r0) && !unicode.IsUpper(r0) { + n = "X" + } + n += strings.Replace(name, "-", "_", -1) + f, ok := v.Type().FieldByNameFunc(func(fieldName string) bool { + if !v.FieldByName(fieldName).CanSet() { + return false + } + f, _ := v.Type().FieldByName(fieldName) + t := newTag(f.Tag.Get("gcfg")) + if t.ident != "" { + return strings.EqualFold(t.ident, name) + } + return strings.EqualFold(n, fieldName) + }) + if !ok { + return reflect.Value{}, tag{} + } + return v.FieldByName(f.Name), newTag(f.Tag.Get("gcfg")) +} + +type setter func(destp interface{}, blank bool, val string, t tag) error + +var errUnsupportedType = fmt.Errorf("unsupported type") +var errBlankUnsupported = fmt.Errorf("blank value not supported for type") + +var setters = []setter{ + typeSetter, textUnmarshalerSetter, kindSetter, scanSetter, +} + +func textUnmarshalerSetter(d interface{}, blank bool, val string, t tag) error { + dtu, ok := d.(textUnmarshaler) + if !ok { + return errUnsupportedType + } + if blank { + return errBlankUnsupported + } + return dtu.UnmarshalText([]byte(val)) +} + +func boolSetter(d interface{}, blank bool, val string, t tag) error { + if blank { + reflect.ValueOf(d).Elem().Set(reflect.ValueOf(true)) + return nil + } + b, err := types.ParseBool(val) + if err == nil { + reflect.ValueOf(d).Elem().Set(reflect.ValueOf(b)) + } + return err +} + +func intMode(mode string) types.IntMode { + var m types.IntMode + if strings.ContainsAny(mode, "dD") { + m |= types.Dec + } + if strings.ContainsAny(mode, "hH") { + m |= types.Hex + } + if strings.ContainsAny(mode, "oO") { + m |= types.Oct + } + return m +} + +var typeModes = map[reflect.Type]types.IntMode{ + reflect.TypeOf(int(0)): types.Dec | types.Hex, + reflect.TypeOf(int8(0)): types.Dec | types.Hex, + reflect.TypeOf(int16(0)): types.Dec | types.Hex, + reflect.TypeOf(int32(0)): types.Dec | types.Hex, + reflect.TypeOf(int64(0)): types.Dec | types.Hex, + reflect.TypeOf(uint(0)): types.Dec | types.Hex, + reflect.TypeOf(uint8(0)): types.Dec | types.Hex, + reflect.TypeOf(uint16(0)): types.Dec | types.Hex, + reflect.TypeOf(uint32(0)): types.Dec | types.Hex, + reflect.TypeOf(uint64(0)): types.Dec | types.Hex, + // use default mode (allow dec/hex/oct) for uintptr type + reflect.TypeOf(big.Int{}): types.Dec | types.Hex, +} + +func intModeDefault(t reflect.Type) types.IntMode { + m, ok := typeModes[t] + if !ok { + m = types.Dec | types.Hex | types.Oct + } + return m +} + +func intSetter(d interface{}, blank bool, val string, t tag) error { + if blank { + return errBlankUnsupported + } + mode := intMode(t.intMode) + if mode == 0 { + mode = intModeDefault(reflect.TypeOf(d).Elem()) + } + return types.ParseInt(d, val, mode) +} + +func stringSetter(d interface{}, blank bool, val string, t tag) error { + if blank { + return errBlankUnsupported + } + dsp, ok := d.(*string) + if !ok { + return errUnsupportedType + } + *dsp = val + return nil +} + +var kindSetters = map[reflect.Kind]setter{ + reflect.String: stringSetter, + reflect.Bool: boolSetter, + reflect.Int: intSetter, + reflect.Int8: intSetter, + reflect.Int16: intSetter, + reflect.Int32: intSetter, + reflect.Int64: intSetter, + reflect.Uint: intSetter, + reflect.Uint8: intSetter, + reflect.Uint16: intSetter, + reflect.Uint32: intSetter, + reflect.Uint64: intSetter, + reflect.Uintptr: intSetter, +} + +var typeSetters = map[reflect.Type]setter{ + reflect.TypeOf(big.Int{}): intSetter, +} + +func typeSetter(d interface{}, blank bool, val string, tt tag) error { + t := reflect.ValueOf(d).Type().Elem() + setter, ok := typeSetters[t] + if !ok { + return errUnsupportedType + } + return setter(d, blank, val, tt) +} + +func kindSetter(d interface{}, blank bool, val string, tt tag) error { + k := reflect.ValueOf(d).Type().Elem().Kind() + setter, ok := kindSetters[k] + if !ok { + return errUnsupportedType + } + return setter(d, blank, val, tt) +} + +func scanSetter(d interface{}, blank bool, val string, tt tag) error { + if blank { + return errBlankUnsupported + } + return types.ScanFully(d, val, 'v') +} + +func set(cfg interface{}, sect, sub, name string, blank bool, value string) error { + vPCfg := reflect.ValueOf(cfg) + if vPCfg.Kind() != reflect.Ptr || vPCfg.Elem().Kind() != reflect.Struct { + panic(fmt.Errorf("config must be a pointer to a struct")) + } + vCfg := vPCfg.Elem() + vSect, _ := fieldFold(vCfg, sect) + if !vSect.IsValid() { + return fmt.Errorf("invalid section: section %q", sect) + } + if vSect.Kind() == reflect.Map { + vst := vSect.Type() + if vst.Key().Kind() != reflect.String || + vst.Elem().Kind() != reflect.Ptr || + vst.Elem().Elem().Kind() != reflect.Struct { + panic(fmt.Errorf("map field for section must have string keys and "+ + " pointer-to-struct values: section %q", sect)) + } + if vSect.IsNil() { + vSect.Set(reflect.MakeMap(vst)) + } + k := reflect.ValueOf(sub) + pv := vSect.MapIndex(k) + if !pv.IsValid() { + vType := vSect.Type().Elem().Elem() + pv = reflect.New(vType) + vSect.SetMapIndex(k, pv) + } + vSect = pv.Elem() + } else if vSect.Kind() != reflect.Struct { + panic(fmt.Errorf("field for section must be a map or a struct: "+ + "section %q", sect)) + } else if sub != "" { + return fmt.Errorf("invalid subsection: "+ + "section %q subsection %q", sect, sub) + } + vVar, t := fieldFold(vSect, name) + if !vVar.IsValid() { + return fmt.Errorf("invalid variable: "+ + "section %q subsection %q variable %q", sect, sub, name) + } + // vVal is either single-valued var, or newly allocated value within multi-valued var + var vVal reflect.Value + // multi-value if unnamed slice type + isMulti := vVar.Type().Name() == "" && vVar.Kind() == reflect.Slice + if isMulti && blank { + vVar.Set(reflect.Zero(vVar.Type())) + return nil + } + if isMulti { + vVal = reflect.New(vVar.Type().Elem()).Elem() + } else { + vVal = vVar + } + isDeref := vVal.Type().Name() == "" && vVal.Type().Kind() == reflect.Ptr + isNew := isDeref && vVal.IsNil() + // vAddr is address of value to set (dereferenced & allocated as needed) + var vAddr reflect.Value + switch { + case isNew: + vAddr = reflect.New(vVal.Type().Elem()) + case isDeref && !isNew: + vAddr = vVal + default: + vAddr = vVal.Addr() + } + vAddrI := vAddr.Interface() + err, ok := error(nil), false + for _, s := range setters { + err = s(vAddrI, blank, value, t) + if err == nil { + ok = true + break + } + if err != errUnsupportedType { + return err + } + } + if !ok { + // in case all setters returned errUnsupportedType + return err + } + if isNew { // set reference if it was dereferenced and newly allocated + vVal.Set(vAddr) + } + if isMulti { // append if multi-valued + vVar.Set(reflect.Append(vVar, vVal)) + } + return nil +} diff --git a/vendor/src/github.com/scalingdata/gcfg/testdata/gcfg_test.gcfg b/vendor/src/github.com/scalingdata/gcfg/testdata/gcfg_test.gcfg new file mode 100644 index 0000000..cddff29 --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/testdata/gcfg_test.gcfg @@ -0,0 +1,3 @@ +; Comment line +[section] +name=value # comment diff --git a/vendor/src/github.com/scalingdata/gcfg/testdata/gcfg_unicode_test.gcfg b/vendor/src/github.com/scalingdata/gcfg/testdata/gcfg_unicode_test.gcfg new file mode 100644 index 0000000..3762a20 --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/testdata/gcfg_unicode_test.gcfg @@ -0,0 +1,3 @@ +; Comment line +[甲] +乙=丙 # comment diff --git a/vendor/src/github.com/scalingdata/gcfg/token/position.go b/vendor/src/github.com/scalingdata/gcfg/token/position.go new file mode 100644 index 0000000..fc45c1e --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/token/position.go @@ -0,0 +1,435 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TODO(gri) consider making this a separate package outside the go directory. + +package token + +import ( + "fmt" + "sort" + "sync" +) + +// ----------------------------------------------------------------------------- +// Positions + +// Position describes an arbitrary source position +// including the file, line, and column location. +// A Position is valid if the line number is > 0. +// +type Position struct { + Filename string // filename, if any + Offset int // offset, starting at 0 + Line int // line number, starting at 1 + Column int // column number, starting at 1 (character count) +} + +// IsValid returns true if the position is valid. +func (pos *Position) IsValid() bool { return pos.Line > 0 } + +// String returns a string in one of several forms: +// +// file:line:column valid position with file name +// line:column valid position without file name +// file invalid position with file name +// - invalid position without file name +// +func (pos Position) String() string { + s := pos.Filename + if pos.IsValid() { + if s != "" { + s += ":" + } + s += fmt.Sprintf("%d:%d", pos.Line, pos.Column) + } + if s == "" { + s = "-" + } + return s +} + +// Pos is a compact encoding of a source position within a file set. +// It can be converted into a Position for a more convenient, but much +// larger, representation. +// +// The Pos value for a given file is a number in the range [base, base+size], +// where base and size are specified when adding the file to the file set via +// AddFile. +// +// To create the Pos value for a specific source offset, first add +// the respective file to the current file set (via FileSet.AddFile) +// and then call File.Pos(offset) for that file. Given a Pos value p +// for a specific file set fset, the corresponding Position value is +// obtained by calling fset.Position(p). +// +// Pos values can be compared directly with the usual comparison operators: +// If two Pos values p and q are in the same file, comparing p and q is +// equivalent to comparing the respective source file offsets. If p and q +// are in different files, p < q is true if the file implied by p was added +// to the respective file set before the file implied by q. +// +type Pos int + +// The zero value for Pos is NoPos; there is no file and line information +// associated with it, and NoPos().IsValid() is false. NoPos is always +// smaller than any other Pos value. The corresponding Position value +// for NoPos is the zero value for Position. +// +const NoPos Pos = 0 + +// IsValid returns true if the position is valid. +func (p Pos) IsValid() bool { + return p != NoPos +} + +// ----------------------------------------------------------------------------- +// File + +// A File is a handle for a file belonging to a FileSet. +// A File has a name, size, and line offset table. +// +type File struct { + set *FileSet + name string // file name as provided to AddFile + base int // Pos value range for this file is [base...base+size] + size int // file size as provided to AddFile + + // lines and infos are protected by set.mutex + lines []int + infos []lineInfo +} + +// Name returns the file name of file f as registered with AddFile. +func (f *File) Name() string { + return f.name +} + +// Base returns the base offset of file f as registered with AddFile. +func (f *File) Base() int { + return f.base +} + +// Size returns the size of file f as registered with AddFile. +func (f *File) Size() int { + return f.size +} + +// LineCount returns the number of lines in file f. +func (f *File) LineCount() int { + f.set.mutex.RLock() + n := len(f.lines) + f.set.mutex.RUnlock() + return n +} + +// AddLine adds the line offset for a new line. +// The line offset must be larger than the offset for the previous line +// and smaller than the file size; otherwise the line offset is ignored. +// +func (f *File) AddLine(offset int) { + f.set.mutex.Lock() + if i := len(f.lines); (i == 0 || f.lines[i-1] < offset) && offset < f.size { + f.lines = append(f.lines, offset) + } + f.set.mutex.Unlock() +} + +// SetLines sets the line offsets for a file and returns true if successful. +// The line offsets are the offsets of the first character of each line; +// for instance for the content "ab\nc\n" the line offsets are {0, 3}. +// An empty file has an empty line offset table. +// Each line offset must be larger than the offset for the previous line +// and smaller than the file size; otherwise SetLines fails and returns +// false. +// +func (f *File) SetLines(lines []int) bool { + // verify validity of lines table + size := f.size + for i, offset := range lines { + if i > 0 && offset <= lines[i-1] || size <= offset { + return false + } + } + + // set lines table + f.set.mutex.Lock() + f.lines = lines + f.set.mutex.Unlock() + return true +} + +// SetLinesForContent sets the line offsets for the given file content. +func (f *File) SetLinesForContent(content []byte) { + var lines []int + line := 0 + for offset, b := range content { + if line >= 0 { + lines = append(lines, line) + } + line = -1 + if b == '\n' { + line = offset + 1 + } + } + + // set lines table + f.set.mutex.Lock() + f.lines = lines + f.set.mutex.Unlock() +} + +// A lineInfo object describes alternative file and line number +// information (such as provided via a //line comment in a .go +// file) for a given file offset. +type lineInfo struct { + // fields are exported to make them accessible to gob + Offset int + Filename string + Line int +} + +// AddLineInfo adds alternative file and line number information for +// a given file offset. The offset must be larger than the offset for +// the previously added alternative line info and smaller than the +// file size; otherwise the information is ignored. +// +// AddLineInfo is typically used to register alternative position +// information for //line filename:line comments in source files. +// +func (f *File) AddLineInfo(offset int, filename string, line int) { + f.set.mutex.Lock() + if i := len(f.infos); i == 0 || f.infos[i-1].Offset < offset && offset < f.size { + f.infos = append(f.infos, lineInfo{offset, filename, line}) + } + f.set.mutex.Unlock() +} + +// Pos returns the Pos value for the given file offset; +// the offset must be <= f.Size(). +// f.Pos(f.Offset(p)) == p. +// +func (f *File) Pos(offset int) Pos { + if offset > f.size { + panic("illegal file offset") + } + return Pos(f.base + offset) +} + +// Offset returns the offset for the given file position p; +// p must be a valid Pos value in that file. +// f.Offset(f.Pos(offset)) == offset. +// +func (f *File) Offset(p Pos) int { + if int(p) < f.base || int(p) > f.base+f.size { + panic("illegal Pos value") + } + return int(p) - f.base +} + +// Line returns the line number for the given file position p; +// p must be a Pos value in that file or NoPos. +// +func (f *File) Line(p Pos) int { + // TODO(gri) this can be implemented much more efficiently + return f.Position(p).Line +} + +func searchLineInfos(a []lineInfo, x int) int { + return sort.Search(len(a), func(i int) bool { return a[i].Offset > x }) - 1 +} + +// info returns the file name, line, and column number for a file offset. +func (f *File) info(offset int) (filename string, line, column int) { + filename = f.name + if i := searchInts(f.lines, offset); i >= 0 { + line, column = i+1, offset-f.lines[i]+1 + } + if len(f.infos) > 0 { + // almost no files have extra line infos + if i := searchLineInfos(f.infos, offset); i >= 0 { + alt := &f.infos[i] + filename = alt.Filename + if i := searchInts(f.lines, alt.Offset); i >= 0 { + line += alt.Line - i - 1 + } + } + } + return +} + +func (f *File) position(p Pos) (pos Position) { + offset := int(p) - f.base + pos.Offset = offset + pos.Filename, pos.Line, pos.Column = f.info(offset) + return +} + +// Position returns the Position value for the given file position p; +// p must be a Pos value in that file or NoPos. +// +func (f *File) Position(p Pos) (pos Position) { + if p != NoPos { + if int(p) < f.base || int(p) > f.base+f.size { + panic("illegal Pos value") + } + pos = f.position(p) + } + return +} + +// ----------------------------------------------------------------------------- +// FileSet + +// A FileSet represents a set of source files. +// Methods of file sets are synchronized; multiple goroutines +// may invoke them concurrently. +// +type FileSet struct { + mutex sync.RWMutex // protects the file set + base int // base offset for the next file + files []*File // list of files in the order added to the set + last *File // cache of last file looked up +} + +// NewFileSet creates a new file set. +func NewFileSet() *FileSet { + s := new(FileSet) + s.base = 1 // 0 == NoPos + return s +} + +// Base returns the minimum base offset that must be provided to +// AddFile when adding the next file. +// +func (s *FileSet) Base() int { + s.mutex.RLock() + b := s.base + s.mutex.RUnlock() + return b + +} + +// AddFile adds a new file with a given filename, base offset, and file size +// to the file set s and returns the file. Multiple files may have the same +// name. The base offset must not be smaller than the FileSet's Base(), and +// size must not be negative. +// +// Adding the file will set the file set's Base() value to base + size + 1 +// as the minimum base value for the next file. The following relationship +// exists between a Pos value p for a given file offset offs: +// +// int(p) = base + offs +// +// with offs in the range [0, size] and thus p in the range [base, base+size]. +// For convenience, File.Pos may be used to create file-specific position +// values from a file offset. +// +func (s *FileSet) AddFile(filename string, base, size int) *File { + s.mutex.Lock() + defer s.mutex.Unlock() + if base < s.base || size < 0 { + panic("illegal base or size") + } + // base >= s.base && size >= 0 + f := &File{s, filename, base, size, []int{0}, nil} + base += size + 1 // +1 because EOF also has a position + if base < 0 { + panic("token.Pos offset overflow (> 2G of source code in file set)") + } + // add the file to the file set + s.base = base + s.files = append(s.files, f) + s.last = f + return f +} + +// Iterate calls f for the files in the file set in the order they were added +// until f returns false. +// +func (s *FileSet) Iterate(f func(*File) bool) { + for i := 0; ; i++ { + var file *File + s.mutex.RLock() + if i < len(s.files) { + file = s.files[i] + } + s.mutex.RUnlock() + if file == nil || !f(file) { + break + } + } +} + +func searchFiles(a []*File, x int) int { + return sort.Search(len(a), func(i int) bool { return a[i].base > x }) - 1 +} + +func (s *FileSet) file(p Pos) *File { + // common case: p is in last file + if f := s.last; f != nil && f.base <= int(p) && int(p) <= f.base+f.size { + return f + } + // p is not in last file - search all files + if i := searchFiles(s.files, int(p)); i >= 0 { + f := s.files[i] + // f.base <= int(p) by definition of searchFiles + if int(p) <= f.base+f.size { + s.last = f + return f + } + } + return nil +} + +// File returns the file that contains the position p. +// If no such file is found (for instance for p == NoPos), +// the result is nil. +// +func (s *FileSet) File(p Pos) (f *File) { + if p != NoPos { + s.mutex.RLock() + f = s.file(p) + s.mutex.RUnlock() + } + return +} + +// Position converts a Pos in the fileset into a general Position. +func (s *FileSet) Position(p Pos) (pos Position) { + if p != NoPos { + s.mutex.RLock() + if f := s.file(p); f != nil { + pos = f.position(p) + } + s.mutex.RUnlock() + } + return +} + +// ----------------------------------------------------------------------------- +// Helper functions + +func searchInts(a []int, x int) int { + // This function body is a manually inlined version of: + // + // return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1 + // + // With better compiler optimizations, this may not be needed in the + // future, but at the moment this change improves the go/printer + // benchmark performance by ~30%. This has a direct impact on the + // speed of gofmt and thus seems worthwhile (2011-04-29). + // TODO(gri): Remove this when compilers have caught up. + i, j := 0, len(a) + for i < j { + h := i + (j-i)/2 // avoid overflow when computing h + // i ≤ h < j + if a[h] <= x { + i = h + 1 + } else { + j = h + } + } + return i - 1 +} diff --git a/vendor/src/github.com/scalingdata/gcfg/token/position_test.go b/vendor/src/github.com/scalingdata/gcfg/token/position_test.go new file mode 100644 index 0000000..160107d --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/token/position_test.go @@ -0,0 +1,181 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package token + +import ( + "fmt" + "testing" +) + +func checkPos(t *testing.T, msg string, p, q Position) { + if p.Filename != q.Filename { + t.Errorf("%s: expected filename = %q; got %q", msg, q.Filename, p.Filename) + } + if p.Offset != q.Offset { + t.Errorf("%s: expected offset = %d; got %d", msg, q.Offset, p.Offset) + } + if p.Line != q.Line { + t.Errorf("%s: expected line = %d; got %d", msg, q.Line, p.Line) + } + if p.Column != q.Column { + t.Errorf("%s: expected column = %d; got %d", msg, q.Column, p.Column) + } +} + +func TestNoPos(t *testing.T) { + if NoPos.IsValid() { + t.Errorf("NoPos should not be valid") + } + var fset *FileSet + checkPos(t, "nil NoPos", fset.Position(NoPos), Position{}) + fset = NewFileSet() + checkPos(t, "fset NoPos", fset.Position(NoPos), Position{}) +} + +var tests = []struct { + filename string + source []byte // may be nil + size int + lines []int +}{ + {"a", []byte{}, 0, []int{}}, + {"b", []byte("01234"), 5, []int{0}}, + {"c", []byte("\n\n\n\n\n\n\n\n\n"), 9, []int{0, 1, 2, 3, 4, 5, 6, 7, 8}}, + {"d", nil, 100, []int{0, 5, 10, 20, 30, 70, 71, 72, 80, 85, 90, 99}}, + {"e", nil, 777, []int{0, 80, 100, 120, 130, 180, 267, 455, 500, 567, 620}}, + {"f", []byte("package p\n\nimport \"fmt\""), 23, []int{0, 10, 11}}, + {"g", []byte("package p\n\nimport \"fmt\"\n"), 24, []int{0, 10, 11}}, + {"h", []byte("package p\n\nimport \"fmt\"\n "), 25, []int{0, 10, 11, 24}}, +} + +func linecol(lines []int, offs int) (int, int) { + prevLineOffs := 0 + for line, lineOffs := range lines { + if offs < lineOffs { + return line, offs - prevLineOffs + 1 + } + prevLineOffs = lineOffs + } + return len(lines), offs - prevLineOffs + 1 +} + +func verifyPositions(t *testing.T, fset *FileSet, f *File, lines []int) { + for offs := 0; offs < f.Size(); offs++ { + p := f.Pos(offs) + offs2 := f.Offset(p) + if offs2 != offs { + t.Errorf("%s, Offset: expected offset %d; got %d", f.Name(), offs, offs2) + } + line, col := linecol(lines, offs) + msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p) + checkPos(t, msg, f.Position(f.Pos(offs)), Position{f.Name(), offs, line, col}) + checkPos(t, msg, fset.Position(p), Position{f.Name(), offs, line, col}) + } +} + +func makeTestSource(size int, lines []int) []byte { + src := make([]byte, size) + for _, offs := range lines { + if offs > 0 { + src[offs-1] = '\n' + } + } + return src +} + +func TestPositions(t *testing.T) { + const delta = 7 // a non-zero base offset increment + fset := NewFileSet() + for _, test := range tests { + // verify consistency of test case + if test.source != nil && len(test.source) != test.size { + t.Errorf("%s: inconsistent test case: expected file size %d; got %d", test.filename, test.size, len(test.source)) + } + + // add file and verify name and size + f := fset.AddFile(test.filename, fset.Base()+delta, test.size) + if f.Name() != test.filename { + t.Errorf("expected filename %q; got %q", test.filename, f.Name()) + } + if f.Size() != test.size { + t.Errorf("%s: expected file size %d; got %d", f.Name(), test.size, f.Size()) + } + if fset.File(f.Pos(0)) != f { + t.Errorf("%s: f.Pos(0) was not found in f", f.Name()) + } + + // add lines individually and verify all positions + for i, offset := range test.lines { + f.AddLine(offset) + if f.LineCount() != i+1 { + t.Errorf("%s, AddLine: expected line count %d; got %d", f.Name(), i+1, f.LineCount()) + } + // adding the same offset again should be ignored + f.AddLine(offset) + if f.LineCount() != i+1 { + t.Errorf("%s, AddLine: expected unchanged line count %d; got %d", f.Name(), i+1, f.LineCount()) + } + verifyPositions(t, fset, f, test.lines[0:i+1]) + } + + // add lines with SetLines and verify all positions + if ok := f.SetLines(test.lines); !ok { + t.Errorf("%s: SetLines failed", f.Name()) + } + if f.LineCount() != len(test.lines) { + t.Errorf("%s, SetLines: expected line count %d; got %d", f.Name(), len(test.lines), f.LineCount()) + } + verifyPositions(t, fset, f, test.lines) + + // add lines with SetLinesForContent and verify all positions + src := test.source + if src == nil { + // no test source available - create one from scratch + src = makeTestSource(test.size, test.lines) + } + f.SetLinesForContent(src) + if f.LineCount() != len(test.lines) { + t.Errorf("%s, SetLinesForContent: expected line count %d; got %d", f.Name(), len(test.lines), f.LineCount()) + } + verifyPositions(t, fset, f, test.lines) + } +} + +func TestLineInfo(t *testing.T) { + fset := NewFileSet() + f := fset.AddFile("foo", fset.Base(), 500) + lines := []int{0, 42, 77, 100, 210, 220, 277, 300, 333, 401} + // add lines individually and provide alternative line information + for _, offs := range lines { + f.AddLine(offs) + f.AddLineInfo(offs, "bar", 42) + } + // verify positions for all offsets + for offs := 0; offs <= f.Size(); offs++ { + p := f.Pos(offs) + _, col := linecol(lines, offs) + msg := fmt.Sprintf("%s (offs = %d, p = %d)", f.Name(), offs, p) + checkPos(t, msg, f.Position(f.Pos(offs)), Position{"bar", offs, 42, col}) + checkPos(t, msg, fset.Position(p), Position{"bar", offs, 42, col}) + } +} + +func TestFiles(t *testing.T) { + fset := NewFileSet() + for i, test := range tests { + fset.AddFile(test.filename, fset.Base(), test.size) + j := 0 + fset.Iterate(func(f *File) bool { + if f.Name() != tests[j].filename { + t.Errorf("expected filename = %s; got %s", tests[j].filename, f.Name()) + } + j++ + return true + }) + if j != i+1 { + t.Errorf("expected %d files; got %d", i+1, j) + } + } +} diff --git a/vendor/src/github.com/scalingdata/gcfg/token/serialize.go b/vendor/src/github.com/scalingdata/gcfg/token/serialize.go new file mode 100644 index 0000000..4adc8f9 --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/token/serialize.go @@ -0,0 +1,56 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package token + +type serializedFile struct { + // fields correspond 1:1 to fields with same (lower-case) name in File + Name string + Base int + Size int + Lines []int + Infos []lineInfo +} + +type serializedFileSet struct { + Base int + Files []serializedFile +} + +// Read calls decode to deserialize a file set into s; s must not be nil. +func (s *FileSet) Read(decode func(interface{}) error) error { + var ss serializedFileSet + if err := decode(&ss); err != nil { + return err + } + + s.mutex.Lock() + s.base = ss.Base + files := make([]*File, len(ss.Files)) + for i := 0; i < len(ss.Files); i++ { + f := &ss.Files[i] + files[i] = &File{s, f.Name, f.Base, f.Size, f.Lines, f.Infos} + } + s.files = files + s.last = nil + s.mutex.Unlock() + + return nil +} + +// Write calls encode to serialize the file set s. +func (s *FileSet) Write(encode func(interface{}) error) error { + var ss serializedFileSet + + s.mutex.Lock() + ss.Base = s.base + files := make([]serializedFile, len(s.files)) + for i, f := range s.files { + files[i] = serializedFile{f.name, f.base, f.size, f.lines, f.infos} + } + ss.Files = files + s.mutex.Unlock() + + return encode(ss) +} diff --git a/vendor/src/github.com/scalingdata/gcfg/token/serialize_test.go b/vendor/src/github.com/scalingdata/gcfg/token/serialize_test.go new file mode 100644 index 0000000..4e925ad --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/token/serialize_test.go @@ -0,0 +1,111 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package token + +import ( + "bytes" + "encoding/gob" + "fmt" + "testing" +) + +// equal returns nil if p and q describe the same file set; +// otherwise it returns an error describing the discrepancy. +func equal(p, q *FileSet) error { + if p == q { + // avoid deadlock if p == q + return nil + } + + // not strictly needed for the test + p.mutex.Lock() + q.mutex.Lock() + defer q.mutex.Unlock() + defer p.mutex.Unlock() + + if p.base != q.base { + return fmt.Errorf("different bases: %d != %d", p.base, q.base) + } + + if len(p.files) != len(q.files) { + return fmt.Errorf("different number of files: %d != %d", len(p.files), len(q.files)) + } + + for i, f := range p.files { + g := q.files[i] + if f.set != p { + return fmt.Errorf("wrong fileset for %q", f.name) + } + if g.set != q { + return fmt.Errorf("wrong fileset for %q", g.name) + } + if f.name != g.name { + return fmt.Errorf("different filenames: %q != %q", f.name, g.name) + } + if f.base != g.base { + return fmt.Errorf("different base for %q: %d != %d", f.name, f.base, g.base) + } + if f.size != g.size { + return fmt.Errorf("different size for %q: %d != %d", f.name, f.size, g.size) + } + for j, l := range f.lines { + m := g.lines[j] + if l != m { + return fmt.Errorf("different offsets for %q", f.name) + } + } + for j, l := range f.infos { + m := g.infos[j] + if l.Offset != m.Offset || l.Filename != m.Filename || l.Line != m.Line { + return fmt.Errorf("different infos for %q", f.name) + } + } + } + + // we don't care about .last - it's just a cache + return nil +} + +func checkSerialize(t *testing.T, p *FileSet) { + var buf bytes.Buffer + encode := func(x interface{}) error { + return gob.NewEncoder(&buf).Encode(x) + } + if err := p.Write(encode); err != nil { + t.Errorf("writing fileset failed: %s", err) + return + } + q := NewFileSet() + decode := func(x interface{}) error { + return gob.NewDecoder(&buf).Decode(x) + } + if err := q.Read(decode); err != nil { + t.Errorf("reading fileset failed: %s", err) + return + } + if err := equal(p, q); err != nil { + t.Errorf("filesets not identical: %s", err) + } +} + +func TestSerialization(t *testing.T) { + p := NewFileSet() + checkSerialize(t, p) + // add some files + for i := 0; i < 10; i++ { + f := p.AddFile(fmt.Sprintf("file%d", i), p.Base()+i, i*100) + checkSerialize(t, p) + // add some lines and alternative file infos + line := 1000 + for offs := 0; offs < f.Size(); offs += 40 + i { + f.AddLine(offs) + if offs%7 == 0 { + f.AddLineInfo(offs, fmt.Sprintf("file%d", offs), line) + line += 33 + } + } + checkSerialize(t, p) + } +} diff --git a/vendor/src/github.com/scalingdata/gcfg/token/token.go b/vendor/src/github.com/scalingdata/gcfg/token/token.go new file mode 100644 index 0000000..b3c7c83 --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/token/token.go @@ -0,0 +1,83 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package token defines constants representing the lexical tokens of the gcfg +// configuration syntax and basic operations on tokens (printing, predicates). +// +// Note that the API for the token package may change to accommodate new +// features or implementation changes in gcfg. +// +package token + +import "strconv" + +// Token is the set of lexical tokens of the gcfg configuration syntax. +type Token int + +// The list of tokens. +const ( + // Special tokens + ILLEGAL Token = iota + EOF + COMMENT + + literal_beg + // Identifiers and basic type literals + // (these tokens stand for classes of literals) + IDENT // section-name, variable-name + STRING // "subsection-name", variable value + literal_end + + operator_beg + // Operators and delimiters + ASSIGN // = + LBRACK // [ + RBRACK // ] + EOL // \n + operator_end +) + +var tokens = [...]string{ + ILLEGAL: "ILLEGAL", + + EOF: "EOF", + COMMENT: "COMMENT", + + IDENT: "IDENT", + STRING: "STRING", + + ASSIGN: "=", + LBRACK: "[", + RBRACK: "]", + EOL: "\n", +} + +// String returns the string corresponding to the token tok. +// For operators and delimiters, the string is the actual token character +// sequence (e.g., for the token ASSIGN, the string is "="). For all other +// tokens the string corresponds to the token constant name (e.g. for the +// token IDENT, the string is "IDENT"). +// +func (tok Token) String() string { + s := "" + if 0 <= tok && tok < Token(len(tokens)) { + s = tokens[tok] + } + if s == "" { + s = "token(" + strconv.Itoa(int(tok)) + ")" + } + return s +} + +// Predicates + +// IsLiteral returns true for tokens corresponding to identifiers +// and basic type literals; it returns false otherwise. +// +func (tok Token) IsLiteral() bool { return literal_beg < tok && tok < literal_end } + +// IsOperator returns true for tokens corresponding to operators and +// delimiters; it returns false otherwise. +// +func (tok Token) IsOperator() bool { return operator_beg < tok && tok < operator_end } diff --git a/vendor/src/github.com/scalingdata/gcfg/types/bool.go b/vendor/src/github.com/scalingdata/gcfg/types/bool.go new file mode 100644 index 0000000..8dcae0d --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/types/bool.go @@ -0,0 +1,23 @@ +package types + +// BoolValues defines the name and value mappings for ParseBool. +var BoolValues = map[string]interface{}{ + "true": true, "yes": true, "on": true, "1": true, + "false": false, "no": false, "off": false, "0": false, +} + +var boolParser = func() *EnumParser { + ep := &EnumParser{} + ep.AddVals(BoolValues) + return ep +}() + +// ParseBool parses bool values according to the definitions in BoolValues. +// Parsing is case-insensitive. +func ParseBool(s string) (bool, error) { + v, err := boolParser.Parse(s) + if err != nil { + return false, err + } + return v.(bool), nil +} diff --git a/vendor/src/github.com/scalingdata/gcfg/types/doc.go b/vendor/src/github.com/scalingdata/gcfg/types/doc.go new file mode 100644 index 0000000..9f9c345 --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/types/doc.go @@ -0,0 +1,4 @@ +// Package types defines helpers for type conversions. +// +// The API for this package is not finalized yet. +package types diff --git a/vendor/src/github.com/scalingdata/gcfg/types/enum.go b/vendor/src/github.com/scalingdata/gcfg/types/enum.go new file mode 100644 index 0000000..1a0c7ef --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/types/enum.go @@ -0,0 +1,44 @@ +package types + +import ( + "fmt" + "reflect" + "strings" +) + +// EnumParser parses "enum" values; i.e. a predefined set of strings to +// predefined values. +type EnumParser struct { + Type string // type name; if not set, use type of first value added + CaseMatch bool // if true, matching of strings is case-sensitive + // PrefixMatch bool + vals map[string]interface{} +} + +// AddVals adds strings and values to an EnumParser. +func (ep *EnumParser) AddVals(vals map[string]interface{}) { + if ep.vals == nil { + ep.vals = make(map[string]interface{}) + } + for k, v := range vals { + if ep.Type == "" { + ep.Type = reflect.TypeOf(v).Name() + } + if !ep.CaseMatch { + k = strings.ToLower(k) + } + ep.vals[k] = v + } +} + +// Parse parses the string and returns the value or an error. +func (ep EnumParser) Parse(s string) (interface{}, error) { + if !ep.CaseMatch { + s = strings.ToLower(s) + } + v, ok := ep.vals[s] + if !ok { + return false, fmt.Errorf("failed to parse %s %#q", ep.Type, s) + } + return v, nil +} diff --git a/vendor/src/github.com/scalingdata/gcfg/types/enum_test.go b/vendor/src/github.com/scalingdata/gcfg/types/enum_test.go new file mode 100644 index 0000000..4bf135e --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/types/enum_test.go @@ -0,0 +1,29 @@ +package types + +import ( + "testing" +) + +func TestEnumParserBool(t *testing.T) { + for _, tt := range []struct { + val string + res bool + ok bool + }{ + {val: "tRuE", res: true, ok: true}, + {val: "False", res: false, ok: true}, + {val: "t", ok: false}, + } { + b, err := ParseBool(tt.val) + switch { + case tt.ok && err != nil: + t.Errorf("%q: got error %v, want %v", tt.val, err, tt.res) + case !tt.ok && err == nil: + t.Errorf("%q: got %v, want error", tt.val, b) + case tt.ok && b != tt.res: + t.Errorf("%q: got %v, want %v", tt.val, b, tt.res) + default: + t.Logf("%q: got %v, %v", tt.val, b, err) + } + } +} diff --git a/vendor/src/github.com/scalingdata/gcfg/types/int.go b/vendor/src/github.com/scalingdata/gcfg/types/int.go new file mode 100644 index 0000000..af7e75c --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/types/int.go @@ -0,0 +1,86 @@ +package types + +import ( + "fmt" + "strings" +) + +// An IntMode is a mode for parsing integer values, representing a set of +// accepted bases. +type IntMode uint8 + +// IntMode values for ParseInt; can be combined using binary or. +const ( + Dec IntMode = 1 << iota + Hex + Oct +) + +// String returns a string representation of IntMode; e.g. `IntMode(Dec|Hex)`. +func (m IntMode) String() string { + var modes []string + if m&Dec != 0 { + modes = append(modes, "Dec") + } + if m&Hex != 0 { + modes = append(modes, "Hex") + } + if m&Oct != 0 { + modes = append(modes, "Oct") + } + return "IntMode(" + strings.Join(modes, "|") + ")" +} + +var errIntAmbig = fmt.Errorf("ambiguous integer value; must include '0' prefix") + +func prefix0(val string) bool { + return strings.HasPrefix(val, "0") || strings.HasPrefix(val, "-0") +} + +func prefix0x(val string) bool { + return strings.HasPrefix(val, "0x") || strings.HasPrefix(val, "-0x") +} + +// ParseInt parses val using mode into intptr, which must be a pointer to an +// integer kind type. Non-decimal value require prefix `0` or `0x` in the cases +// when mode permits ambiguity of base; otherwise the prefix can be omitted. +func ParseInt(intptr interface{}, val string, mode IntMode) error { + val = strings.TrimSpace(val) + verb := byte(0) + switch mode { + case Dec: + verb = 'd' + case Dec + Hex: + if prefix0x(val) { + verb = 'v' + } else { + verb = 'd' + } + case Dec + Oct: + if prefix0(val) && !prefix0x(val) { + verb = 'v' + } else { + verb = 'd' + } + case Dec + Hex + Oct: + verb = 'v' + case Hex: + if prefix0x(val) { + verb = 'v' + } else { + verb = 'x' + } + case Oct: + verb = 'o' + case Hex + Oct: + if prefix0(val) { + verb = 'v' + } else { + return errIntAmbig + } + } + if verb == 0 { + panic("unsupported mode") + } + return ScanFully(intptr, val, verb) +} diff --git a/vendor/src/github.com/scalingdata/gcfg/types/int_test.go b/vendor/src/github.com/scalingdata/gcfg/types/int_test.go new file mode 100644 index 0000000..b63dbcb --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/types/int_test.go @@ -0,0 +1,67 @@ +package types + +import ( + "reflect" + "testing" +) + +func elem(p interface{}) interface{} { + return reflect.ValueOf(p).Elem().Interface() +} + +func TestParseInt(t *testing.T) { + for _, tt := range []struct { + val string + mode IntMode + exp interface{} + ok bool + }{ + {"0", Dec, int(0), true}, + {"10", Dec, int(10), true}, + {"-10", Dec, int(-10), true}, + {"x", Dec, int(0), false}, + {"0xa", Hex, int(0xa), true}, + {"a", Hex, int(0xa), true}, + {"10", Hex, int(0x10), true}, + {"-0xa", Hex, int(-0xa), true}, + {"0x", Hex, int(0x0), true}, // Scanf doesn't require digit behind 0x + {"-0x", Hex, int(0x0), true}, // Scanf doesn't require digit behind 0x + {"-a", Hex, int(-0xa), true}, + {"-10", Hex, int(-0x10), true}, + {"x", Hex, int(0), false}, + {"10", Oct, int(010), true}, + {"010", Oct, int(010), true}, + {"-10", Oct, int(-010), true}, + {"-010", Oct, int(-010), true}, + {"10", Dec | Hex, int(10), true}, + {"010", Dec | Hex, int(10), true}, + {"0x10", Dec | Hex, int(0x10), true}, + {"10", Dec | Oct, int(10), true}, + {"010", Dec | Oct, int(010), true}, + {"0x10", Dec | Oct, int(0), false}, + {"10", Hex | Oct, int(0), false}, // need prefix to distinguish Hex/Oct + {"010", Hex | Oct, int(010), true}, + {"0x10", Hex | Oct, int(0x10), true}, + {"10", Dec | Hex | Oct, int(10), true}, + {"010", Dec | Hex | Oct, int(010), true}, + {"0x10", Dec | Hex | Oct, int(0x10), true}, + } { + typ := reflect.TypeOf(tt.exp) + res := reflect.New(typ).Interface() + err := ParseInt(res, tt.val, tt.mode) + switch { + case tt.ok && err != nil: + t.Errorf("ParseInt(%v, %#v, %v): fail; got error %v, want ok", + typ, tt.val, tt.mode, err) + case !tt.ok && err == nil: + t.Errorf("ParseInt(%v, %#v, %v): fail; got %v, want error", + typ, tt.val, tt.mode, elem(res)) + case tt.ok && !reflect.DeepEqual(elem(res), tt.exp): + t.Errorf("ParseInt(%v, %#v, %v): fail; got %v, want %v", + typ, tt.val, tt.mode, elem(res), tt.exp) + default: + t.Logf("ParseInt(%v, %#v, %s): pass; got %v, error %v", + typ, tt.val, tt.mode, elem(res), err) + } + } +} diff --git a/vendor/src/github.com/scalingdata/gcfg/types/scan.go b/vendor/src/github.com/scalingdata/gcfg/types/scan.go new file mode 100644 index 0000000..db2f6ed --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/types/scan.go @@ -0,0 +1,23 @@ +package types + +import ( + "fmt" + "io" + "reflect" +) + +// ScanFully uses fmt.Sscanf with verb to fully scan val into ptr. +func ScanFully(ptr interface{}, val string, verb byte) error { + t := reflect.ValueOf(ptr).Elem().Type() + // attempt to read extra bytes to make sure the value is consumed + var b []byte + n, err := fmt.Sscanf(val, "%"+string(verb)+"%s", ptr, &b) + switch { + case n < 1 || n == 1 && err != io.EOF: + return fmt.Errorf("failed to parse %q as %v: %v", val, t, err) + case n > 1: + return fmt.Errorf("failed to parse %q as %v: extra characters %q", val, t, string(b)) + } + // n == 1 && err == io.EOF + return nil +} diff --git a/vendor/src/github.com/scalingdata/gcfg/types/scan_test.go b/vendor/src/github.com/scalingdata/gcfg/types/scan_test.go new file mode 100644 index 0000000..a8083e0 --- /dev/null +++ b/vendor/src/github.com/scalingdata/gcfg/types/scan_test.go @@ -0,0 +1,36 @@ +package types + +import ( + "reflect" + "testing" +) + +func TestScanFully(t *testing.T) { + for _, tt := range []struct { + val string + verb byte + res interface{} + ok bool + }{ + {"a", 'v', int(0), false}, + {"0x", 'v', int(0), true}, + {"0x", 'd', int(0), false}, + } { + d := reflect.New(reflect.TypeOf(tt.res)).Interface() + err := ScanFully(d, tt.val, tt.verb) + switch { + case tt.ok && err != nil: + t.Errorf("ScanFully(%T, %q, '%c'): want ok, got error %v", + d, tt.val, tt.verb, err) + case !tt.ok && err == nil: + t.Errorf("ScanFully(%T, %q, '%c'): want error, got %v", + d, tt.val, tt.verb, elem(d)) + case tt.ok && err == nil && !reflect.DeepEqual(tt.res, elem(d)): + t.Errorf("ScanFully(%T, %q, '%c'): want %v, got %v", + d, tt.val, tt.verb, tt.res, elem(d)) + default: + t.Logf("ScanFully(%T, %q, '%c') = %v; *ptr==%v", + d, tt.val, tt.verb, err, elem(d)) + } + } +}