diff --git a/.gitignore b/.gitignore index df9c7997..9504cfaf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -.bitrise.secrets.yml -.bitrise -out/ _tmp +.gows* +.bitrise* \ No newline at end of file diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 087b55a5..5d4437c9 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,58 +1,66 @@ { "ImportPath": "github.com/bitrise-io/steps-xcode-archive", - "GoVersion": "go1.7", + "GoVersion": "go1.8", "GodepVersion": "v79", "Packages": [ "./..." ], "Deps": [ - { - "ImportPath": "github.com/DHowett/go-plist", - "Rev": "fd61394c0abc85ba629ff8471fa35d4309cd6609" - }, { "ImportPath": "github.com/bitrise-io/go-utils/colorstring", - "Rev": "e9b911a4cdf5eda62401973d0c299e7e42637315" + "Rev": "9c30a7825aa1064e423a0f1dba751fec990b07be" }, { "ImportPath": "github.com/bitrise-io/go-utils/command", - "Rev": "e9b911a4cdf5eda62401973d0c299e7e42637315" + "Rev": "9c30a7825aa1064e423a0f1dba751fec990b07be" }, { "ImportPath": "github.com/bitrise-io/go-utils/errorutil", - "Rev": "e9b911a4cdf5eda62401973d0c299e7e42637315" + "Rev": "9c30a7825aa1064e423a0f1dba751fec990b07be" }, { "ImportPath": "github.com/bitrise-io/go-utils/fileutil", - "Rev": "e9b911a4cdf5eda62401973d0c299e7e42637315" + "Rev": "9c30a7825aa1064e423a0f1dba751fec990b07be" }, { "ImportPath": "github.com/bitrise-io/go-utils/log", - "Rev": "e9b911a4cdf5eda62401973d0c299e7e42637315" + "Rev": "9c30a7825aa1064e423a0f1dba751fec990b07be" }, { "ImportPath": "github.com/bitrise-io/go-utils/pathutil", - "Rev": "e9b911a4cdf5eda62401973d0c299e7e42637315" + "Rev": "9c30a7825aa1064e423a0f1dba751fec990b07be" }, { "ImportPath": "github.com/bitrise-tools/go-xcode/exportoptions", - "Rev": "dfe3c2b59593ee83ef605fba21fa92501d1c68bf" + "Rev": "ab78c66cd596d02b8eff11b4c72de1f768bb93bb" + }, + { + "ImportPath": "github.com/bitrise-tools/go-xcode/models", + "Rev": "ab78c66cd596d02b8eff11b4c72de1f768bb93bb" + }, + { + "ImportPath": "github.com/bitrise-tools/go-xcode/plistutil", + "Rev": "ab78c66cd596d02b8eff11b4c72de1f768bb93bb" }, { "ImportPath": "github.com/bitrise-tools/go-xcode/provisioningprofile", - "Rev": "dfe3c2b59593ee83ef605fba21fa92501d1c68bf" + "Rev": "ab78c66cd596d02b8eff11b4c72de1f768bb93bb" + }, + { + "ImportPath": "github.com/bitrise-tools/go-xcode/utility", + "Rev": "ab78c66cd596d02b8eff11b4c72de1f768bb93bb" }, { "ImportPath": "github.com/bitrise-tools/go-xcode/xcarchive", - "Rev": "dfe3c2b59593ee83ef605fba21fa92501d1c68bf" + "Rev": "ab78c66cd596d02b8eff11b4c72de1f768bb93bb" }, { "ImportPath": "github.com/bitrise-tools/go-xcode/xcodebuild", - "Rev": "dfe3c2b59593ee83ef605fba21fa92501d1c68bf" + "Rev": "ab78c66cd596d02b8eff11b4c72de1f768bb93bb" }, { "ImportPath": "github.com/bitrise-tools/go-xcode/xcpretty", - "Rev": "dfe3c2b59593ee83ef605fba21fa92501d1c68bf" + "Rev": "ab78c66cd596d02b8eff11b4c72de1f768bb93bb" }, { "ImportPath": "github.com/hashicorp/go-version", @@ -61,6 +69,10 @@ { "ImportPath": "github.com/kballard/go-shellquote", "Rev": "d8ec1a69a250a17bb0e419c386eac1f3711dc142" + }, + { + "ImportPath": "howett.net/plist", + "Rev": "2f847ea62b17703c861484a09f0e5841328a2cb9" } ] } diff --git a/gows.yml b/gows.yml new file mode 100644 index 00000000..9439d8ac --- /dev/null +++ b/gows.yml @@ -0,0 +1 @@ +package_name: github.com/bitrise-io/steps-xcode-archive diff --git a/main.go b/main.go index c2d506ef..9410aa45 100644 --- a/main.go +++ b/main.go @@ -472,17 +472,18 @@ is available in the $BITRISE_XCODE_RAW_RESULT_TEXT_PATH environment variable`) under the Products/Applications folder */ - embeddedProfilePth, err := xcarchive.EmbeddedMobileProvisionPth(tmpArchivePath) + embeddedProfilePth, err := xcarchive.FindEmbeddedMobileProvision(tmpArchivePath) if err != nil { fail("Failed to get embedded profile path, error: %s", err) } - provProfile, err := provisioningprofile.NewFromFile(embeddedProfilePth) + provProfilePlistData, err := provisioningprofile.NewPlistDataFromFile(embeddedProfilePth) if err != nil { fail("Failed to create provisioning profile model, error: %s", err) } - if provProfile.Name == nil { + name, found := provProfilePlistData.GetString("Name") + if !found { fail("Profile name empty") } @@ -490,7 +491,7 @@ is available in the $BITRISE_XCODE_RAW_RESULT_TEXT_PATH environment variable`) legacyExportCmd.SetExportFormat("ipa") legacyExportCmd.SetArchivePath(tmpArchivePath) legacyExportCmd.SetExportPath(ipaPath) - legacyExportCmd.SetExportProvisioningProfileName(*provProfile.Name) + legacyExportCmd.SetExportProvisioningProfileName(name) if configs.OutputTool == "xcpretty" { xcprettyCmd := xcpretty.New(legacyExportCmd) @@ -534,17 +535,17 @@ is available in the $BITRISE_XCODE_RAW_RESULT_TEXT_PATH environment variable`) if configs.ExportMethod == "auto-detect" { log.Printf("auto-detect export method, based on embedded profile") - embeddedProfilePth, err := xcarchive.EmbeddedMobileProvisionPth(tmpArchivePath) + embeddedProfilePth, err := xcarchive.FindEmbeddedMobileProvision(tmpArchivePath) if err != nil { fail("Failed to get embedded profile path, error: %s", err) } - provProfile, err := provisioningprofile.NewFromFile(embeddedProfilePth) + provProfilePlistData, err := provisioningprofile.NewPlistDataFromFile(embeddedProfilePth) if err != nil { fail("Failed to create provisioning profile model, error: %s", err) } - method = provProfile.GetExportMethod() + method = provisioningprofile.GetExportMethod(provProfilePlistData) log.Printf("detected export method: %s", method) } else { log.Printf("using export-method input: %s", configs.ExportMethod) diff --git a/step.yml b/step.yml index b34b1516..2ad1aea3 100644 --- a/step.yml +++ b/step.yml @@ -24,9 +24,6 @@ deps: - name: xcode brew: - name: go - apt_get: - - name: golang - bin_name: go toolkit: go: package_name: github.com/bitrise-io/steps-xcode-archive diff --git a/vendor/github.com/DHowett/go-plist/bplist.go b/vendor/github.com/DHowett/go-plist/bplist.go deleted file mode 100644 index 45f07b42..00000000 --- a/vendor/github.com/DHowett/go-plist/bplist.go +++ /dev/null @@ -1,601 +0,0 @@ -package plist - -import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "hash/crc32" - "io" - "math" - "runtime" - "time" - "unicode/utf16" -) - -type bplistTrailer struct { - Unused [5]uint8 - SortVersion uint8 - OffsetIntSize uint8 - ObjectRefSize uint8 - NumObjects uint64 - TopObject uint64 - OffsetTableOffset uint64 -} - -const ( - bpTagNull uint8 = 0x00 - bpTagBoolFalse = 0x08 - bpTagBoolTrue = 0x09 - bpTagInteger = 0x10 - bpTagReal = 0x20 - bpTagDate = 0x30 - bpTagData = 0x40 - bpTagASCIIString = 0x50 - bpTagUTF16String = 0x60 - bpTagUID = 0x80 - bpTagArray = 0xA0 - bpTagDictionary = 0xD0 -) - -type bplistGenerator struct { - writer *countedWriter - uniqmap map[interface{}]uint64 - objmap map[*plistValue]uint64 - objtable []*plistValue - nobjects uint64 - trailer bplistTrailer -} - -func (p *bplistGenerator) flattenPlistValue(pval *plistValue) { - switch pval.kind { - case String, Integer, Real: - if _, ok := p.uniqmap[pval.value]; ok { - return - } - p.uniqmap[pval.value] = p.nobjects - case Date: - k := pval.value.(time.Time).UnixNano() - if _, ok := p.uniqmap[k]; ok { - return - } - p.uniqmap[k] = p.nobjects - case Data: - // Data are uniqued by their checksums. - // The wonderful difference between uint64 (which we use for numbers) - // and uint32 makes this possible. - // Todo: Look at calculating this only once and storing it somewhere; - // crc32 is fairly quick, however. - uniqkey := crc32.ChecksumIEEE(pval.value.([]byte)) - if _, ok := p.uniqmap[uniqkey]; ok { - return - } - p.uniqmap[uniqkey] = p.nobjects - } - - p.objtable = append(p.objtable, pval) - p.objmap[pval] = p.nobjects - p.nobjects++ - - switch pval.kind { - case Dictionary: - dict := pval.value.(*dictionary) - dict.populateArrays() - for _, k := range dict.keys { - p.flattenPlistValue(&plistValue{String, k}) - } - for _, v := range dict.values { - p.flattenPlistValue(v) - } - case Array: - subvalues := pval.value.([]*plistValue) - for _, v := range subvalues { - p.flattenPlistValue(v) - } - } -} - -func (p *bplistGenerator) indexForPlistValue(pval *plistValue) (uint64, bool) { - var v uint64 - var ok bool - switch pval.kind { - case String, Integer, Real: - v, ok = p.uniqmap[pval.value] - case Date: - v, ok = p.uniqmap[pval.value.(time.Time).UnixNano()] - case Data: - v, ok = p.uniqmap[crc32.ChecksumIEEE(pval.value.([]byte))] - default: - v, ok = p.objmap[pval] - } - return v, ok -} - -func (p *bplistGenerator) generateDocument(rootpval *plistValue) { - p.objtable = make([]*plistValue, 0, 15) - p.uniqmap = make(map[interface{}]uint64) - p.objmap = make(map[*plistValue]uint64) - p.flattenPlistValue(rootpval) - - p.trailer.NumObjects = uint64(len(p.objtable)) - p.trailer.ObjectRefSize = uint8(minimumSizeForInt(p.trailer.NumObjects)) - - p.writer.Write([]byte("bplist00")) - - offtable := make([]uint64, p.trailer.NumObjects) - for i, pval := range p.objtable { - offtable[i] = uint64(p.writer.BytesWritten()) - p.writePlistValue(pval) - } - - p.trailer.OffsetIntSize = uint8(minimumSizeForInt(uint64(p.writer.BytesWritten()))) - p.trailer.TopObject = p.objmap[rootpval] - p.trailer.OffsetTableOffset = uint64(p.writer.BytesWritten()) - - for _, offset := range offtable { - p.writeSizedInt(offset, int(p.trailer.OffsetIntSize)) - } - - binary.Write(p.writer, binary.BigEndian, p.trailer) -} - -func (p *bplistGenerator) writePlistValue(pval *plistValue) { - if pval == nil { - return - } - - switch pval.kind { - case Dictionary: - p.writeDictionaryTag(pval.value.(*dictionary)) - case Array: - p.writeArrayTag(pval.value.([]*plistValue)) - case String: - p.writeStringTag(pval.value.(string)) - case Integer: - p.writeIntTag(pval.value.(signedInt).value) - case Real: - p.writeRealTag(pval.value.(sizedFloat).value, pval.value.(sizedFloat).bits) - case Boolean: - p.writeBoolTag(pval.value.(bool)) - case Data: - p.writeDataTag(pval.value.([]byte)) - case Date: - p.writeDateTag(pval.value.(time.Time)) - } -} - -func minimumSizeForInt(n uint64) int { - switch { - case n <= uint64(0xff): - return 1 - case n <= uint64(0xffff): - return 2 - case n <= uint64(0xffffffff): - return 4 - default: - return 8 - } - panic(errors.New("illegal integer size")) -} - -func (p *bplistGenerator) writeSizedInt(n uint64, nbytes int) { - var val interface{} - switch nbytes { - case 1: - val = uint8(n) - case 2: - val = uint16(n) - case 4: - val = uint32(n) - case 8: - val = n - default: - panic(errors.New("illegal integer size")) - } - binary.Write(p.writer, binary.BigEndian, val) -} - -func (p *bplistGenerator) writeBoolTag(v bool) { - tag := uint8(bpTagBoolFalse) - if v { - tag = bpTagBoolTrue - } - binary.Write(p.writer, binary.BigEndian, tag) -} - -func (p *bplistGenerator) writeIntTag(n uint64) { - var tag uint8 - var val interface{} - switch { - case n <= uint64(0xff): - val = uint8(n) - tag = bpTagInteger | 0x0 - case n <= uint64(0xffff): - val = uint16(n) - tag = bpTagInteger | 0x1 - case n <= uint64(0xffffffff): - val = uint32(n) - tag = bpTagInteger | 0x2 - default: - val = n - tag = bpTagInteger | 0x3 - } - - binary.Write(p.writer, binary.BigEndian, tag) - binary.Write(p.writer, binary.BigEndian, val) -} - -func (p *bplistGenerator) writeRealTag(n float64, bits int) { - var tag uint8 = bpTagReal | 0x3 - var val interface{} = n - if bits == 32 { - val = float32(n) - tag = bpTagReal | 0x2 - } - - binary.Write(p.writer, binary.BigEndian, tag) - binary.Write(p.writer, binary.BigEndian, val) -} - -func (p *bplistGenerator) writeDateTag(t time.Time) { - tag := uint8(bpTagDate) | 0x3 - val := float64(t.In(time.UTC).UnixNano()) / float64(time.Second) - val -= 978307200 // Adjust to Apple Epoch - - binary.Write(p.writer, binary.BigEndian, tag) - binary.Write(p.writer, binary.BigEndian, val) -} - -func (p *bplistGenerator) writeCountedTag(tag uint8, count uint64) { - marker := tag - if count >= 0xF { - marker |= 0xF - } else { - marker |= uint8(count) - } - - binary.Write(p.writer, binary.BigEndian, marker) - - if count >= 0xF { - p.writeIntTag(count) - } -} - -func (p *bplistGenerator) writeDataTag(data []byte) { - p.writeCountedTag(bpTagData, uint64(len(data))) - binary.Write(p.writer, binary.BigEndian, data) -} - -func (p *bplistGenerator) writeStringTag(str string) { - for _, r := range str { - if r > 0xFF { - utf16Runes := utf16.Encode([]rune(str)) - p.writeCountedTag(bpTagUTF16String, uint64(len(utf16Runes))) - binary.Write(p.writer, binary.BigEndian, utf16Runes) - return - } - } - - p.writeCountedTag(bpTagASCIIString, uint64(len(str))) - binary.Write(p.writer, binary.BigEndian, []byte(str)) -} - -func (p *bplistGenerator) writeDictionaryTag(dict *dictionary) { - p.writeCountedTag(bpTagDictionary, uint64(dict.count)) - vals := make([]uint64, dict.count*2) - cnt := dict.count - for i, k := range dict.keys { - keyIdx, ok := p.uniqmap[k] - if !ok { - panic(errors.New("failed to find key " + k + " in object map during serialization")) - } - vals[i] = keyIdx - } - for i, v := range dict.values { - objIdx, ok := p.indexForPlistValue(v) - if !ok { - panic(errors.New("failed to find value in object map during serialization")) - } - vals[i+cnt] = objIdx - } - - for _, v := range vals { - p.writeSizedInt(v, int(p.trailer.ObjectRefSize)) - } -} - -func (p *bplistGenerator) writeArrayTag(arr []*plistValue) { - p.writeCountedTag(bpTagArray, uint64(len(arr))) - for _, v := range arr { - objIdx, ok := p.indexForPlistValue(v) - if !ok { - panic(errors.New("failed to find value in object map during serialization")) - } - - p.writeSizedInt(objIdx, int(p.trailer.ObjectRefSize)) - } -} - -func (p *bplistGenerator) Indent(i string) { - // There's nothing to indent. -} - -func newBplistGenerator(w io.Writer) *bplistGenerator { - return &bplistGenerator{ - writer: &countedWriter{Writer: mustWriter{w}}, - } -} - -type bplistParser struct { - reader io.ReadSeeker - version int - buf []byte - objrefs map[uint64]*plistValue - offtable []uint64 - trailer bplistTrailer - trailerOffset int64 -} - -func (p *bplistParser) parseDocument() (pval *plistValue, parseError error) { - defer func() { - if r := recover(); r != nil { - if _, ok := r.(runtime.Error); ok { - panic(r) - } - if _, ok := r.(invalidPlistError); ok { - parseError = r.(error) - } else { - // Wrap all non-invalid-plist errors. - parseError = plistParseError{"binary", r.(error)} - } - } - }() - - magic := make([]byte, 6) - ver := make([]byte, 2) - p.reader.Seek(0, 0) - p.reader.Read(magic) - if !bytes.Equal(magic, []byte("bplist")) { - panic(invalidPlistError{"binary", errors.New("mismatched magic")}) - } - - _, err := p.reader.Read(ver) - if err != nil { - panic(err) - } - - p.version = int(mustParseInt(string(ver), 10, 0)) - - if p.version > 1 { - panic(fmt.Errorf("unexpected version %d", p.version)) - } - - p.objrefs = make(map[uint64]*plistValue) - p.trailerOffset, err = p.reader.Seek(-32, 2) - if err != nil && err != io.EOF { - panic(err) - } - - err = binary.Read(p.reader, binary.BigEndian, &p.trailer) - if err != nil && err != io.EOF { - panic(err) - } - - if p.trailer.NumObjects > uint64(math.Pow(2, 8*float64(p.trailer.ObjectRefSize))) { - panic(fmt.Errorf("binary property list contains more objects (%v) than its object ref size (%v bytes) can support", p.trailer.NumObjects, p.trailer.ObjectRefSize)) - } - - if p.trailer.TopObject >= p.trailer.NumObjects { - panic(fmt.Errorf("top object index %v is out of range (only %v objects exist)", p.trailer.TopObject, p.trailer.NumObjects)) - } - p.offtable = make([]uint64, p.trailer.NumObjects) - - // SEEK_SET - _, err = p.reader.Seek(int64(p.trailer.OffsetTableOffset), 0) - if err != nil && err != io.EOF { - panic(err) - } - - for i := uint64(0); i < p.trailer.NumObjects; i++ { - off := p.readSizedInt(int(p.trailer.OffsetIntSize)) - if off >= uint64(p.trailerOffset) { - panic(fmt.Errorf("object %v starts beyond end of plist trailer (%v vs %v)", i, off, p.trailerOffset)) - } - p.offtable[i] = off - } - - for _, off := range p.offtable { - p.valueAtOffset(off) - } - - pval = p.valueAtOffset(p.offtable[p.trailer.TopObject]) - return -} - -func (p *bplistParser) readSizedInt(nbytes int) uint64 { - switch nbytes { - case 1: - var val uint8 - binary.Read(p.reader, binary.BigEndian, &val) - return uint64(val) - case 2: - var val uint16 - binary.Read(p.reader, binary.BigEndian, &val) - return uint64(val) - case 4: - var val uint32 - binary.Read(p.reader, binary.BigEndian, &val) - return uint64(val) - case 8: - var val uint64 - binary.Read(p.reader, binary.BigEndian, &val) - return uint64(val) - case 16: - var high, low uint64 - binary.Read(p.reader, binary.BigEndian, &high) - binary.Read(p.reader, binary.BigEndian, &low) - // TODO: int128 support (!) - return uint64(low) - } - panic(errors.New("illegal integer size")) -} - -func (p *bplistParser) countForTag(tag uint8) uint64 { - cnt := uint64(tag & 0x0F) - if cnt == 0xF { - var intTag uint8 - binary.Read(p.reader, binary.BigEndian, &intTag) - cnt = p.readSizedInt(1 << (intTag & 0xF)) - } - return cnt -} - -func (p *bplistParser) valueAtOffset(off uint64) *plistValue { - if pval, ok := p.objrefs[off]; ok { - return pval - } - pval := p.parseTagAtOffset(int64(off)) - p.objrefs[off] = pval - return pval -} - -func (p *bplistParser) parseTagAtOffset(off int64) *plistValue { - var tag uint8 - _, err := p.reader.Seek(off, 0) - if err != nil { - panic(err) - } - err = binary.Read(p.reader, binary.BigEndian, &tag) - if err != nil { - panic(err) - } - - switch tag & 0xF0 { - case bpTagNull: - switch tag & 0x0F { - case bpTagBoolTrue, bpTagBoolFalse: - return &plistValue{Boolean, tag == bpTagBoolTrue} - } - return nil - case bpTagInteger: - val := p.readSizedInt(1 << (tag & 0xF)) - return &plistValue{Integer, signedInt{val, false}} - case bpTagReal: - nbytes := 1 << (tag & 0x0F) - switch nbytes { - case 4: - var val float32 - binary.Read(p.reader, binary.BigEndian, &val) - return &plistValue{Real, sizedFloat{float64(val), 32}} - case 8: - var val float64 - binary.Read(p.reader, binary.BigEndian, &val) - return &plistValue{Real, sizedFloat{float64(val), 64}} - } - panic(errors.New("illegal float size")) - case bpTagDate: - var val float64 - binary.Read(p.reader, binary.BigEndian, &val) - - // Apple Epoch is 20110101000000Z - // Adjust for UNIX Time - val += 978307200 - - sec, fsec := math.Modf(val) - time := time.Unix(int64(sec), int64(fsec*float64(time.Second))).In(time.UTC) - return &plistValue{Date, time} - case bpTagData: - cnt := p.countForTag(tag) - if int64(cnt) > p.trailerOffset-int64(off) { - panic(fmt.Errorf("data at %x longer than file (%v bytes, max is %v)", off, cnt, p.trailerOffset-int64(off))) - } - - bytes := make([]byte, cnt) - binary.Read(p.reader, binary.BigEndian, bytes) - return &plistValue{Data, bytes} - case bpTagASCIIString, bpTagUTF16String: - cnt := p.countForTag(tag) - if int64(cnt) > p.trailerOffset-int64(off) { - panic(fmt.Errorf("string at %x longer than file (%v bytes, max is %v)", off, cnt, p.trailerOffset-int64(off))) - } - - if tag&0xF0 == bpTagASCIIString { - bytes := make([]byte, cnt) - binary.Read(p.reader, binary.BigEndian, bytes) - return &plistValue{String, string(bytes)} - } else { - bytes := make([]uint16, cnt) - binary.Read(p.reader, binary.BigEndian, bytes) - runes := utf16.Decode(bytes) - return &plistValue{String, string(runes)} - } - case bpTagUID: // Somehow different than int: low half is nbytes - 1 instead of log2(nbytes) - val := p.readSizedInt(int(tag&0xF) + 1) - return &plistValue{Integer, signedInt{val, false}} - case bpTagDictionary: - cnt := p.countForTag(tag) - - subvalues := make(map[string]*plistValue) - indices := make([]uint64, cnt*2) - for i := uint64(0); i < cnt*2; i++ { - idx := p.readSizedInt(int(p.trailer.ObjectRefSize)) - - if idx >= p.trailer.NumObjects { - panic(fmt.Errorf("dictionary contains invalid entry index %d (max %d)", idx, p.trailer.NumObjects)) - } - - indices[i] = idx - } - for i := uint64(0); i < cnt; i++ { - keyOffset := p.offtable[indices[i]] - valueOffset := p.offtable[indices[i+cnt]] - if keyOffset == uint64(off) { - panic(fmt.Errorf("dictionary contains self-referential key %x (index %d)", off, i)) - } - if valueOffset == uint64(off) { - panic(fmt.Errorf("dictionary contains self-referential value %x (index %d)", off, i)) - } - - kval := p.valueAtOffset(keyOffset) - if kval == nil || kval.kind != String { - panic(fmt.Errorf("dictionary contains non-string key at index %d", i)) - } - - key, ok := kval.value.(string) - if !ok { - panic(fmt.Errorf("string-type plist value contains non-string at index %d", i)) - } - subvalues[key] = p.valueAtOffset(valueOffset) - } - - return &plistValue{Dictionary, &dictionary{m: subvalues}} - case bpTagArray: - cnt := p.countForTag(tag) - - arr := make([]*plistValue, cnt) - indices := make([]uint64, cnt) - for i := uint64(0); i < cnt; i++ { - idx := p.readSizedInt(int(p.trailer.ObjectRefSize)) - - if idx >= p.trailer.NumObjects { - panic(fmt.Errorf("array contains invalid entry index %d (max %d)", idx, p.trailer.NumObjects)) - } - - indices[i] = idx - } - for i := uint64(0); i < cnt; i++ { - valueOffset := p.offtable[indices[i]] - if valueOffset == uint64(off) { - panic(fmt.Errorf("array contains self-referential value %x (index %d)", off, i)) - } - arr[i] = p.valueAtOffset(valueOffset) - } - - return &plistValue{Array, arr} - } - panic(fmt.Errorf("unexpected atom 0x%2.02x at offset %d", tag, off)) -} - -func newBplistParser(r io.ReadSeeker) *bplistParser { - return &bplistParser{reader: r} -} diff --git a/vendor/github.com/DHowett/go-plist/plist.go b/vendor/github.com/DHowett/go-plist/plist.go deleted file mode 100644 index 17201fda..00000000 --- a/vendor/github.com/DHowett/go-plist/plist.go +++ /dev/null @@ -1,141 +0,0 @@ -package plist - -import ( - "reflect" - "sort" -) - -// Property list format constants -const ( - // Used by Decoder to represent an invalid property list. - InvalidFormat int = 0 - - // Used to indicate total abandon with regards to Encoder's output format. - AutomaticFormat = 0 - - XMLFormat = 1 - BinaryFormat = 2 - OpenStepFormat = 3 - GNUStepFormat = 4 -) - -var FormatNames = map[int]string{ - InvalidFormat: "unknown/invalid", - XMLFormat: "XML", - BinaryFormat: "Binary", - OpenStepFormat: "OpenStep", - GNUStepFormat: "GNUStep", -} - -type plistKind uint - -const ( - Invalid plistKind = iota - Dictionary - Array - String - Integer - Real - Boolean - Data - Date -) - -var plistKindNames map[plistKind]string = map[plistKind]string{ - Invalid: "invalid", - Dictionary: "dictionary", - Array: "array", - String: "string", - Integer: "integer", - Real: "real", - Boolean: "boolean", - Data: "data", - Date: "date", -} - -type plistValue struct { - kind plistKind - value interface{} -} - -type signedInt struct { - value uint64 - signed bool -} - -type sizedFloat struct { - value float64 - bits int -} - -type dictionary struct { - count int - m map[string]*plistValue - keys sort.StringSlice - values []*plistValue -} - -func (d *dictionary) Len() int { - return d.count -} - -func (d *dictionary) Less(i, j int) bool { - return d.keys.Less(i, j) -} - -func (d *dictionary) Swap(i, j int) { - d.keys.Swap(i, j) - d.values[i], d.values[j] = d.values[j], d.values[i] -} - -func (d *dictionary) populateArrays() { - if d.count > 0 { - return - } - - l := len(d.m) - d.count = l - d.keys = make([]string, l) - d.values = make([]*plistValue, l) - i := 0 - for k, v := range d.m { - d.keys[i] = k - d.values[i] = v - i++ - } - sort.Sort(d) -} - -type unknownTypeError struct { - typ reflect.Type -} - -func (u *unknownTypeError) Error() string { - return "plist: can't marshal value of type " + u.typ.String() -} - -type invalidPlistError struct { - format string - err error -} - -func (e invalidPlistError) Error() string { - s := "plist: invalid " + e.format + " property list" - if e.err != nil { - s += ": " + e.err.Error() - } - return s -} - -type plistParseError struct { - format string - err error -} - -func (e plistParseError) Error() string { - s := "plist: error parsing " + e.format + " property list" - if e.err != nil { - s += ": " + e.err.Error() - } - return s -} diff --git a/vendor/github.com/DHowett/go-plist/text.go b/vendor/github.com/DHowett/go-plist/text.go deleted file mode 100644 index 60cb0db0..00000000 --- a/vendor/github.com/DHowett/go-plist/text.go +++ /dev/null @@ -1,569 +0,0 @@ -package plist - -import ( - "bufio" - "encoding/hex" - "errors" - "io" - "runtime" - "strconv" - "strings" - "time" -) - -type textPlistGenerator struct { - writer io.Writer - format int - - quotableTable *[4]uint64 - - indent string - depth int - - dictKvDelimiter, dictEntryDelimiter, arrayDelimiter []byte -} - -var ( - textPlistTimeLayout = "2006-01-02 15:04:05 -0700" - padding = "0000" -) - -func (p *textPlistGenerator) generateDocument(pval *plistValue) { - p.writePlistValue(pval) -} - -func (p *textPlistGenerator) plistQuotedString(str string) string { - if str == "" { - return `""` - } - s := "" - quot := false - for _, r := range str { - if r > 0xFF { - quot = true - s += `\U` - us := strconv.FormatInt(int64(r), 16) - s += padding[len(us):] - s += us - } else if r > 0x7F { - quot = true - s += `\` - us := strconv.FormatInt(int64(r), 8) - s += padding[1+len(us):] - s += us - } else { - c := uint8(r) - if (*p.quotableTable)[c/64]&(1<<(c%64)) > 0 { - quot = true - } - - switch c { - case '\a': - s += `\a` - case '\b': - s += `\b` - case '\v': - s += `\v` - case '\f': - s += `\f` - case '\\': - s += `\\` - case '"': - s += `\"` - case '\t', '\r', '\n': - fallthrough - default: - s += string(c) - } - } - } - if quot { - s = `"` + s + `"` - } - return s -} - -func (p *textPlistGenerator) deltaIndent(depthDelta int) { - if depthDelta < 0 { - p.depth-- - } else if depthDelta > 0 { - p.depth++ - } -} - -func (p *textPlistGenerator) writeIndent() { - if len(p.indent) == 0 { - return - } - if len(p.indent) > 0 { - p.writer.Write([]byte("\n")) - for i := 0; i < p.depth; i++ { - io.WriteString(p.writer, p.indent) - } - } -} - -func (p *textPlistGenerator) writePlistValue(pval *plistValue) { - if pval == nil { - return - } - - switch pval.kind { - case Dictionary: - p.writer.Write([]byte(`{`)) - p.deltaIndent(1) - dict := pval.value.(*dictionary) - dict.populateArrays() - for i, k := range dict.keys { - p.writeIndent() - io.WriteString(p.writer, p.plistQuotedString(k)) - p.writer.Write(p.dictKvDelimiter) - p.writePlistValue(dict.values[i]) - p.writer.Write(p.dictEntryDelimiter) - } - p.deltaIndent(-1) - p.writeIndent() - p.writer.Write([]byte(`}`)) - case Array: - p.writer.Write([]byte(`(`)) - p.deltaIndent(1) - values := pval.value.([]*plistValue) - for _, v := range values { - p.writeIndent() - p.writePlistValue(v) - p.writer.Write(p.arrayDelimiter) - } - p.deltaIndent(-1) - p.writeIndent() - p.writer.Write([]byte(`)`)) - case String: - io.WriteString(p.writer, p.plistQuotedString(pval.value.(string))) - case Integer: - if p.format == GNUStepFormat { - p.writer.Write([]byte(`<*I`)) - } - if pval.value.(signedInt).signed { - io.WriteString(p.writer, strconv.FormatInt(int64(pval.value.(signedInt).value), 10)) - } else { - io.WriteString(p.writer, strconv.FormatUint(pval.value.(signedInt).value, 10)) - } - if p.format == GNUStepFormat { - p.writer.Write([]byte(`>`)) - } - case Real: - if p.format == GNUStepFormat { - p.writer.Write([]byte(`<*R`)) - } - io.WriteString(p.writer, strconv.FormatFloat(pval.value.(sizedFloat).value, 'g', -1, 64)) - if p.format == GNUStepFormat { - p.writer.Write([]byte(`>`)) - } - case Boolean: - b := pval.value.(bool) - if p.format == GNUStepFormat { - if b { - p.writer.Write([]byte(`<*BY>`)) - } else { - p.writer.Write([]byte(`<*BN>`)) - } - } else { - if b { - p.writer.Write([]byte(`1`)) - } else { - p.writer.Write([]byte(`0`)) - } - } - case Data: - b := pval.value.([]byte) - var hexencoded [9]byte - var l int - var asc = 9 - hexencoded[8] = ' ' - - p.writer.Write([]byte(`<`)) - for i := 0; i < len(b); i += 4 { - l = i + 4 - if l >= len(b) { - l = len(b) - // We no longer need the space - or the rest of the buffer. - // (we used >= above to get this part without another conditional :P) - asc = (l - i) * 2 - } - // Fill the buffer (only up to 8 characters, to preserve the space we implicitly include - // at the end of every encode) - hex.Encode(hexencoded[:8], b[i:l]) - io.WriteString(p.writer, string(hexencoded[:asc])) - } - p.writer.Write([]byte(`>`)) - case Date: - if p.format == GNUStepFormat { - p.writer.Write([]byte(`<*D`)) - io.WriteString(p.writer, pval.value.(time.Time).In(time.UTC).Format(textPlistTimeLayout)) - p.writer.Write([]byte(`>`)) - } else { - io.WriteString(p.writer, p.plistQuotedString(pval.value.(time.Time).In(time.UTC).Format(textPlistTimeLayout))) - } - } -} - -func (p *textPlistGenerator) Indent(i string) { - p.indent = i - if i == "" { - p.dictKvDelimiter = []byte(`=`) - } else { - // For pretty-printing - p.dictKvDelimiter = []byte(` = `) - } -} - -func newTextPlistGenerator(w io.Writer, format int) *textPlistGenerator { - table := &osQuotable - if format == GNUStepFormat { - table = &gsQuotable - } - return &textPlistGenerator{ - writer: mustWriter{w}, - format: format, - quotableTable: table, - dictKvDelimiter: []byte(`=`), - arrayDelimiter: []byte(`,`), - dictEntryDelimiter: []byte(`;`), - } -} - -type byteReader interface { - io.Reader - io.ByteScanner - Peek(n int) ([]byte, error) - ReadBytes(delim byte) ([]byte, error) -} - -type textPlistParser struct { - reader byteReader - whitespaceReplacer *strings.Replacer - format int -} - -func (p *textPlistParser) parseDocument() (pval *plistValue, parseError error) { - defer func() { - if r := recover(); r != nil { - if _, ok := r.(runtime.Error); ok { - panic(r) - } - if _, ok := r.(invalidPlistError); ok { - parseError = r.(error) - } else { - // Wrap all non-invalid-plist errors. - parseError = plistParseError{"text", r.(error)} - } - } - }() - pval = p.parsePlistValue() - return -} - -func (p *textPlistParser) chugWhitespace() { -ws: - for { - c, err := p.reader.ReadByte() - if err != nil && err != io.EOF { - panic(err) - } - if whitespace[c/64]&(1<<(c%64)) == 0 { - if c == '/' && err != io.EOF { - // A / at the end of the file is not the begining of a comment. - cs, err := p.reader.Peek(1) - if err != nil && err != io.EOF { - panic(err) - } - c = cs[0] - switch c { - case '/': - for { - c, err = p.reader.ReadByte() - if err != nil && err != io.EOF { - panic(err) - } else if err == io.EOF { - break - } - // TODO: UTF-8 - if c == '\n' || c == '\r' { - break - } - } - case '*': - // Peek returned a value here, so it is safe to read. - _, _ = p.reader.ReadByte() - star := false - for { - c, err = p.reader.ReadByte() - if err != nil { - panic(err) - } - if c == '*' { - star = true - } else if c == '/' && star { - break - } else { - star = false - } - } - default: - p.reader.UnreadByte() // Not the beginning of a // or /* comment - break ws - } - continue - } - p.reader.UnreadByte() - break - } - } -} - -func (p *textPlistParser) parseQuotedString() *plistValue { - escaping := false - s := "" - for { - byt, err := p.reader.ReadByte() - // EOF here is an error: we're inside a quoted string! - if err != nil { - panic(err) - } - c := rune(byt) - if !escaping { - if c == '"' { - break - } else if c == '\\' { - escaping = true - continue - } - } else { - escaping = false - // Everything that is not listed here passes through unharmed. - switch c { - case 'a': - c = '\a' - case 'b': - c = '\b' - case 'v': - c = '\v' - case 'f': - c = '\f' - case 't': - c = '\t' - case 'r': - c = '\r' - case 'n': - c = '\n' - case 'x', 'u', 'U': // hex and unicode - l := 4 - if c == 'x' { - l = 2 - } - hex := make([]byte, l) - p.reader.Read(hex) - newc := mustParseInt(string(hex), 16, 16) - c = rune(newc) - case '0', '1', '2', '3', '4', '5', '6', '7': // octal! - oct := make([]byte, 3) - oct[0] = uint8(c) - p.reader.Read(oct[1:]) - newc := mustParseInt(string(oct), 8, 16) - c = rune(newc) - } - } - s += string(c) - } - return &plistValue{String, s} -} - -func (p *textPlistParser) parseUnquotedString() *plistValue { - s := "" - for { - c, err := p.reader.ReadByte() - if err != nil { - if err == io.EOF { - break - } - panic(err) - } - // if we encounter a character that must be quoted, we're done. - // the GNUStep quote table is more lax here, so we use it instead of the OpenStep one. - if gsQuotable[c/64]&(1<<(c%64)) > 0 { - p.reader.UnreadByte() - break - } - s += string(c) - } - return &plistValue{String, s} -} - -func (p *textPlistParser) parseDictionary() *plistValue { - var keypv *plistValue - subval := make(map[string]*plistValue) - for { - p.chugWhitespace() - - c, err := p.reader.ReadByte() - // EOF here is an error: we're inside a dictionary! - if err != nil { - panic(err) - } - - if c == '}' { - break - } else if c == '"' { - keypv = p.parseQuotedString() - } else { - p.reader.UnreadByte() // Whoops, ate part of the string - keypv = p.parseUnquotedString() - } - if keypv == nil { - // TODO better error - panic(errors.New("missing dictionary key")) - } - - p.chugWhitespace() - c, err = p.reader.ReadByte() - if err != nil { - panic(err) - } - - if c != '=' { - panic(errors.New("missing = in dictionary")) - } - - // whitespace is guzzled within - val := p.parsePlistValue() - - p.chugWhitespace() - c, err = p.reader.ReadByte() - if err != nil { - panic(err) - } - - if c != ';' { - panic(errors.New("missing ; in dictionary")) - } - - subval[keypv.value.(string)] = val - } - return &plistValue{Dictionary, &dictionary{m: subval}} -} - -func (p *textPlistParser) parseArray() *plistValue { - subval := make([]*plistValue, 0, 10) - for { - c, err := p.reader.ReadByte() - // EOF here is an error: we're inside an array! - if err != nil { - panic(err) - } - - if c == ')' { - break - } else if c == ',' { - continue - } - - p.reader.UnreadByte() - pval := p.parsePlistValue() - if pval.kind == String && pval.value.(string) == "" { - continue - } - subval = append(subval, pval) - } - return &plistValue{Array, subval} -} - -func (p *textPlistParser) parseGNUStepValue(v []byte) *plistValue { - if len(v) < 3 { - panic(errors.New("invalid GNUStep extended value")) - } - typ := v[1] - v = v[2:] - switch typ { - case 'I': - if v[0] == '-' { - n := mustParseInt(string(v), 10, 64) - return &plistValue{Integer, signedInt{uint64(n), true}} - } else { - n := mustParseUint(string(v), 10, 64) - return &plistValue{Integer, signedInt{n, false}} - } - case 'R': - n := mustParseFloat(string(v), 64) - return &plistValue{Real, sizedFloat{n, 64}} - case 'B': - b := v[0] == 'Y' - return &plistValue{Boolean, b} - case 'D': - t, err := time.Parse(textPlistTimeLayout, string(v)) - if err != nil { - panic(err) - } - - return &plistValue{Date, t.In(time.UTC)} - } - panic(errors.New("invalid GNUStep type " + string(typ))) - return nil -} - -func (p *textPlistParser) parsePlistValue() *plistValue { - for { - p.chugWhitespace() - - c, err := p.reader.ReadByte() - if err != nil && err != io.EOF { - panic(err) - } - switch c { - case '<': - bytes, err := p.reader.ReadBytes('>') - if err != nil { - panic(err) - } - bytes = bytes[:len(bytes)-1] - - if len(bytes) == 0 { - panic(errors.New("invalid empty angle-bracketed element")) - } - - if bytes[0] == '*' { - p.format = GNUStepFormat - return p.parseGNUStepValue(bytes) - } else { - s := p.whitespaceReplacer.Replace(string(bytes)) - data, err := hex.DecodeString(s) - if err != nil { - panic(err) - } - return &plistValue{Data, data} - } - case '"': - return p.parseQuotedString() - case '{': - return p.parseDictionary() - case '(': - return p.parseArray() - default: - p.reader.UnreadByte() // Place back in buffer for parseUnquotedString - return p.parseUnquotedString() - } - } - return nil -} - -func newTextPlistParser(r io.Reader) *textPlistParser { - var reader byteReader - if rd, ok := r.(byteReader); ok { - reader = rd - } else { - reader = bufio.NewReader(r) - } - return &textPlistParser{ - reader: reader, - whitespaceReplacer: strings.NewReplacer("\t", "", "\n", "", " ", "", "\r", ""), - format: OpenStepFormat, - } -} diff --git a/vendor/github.com/DHowett/go-plist/unmarshal.go b/vendor/github.com/DHowett/go-plist/unmarshal.go deleted file mode 100644 index fc50ed4a..00000000 --- a/vendor/github.com/DHowett/go-plist/unmarshal.go +++ /dev/null @@ -1,276 +0,0 @@ -package plist - -import ( - "encoding" - "fmt" - "reflect" - "time" -) - -type incompatibleDecodeTypeError struct { - typ reflect.Type - pKind plistKind -} - -func (u *incompatibleDecodeTypeError) Error() string { - return fmt.Sprintf("plist: type mismatch: tried to decode %v into value of type %v", plistKindNames[u.pKind], u.typ) -} - -var ( - textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() -) - -func isEmptyInterface(v reflect.Value) bool { - return v.Kind() == reflect.Interface && v.NumMethod() == 0 -} - -func (p *Decoder) unmarshalTextInterface(pval *plistValue, unmarshalable encoding.TextUnmarshaler) { - err := unmarshalable.UnmarshalText([]byte(pval.value.(string))) - if err != nil { - panic(err) - } -} - -func (p *Decoder) unmarshalTime(pval *plistValue, val reflect.Value) { - val.Set(reflect.ValueOf(pval.value.(time.Time))) -} - -func (p *Decoder) unmarshalLaxString(s string, val reflect.Value) { - switch val.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - i := mustParseInt(s, 10, 64) - val.SetInt(i) - return - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - i := mustParseUint(s, 10, 64) - val.SetUint(i) - return - case reflect.Float32, reflect.Float64: - f := mustParseFloat(s, 64) - val.SetFloat(f) - return - case reflect.Bool: - b := mustParseBool(s) - val.SetBool(b) - return - case reflect.Struct: - if val.Type() == timeType { - t, err := time.Parse(textPlistTimeLayout, s) - if err != nil { - panic(err) - } - val.Set(reflect.ValueOf(t.In(time.UTC))) - return - } - fallthrough - default: - panic(&incompatibleDecodeTypeError{val.Type(), String}) - } -} - -func (p *Decoder) unmarshal(pval *plistValue, val reflect.Value) { - if pval == nil { - return - } - - if val.Kind() == reflect.Ptr { - if val.IsNil() { - val.Set(reflect.New(val.Type().Elem())) - } - val = val.Elem() - } - - if isEmptyInterface(val) { - v := p.valueInterface(pval) - val.Set(reflect.ValueOf(v)) - return - } - - incompatibleTypeError := &incompatibleDecodeTypeError{val.Type(), pval.kind} - - // time.Time implements TextMarshaler, but we need to parse it as RFC3339 - if pval.kind == Date { - if val.Type() == timeType { - p.unmarshalTime(pval, val) - return - } - panic(incompatibleTypeError) - } - - if val.CanInterface() && val.Type().Implements(textUnmarshalerType) && val.Type() != timeType { - p.unmarshalTextInterface(pval, val.Interface().(encoding.TextUnmarshaler)) - return - } - - if val.CanAddr() { - pv := val.Addr() - if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) && val.Type() != timeType { - p.unmarshalTextInterface(pval, pv.Interface().(encoding.TextUnmarshaler)) - return - } - } - - typ := val.Type() - - switch pval.kind { - case String: - if val.Kind() == reflect.String { - val.SetString(pval.value.(string)) - return - } - if p.lax { - p.unmarshalLaxString(pval.value.(string), val) - return - } - - panic(incompatibleTypeError) - case Integer: - switch val.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - val.SetInt(int64(pval.value.(signedInt).value)) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - val.SetUint(pval.value.(signedInt).value) - default: - panic(incompatibleTypeError) - } - case Real: - if val.Kind() == reflect.Float32 || val.Kind() == reflect.Float64 { - val.SetFloat(pval.value.(sizedFloat).value) - } else { - panic(incompatibleTypeError) - } - case Boolean: - if val.Kind() == reflect.Bool { - val.SetBool(pval.value.(bool)) - } else { - panic(incompatibleTypeError) - } - case Data: - if val.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 { - val.SetBytes(pval.value.([]byte)) - } else { - panic(incompatibleTypeError) - } - case Array: - p.unmarshalArray(pval, val) - case Dictionary: - p.unmarshalDictionary(pval, val) - } -} - -func (p *Decoder) unmarshalArray(pval *plistValue, val reflect.Value) { - subvalues := pval.value.([]*plistValue) - - var n int - if val.Kind() == reflect.Slice { - // Slice of element values. - // Grow slice. - cnt := len(subvalues) + val.Len() - if cnt >= val.Cap() { - ncap := 2 * cnt - if ncap < 4 { - ncap = 4 - } - new := reflect.MakeSlice(val.Type(), val.Len(), ncap) - reflect.Copy(new, val) - val.Set(new) - } - n = val.Len() - val.SetLen(cnt) - } else if val.Kind() == reflect.Array { - if len(subvalues) > val.Cap() { - panic(fmt.Errorf("plist: attempted to unmarshal %d values into an array of size %d", len(subvalues), val.Cap())) - } - } else { - panic(&incompatibleDecodeTypeError{val.Type(), pval.kind}) - } - - // Recur to read element into slice. - for _, sval := range subvalues { - p.unmarshal(sval, val.Index(n)) - n++ - } - return -} - -func (p *Decoder) unmarshalDictionary(pval *plistValue, val reflect.Value) { - typ := val.Type() - switch val.Kind() { - case reflect.Struct: - tinfo, err := getTypeInfo(typ) - if err != nil { - panic(err) - } - - subvalues := pval.value.(*dictionary).m - for _, finfo := range tinfo.fields { - p.unmarshal(subvalues[finfo.name], finfo.value(val)) - } - case reflect.Map: - if val.IsNil() { - val.Set(reflect.MakeMap(typ)) - } - - subvalues := pval.value.(*dictionary).m - for k, sval := range subvalues { - keyv := reflect.ValueOf(k).Convert(typ.Key()) - mapElem := val.MapIndex(keyv) - if !mapElem.IsValid() { - mapElem = reflect.New(typ.Elem()).Elem() - } - - p.unmarshal(sval, mapElem) - val.SetMapIndex(keyv, mapElem) - } - default: - panic(&incompatibleDecodeTypeError{typ, pval.kind}) - } -} - -/* *Interface is modelled after encoding/json */ -func (p *Decoder) valueInterface(pval *plistValue) interface{} { - switch pval.kind { - case String: - return pval.value.(string) - case Integer: - if pval.value.(signedInt).signed { - return int64(pval.value.(signedInt).value) - } - return pval.value.(signedInt).value - case Real: - bits := pval.value.(sizedFloat).bits - switch bits { - case 32: - return float32(pval.value.(sizedFloat).value) - case 64: - return pval.value.(sizedFloat).value - } - case Boolean: - return pval.value.(bool) - case Array: - return p.arrayInterface(pval.value.([]*plistValue)) - case Dictionary: - return p.dictionaryInterface(pval.value.(*dictionary)) - case Data: - return pval.value.([]byte) - case Date: - return pval.value.(time.Time) - } - return nil -} - -func (p *Decoder) arrayInterface(subvalues []*plistValue) []interface{} { - out := make([]interface{}, len(subvalues)) - for i, subv := range subvalues { - out[i] = p.valueInterface(subv) - } - return out -} - -func (p *Decoder) dictionaryInterface(dict *dictionary) map[string]interface{} { - out := make(map[string]interface{}) - for k, subv := range dict.m { - out[k] = p.valueInterface(subv) - } - return out -} diff --git a/vendor/github.com/bitrise-io/go-utils/command/command.go b/vendor/github.com/bitrise-io/go-utils/command/command.go index 040dbaf9..4cd005a3 100644 --- a/vendor/github.com/bitrise-io/go-utils/command/command.go +++ b/vendor/github.com/bitrise-io/go-utils/command/command.go @@ -32,15 +32,20 @@ func NewWithStandardOuts(name string, args ...string) *Model { return New(name, args...).SetStdout(os.Stdout).SetStderr(os.Stderr) } -// NewFromSlice ... -func NewFromSlice(slice ...string) (*Model, error) { - if len(slice) == 0 { +// NewWithParams ... +func NewWithParams(params ...string) (*Model, error) { + if len(params) == 0 { return nil, errors.New("no command provided") - } else if len(slice) == 1 { - return New(slice[0]), nil + } else if len(params) == 1 { + return New(params[0]), nil } - return New(slice[0], slice[1:]...), nil + return New(params[0], params[1:]...), nil +} + +// NewFromSlice ... +func NewFromSlice(slice []string) (*Model, error) { + return NewWithParams(slice...) } // NewWithCmd ... diff --git a/vendor/github.com/bitrise-io/go-utils/command/git.go b/vendor/github.com/bitrise-io/go-utils/command/git.go deleted file mode 100644 index 52f3dacf..00000000 --- a/vendor/github.com/bitrise-io/go-utils/command/git.go +++ /dev/null @@ -1,204 +0,0 @@ -package command - -import ( - "errors" - "fmt" - "log" - "os/exec" - "strings" - - "github.com/bitrise-io/go-utils/pathutil" -) - -// GitClone ... -func GitClone(uri, pth string) (err error) { - if uri == "" { - return errors.New("Git Clone 'uri' missing") - } - if pth == "" { - return errors.New("Git Clone 'pth' missing") - } - if err = RunCommand("git", "clone", "--recursive", uri, pth); err != nil { - log.Printf(" [!] Failed to git clone from (%s) to (%s)", uri, pth) - return - } - return -} - -// GitCloneTagOrBranch ... -func GitCloneTagOrBranch(uri, pth, tagOrBranch string) error { - if uri == "" { - return errors.New("Git Clone 'uri' missing") - } - if pth == "" { - return errors.New("Git Clone 'path' missing") - } - if tagOrBranch == "" { - return errors.New("Git Clone 'tag or branch' missing") - } - return RunCommand("git", "clone", "--recursive", "--branch", tagOrBranch, uri, pth) -} - -// GitCloneTag ... -func GitCloneTag(uri, pth, tag string) error { - if uri == "" { - return errors.New("Git Clone 'uri' missing") - } - if pth == "" { - return errors.New("Git Clone 'path' missing") - } - if tag == "" { - return errors.New("Git Clone 'tag or branch' missing") - } - if err := RunCommand("git", "clone", "--recursive", "--branch", tag, uri, pth); err != nil { - return fmt.Errorf("Git clone failed, err: %s", err) - } - - out, err := RunCommandInDirAndReturnCombinedStdoutAndStderr(pth, "git", "branch") - if err != nil { - return fmt.Errorf("Failed to get git branches, err: %s", err) - } - - if out != "* (no branch)" { - return fmt.Errorf("Current HEAD is not detached head, current branch should be: '* (no branch)', got: %s", out) - } - return nil -} - -// GitCloneTagOrBranchAndValidateCommitHash ... -func GitCloneTagOrBranchAndValidateCommitHash(uri, pth, version, commithash string) (err error) { - if uri == "" { - return errors.New("Git Clone 'uri' missing") - } - if pth == "" { - return errors.New("Git Clone 'pth' missing") - } - if version == "" { - return errors.New("Git Clone 'version' missing") - } - if commithash == "" { - return errors.New("Git Clone 'commithash' missing") - } - if err = RunCommand("git", "clone", "--recursive", uri, pth, "--branch", version); err != nil { - return - } - - // cleanup - defer func() { - if err != nil { - if err := RemoveDir(pth); err != nil { - log.Printf(" [!] Failed to cleanup path (%s) error: (%v) ", pth, err) - } - } - }() - - latestCommit, err := GitGetLatestCommitHashOnHead(pth) - if err != nil { - return - } - if commithash != latestCommit { - return fmt.Errorf("Commit hash doesn't match the one specified for the version tag. (version tag: %s) (expected commit hash: %s) (got: %s)", version, latestCommit, commithash) - } - - return -} - -// GitPull ... -func GitPull(pth string) error { - err := RunCommandInDir(pth, "git", "pull") - if err != nil { - log.Printf(" [!] Git pull failed, error (%v)", err) - return err - } - return nil -} - -// GitUpdate ... -func GitUpdate(git, pth string) error { - if exists, err := pathutil.IsPathExists(pth); err != nil { - return err - } else if !exists { - fmt.Println("Git path does not exist, do clone") - return GitClone(git, pth) - } - - fmt.Println("Git path exist, do pull") - return GitPull(pth) -} - -// GitCheckout ... -func GitCheckout(dir, branchOrTag string) error { - if branchOrTag == "" { - return errors.New("Git Clone 'branchOrTag' missing") - } - return RunCommandInDir(dir, "git", "checkout", branchOrTag) -} - -// GitCreateAndCheckoutBranch ... -func GitCreateAndCheckoutBranch(repoPath, branch string) error { - if branch == "" { - return errors.New("Git checkout 'branch' missing") - } - return RunCommandInDir(repoPath, "git", "checkout", "-b", branch) -} - -// GitAddFile ... -func GitAddFile(repoPath, filePath string) error { - if filePath == "" { - return errors.New("Git add 'file' missing") - } - return RunCommandInDir(repoPath, "git", "add", filePath) -} - -// GitPushToOrigin ... -func GitPushToOrigin(repoPath, branch string) error { - if branch == "" { - return errors.New("Git push 'branch' missing") - } - return RunCommandInDir(repoPath, "git", "push", "-u", "origin", branch) -} - -// GitCheckIsNoChanges ... -func GitCheckIsNoChanges(repoPath string) error { - out, err := RunCommandInDirAndReturnCombinedStdoutAndStderr(repoPath, "git", "status", "--porcelain") - if err != nil { - log.Println(" [!] Failed to git check changes:", out) - return err - } - if out != "" { - return errors.New("Uncommited changes: " + out) - } - return nil -} - -// GitCommit ... -func GitCommit(repoPath string, message string) error { - if message == "" { - return errors.New("Git commit 'message' missing") - } - return RunCommandInDir(repoPath, "git", "commit", "-m", message) -} - -// GitGetLatestCommitHashOnHead ... -func GitGetLatestCommitHashOnHead(pth string) (string, error) { - cmd := exec.Command("git", "rev-parse", "HEAD") - cmd.Dir = pth - bytes, err := cmd.CombinedOutput() - cmdOutput := string(bytes) - if err != nil { - log.Printf(" [!] Output: %s", cmdOutput) - } - return strings.TrimSpace(cmdOutput), err -} - -// GitGetCommitHashOfHEAD ... -func GitGetCommitHashOfHEAD(pth string) (string, error) { - cmd := exec.Command("git", "rev-parse", "HEAD") - cmd.Dir = pth - bytes, err := cmd.CombinedOutput() - cmdOutput := string(bytes) - if err != nil { - log.Printf(" [!] Output: %s", cmdOutput) - } - return strings.TrimSpace(cmdOutput), err -} diff --git a/vendor/github.com/bitrise-io/go-utils/pathutil/pathutil.go b/vendor/github.com/bitrise-io/go-utils/pathutil/pathutil.go index a0b08661..4e31d3cd 100644 --- a/vendor/github.com/bitrise-io/go-utils/pathutil/pathutil.go +++ b/vendor/github.com/bitrise-io/go-utils/pathutil/pathutil.go @@ -9,6 +9,32 @@ import ( "strings" ) +// RevokableChangeDir ... +func RevokableChangeDir(dir string) (func() error, error) { + origDir, err := CurrentWorkingDirectoryAbsolutePath() + if err != nil { + return nil, err + } + + revokeFn := func() error { + return os.Chdir(origDir) + } + + return revokeFn, os.Chdir(dir) +} + +// ChangeDirForFunction ... +func ChangeDirForFunction(dir string, fn func()) error { + revokeFn, err := RevokableChangeDir(dir) + if err != nil { + return err + } + + fn() + + return revokeFn() +} + // IsRelativePath ... func IsRelativePath(pth string) bool { if strings.HasPrefix(pth, "./") { diff --git a/vendor/github.com/bitrise-tools/go-xcode/exportoptions/appstore_options.go b/vendor/github.com/bitrise-tools/go-xcode/exportoptions/appstore_options.go index 0e90cad0..3be1c908 100644 --- a/vendor/github.com/bitrise-tools/go-xcode/exportoptions/appstore_options.go +++ b/vendor/github.com/bitrise-tools/go-xcode/exportoptions/appstore_options.go @@ -3,7 +3,7 @@ package exportoptions import ( "fmt" - plist "github.com/DHowett/go-plist" + "howett.net/plist" ) // AppStoreOptionsModel ... diff --git a/vendor/github.com/bitrise-tools/go-xcode/exportoptions/exportoptions.go b/vendor/github.com/bitrise-tools/go-xcode/exportoptions/exportoptions.go index d2550a94..7e0cdf90 100644 --- a/vendor/github.com/bitrise-tools/go-xcode/exportoptions/exportoptions.go +++ b/vendor/github.com/bitrise-tools/go-xcode/exportoptions/exportoptions.go @@ -4,9 +4,9 @@ import ( "fmt" "path/filepath" - plist "github.com/DHowett/go-plist" "github.com/bitrise-io/go-utils/fileutil" "github.com/bitrise-io/go-utils/pathutil" + "howett.net/plist" ) // ExportOptions ... diff --git a/vendor/github.com/bitrise-tools/go-xcode/exportoptions/non_appstore_options.go b/vendor/github.com/bitrise-tools/go-xcode/exportoptions/non_appstore_options.go index 9ab04092..7eecdb20 100644 --- a/vendor/github.com/bitrise-tools/go-xcode/exportoptions/non_appstore_options.go +++ b/vendor/github.com/bitrise-tools/go-xcode/exportoptions/non_appstore_options.go @@ -3,7 +3,7 @@ package exportoptions import ( "fmt" - plist "github.com/DHowett/go-plist" + "howett.net/plist" ) // NonAppStoreOptionsModel ... diff --git a/vendor/github.com/bitrise-tools/go-xcode/models/models.go b/vendor/github.com/bitrise-tools/go-xcode/models/models.go new file mode 100644 index 00000000..42ea3721 --- /dev/null +++ b/vendor/github.com/bitrise-tools/go-xcode/models/models.go @@ -0,0 +1,8 @@ +package models + +// XcodebuildVersionModel ... +type XcodebuildVersionModel struct { + Version string + BuildVersion string + MajorVersion int64 +} diff --git a/vendor/github.com/bitrise-tools/go-xcode/plistutil/plistutil.go b/vendor/github.com/bitrise-tools/go-xcode/plistutil/plistutil.go new file mode 100644 index 00000000..0ca054b3 --- /dev/null +++ b/vendor/github.com/bitrise-tools/go-xcode/plistutil/plistutil.go @@ -0,0 +1,156 @@ +package plistutil + +import ( + "time" + + "github.com/bitrise-io/go-utils/fileutil" + "howett.net/plist" +) + +// PlistData ... +type PlistData map[string]interface{} + +// NewPlistDataFromContent ... +func NewPlistDataFromContent(plistContent string) (PlistData, error) { + var data PlistData + if _, err := plist.Unmarshal([]byte(plistContent), &data); err != nil { + return PlistData{}, err + } + return data, nil +} + +// NewPlistDataFromFile ... +func NewPlistDataFromFile(plistPth string) (PlistData, error) { + content, err := fileutil.ReadStringFromFile(plistPth) + if err != nil { + return PlistData{}, err + } + return NewPlistDataFromContent(content) +} + +// GetString ... +func (data PlistData) GetString(forKey string) (string, bool) { + value, ok := data[forKey] + if !ok { + return "", false + } + + casted, ok := value.(string) + if !ok { + return "", false + } + + return casted, true +} + +// GetUInt64 ... +func (data PlistData) GetUInt64(forKey string) (uint64, bool) { + value, ok := data[forKey] + if !ok { + return 0, false + } + + casted, ok := value.(uint64) + if !ok { + return 0, false + } + return casted, true +} + +// GetBool ... +func (data PlistData) GetBool(forKey string) (bool, bool) { + value, ok := data[forKey] + if !ok { + return false, false + } + + casted, ok := value.(bool) + if !ok { + return false, false + } + + return casted, true +} + +// GetTime ... +func (data PlistData) GetTime(forKey string) (time.Time, bool) { + value, ok := data[forKey] + if !ok { + return time.Time{}, false + } + + casted, ok := value.(time.Time) + if !ok { + return time.Time{}, false + } + return casted, true +} + +// GetUInt64Array ... +func (data PlistData) GetUInt64Array(forKey string) ([]uint64, bool) { + value, ok := data[forKey] + if !ok { + return nil, false + } + + if casted, ok := value.([]uint64); ok { + return casted, true + } + + casted, ok := value.([]interface{}) + if !ok { + return nil, false + } + + array := []uint64{} + for _, v := range casted { + casted, ok := v.(uint64) + if !ok { + return nil, false + } + + array = append(array, casted) + } + return array, true +} + +// GetStringArray ... +func (data PlistData) GetStringArray(forKey string) ([]string, bool) { + value, ok := data[forKey] + if !ok { + return nil, false + } + + if casted, ok := value.([]string); ok { + return casted, true + } + + casted, ok := value.([]interface{}) + if !ok { + return nil, false + } + + array := []string{} + for _, v := range casted { + casted, ok := v.(string) + if !ok { + return nil, false + } + + array = append(array, casted) + } + return array, true +} + +// GetMapStringInterface ... +func (data PlistData) GetMapStringInterface(forKey string) (PlistData, bool) { + value, ok := data[forKey] + if !ok { + return nil, false + } + + if casted, ok := value.(map[string]interface{}); ok { + return casted, true + } + return nil, false +} diff --git a/vendor/github.com/bitrise-tools/go-xcode/plistutil/plistutil_test_file_content.go b/vendor/github.com/bitrise-tools/go-xcode/plistutil/plistutil_test_file_content.go new file mode 100644 index 00000000..2b0c2362 --- /dev/null +++ b/vendor/github.com/bitrise-tools/go-xcode/plistutil/plistutil_test_file_content.go @@ -0,0 +1,295 @@ +package plistutil + +const infoPlistContent = ` + + + + CFBundleName + ios-simple-objc + DTXcode + 0832 + DTSDKName + iphoneos10.3 + UILaunchStoryboardName + LaunchScreen + DTSDKBuild + 14E269 + CFBundleDevelopmentRegion + en + CFBundleVersion + 1 + BuildMachineOSBuild + 16F73 + DTPlatformName + iphoneos + CFBundlePackageType + APPL + UIMainStoryboardFile + Main + CFBundleSupportedPlatforms + + iPhoneOS + + CFBundleShortVersionString + 1.0 + CFBundleInfoDictionaryVersion + 6.0 + UIRequiredDeviceCapabilities + + armv7 + + CFBundleExecutable + ios-simple-objc + DTCompiler + com.apple.compilers.llvm.clang.1_0 + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CFBundleIdentifier + Bitrise.ios-simple-objc + MinimumOSVersion + 8.1 + DTXcodeBuild + 8E2002 + DTPlatformVersion + 10.3 + LSRequiresIPhoneOS + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CFBundleSignature + ???? + UIDeviceFamily + + 1 + 2 + + DTPlatformBuild + 14E269 + + +` + +const developmentProfileContent = ` + + + + AppIDName + Bitrise Test + ApplicationIdentifierPrefix + + 9NS4 + + CreationDate + 2016-09-22T11:28:46Z + Platform + + iOS + + DeveloperCertificates + + + + Entitlements + + keychain-access-groups + + 9NS4.* + + get-task-allow + + application-identifier + 9NS4.* + com.apple.developer.team-identifier + 9NS4 + + ExpirationDate + 2017-09-22T11:28:46Z + Name + Bitrise Test Development + ProvisionedDevices + + b138 + + TeamIdentifier + + 9NS4 + + TeamName + Some Dude + TimeToLive + 365 + UUID + 4b617a5f + Version + 1 +` + +const appStoreProfileContent = ` + + + + AppIDName + Bitrise Test + ApplicationIdentifierPrefix + + 9NS4 + + CreationDate + 2016-09-22T11:29:12Z + Platform + + iOS + + DeveloperCertificates + + + + Entitlements + + keychain-access-groups + + 9NS4.* + + get-task-allow + + application-identifier + 9NS4.* + com.apple.developer.team-identifier + 9NS4 + beta-reports-active + + + ExpirationDate + 2017-09-21T13:20:06Z + Name + Bitrise Test App Store + TeamIdentifier + + 9NS4 + + TeamName + Some Dude + TimeToLive + 364 + UUID + a60668dd + Version + 1 +` + +const adHocProfileContent = ` + + + + AppIDName + Bitrise Test + ApplicationIdentifierPrefix + + 9NS4 + + CreationDate + 2016-09-22T11:29:38Z + Platform + + iOS + + DeveloperCertificates + + + + Entitlements + + keychain-access-groups + + 9NS4.* + + get-task-allow + + application-identifier + 9NS4.* + com.apple.developer.team-identifier + 9NS4 + + ExpirationDate + 2017-09-21T13:20:06Z + Name + Bitrise Test Ad Hoc + ProvisionedDevices + + b138 + + TeamIdentifier + + 9NS4 + + TeamName + Some Dude + TimeToLive + 364 + UUID + 26668300 + Version + 1 +` + +const enterpriseProfileContent = ` + + + + AppIDName + Bitrise Test + ApplicationIdentifierPrefix + + PF3BP78LQ8 + + CreationDate + 2015-10-05T13:32:46Z + Platform + + iOS + + DeveloperCertificates + + + + Entitlements + + keychain-access-groups + + PF3BP78LQ8.* + + get-task-allow + + application-identifier + 9NS4.* + com.apple.developer.team-identifier + 9NS4 + + ExpirationDate + 2016-10-04T13:32:46Z + Name + Bitrise Test Enterprise + ProvisionsAllDevices + + TeamIdentifier + + PF3BP78LQ8 + + TeamName + Some Dude + TimeToLive + 365 + UUID + 8d6caa15 + Version + 1 +` diff --git a/vendor/github.com/bitrise-tools/go-xcode/provisioningprofile/provisioningprofile.go b/vendor/github.com/bitrise-tools/go-xcode/provisioningprofile/provisioningprofile.go index 58b8afce..fb191a51 100644 --- a/vendor/github.com/bitrise-tools/go-xcode/provisioningprofile/provisioningprofile.go +++ b/vendor/github.com/bitrise-tools/go-xcode/provisioningprofile/provisioningprofile.go @@ -4,47 +4,25 @@ import ( "fmt" "strings" - plist "github.com/DHowett/go-plist" "github.com/bitrise-io/go-utils/command" "github.com/bitrise-tools/go-xcode/exportoptions" + "github.com/bitrise-tools/go-xcode/plistutil" ) const ( notValidParameterErrorMessage = "security: SecPolicySetValue: One or more parameters passed to a function were not valid." ) -// EntitlementsModel ... -type EntitlementsModel struct { - GetTaskAllow *bool `plist:"get-task-allow"` - DeveloperTeamID *string `plist:"com.apple.developer.team-identifier"` -} - -// Model ... -type Model struct { - Name *string `plist:"Name"` - ProvisionedDevices *[]string `plist:"ProvisionedDevices"` - ProvisionsAllDevices *bool `plist:"ProvisionsAllDevices"` - Entitlements *EntitlementsModel `plist:"Entitlements"` -} - -func newFromProfileContent(content string) (Model, error) { - var mobileProvision Model - if _, err := plist.Unmarshal([]byte(content), &mobileProvision); err != nil { - return Model{}, fmt.Errorf("failed to mobileprovision, error: %s", err) - } - - return mobileProvision, nil -} - -// NewFromFile ... -func NewFromFile(pth string) (Model, error) { - cmd := command.New("security", "cms", "-D", "-i", pth) +// NewPlistDataFromFile ... +func NewPlistDataFromFile(provisioningProfilePth string) (plistutil.PlistData, error) { + cmd := command.New("security", "cms", "-D", "-i", provisioningProfilePth) out, err := cmd.RunAndReturnTrimmedCombinedOutput() if err != nil { - return Model{}, fmt.Errorf("command failed, error: %s", err) + return nil, fmt.Errorf("command failed, error: %s", err) } + // fix: security: SecPolicySetValue: One or more parameters passed to a function were not valid. outSplit := strings.Split(out, "\n") if len(outSplit) > 0 { if strings.Contains(outSplit[0], notValidParameterErrorMessage) { @@ -52,38 +30,42 @@ func NewFromFile(pth string) (Model, error) { out = strings.Join(fixedOutSplit, "\n") } } + // --- - return newFromProfileContent(out) + return plistutil.NewPlistDataFromContent(out) } // GetExportMethod ... -func (profile Model) GetExportMethod() exportoptions.Method { - method := exportoptions.MethodDefault - if profile.ProvisionedDevices == nil { - if profile.ProvisionsAllDevices != nil && *profile.ProvisionsAllDevices { - method = exportoptions.MethodEnterprise - } else { - method = exportoptions.MethodAppStore +func GetExportMethod(data plistutil.PlistData) exportoptions.Method { + _, ok := data.GetStringArray("ProvisionedDevices") + if !ok { + if allDevices, ok := data.GetBool("ProvisionsAllDevices"); ok && allDevices { + return exportoptions.MethodEnterprise } - } else if profile.Entitlements != nil { - entitlements := *profile.Entitlements - if entitlements.GetTaskAllow != nil && *entitlements.GetTaskAllow { - method = exportoptions.MethodDevelopment - } else { - method = exportoptions.MethodAdHoc + return exportoptions.MethodAppStore + } + + entitlements, ok := data.GetMapStringInterface("Entitlements") + if ok { + if allow, ok := entitlements.GetBool("get-task-allow"); ok && allow { + return exportoptions.MethodDevelopment } + return exportoptions.MethodAdHoc } - return method + + return exportoptions.MethodDefault } // GetDeveloperTeam ... -func (profile Model) GetDeveloperTeam() string { - developerTeamID := "" - if profile.Entitlements != nil { - entitlements := *profile.Entitlements - if entitlements.DeveloperTeamID != nil { - developerTeamID = *entitlements.DeveloperTeamID - } +func GetDeveloperTeam(data plistutil.PlistData) string { + entitlements, ok := data.GetMapStringInterface("Entitlements") + if !ok { + return "" + } + + teamID, ok := entitlements.GetString("com.apple.developer.team-identifier") + if !ok { + return "" } - return developerTeamID + return teamID } diff --git a/vendor/github.com/bitrise-tools/go-xcode/utility/path.go b/vendor/github.com/bitrise-tools/go-xcode/utility/path.go new file mode 100644 index 00000000..4642d3e5 --- /dev/null +++ b/vendor/github.com/bitrise-tools/go-xcode/utility/path.go @@ -0,0 +1,69 @@ +package utility + +import ( + "io/ioutil" + "path/filepath" + "strings" +) + +// FilterFunc ... +type FilterFunc func(pth string) (bool, error) + +// FilterPaths ... +func FilterPaths(paths []string, filters ...FilterFunc) ([]string, error) { + filtered := []string{} + + for _, pth := range paths { + allowed := true + for _, filter := range filters { + if allows, err := filter(pth); err != nil { + return []string{}, err + } else if !allows { + allowed = false + break + } + } + if allowed { + filtered = append(filtered, pth) + } + } + + return filtered, nil +} + +// ListEntries ... +func ListEntries(dir string, filters ...FilterFunc) ([]string, error) { + absDir, err := filepath.Abs(dir) + if err != nil { + return []string{}, err + } + + entries, err := ioutil.ReadDir(absDir) + if err != nil { + return []string{}, err + } + + paths := []string{} + for _, entry := range entries { + pth := filepath.Join(absDir, entry.Name()) + paths = append(paths, pth) + } + + return FilterPaths(paths, filters...) +} + +// ExtensionFilter ... +func ExtensionFilter(ext string, allowed bool) FilterFunc { + return func(pth string) (bool, error) { + e := filepath.Ext(pth) + return (allowed == strings.EqualFold(ext, e)), nil + } +} + +// BaseFilter ... +func BaseFilter(base string, allowed bool) FilterFunc { + return func(pth string) (bool, error) { + b := filepath.Base(pth) + return (allowed == strings.EqualFold(base, b)), nil + } +} diff --git a/vendor/github.com/bitrise-tools/go-xcode/utility/utility.go b/vendor/github.com/bitrise-tools/go-xcode/utility/utility.go new file mode 100644 index 00000000..c9f94c6e --- /dev/null +++ b/vendor/github.com/bitrise-tools/go-xcode/utility/utility.go @@ -0,0 +1,51 @@ +package utility + +import ( + "fmt" + "strconv" + "strings" + + "github.com/bitrise-io/go-utils/command" + "github.com/bitrise-tools/go-xcode/models" +) + +func getXcodeVersionFromXcodebuildOutput(outStr string) (models.XcodebuildVersionModel, error) { + split := strings.Split(outStr, "\n") + if len(split) == 0 { + return models.XcodebuildVersionModel{}, fmt.Errorf("failed to parse xcodebuild version output (%s)", outStr) + } + + xcodebuildVersion := split[0] + buildVersion := split[1] + + split = strings.Split(xcodebuildVersion, " ") + if len(split) != 2 { + return models.XcodebuildVersionModel{}, fmt.Errorf("failed to parse xcodebuild version output (%s)", outStr) + } + + version := split[1] + + split = strings.Split(version, ".") + majorVersionStr := split[0] + + majorVersion, err := strconv.ParseInt(majorVersionStr, 10, 32) + if err != nil { + return models.XcodebuildVersionModel{}, fmt.Errorf("failed to parse xcodebuild version output (%s), error: %s", outStr, err) + } + + return models.XcodebuildVersionModel{ + Version: xcodebuildVersion, + BuildVersion: buildVersion, + MajorVersion: majorVersion, + }, nil +} + +// GetXcodeVersion ... +func GetXcodeVersion() (models.XcodebuildVersionModel, error) { + cmd := command.New("xcodebuild", "-version") + outStr, err := cmd.RunAndReturnTrimmedCombinedOutput() + if err != nil { + return models.XcodebuildVersionModel{}, fmt.Errorf("xcodebuild -version failed, err: %s, details: %s", err, outStr) + } + return getXcodeVersionFromXcodebuildOutput(outStr) +} diff --git a/vendor/github.com/bitrise-tools/go-xcode/xcarchive/xcarchive.go b/vendor/github.com/bitrise-tools/go-xcode/xcarchive/xcarchive.go index dd5471c0..d9da0b5e 100644 --- a/vendor/github.com/bitrise-tools/go-xcode/xcarchive/xcarchive.go +++ b/vendor/github.com/bitrise-tools/go-xcode/xcarchive/xcarchive.go @@ -4,29 +4,52 @@ import ( "fmt" "path/filepath" "strings" + + "github.com/bitrise-io/go-utils/pathutil" + "github.com/bitrise-tools/go-xcode/utility" ) -// EmbeddedMobileProvisionPth ... -func EmbeddedMobileProvisionPth(archivePth string) (string, error) { - applicationPth := filepath.Join(archivePth, "/Products/Applications") - mobileProvisionPthPattern := filepath.Join(applicationPth, "*.app/embedded.mobileprovision") - mobileProvisionPths, err := filepath.Glob(mobileProvisionPthPattern) +// FindEmbeddedMobileProvision ... +func FindEmbeddedMobileProvision(archivePth string) (string, error) { + if exist, err := pathutil.IsDirExists(archivePth); err != nil { + return "", fmt.Errorf("failed to check if archive exist, error: %s", err) + } else if !exist { + return "", fmt.Errorf("archive not exist at: %s", archivePth) + } + + applicationsDirPth := filepath.Join(archivePth, "Products/Applications") + apps, err := utility.ListEntries(applicationsDirPth, utility.ExtensionFilter(".app", true)) if err != nil { - return "", fmt.Errorf("failed to find embedded.mobileprovision with pattern: %s, error: %s", mobileProvisionPthPattern, err) + return "", err } - if len(mobileProvisionPths) == 0 { - return "", fmt.Errorf("no embedded.mobileprovision with pattern: %s", mobileProvisionPthPattern) + + for _, app := range apps { + embeddedProfiles, err := utility.ListEntries(app, utility.BaseFilter("embedded.mobileprovision", true)) + if err != nil { + return "", err + } + if len(embeddedProfiles) > 0 { + return embeddedProfiles[0], nil + } } - return mobileProvisionPths[0], nil + + return "", fmt.Errorf("no embedded.mobileprovision found") } // FindDSYMs ... func FindDSYMs(archivePth string) (string, []string, error) { - pattern := filepath.Join(archivePth, "dSYMs", "*.dSYM") - dsyms, err := filepath.Glob(pattern) + if exist, err := pathutil.IsDirExists(archivePth); err != nil { + return "", []string{}, fmt.Errorf("failed to check if archive exist, error: %s", err) + } else if !exist { + return "", []string{}, fmt.Errorf("archive not exist at: %s", archivePth) + } + + dsymsDirPth := filepath.Join(archivePth, "dSYMs") + dsyms, err := utility.ListEntries(dsymsDirPth, utility.ExtensionFilter(".dsym", true)) if err != nil { - return "", []string{}, fmt.Errorf("failed to find dSYM with pattern: %s, error: %s", pattern, err) + return "", []string{}, err } + appDSYM := "" frameworkDSYMs := []string{} for _, dsym := range dsyms { @@ -36,19 +59,29 @@ func FindDSYMs(archivePth string) (string, []string, error) { frameworkDSYMs = append(frameworkDSYMs, dsym) } } + if appDSYM == "" && len(frameworkDSYMs) == 0 { + return "", []string{}, fmt.Errorf("no dsym found") + } + return appDSYM, frameworkDSYMs, nil } // FindApp ... func FindApp(archivePth string) (string, error) { - pattern := filepath.Join(archivePth, "Products/Applications", "*.app") - apps, err := filepath.Glob(pattern) + if exist, err := pathutil.IsDirExists(archivePth); err != nil { + return "", fmt.Errorf("failed to check if archive exist, error: %s", err) + } else if !exist { + return "", fmt.Errorf("archive not exist at: %s", archivePth) + } + + applicationsDirPth := filepath.Join(archivePth, "Products/Applications") + apps, err := utility.ListEntries(applicationsDirPth, utility.ExtensionFilter(".app", true)) if err != nil { - return "", fmt.Errorf("failed to find .app directory with pattern: %s, error: %s", pattern, err) + return "", err } if len(apps) == 0 { - return "", fmt.Errorf("no app found with pattern (%s)", pattern) + return "", fmt.Errorf("no app found") } return apps[0], nil diff --git a/vendor/github.com/bitrise-tools/go-xcode/xcodebuild/show_build_settings.go b/vendor/github.com/bitrise-tools/go-xcode/xcodebuild/show_build_settings.go new file mode 100644 index 00000000..912c8575 --- /dev/null +++ b/vendor/github.com/bitrise-tools/go-xcode/xcodebuild/show_build_settings.go @@ -0,0 +1,88 @@ +package xcodebuild + +import ( + "bufio" + "os/exec" + "strings" + + "github.com/bitrise-io/go-utils/command" +) + +// ShowBuildSettingsCommandModel ... +type ShowBuildSettingsCommandModel struct { + projectPath string + isWorkspace bool +} + +// NewShowBuildSettingsCommand ... +func NewShowBuildSettingsCommand(projectPath string, isWorkspace bool) *ShowBuildSettingsCommandModel { + return &ShowBuildSettingsCommandModel{ + projectPath: projectPath, + isWorkspace: isWorkspace, + } +} + +func (c *ShowBuildSettingsCommandModel) cmdSlice() []string { + slice := []string{toolName} + + if c.projectPath != "" { + if c.isWorkspace { + slice = append(slice, "-workspace", c.projectPath) + } else { + slice = append(slice, "-project", c.projectPath) + } + } + + return slice +} + +// PrintableCmd ... +func (c ShowBuildSettingsCommandModel) PrintableCmd() string { + cmdSlice := c.cmdSlice() + return command.PrintableCommandArgs(false, cmdSlice) +} + +// Command ... +func (c ShowBuildSettingsCommandModel) Command() *command.Model { + cmdSlice := c.cmdSlice() + return command.New(cmdSlice[0], cmdSlice[1:]...) +} + +// Cmd ... +func (c ShowBuildSettingsCommandModel) Cmd() *exec.Cmd { + command := c.Command() + return command.GetCmd() +} + +func parseBuildSettings(out string) (map[string]string, error) { + settings := map[string]string{} + + scanner := bufio.NewScanner(strings.NewReader(out)) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + + if split := strings.Split(line, "="); len(split) == 2 { + key := strings.TrimSpace(split[0]) + value := strings.TrimSpace(split[1]) + value = strings.Trim(value, `"`) + + settings[key] = value + } + } + if err := scanner.Err(); err != nil { + return map[string]string{}, err + } + + return settings, nil +} + +// RunAndReturnSettings ... +func (c ShowBuildSettingsCommandModel) RunAndReturnSettings() (map[string]string, error) { + command := c.Command() + out, err := command.RunAndReturnTrimmedCombinedOutput() + if err != nil { + return map[string]string{}, err + } + + return parseBuildSettings(out) +} diff --git a/vendor/github.com/bitrise-tools/go-xcode/xcodebuild/test.go b/vendor/github.com/bitrise-tools/go-xcode/xcodebuild/test.go index 7dbfe1bb..2953ca38 100644 --- a/vendor/github.com/bitrise-tools/go-xcode/xcodebuild/test.go +++ b/vendor/github.com/bitrise-tools/go-xcode/xcodebuild/test.go @@ -1,5 +1,12 @@ package xcodebuild +import ( + "os" + "os/exec" + + "github.com/bitrise-io/go-utils/command" +) + /* xcodebuild [-project ] \ -scheme \ @@ -98,8 +105,38 @@ func (c *TestCommandModel) cmdSlice() []string { slice = append(slice, c.customBuildActions...) slice = append(slice, "test") - + if c.destination != "" { + slice = append(slice, "-destination", c.destination) + } slice = append(slice, c.customOptions...) return slice } + +// PrintableCmd ... +func (c TestCommandModel) PrintableCmd() string { + cmdSlice := c.cmdSlice() + return command.PrintableCommandArgs(false, cmdSlice) +} + +// Command ... +func (c TestCommandModel) Command() *command.Model { + cmdSlice := c.cmdSlice() + return command.New(cmdSlice[0], cmdSlice[1:]...) +} + +// Cmd ... +func (c TestCommandModel) Cmd() *exec.Cmd { + command := c.Command() + return command.GetCmd() +} + +// Run ... +func (c TestCommandModel) Run() error { + command := c.Command() + + command.SetStdout(os.Stdout) + command.SetStderr(os.Stderr) + + return command.Run() +} diff --git a/vendor/howett.net/plist/.travis.yml b/vendor/howett.net/plist/.travis.yml new file mode 100644 index 00000000..b1f27e39 --- /dev/null +++ b/vendor/howett.net/plist/.travis.yml @@ -0,0 +1,8 @@ +language: go +go_import_path: "howett.net/plist" +go: + - 1.2 + - master +script: + - go test -v + - go test -tags appengine diff --git a/vendor/github.com/DHowett/go-plist/LICENSE b/vendor/howett.net/plist/LICENSE similarity index 100% rename from vendor/github.com/DHowett/go-plist/LICENSE rename to vendor/howett.net/plist/LICENSE diff --git a/vendor/github.com/DHowett/go-plist/README.md b/vendor/howett.net/plist/README.md similarity index 100% rename from vendor/github.com/DHowett/go-plist/README.md rename to vendor/howett.net/plist/README.md diff --git a/vendor/howett.net/plist/bplist.go b/vendor/howett.net/plist/bplist.go new file mode 100644 index 00000000..962793a9 --- /dev/null +++ b/vendor/howett.net/plist/bplist.go @@ -0,0 +1,26 @@ +package plist + +type bplistTrailer struct { + Unused [5]uint8 + SortVersion uint8 + OffsetIntSize uint8 + ObjectRefSize uint8 + NumObjects uint64 + TopObject uint64 + OffsetTableOffset uint64 +} + +const ( + bpTagNull uint8 = 0x00 + bpTagBoolFalse = 0x08 + bpTagBoolTrue = 0x09 + bpTagInteger = 0x10 + bpTagReal = 0x20 + bpTagDate = 0x30 + bpTagData = 0x40 + bpTagASCIIString = 0x50 + bpTagUTF16String = 0x60 + bpTagUID = 0x80 + bpTagArray = 0xA0 + bpTagDictionary = 0xD0 +) diff --git a/vendor/howett.net/plist/bplist_generator.go b/vendor/howett.net/plist/bplist_generator.go new file mode 100644 index 00000000..5b6513d1 --- /dev/null +++ b/vendor/howett.net/plist/bplist_generator.go @@ -0,0 +1,290 @@ +package plist + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "time" + "unicode/utf16" +) + +func bplistMinimumIntSize(n uint64) int { + switch { + case n <= uint64(0xff): + return 1 + case n <= uint64(0xffff): + return 2 + case n <= uint64(0xffffffff): + return 4 + default: + return 8 + } +} + +func bplistValueShouldUnique(pval cfValue) bool { + switch pval.(type) { + case cfString, *cfNumber, *cfReal, cfDate, cfData: + return true + } + return false +} + +type bplistGenerator struct { + writer *countedWriter + objmap map[interface{}]uint64 // maps pValue.hash()es to object locations + objtable []cfValue + trailer bplistTrailer +} + +func (p *bplistGenerator) flattenPlistValue(pval cfValue) { + key := pval.hash() + if bplistValueShouldUnique(pval) { + if _, ok := p.objmap[key]; ok { + return + } + } + + p.objmap[key] = uint64(len(p.objtable)) + p.objtable = append(p.objtable, pval) + + switch pval := pval.(type) { + case *cfDictionary: + pval.sort() + for _, k := range pval.keys { + p.flattenPlistValue(cfString(k)) + } + for _, v := range pval.values { + p.flattenPlistValue(v) + } + case *cfArray: + for _, v := range pval.values { + p.flattenPlistValue(v) + } + } +} + +func (p *bplistGenerator) indexForPlistValue(pval cfValue) (uint64, bool) { + v, ok := p.objmap[pval.hash()] + return v, ok +} + +func (p *bplistGenerator) generateDocument(root cfValue) { + p.objtable = make([]cfValue, 0, 16) + p.objmap = make(map[interface{}]uint64) + p.flattenPlistValue(root) + + p.trailer.NumObjects = uint64(len(p.objtable)) + p.trailer.ObjectRefSize = uint8(bplistMinimumIntSize(p.trailer.NumObjects)) + + p.writer.Write([]byte("bplist00")) + + offtable := make([]uint64, p.trailer.NumObjects) + for i, pval := range p.objtable { + offtable[i] = uint64(p.writer.BytesWritten()) + p.writePlistValue(pval) + } + + p.trailer.OffsetIntSize = uint8(bplistMinimumIntSize(uint64(p.writer.BytesWritten()))) + p.trailer.TopObject = p.objmap[root.hash()] + p.trailer.OffsetTableOffset = uint64(p.writer.BytesWritten()) + + for _, offset := range offtable { + p.writeSizedInt(offset, int(p.trailer.OffsetIntSize)) + } + + binary.Write(p.writer, binary.BigEndian, p.trailer) +} + +func (p *bplistGenerator) writePlistValue(pval cfValue) { + if pval == nil { + return + } + + switch pval := pval.(type) { + case *cfDictionary: + p.writeDictionaryTag(pval) + case *cfArray: + p.writeArrayTag(pval.values) + case cfString: + p.writeStringTag(string(pval)) + case *cfNumber: + p.writeIntTag(pval.value) + case *cfReal: + if pval.wide { + p.writeRealTag(pval.value, 64) + } else { + p.writeRealTag(pval.value, 32) + } + case cfBoolean: + p.writeBoolTag(bool(pval)) + case cfData: + p.writeDataTag([]byte(pval)) + case cfDate: + p.writeDateTag(time.Time(pval)) + case cfUID: + p.writeUIDTag(UID(pval)) + default: + panic(fmt.Errorf("unknown plist type %t", pval)) + } +} + +func (p *bplistGenerator) writeSizedInt(n uint64, nbytes int) { + var val interface{} + switch nbytes { + case 1: + val = uint8(n) + case 2: + val = uint16(n) + case 4: + val = uint32(n) + case 8: + val = n + default: + panic(errors.New("illegal integer size")) + } + binary.Write(p.writer, binary.BigEndian, val) +} + +func (p *bplistGenerator) writeBoolTag(v bool) { + tag := uint8(bpTagBoolFalse) + if v { + tag = bpTagBoolTrue + } + binary.Write(p.writer, binary.BigEndian, tag) +} + +func (p *bplistGenerator) writeIntTag(n uint64) { + var tag uint8 + var val interface{} + switch { + case n <= uint64(0xff): + val = uint8(n) + tag = bpTagInteger | 0x0 + case n <= uint64(0xffff): + val = uint16(n) + tag = bpTagInteger | 0x1 + case n <= uint64(0xffffffff): + val = uint32(n) + tag = bpTagInteger | 0x2 + default: + val = n + tag = bpTagInteger | 0x3 + } + + binary.Write(p.writer, binary.BigEndian, tag) + binary.Write(p.writer, binary.BigEndian, val) +} + +func (p *bplistGenerator) writeUIDTag(u UID) { + nbytes := bplistMinimumIntSize(uint64(u)) + tag := uint8(bpTagUID | (nbytes - 1)) + + binary.Write(p.writer, binary.BigEndian, tag) + p.writeSizedInt(uint64(u), nbytes) +} + +func (p *bplistGenerator) writeRealTag(n float64, bits int) { + var tag uint8 = bpTagReal | 0x3 + var val interface{} = n + if bits == 32 { + val = float32(n) + tag = bpTagReal | 0x2 + } + + binary.Write(p.writer, binary.BigEndian, tag) + binary.Write(p.writer, binary.BigEndian, val) +} + +func (p *bplistGenerator) writeDateTag(t time.Time) { + tag := uint8(bpTagDate) | 0x3 + val := float64(t.In(time.UTC).UnixNano()) / float64(time.Second) + val -= 978307200 // Adjust to Apple Epoch + + binary.Write(p.writer, binary.BigEndian, tag) + binary.Write(p.writer, binary.BigEndian, val) +} + +func (p *bplistGenerator) writeCountedTag(tag uint8, count uint64) { + marker := tag + if count >= 0xF { + marker |= 0xF + } else { + marker |= uint8(count) + } + + binary.Write(p.writer, binary.BigEndian, marker) + + if count >= 0xF { + p.writeIntTag(count) + } +} + +func (p *bplistGenerator) writeDataTag(data []byte) { + p.writeCountedTag(bpTagData, uint64(len(data))) + binary.Write(p.writer, binary.BigEndian, data) +} + +func (p *bplistGenerator) writeStringTag(str string) { + for _, r := range str { + if r > 0x7F { + utf16Runes := utf16.Encode([]rune(str)) + p.writeCountedTag(bpTagUTF16String, uint64(len(utf16Runes))) + binary.Write(p.writer, binary.BigEndian, utf16Runes) + return + } + } + + p.writeCountedTag(bpTagASCIIString, uint64(len(str))) + binary.Write(p.writer, binary.BigEndian, []byte(str)) +} + +func (p *bplistGenerator) writeDictionaryTag(dict *cfDictionary) { + // assumption: sorted already; flattenPlistValue did this. + cnt := len(dict.keys) + p.writeCountedTag(bpTagDictionary, uint64(cnt)) + vals := make([]uint64, cnt*2) + for i, k := range dict.keys { + // invariant: keys have already been "uniqued" (as PStrings) + keyIdx, ok := p.objmap[cfString(k).hash()] + if !ok { + panic(errors.New("failed to find key " + k + " in object map during serialization")) + } + vals[i] = keyIdx + } + + for i, v := range dict.values { + // invariant: values have already been "uniqued" + objIdx, ok := p.indexForPlistValue(v) + if !ok { + panic(errors.New("failed to find value in object map during serialization")) + } + vals[i+cnt] = objIdx + } + + for _, v := range vals { + p.writeSizedInt(v, int(p.trailer.ObjectRefSize)) + } +} + +func (p *bplistGenerator) writeArrayTag(arr []cfValue) { + p.writeCountedTag(bpTagArray, uint64(len(arr))) + for _, v := range arr { + objIdx, ok := p.indexForPlistValue(v) + if !ok { + panic(errors.New("failed to find value in object map during serialization")) + } + + p.writeSizedInt(objIdx, int(p.trailer.ObjectRefSize)) + } +} + +func (p *bplistGenerator) Indent(i string) { + // There's nothing to indent. +} + +func newBplistGenerator(w io.Writer) *bplistGenerator { + return &bplistGenerator{ + writer: &countedWriter{Writer: mustWriter{w}}, + } +} diff --git a/vendor/howett.net/plist/bplist_parser.go b/vendor/howett.net/plist/bplist_parser.go new file mode 100644 index 00000000..224354bb --- /dev/null +++ b/vendor/howett.net/plist/bplist_parser.go @@ -0,0 +1,338 @@ +package plist + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "math" + "runtime" + "time" + "unicode/utf16" +) + +type offset uint64 + +type bplistParser struct { + buffer []byte + + reader io.ReadSeeker + version int + objects []cfValue // object ID to object + trailer bplistTrailer + trailerOffset uint64 + + containerStack []offset // slice of object offsets; manipulated during container deserialization +} + +func (p *bplistParser) validateDocumentTrailer() { + if p.trailer.OffsetTableOffset >= p.trailerOffset { + panic(fmt.Errorf("offset table beyond beginning of trailer (0x%x, trailer@0x%x)", p.trailer.OffsetTableOffset, p.trailerOffset)) + } + + if p.trailer.OffsetTableOffset < 9 { + panic(fmt.Errorf("offset table begins inside header (0x%x)", p.trailer.OffsetTableOffset)) + } + + if p.trailerOffset > (p.trailer.NumObjects*uint64(p.trailer.OffsetIntSize))+p.trailer.OffsetTableOffset { + panic(errors.New("garbage between offset table and trailer")) + } + + if p.trailer.OffsetTableOffset+(uint64(p.trailer.OffsetIntSize)*p.trailer.NumObjects) > p.trailerOffset { + panic(errors.New("offset table isn't long enough to address every object")) + } + + maxObjectRef := uint64(1) << (8 * p.trailer.ObjectRefSize) + if p.trailer.NumObjects > maxObjectRef { + panic(fmt.Errorf("more objects (%v) than object ref size (%v bytes) can support", p.trailer.NumObjects, p.trailer.ObjectRefSize)) + } + + if p.trailer.OffsetIntSize < uint8(8) && (uint64(1)<<(8*p.trailer.OffsetIntSize)) <= p.trailer.OffsetTableOffset { + panic(errors.New("offset size isn't big enough to address entire file")) + } + + if p.trailer.TopObject >= p.trailer.NumObjects { + panic(fmt.Errorf("top object #%d is out of range (only %d exist)", p.trailer.TopObject, p.trailer.NumObjects)) + } +} + +func (p *bplistParser) parseDocument() (pval cfValue, parseError error) { + defer func() { + if r := recover(); r != nil { + if _, ok := r.(runtime.Error); ok { + panic(r) + } + + parseError = plistParseError{"binary", r.(error)} + } + }() + + p.buffer, _ = ioutil.ReadAll(p.reader) + + l := len(p.buffer) + if l < 40 { + panic(errors.New("not enough data")) + } + + if !bytes.Equal(p.buffer[0:6], []byte{'b', 'p', 'l', 'i', 's', 't'}) { + panic(errors.New("incomprehensible magic")) + } + + p.version = int(((p.buffer[6] - '0') * 10) + (p.buffer[7] - '0')) + + if p.version > 1 { + panic(fmt.Errorf("unexpected version %d", p.version)) + } + + p.trailerOffset = uint64(l - 32) + p.trailer = bplistTrailer{ + SortVersion: p.buffer[p.trailerOffset+5], + OffsetIntSize: p.buffer[p.trailerOffset+6], + ObjectRefSize: p.buffer[p.trailerOffset+7], + NumObjects: binary.BigEndian.Uint64(p.buffer[p.trailerOffset+8:]), + TopObject: binary.BigEndian.Uint64(p.buffer[p.trailerOffset+16:]), + OffsetTableOffset: binary.BigEndian.Uint64(p.buffer[p.trailerOffset+24:]), + } + + p.validateDocumentTrailer() + + // INVARIANTS: + // - Entire offset table is before trailer + // - Offset table begins after header + // - Offset table can address entire document + // - Object IDs are big enough to support the number of objects in this plist + // - Top object is in range + + p.objects = make([]cfValue, p.trailer.NumObjects) + + pval = p.objectAtIndex(p.trailer.TopObject) + return +} + +// parseSizedInteger returns a 128-bit integer as low64, high64 +func (p *bplistParser) parseSizedInteger(off offset, nbytes int) (uint64, uint64, offset) { + switch nbytes { + case 1: + return uint64(p.buffer[off]), 0, off + offset(nbytes) + case 2: + return uint64(binary.BigEndian.Uint16(p.buffer[off:])), 0, off + offset(nbytes) + case 4: + return uint64(binary.BigEndian.Uint32(p.buffer[off:])), 0, off + offset(nbytes) + case 8: + return binary.BigEndian.Uint64(p.buffer[off:]), 0, off + offset(nbytes) + case 16: + return binary.BigEndian.Uint64(p.buffer[off+8:]), binary.BigEndian.Uint64(p.buffer[off:]), off + offset(nbytes) + } + panic(errors.New("illegal integer size")) +} + +func (p *bplistParser) parseObjectRefAtOffset(off offset) (uint64, offset) { + oid, _, next := p.parseSizedInteger(off, int(p.trailer.ObjectRefSize)) + return oid, next +} + +func (p *bplistParser) parseOffsetAtOffset(off offset) (offset, offset) { + parsedOffset, _, next := p.parseSizedInteger(off, int(p.trailer.OffsetIntSize)) + return offset(parsedOffset), next +} + +func (p *bplistParser) objectAtIndex(index uint64) cfValue { + if index >= p.trailer.NumObjects { + panic(fmt.Errorf("invalid object#%d (max %d)", index, p.trailer.NumObjects)) + } + + if pval := p.objects[index]; pval != nil { + return pval + } + + off, _ := p.parseOffsetAtOffset(offset(p.trailer.OffsetTableOffset + (index * uint64(p.trailer.OffsetIntSize)))) + if off > offset(p.trailer.OffsetTableOffset-1) { + panic(fmt.Errorf("object#%d starts beyond beginning of object table (0x%x, table@0x%x)", index, off, p.trailer.OffsetTableOffset)) + } + + pval := p.parseTagAtOffset(off) + p.objects[index] = pval + return pval + +} + +func (p *bplistParser) pushNestedObject(off offset) { + for _, v := range p.containerStack { + if v == off { + p.panicNestedObject(off) + } + } + p.containerStack = append(p.containerStack, off) +} + +func (p *bplistParser) panicNestedObject(off offset) { + ids := "" + for _, v := range p.containerStack { + ids += fmt.Sprintf("0x%x > ", v) + } + + // %s0x%d: ids above ends with " > " + panic(fmt.Errorf("self-referential collection@0x%x (%s0x%x) cannot be deserialized", off, ids, off)) +} + +func (p *bplistParser) popNestedObject() { + p.containerStack = p.containerStack[:len(p.containerStack)-1] +} + +func (p *bplistParser) parseTagAtOffset(off offset) cfValue { + tag := p.buffer[off] + + switch tag & 0xF0 { + case bpTagNull: + switch tag & 0x0F { + case bpTagBoolTrue, bpTagBoolFalse: + return cfBoolean(tag == bpTagBoolTrue) + } + case bpTagInteger: + lo, hi, _ := p.parseIntegerAtOffset(off) + return &cfNumber{ + signed: hi == 0xFFFFFFFFFFFFFFFF, // a signed integer is stored as a 128-bit integer with the top 64 bits set + value: lo, + } + case bpTagReal: + nbytes := 1 << (tag & 0x0F) + switch nbytes { + case 4: + bits := binary.BigEndian.Uint32(p.buffer[off+1:]) + return &cfReal{wide: false, value: float64(math.Float32frombits(bits))} + case 8: + bits := binary.BigEndian.Uint64(p.buffer[off+1:]) + return &cfReal{wide: true, value: math.Float64frombits(bits)} + } + panic(errors.New("illegal float size")) + case bpTagDate: + bits := binary.BigEndian.Uint64(p.buffer[off+1:]) + val := math.Float64frombits(bits) + + // Apple Epoch is 20110101000000Z + // Adjust for UNIX Time + val += 978307200 + + sec, fsec := math.Modf(val) + time := time.Unix(int64(sec), int64(fsec*float64(time.Second))).In(time.UTC) + return cfDate(time) + case bpTagData: + data := p.parseDataAtOffset(off) + return cfData(data) + case bpTagASCIIString: + str := p.parseASCIIStringAtOffset(off) + return cfString(str) + case bpTagUTF16String: + str := p.parseUTF16StringAtOffset(off) + return cfString(str) + case bpTagUID: // Somehow different than int: low half is nbytes - 1 instead of log2(nbytes) + lo, _, _ := p.parseSizedInteger(off+1, int(tag&0xF)+1) + return cfUID(lo) + case bpTagDictionary: + return p.parseDictionaryAtOffset(off) + case bpTagArray: + return p.parseArrayAtOffset(off) + } + panic(fmt.Errorf("unexpected atom 0x%2.02x at offset 0x%x", tag, off)) +} + +func (p *bplistParser) parseIntegerAtOffset(off offset) (uint64, uint64, offset) { + tag := p.buffer[off] + return p.parseSizedInteger(off+1, 1<<(tag&0xF)) +} + +func (p *bplistParser) countForTagAtOffset(off offset) (uint64, offset) { + tag := p.buffer[off] + cnt := uint64(tag & 0x0F) + if cnt == 0xF { + cnt, _, off = p.parseIntegerAtOffset(off + 1) + return cnt, off + } + return cnt, off + 1 +} + +func (p *bplistParser) parseDataAtOffset(off offset) []byte { + len, start := p.countForTagAtOffset(off) + if start+offset(len) > offset(p.trailer.OffsetTableOffset) { + panic(fmt.Errorf("data@0x%x too long (%v bytes, max is %v)", off, len, p.trailer.OffsetTableOffset-uint64(start))) + } + return p.buffer[start : start+offset(len)] +} + +func (p *bplistParser) parseASCIIStringAtOffset(off offset) string { + len, start := p.countForTagAtOffset(off) + if start+offset(len) > offset(p.trailer.OffsetTableOffset) { + panic(fmt.Errorf("ascii string@0x%x too long (%v bytes, max is %v)", off, len, p.trailer.OffsetTableOffset-uint64(start))) + } + + return zeroCopy8BitString(p.buffer, int(start), int(len)) +} + +func (p *bplistParser) parseUTF16StringAtOffset(off offset) string { + len, start := p.countForTagAtOffset(off) + bytes := len * 2 + if start+offset(bytes) > offset(p.trailer.OffsetTableOffset) { + panic(fmt.Errorf("utf16 string@0x%x too long (%v bytes, max is %v)", off, bytes, p.trailer.OffsetTableOffset-uint64(start))) + } + + u16s := make([]uint16, len) + for i := offset(0); i < offset(len); i++ { + u16s[i] = binary.BigEndian.Uint16(p.buffer[start+(i*2):]) + } + runes := utf16.Decode(u16s) + return string(runes) +} + +func (p *bplistParser) parseObjectListAtOffset(off offset, count uint64) []cfValue { + if off+offset(count*uint64(p.trailer.ObjectRefSize)) > offset(p.trailer.OffsetTableOffset) { + panic(fmt.Errorf("list@0x%x length (%v) puts its end beyond the offset table at 0x%x", off, count, p.trailer.OffsetTableOffset)) + } + objects := make([]cfValue, count) + + next := off + var oid uint64 + for i := uint64(0); i < count; i++ { + oid, next = p.parseObjectRefAtOffset(next) + objects[i] = p.objectAtIndex(oid) + } + + return objects +} + +func (p *bplistParser) parseDictionaryAtOffset(off offset) *cfDictionary { + p.pushNestedObject(off) + defer p.popNestedObject() + + // a dictionary is an object list of [key key key val val val] + cnt, start := p.countForTagAtOffset(off) + objects := p.parseObjectListAtOffset(start, cnt*2) + + keys := make([]string, cnt) + for i := uint64(0); i < cnt; i++ { + if str, ok := objects[i].(cfString); ok { + keys[i] = string(str) + } else { + panic(fmt.Errorf("dictionary@0x%x contains non-string key at index %d", off, i)) + } + } + + return &cfDictionary{ + keys: keys, + values: objects[cnt:], + } +} + +func (p *bplistParser) parseArrayAtOffset(off offset) *cfArray { + p.pushNestedObject(off) + defer p.popNestedObject() + + // an array is just an object list + cnt, start := p.countForTagAtOffset(off) + return &cfArray{p.parseObjectListAtOffset(start, cnt)} +} + +func newBplistParser(r io.ReadSeeker) *bplistParser { + return &bplistParser{reader: r} +} diff --git a/vendor/github.com/DHowett/go-plist/decode.go b/vendor/howett.net/plist/decode.go similarity index 96% rename from vendor/github.com/DHowett/go-plist/decode.go rename to vendor/howett.net/plist/decode.go index a927d6de..4c646677 100644 --- a/vendor/github.com/DHowett/go-plist/decode.go +++ b/vendor/howett.net/plist/decode.go @@ -8,7 +8,7 @@ import ( ) type parser interface { - parseDocument() (*plistValue, error) + parseDocument() (cfValue, error) } // A Decoder reads a property list from an input stream. @@ -38,7 +38,7 @@ func (p *Decoder) Decode(v interface{}) (err error) { p.reader.Seek(0, 0) var parser parser - var pval *plistValue + var pval cfValue if bytes.Equal(header, []byte("bplist")) { parser = newBplistParser(p.reader) pval, err = parser.parseDocument() @@ -93,6 +93,7 @@ func NewDecoder(r io.ReadSeeker) *Decoder { // in the interface value. If the interface value is nil, Unmarshal stores one of the following in the interface value: // // string, bool, uint64, float64 +// plist.UID for "CoreFoundation Keyed Archiver UIDs" (convertible to uint64) // []byte, for plist data // []interface{}, for plist arrays // map[string]interface{}, for plist dictionaries diff --git a/vendor/github.com/DHowett/go-plist/doc.go b/vendor/howett.net/plist/doc.go similarity index 100% rename from vendor/github.com/DHowett/go-plist/doc.go rename to vendor/howett.net/plist/doc.go diff --git a/vendor/github.com/DHowett/go-plist/encode.go b/vendor/howett.net/plist/encode.go similarity index 99% rename from vendor/github.com/DHowett/go-plist/encode.go rename to vendor/howett.net/plist/encode.go index 743da9c1..f81309b5 100644 --- a/vendor/github.com/DHowett/go-plist/encode.go +++ b/vendor/howett.net/plist/encode.go @@ -9,7 +9,7 @@ import ( ) type generator interface { - generateDocument(*plistValue) + generateDocument(cfValue) Indent(string) } diff --git a/vendor/github.com/DHowett/go-plist/fuzz.go b/vendor/howett.net/plist/fuzz.go similarity index 100% rename from vendor/github.com/DHowett/go-plist/fuzz.go rename to vendor/howett.net/plist/fuzz.go diff --git a/vendor/github.com/DHowett/go-plist/marshal.go b/vendor/howett.net/plist/marshal.go similarity index 54% rename from vendor/github.com/DHowett/go-plist/marshal.go rename to vendor/howett.net/plist/marshal.go index 81e79b1e..c461dfb5 100644 --- a/vendor/github.com/DHowett/go-plist/marshal.go +++ b/vendor/howett.net/plist/marshal.go @@ -25,45 +25,76 @@ func isEmptyValue(v reflect.Value) bool { } var ( - textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() - timeType = reflect.TypeOf((*time.Time)(nil)).Elem() + plistMarshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem() + textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() + timeType = reflect.TypeOf((*time.Time)(nil)).Elem() ) -func (p *Encoder) marshalTextInterface(marshalable encoding.TextMarshaler) *plistValue { +func implementsInterface(val reflect.Value, interfaceType reflect.Type) (interface{}, bool) { + if val.CanInterface() && val.Type().Implements(interfaceType) { + return val.Interface(), true + } + + if val.CanAddr() { + pv := val.Addr() + if pv.CanInterface() && pv.Type().Implements(interfaceType) { + return pv.Interface(), true + } + } + return nil, false +} + +func (p *Encoder) marshalPlistInterface(marshalable Marshaler) cfValue { + value, err := marshalable.MarshalPlist() + if err != nil { + panic(err) + } + return p.marshal(reflect.ValueOf(value)) +} + +// marshalTextInterface marshals a TextMarshaler to a plist string. +func (p *Encoder) marshalTextInterface(marshalable encoding.TextMarshaler) cfValue { s, err := marshalable.MarshalText() if err != nil { panic(err) } - return &plistValue{String, string(s)} + return cfString(s) } -func (p *Encoder) marshalStruct(typ reflect.Type, val reflect.Value) *plistValue { +// marshalStruct marshals a reflected struct value to a plist dictionary +func (p *Encoder) marshalStruct(typ reflect.Type, val reflect.Value) cfValue { tinfo, _ := getTypeInfo(typ) - dict := &dictionary{ - m: make(map[string]*plistValue, len(tinfo.fields)), + dict := &cfDictionary{ + keys: make([]string, 0, len(tinfo.fields)), + values: make([]cfValue, 0, len(tinfo.fields)), } for _, finfo := range tinfo.fields { value := finfo.value(val) if !value.IsValid() || finfo.omitEmpty && isEmptyValue(value) { continue } - dict.m[finfo.name] = p.marshal(value) + dict.keys = append(dict.keys, finfo.name) + dict.values = append(dict.values, p.marshal(value)) } - return &plistValue{Dictionary, dict} + return dict } -func (p *Encoder) marshalTime(val reflect.Value) *plistValue { +func (p *Encoder) marshalTime(val reflect.Value) cfValue { time := val.Interface().(time.Time) - return &plistValue{Date, time} + return cfDate(time) } -func (p *Encoder) marshal(val reflect.Value) *plistValue { +func (p *Encoder) marshal(val reflect.Value) cfValue { if !val.IsValid() { return nil } + if receiver, can := implementsInterface(val, plistMarshalerType); can { + return p.marshalPlistInterface(receiver.(Marshaler)) + } + // time.Time implements TextMarshaler, but we need to store it in RFC3339 if val.Type() == timeType { return p.marshalTime(val) @@ -76,14 +107,8 @@ func (p *Encoder) marshal(val reflect.Value) *plistValue { } // Check for text marshaler. - if val.CanInterface() && val.Type().Implements(textMarshalerType) { - return p.marshalTextInterface(val.Interface().(encoding.TextMarshaler)) - } - if val.CanAddr() { - pv := val.Addr() - if pv.CanInterface() && pv.Type().Implements(textMarshalerType) { - return p.marshalTextInterface(pv.Interface().(encoding.TextMarshaler)) - } + if receiver, can := implementsInterface(val, textMarshalerType); can { + return p.marshalTextInterface(receiver.(encoding.TextMarshaler)) } // Descend into pointers or interfaces @@ -98,21 +123,27 @@ func (p *Encoder) marshal(val reflect.Value) *plistValue { typ := val.Type() + if typ == uidType { + return cfUID(val.Uint()) + } + if val.Kind() == reflect.Struct { return p.marshalStruct(typ, val) } switch val.Kind() { case reflect.String: - return &plistValue{String, val.String()} + return cfString(val.String()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return &plistValue{Integer, signedInt{uint64(val.Int()), true}} + return &cfNumber{signed: true, value: uint64(val.Int())} case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return &plistValue{Integer, signedInt{uint64(val.Uint()), false}} - case reflect.Float32, reflect.Float64: - return &plistValue{Real, sizedFloat{val.Float(), val.Type().Bits()}} + return &cfNumber{signed: false, value: val.Uint()} + case reflect.Float32: + return &cfReal{wide: false, value: val.Float()} + case reflect.Float64: + return &cfReal{wide: true, value: val.Float()} case reflect.Bool: - return &plistValue{Boolean, val.Bool()} + return cfBoolean(val.Bool()) case reflect.Slice, reflect.Array: if typ.Elem().Kind() == reflect.Uint8 { bytes := []byte(nil) @@ -122,15 +153,15 @@ func (p *Encoder) marshal(val reflect.Value) *plistValue { bytes = make([]byte, val.Len()) reflect.Copy(reflect.ValueOf(bytes), val) } - return &plistValue{Data, bytes} + return cfData(bytes) } else { - subvalues := make([]*plistValue, val.Len()) - for idx, length := 0, val.Len(); idx < length; idx++ { - if subpval := p.marshal(val.Index(idx)); subpval != nil { - subvalues[idx] = subpval + values := make([]cfValue, val.Len()) + for i, length := 0, val.Len(); i < length; i++ { + if subpval := p.marshal(val.Index(i)); subpval != nil { + values[i] = subpval } } - return &plistValue{Array, subvalues} + return &cfArray{values} } case reflect.Map: if typ.Key().Kind() != reflect.String { @@ -138,17 +169,18 @@ func (p *Encoder) marshal(val reflect.Value) *plistValue { } l := val.Len() - dict := &dictionary{ - m: make(map[string]*plistValue, l), + dict := &cfDictionary{ + keys: make([]string, 0, l), + values: make([]cfValue, 0, l), } for _, keyv := range val.MapKeys() { if subpval := p.marshal(val.MapIndex(keyv)); subpval != nil { - dict.m[keyv.String()] = subpval + dict.keys = append(dict.keys, keyv.String()) + dict.values = append(dict.values, subpval) } } - return &plistValue{Dictionary, dict} + return dict default: panic(&unknownTypeError{typ}) } - return nil } diff --git a/vendor/github.com/DHowett/go-plist/must.go b/vendor/howett.net/plist/must.go similarity index 100% rename from vendor/github.com/DHowett/go-plist/must.go rename to vendor/howett.net/plist/must.go diff --git a/vendor/howett.net/plist/plist.go b/vendor/howett.net/plist/plist.go new file mode 100644 index 00000000..a4078b23 --- /dev/null +++ b/vendor/howett.net/plist/plist.go @@ -0,0 +1,85 @@ +package plist + +import ( + "reflect" +) + +// Property list format constants +const ( + // Used by Decoder to represent an invalid property list. + InvalidFormat int = 0 + + // Used to indicate total abandon with regards to Encoder's output format. + AutomaticFormat = 0 + + XMLFormat = 1 + BinaryFormat = 2 + OpenStepFormat = 3 + GNUStepFormat = 4 +) + +var FormatNames = map[int]string{ + InvalidFormat: "unknown/invalid", + XMLFormat: "XML", + BinaryFormat: "Binary", + OpenStepFormat: "OpenStep", + GNUStepFormat: "GNUStep", +} + +type unknownTypeError struct { + typ reflect.Type +} + +func (u *unknownTypeError) Error() string { + return "plist: can't marshal value of type " + u.typ.String() +} + +type invalidPlistError struct { + format string + err error +} + +func (e invalidPlistError) Error() string { + s := "plist: invalid " + e.format + " property list" + if e.err != nil { + s += ": " + e.err.Error() + } + return s +} + +type plistParseError struct { + format string + err error +} + +func (e plistParseError) Error() string { + s := "plist: error parsing " + e.format + " property list" + if e.err != nil { + s += ": " + e.err.Error() + } + return s +} + +// A UID represents a unique object identifier. UIDs are serialized in a manner distinct from +// that of integers. +// +// UIDs cannot be serialized in OpenStepFormat or GNUStepFormat property lists. +type UID uint64 + +// Marshaler is the interface implemented by types that can marshal themselves into valid +// property list objects. The returned value is marshaled in place of the original value +// implementing Marshaler +// +// If an error is returned by MarshalPlist, marshaling stops and the error is returned. +type Marshaler interface { + MarshalPlist() (interface{}, error) +} + +// Unmarshaler is the interface implemented by types that can unmarshal themselves from +// property list objects. The UnmarshalPlist method receives a function that may +// be called to unmarshal the original property list value into a field or variable. +// +// It is safe to call the unmarshal function more than once. +type Unmarshaler interface { + UnmarshalPlist(unmarshal func(interface{}) error) error +} diff --git a/vendor/howett.net/plist/plist_types.go b/vendor/howett.net/plist/plist_types.go new file mode 100644 index 00000000..954ef34e --- /dev/null +++ b/vendor/howett.net/plist/plist_types.go @@ -0,0 +1,139 @@ +package plist + +import ( + "hash/crc32" + "sort" + "time" +) + +type cfValue interface { + typeName() string + hash() interface{} +} + +type cfDictionary struct { + keys sort.StringSlice + values []cfValue +} + +func (*cfDictionary) typeName() string { + return "dictionary" +} + +func (p *cfDictionary) hash() interface{} { + return p +} + +func (p *cfDictionary) Len() int { + return len(p.keys) +} + +func (p *cfDictionary) Less(i, j int) bool { + return p.keys.Less(i, j) +} + +func (p *cfDictionary) Swap(i, j int) { + p.keys.Swap(i, j) + p.values[i], p.values[j] = p.values[j], p.values[i] +} + +func (p *cfDictionary) sort() { + sort.Sort(p) +} + +type cfArray struct { + values []cfValue +} + +func (*cfArray) typeName() string { + return "array" +} + +func (p *cfArray) hash() interface{} { + return p +} + +type cfString string + +func (cfString) typeName() string { + return "string" +} + +func (p cfString) hash() interface{} { + return string(p) +} + +type cfNumber struct { + signed bool + value uint64 +} + +func (*cfNumber) typeName() string { + return "integer" +} + +func (p *cfNumber) hash() interface{} { + if p.signed { + return int64(p.value) + } + return p.value +} + +type cfReal struct { + wide bool + value float64 +} + +func (cfReal) typeName() string { + return "real" +} + +func (p *cfReal) hash() interface{} { + if p.wide { + return p.value + } + return float32(p.value) +} + +type cfBoolean bool + +func (cfBoolean) typeName() string { + return "boolean" +} + +func (p cfBoolean) hash() interface{} { + return bool(p) +} + +type cfUID UID + +func (cfUID) typeName() string { + return "UID" +} + +func (p cfUID) hash() interface{} { + return p +} + +type cfData []byte + +func (cfData) typeName() string { + return "data" +} + +func (p cfData) hash() interface{} { + // Data are uniqued by their checksums. + // Todo: Look at calculating this only once and storing it somewhere; + // crc32 is fairly quick, however. + return crc32.ChecksumIEEE([]byte(p)) +} + +type cfDate time.Time + +func (cfDate) typeName() string { + return "date" +} + +func (p cfDate) hash() interface{} { + return time.Time(p) +} diff --git a/vendor/howett.net/plist/text_generator.go b/vendor/howett.net/plist/text_generator.go new file mode 100644 index 00000000..53078ba5 --- /dev/null +++ b/vendor/howett.net/plist/text_generator.go @@ -0,0 +1,226 @@ +package plist + +import ( + "encoding/hex" + "io" + "strconv" + "time" +) + +type textPlistGenerator struct { + writer io.Writer + format int + + quotableTable *characterSet + + indent string + depth int + + dictKvDelimiter, dictEntryDelimiter, arrayDelimiter []byte +} + +var ( + textPlistTimeLayout = "2006-01-02 15:04:05 -0700" + padding = "0000" +) + +func (p *textPlistGenerator) generateDocument(pval cfValue) { + p.writePlistValue(pval) +} + +func (p *textPlistGenerator) plistQuotedString(str string) string { + if str == "" { + return `""` + } + s := "" + quot := false + for _, r := range str { + if r > 0xFF { + quot = true + s += `\U` + us := strconv.FormatInt(int64(r), 16) + s += padding[len(us):] + s += us + } else if r > 0x7F { + quot = true + s += `\` + us := strconv.FormatInt(int64(r), 8) + s += padding[1+len(us):] + s += us + } else { + c := uint8(r) + if p.quotableTable.ContainsByte(c) { + quot = true + } + + switch c { + case '\a': + s += `\a` + case '\b': + s += `\b` + case '\v': + s += `\v` + case '\f': + s += `\f` + case '\\': + s += `\\` + case '"': + s += `\"` + case '\t', '\r', '\n': + fallthrough + default: + s += string(c) + } + } + } + if quot { + s = `"` + s + `"` + } + return s +} + +func (p *textPlistGenerator) deltaIndent(depthDelta int) { + if depthDelta < 0 { + p.depth-- + } else if depthDelta > 0 { + p.depth++ + } +} + +func (p *textPlistGenerator) writeIndent() { + if len(p.indent) == 0 { + return + } + if len(p.indent) > 0 { + p.writer.Write([]byte("\n")) + for i := 0; i < p.depth; i++ { + io.WriteString(p.writer, p.indent) + } + } +} + +func (p *textPlistGenerator) writePlistValue(pval cfValue) { + if pval == nil { + return + } + + switch pval := pval.(type) { + case *cfDictionary: + pval.sort() + p.writer.Write([]byte(`{`)) + p.deltaIndent(1) + for i, k := range pval.keys { + p.writeIndent() + io.WriteString(p.writer, p.plistQuotedString(k)) + p.writer.Write(p.dictKvDelimiter) + p.writePlistValue(pval.values[i]) + p.writer.Write(p.dictEntryDelimiter) + } + p.deltaIndent(-1) + p.writeIndent() + p.writer.Write([]byte(`}`)) + case *cfArray: + p.writer.Write([]byte(`(`)) + p.deltaIndent(1) + for _, v := range pval.values { + p.writeIndent() + p.writePlistValue(v) + p.writer.Write(p.arrayDelimiter) + } + p.deltaIndent(-1) + p.writeIndent() + p.writer.Write([]byte(`)`)) + case cfString: + io.WriteString(p.writer, p.plistQuotedString(string(pval))) + case *cfNumber: + if p.format == GNUStepFormat { + p.writer.Write([]byte(`<*I`)) + } + if pval.signed { + io.WriteString(p.writer, strconv.FormatInt(int64(pval.value), 10)) + } else { + io.WriteString(p.writer, strconv.FormatUint(pval.value, 10)) + } + if p.format == GNUStepFormat { + p.writer.Write([]byte(`>`)) + } + case *cfReal: + if p.format == GNUStepFormat { + p.writer.Write([]byte(`<*R`)) + } + // GNUstep does not differentiate between 32/64-bit floats. + io.WriteString(p.writer, strconv.FormatFloat(pval.value, 'g', -1, 64)) + if p.format == GNUStepFormat { + p.writer.Write([]byte(`>`)) + } + case cfBoolean: + if p.format == GNUStepFormat { + if pval { + p.writer.Write([]byte(`<*BY>`)) + } else { + p.writer.Write([]byte(`<*BN>`)) + } + } else { + if pval { + p.writer.Write([]byte(`1`)) + } else { + p.writer.Write([]byte(`0`)) + } + } + case cfData: + var hexencoded [9]byte + var l int + var asc = 9 + hexencoded[8] = ' ' + + p.writer.Write([]byte(`<`)) + b := []byte(pval) + for i := 0; i < len(b); i += 4 { + l = i + 4 + if l >= len(b) { + l = len(b) + // We no longer need the space - or the rest of the buffer. + // (we used >= above to get this part without another conditional :P) + asc = (l - i) * 2 + } + // Fill the buffer (only up to 8 characters, to preserve the space we implicitly include + // at the end of every encode) + hex.Encode(hexencoded[:8], b[i:l]) + io.WriteString(p.writer, string(hexencoded[:asc])) + } + p.writer.Write([]byte(`>`)) + case cfDate: + if p.format == GNUStepFormat { + p.writer.Write([]byte(`<*D`)) + io.WriteString(p.writer, time.Time(pval).In(time.UTC).Format(textPlistTimeLayout)) + p.writer.Write([]byte(`>`)) + } else { + io.WriteString(p.writer, p.plistQuotedString(time.Time(pval).In(time.UTC).Format(textPlistTimeLayout))) + } + } +} + +func (p *textPlistGenerator) Indent(i string) { + p.indent = i + if i == "" { + p.dictKvDelimiter = []byte(`=`) + } else { + // For pretty-printing + p.dictKvDelimiter = []byte(` = `) + } +} + +func newTextPlistGenerator(w io.Writer, format int) *textPlistGenerator { + table := &osQuotable + if format == GNUStepFormat { + table = &gsQuotable + } + return &textPlistGenerator{ + writer: mustWriter{w}, + format: format, + quotableTable: table, + dictKvDelimiter: []byte(`=`), + arrayDelimiter: []byte(`,`), + dictEntryDelimiter: []byte(`;`), + } +} diff --git a/vendor/howett.net/plist/text_parser.go b/vendor/howett.net/plist/text_parser.go new file mode 100644 index 00000000..7e49d6f7 --- /dev/null +++ b/vendor/howett.net/plist/text_parser.go @@ -0,0 +1,515 @@ +package plist + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "runtime" + "strings" + "time" + "unicode/utf16" + "unicode/utf8" +) + +type textPlistParser struct { + reader io.Reader + format int + + input string + start int + pos int + width int +} + +func convertU16(buffer []byte, bo binary.ByteOrder) (string, error) { + if len(buffer)%2 != 0 { + return "", errors.New("truncated utf16") + } + + tmp := make([]uint16, len(buffer)/2) + for i := 0; i < len(buffer); i += 2 { + tmp[i/2] = bo.Uint16(buffer[i : i+2]) + } + return string(utf16.Decode(tmp)), nil +} + +func guessEncodingAndConvert(buffer []byte) (string, error) { + if len(buffer) >= 3 && buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF { + // UTF-8 BOM + return zeroCopy8BitString(buffer, 3, len(buffer)-3), nil + } else if len(buffer) >= 2 { + // UTF-16 guesses + + switch { + // stream is big-endian (BOM is FE FF or head is 00 XX) + case (buffer[0] == 0xFE && buffer[1] == 0xFF): + return convertU16(buffer[2:], binary.BigEndian) + case (buffer[0] == 0 && buffer[1] != 0): + return convertU16(buffer, binary.BigEndian) + + // stream is little-endian (BOM is FE FF or head is XX 00) + case (buffer[0] == 0xFF && buffer[1] == 0xFE): + return convertU16(buffer[2:], binary.LittleEndian) + case (buffer[0] != 0 && buffer[1] == 0): + return convertU16(buffer, binary.LittleEndian) + } + } + + // fallback: assume ASCII (not great!) + return zeroCopy8BitString(buffer, 0, len(buffer)), nil +} + +func (p *textPlistParser) parseDocument() (pval cfValue, parseError error) { + defer func() { + if r := recover(); r != nil { + if _, ok := r.(runtime.Error); ok { + panic(r) + } + // Wrap all non-invalid-plist errors. + parseError = plistParseError{"text", r.(error)} + } + }() + + buffer, err := ioutil.ReadAll(p.reader) + if err != nil { + panic(err) + } + + p.input, err = guessEncodingAndConvert(buffer) + if err != nil { + panic(err) + } + + val := p.parsePlistValue() + + p.skipWhitespaceAndComments() + if p.peek() != eof { + if _, ok := val.(cfString); !ok { + p.error("garbage after end of document") + } + + p.start = 0 + p.pos = 0 + val = p.parseDictionary(true) + } + + pval = val + + return +} + +const eof rune = -1 + +func (p *textPlistParser) error(e string, args ...interface{}) { + line := strings.Count(p.input[:p.pos], "\n") + char := p.pos - strings.LastIndex(p.input[:p.pos], "\n") - 1 + panic(fmt.Errorf("%s at line %d character %d", fmt.Sprintf(e, args...), line, char)) +} + +func (p *textPlistParser) next() rune { + if int(p.pos) >= len(p.input) { + p.width = 0 + return eof + } + r, w := utf8.DecodeRuneInString(p.input[p.pos:]) + p.width = w + p.pos += p.width + return r +} + +func (p *textPlistParser) backup() { + p.pos -= p.width +} + +func (p *textPlistParser) peek() rune { + r := p.next() + p.backup() + return r +} + +func (p *textPlistParser) emit() string { + s := p.input[p.start:p.pos] + p.start = p.pos + return s +} + +func (p *textPlistParser) ignore() { + p.start = p.pos +} + +func (p *textPlistParser) empty() bool { + return p.start == p.pos +} + +func (p *textPlistParser) scanUntil(ch rune) { + if x := strings.IndexRune(p.input[p.pos:], ch); x >= 0 { + p.pos += x + return + } + p.pos = len(p.input) +} + +func (p *textPlistParser) scanUntilAny(chs string) { + if x := strings.IndexAny(p.input[p.pos:], chs); x >= 0 { + p.pos += x + return + } + p.pos = len(p.input) +} + +func (p *textPlistParser) scanCharactersInSet(ch *characterSet) { + for ch.Contains(p.next()) { + } + p.backup() +} + +func (p *textPlistParser) scanCharactersNotInSet(ch *characterSet) { + var r rune + for { + r = p.next() + if r == eof || ch.Contains(r) { + break + } + } + p.backup() +} + +func (p *textPlistParser) skipWhitespaceAndComments() { + for { + p.scanCharactersInSet(&whitespace) + if strings.HasPrefix(p.input[p.pos:], "//") { + p.scanCharactersNotInSet(&newlineCharacterSet) + } else if strings.HasPrefix(p.input[p.pos:], "/*") { + if x := strings.Index(p.input[p.pos:], "*/"); x >= 0 { + p.pos += x + 2 // skip the */ as well + continue // consume more whitespace + } else { + p.error("unexpected eof in block comment") + } + } else { + break + } + } + p.ignore() +} + +func (p *textPlistParser) parseOctalDigits(max int) uint64 { + var val uint64 + + for i := 0; i < max; i++ { + r := p.next() + + if r >= '0' && r <= '7' { + val <<= 3 + val |= uint64((r - '0')) + } else { + p.backup() + break + } + } + return val +} + +func (p *textPlistParser) parseHexDigits(max int) uint64 { + var val uint64 + + for i := 0; i < max; i++ { + r := p.next() + + if r >= 'a' && r <= 'f' { + val <<= 4 + val |= 10 + uint64((r - 'a')) + } else if r >= 'A' && r <= 'F' { + val <<= 4 + val |= 10 + uint64((r - 'A')) + } else if r >= '0' && r <= '9' { + val <<= 4 + val |= uint64((r - '0')) + } else { + p.backup() + break + } + } + return val +} + +// the \ has already been consumed +func (p *textPlistParser) parseEscape() string { + var s string + switch p.next() { + case 'a': + s = "\a" + case 'b': + s = "\b" + case 'v': + s = "\v" + case 'f': + s = "\f" + case 't': + s = "\t" + case 'r': + s = "\r" + case 'n': + s = "\n" + case '\\': + s = `\` + case '"': + s = `"` + case 'x': + s = string(rune(p.parseHexDigits(2))) + case 'u', 'U': + s = string(rune(p.parseHexDigits(4))) + case '0', '1', '2', '3', '4', '5', '6', '7': + p.backup() // we've already consumed one of the digits + s = string(rune(p.parseOctalDigits(3))) + default: + p.backup() // everything else should be accepted + } + p.ignore() // skip the entire escape sequence + return s +} + +// the " has already been consumed +func (p *textPlistParser) parseQuotedString() cfString { + p.ignore() // ignore the " + + slowPath := false + s := "" + + for { + p.scanUntilAny(`"\`) + switch p.peek() { + case eof: + p.error("unexpected eof in quoted string") + case '"': + section := p.emit() + p.pos++ // skip " + if !slowPath { + return cfString(section) + } else { + s += section + return cfString(s) + } + case '\\': + slowPath = true + s += p.emit() + p.next() // consume \ + s += p.parseEscape() + } + } +} + +func (p *textPlistParser) parseUnquotedString() cfString { + p.scanCharactersNotInSet(&gsQuotable) + s := p.emit() + if s == "" { + p.error("invalid unquoted string (found an unquoted character that should be quoted?)") + } + + return cfString(s) +} + +// the { has already been consumed +func (p *textPlistParser) parseDictionary(ignoreEof bool) *cfDictionary { + //p.ignore() // ignore the { + var keypv cfValue + keys := make([]string, 0, 32) + values := make([]cfValue, 0, 32) +outer: + for { + p.skipWhitespaceAndComments() + + switch p.next() { + case eof: + if !ignoreEof { + p.error("unexpected eof in dictionary") + } + fallthrough + case '}': + break outer + case '"': + keypv = p.parseQuotedString() + default: + p.backup() + keypv = p.parseUnquotedString() + } + + // INVARIANT: key can't be nil; parseQuoted and parseUnquoted + // will panic out before they return nil. + + p.skipWhitespaceAndComments() + + var val cfValue + n := p.next() + if n == ';' { + val = keypv + } else if n == '=' { + // whitespace is consumed within + val = p.parsePlistValue() + + p.skipWhitespaceAndComments() + + if p.next() != ';' { + p.error("missing ; in dictionary") + } + } else { + p.error("missing = in dictionary") + } + + keys = append(keys, string(keypv.(cfString))) + values = append(values, val) + } + + return &cfDictionary{keys: keys, values: values} +} + +// the ( has already been consumed +func (p *textPlistParser) parseArray() *cfArray { + //p.ignore() // ignore the ( + values := make([]cfValue, 0, 32) +outer: + for { + p.skipWhitespaceAndComments() + + switch p.next() { + case eof: + p.error("unexpected eof in array") + case ')': + break outer // done here + case ',': + continue // restart; ,) is valid and we don't want to blow it + default: + p.backup() + } + + pval := p.parsePlistValue() // whitespace is consumed within + if str, ok := pval.(cfString); ok && string(str) == "" { + // Empty strings in arrays are apparently skipped? + // TODO: Figure out why this was implemented. + continue + } + values = append(values, pval) + } + return &cfArray{values} +} + +// the <* have already been consumed +func (p *textPlistParser) parseGNUStepValue() cfValue { + typ := p.next() + p.ignore() + p.scanUntil('>') + + if typ == eof || typ == '>' || p.empty() || p.peek() == eof { + p.error("invalid GNUStep extended value") + } + + v := p.emit() + p.next() // consume the > + + switch typ { + case 'I': + if v[0] == '-' { + n := mustParseInt(v, 10, 64) + return &cfNumber{signed: true, value: uint64(n)} + } else { + n := mustParseUint(v, 10, 64) + return &cfNumber{signed: false, value: n} + } + case 'R': + n := mustParseFloat(v, 64) + return &cfReal{wide: true, value: n} // TODO(DH) 32/64 + case 'B': + b := v[0] == 'Y' + return cfBoolean(b) + case 'D': + t, err := time.Parse(textPlistTimeLayout, v) + if err != nil { + p.error(err.Error()) + } + + return cfDate(t.In(time.UTC)) + } + p.error("invalid GNUStep type " + string(typ)) + return nil +} + +// The < has already been consumed +func (p *textPlistParser) parseHexData() cfData { + buf := make([]byte, 256) + i := 0 + c := 0 + + for { + r := p.next() + switch r { + case eof: + p.error("unexpected eof in data") + case '>': + if c&1 == 1 { + p.error("uneven number of hex digits in data") + } + p.ignore() + return cfData(buf[:i]) + case ' ', '\t', '\n', '\r', '\u2028', '\u2029': // more lax than apple here: skip spaces + continue + } + + buf[i] <<= 4 + if r >= 'a' && r <= 'f' { + buf[i] |= 10 + byte((r - 'a')) + } else if r >= 'A' && r <= 'F' { + buf[i] |= 10 + byte((r - 'A')) + } else if r >= '0' && r <= '9' { + buf[i] |= byte((r - '0')) + } else { + p.error("unexpected hex digit `%c'", r) + } + + c++ + if c&1 == 0 { + i++ + if i >= len(buf) { + realloc := make([]byte, len(buf)*2) + copy(realloc, buf) + buf = realloc + } + } + } +} + +func (p *textPlistParser) parsePlistValue() cfValue { + for { + p.skipWhitespaceAndComments() + + switch p.next() { + case eof: + return &cfDictionary{} + case '<': + if p.next() == '*' { + p.format = GNUStepFormat + return p.parseGNUStepValue() + } + + p.backup() + return p.parseHexData() + case '"': + return p.parseQuotedString() + case '{': + return p.parseDictionary(false) + case '(': + return p.parseArray() + default: + p.backup() + return p.parseUnquotedString() + } + } +} + +func newTextPlistParser(r io.Reader) *textPlistParser { + return &textPlistParser{ + reader: r, + format: OpenStepFormat, + } +} diff --git a/vendor/github.com/DHowett/go-plist/text_tables.go b/vendor/howett.net/plist/text_tables.go similarity index 55% rename from vendor/github.com/DHowett/go-plist/text_tables.go rename to vendor/howett.net/plist/text_tables.go index ec6586b0..319c55c5 100644 --- a/vendor/github.com/DHowett/go-plist/text_tables.go +++ b/vendor/howett.net/plist/text_tables.go @@ -1,9 +1,19 @@ package plist +type characterSet [4]uint64 + +func (s *characterSet) Contains(ch rune) bool { + return ch >= 0 && ch <= 255 && s.ContainsByte(byte(ch)) +} + +func (s *characterSet) ContainsByte(ch byte) bool { + return (s[ch/64]&(1<<(ch%64)) > 0) +} + // Bitmap of characters that must be inside a quoted string // when written to an old-style property list // Low bits represent lower characters, and each uint64 represents 64 characters. -var gsQuotable = [4]uint64{ +var gsQuotable = characterSet{ 0x78001385ffffffff, 0xa800000138000000, 0xffffffffffffffff, @@ -11,16 +21,23 @@ var gsQuotable = [4]uint64{ } // 7f instead of 3f in the top line: CFOldStylePlist.c says . is valid, but they quote it. -var osQuotable = [4]uint64{ +var osQuotable = characterSet{ 0xf4007f6fffffffff, 0xf8000001f8000001, 0xffffffffffffffff, 0xffffffffffffffff, } -var whitespace = [4]uint64{ +var whitespace = characterSet{ 0x0000000100003f00, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, } + +var newlineCharacterSet = characterSet{ + 0x0000000000002400, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, +} diff --git a/vendor/github.com/DHowett/go-plist/typeinfo.go b/vendor/howett.net/plist/typeinfo.go similarity index 100% rename from vendor/github.com/DHowett/go-plist/typeinfo.go rename to vendor/howett.net/plist/typeinfo.go diff --git a/vendor/howett.net/plist/unmarshal.go b/vendor/howett.net/plist/unmarshal.go new file mode 100644 index 00000000..e38cbe53 --- /dev/null +++ b/vendor/howett.net/plist/unmarshal.go @@ -0,0 +1,320 @@ +package plist + +import ( + "encoding" + "fmt" + "reflect" + "runtime" + "time" +) + +type incompatibleDecodeTypeError struct { + dest reflect.Type + src string // type name (from cfValue) +} + +func (u *incompatibleDecodeTypeError) Error() string { + return fmt.Sprintf("plist: type mismatch: tried to decode plist type `%v' into value of type `%v'", u.src, u.dest) +} + +var ( + plistUnmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem() + textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() + uidType = reflect.TypeOf(UID(0)) +) + +func isEmptyInterface(v reflect.Value) bool { + return v.Kind() == reflect.Interface && v.NumMethod() == 0 +} + +func (p *Decoder) unmarshalPlistInterface(pval cfValue, unmarshalable Unmarshaler) { + err := unmarshalable.UnmarshalPlist(func(i interface{}) (err error) { + defer func() { + if r := recover(); r != nil { + if _, ok := r.(runtime.Error); ok { + panic(r) + } + err = r.(error) + } + }() + p.unmarshal(pval, reflect.ValueOf(i)) + return + }) + + if err != nil { + panic(err) + } +} + +func (p *Decoder) unmarshalTextInterface(pval cfString, unmarshalable encoding.TextUnmarshaler) { + err := unmarshalable.UnmarshalText([]byte(pval)) + if err != nil { + panic(err) + } +} + +func (p *Decoder) unmarshalTime(pval cfDate, val reflect.Value) { + val.Set(reflect.ValueOf(time.Time(pval))) +} + +func (p *Decoder) unmarshalLaxString(s string, val reflect.Value) { + switch val.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + i := mustParseInt(s, 10, 64) + val.SetInt(i) + return + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + i := mustParseUint(s, 10, 64) + val.SetUint(i) + return + case reflect.Float32, reflect.Float64: + f := mustParseFloat(s, 64) + val.SetFloat(f) + return + case reflect.Bool: + b := mustParseBool(s) + val.SetBool(b) + return + case reflect.Struct: + if val.Type() == timeType { + t, err := time.Parse(textPlistTimeLayout, s) + if err != nil { + panic(err) + } + val.Set(reflect.ValueOf(t.In(time.UTC))) + return + } + fallthrough + default: + panic(&incompatibleDecodeTypeError{val.Type(), "string"}) + } +} + +func (p *Decoder) unmarshal(pval cfValue, val reflect.Value) { + if pval == nil { + return + } + + if val.Kind() == reflect.Ptr { + if val.IsNil() { + val.Set(reflect.New(val.Type().Elem())) + } + val = val.Elem() + } + + if isEmptyInterface(val) { + v := p.valueInterface(pval) + val.Set(reflect.ValueOf(v)) + return + } + + incompatibleTypeError := &incompatibleDecodeTypeError{val.Type(), pval.typeName()} + + // time.Time implements TextMarshaler, but we need to parse it as RFC3339 + if date, ok := pval.(cfDate); ok { + if val.Type() == timeType { + p.unmarshalTime(date, val) + return + } + panic(incompatibleTypeError) + } + + if receiver, can := implementsInterface(val, plistUnmarshalerType); can { + p.unmarshalPlistInterface(pval, receiver.(Unmarshaler)) + return + } + + if val.Type() != timeType { + if receiver, can := implementsInterface(val, textUnmarshalerType); can { + if str, ok := pval.(cfString); ok { + p.unmarshalTextInterface(str, receiver.(encoding.TextUnmarshaler)) + } else { + panic(incompatibleTypeError) + } + return + } + } + + typ := val.Type() + + switch pval := pval.(type) { + case cfString: + if val.Kind() == reflect.String { + val.SetString(string(pval)) + return + } + if p.lax { + p.unmarshalLaxString(string(pval), val) + return + } + + panic(incompatibleTypeError) + case *cfNumber: + switch val.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + val.SetInt(int64(pval.value)) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + val.SetUint(pval.value) + default: + panic(incompatibleTypeError) + } + case *cfReal: + if val.Kind() == reflect.Float32 || val.Kind() == reflect.Float64 { + // TODO: Consider warning on a downcast (storing a 64-bit value in a 32-bit reflect) + val.SetFloat(pval.value) + } else { + panic(incompatibleTypeError) + } + case cfBoolean: + if val.Kind() == reflect.Bool { + val.SetBool(bool(pval)) + } else { + panic(incompatibleTypeError) + } + case cfData: + if val.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 { + val.SetBytes([]byte(pval)) + } else { + panic(incompatibleTypeError) + } + case cfUID: + if val.Type() == uidType { + val.SetUint(uint64(pval)) + } else { + switch val.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + val.SetInt(int64(pval)) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + val.SetUint(uint64(pval)) + default: + panic(incompatibleTypeError) + } + } + case *cfArray: + p.unmarshalArray(pval, val) + case *cfDictionary: + p.unmarshalDictionary(pval, val) + } +} + +func (p *Decoder) unmarshalArray(a *cfArray, val reflect.Value) { + var n int + if val.Kind() == reflect.Slice { + // Slice of element values. + // Grow slice. + cnt := len(a.values) + val.Len() + if cnt >= val.Cap() { + ncap := 2 * cnt + if ncap < 4 { + ncap = 4 + } + new := reflect.MakeSlice(val.Type(), val.Len(), ncap) + reflect.Copy(new, val) + val.Set(new) + } + n = val.Len() + val.SetLen(cnt) + } else if val.Kind() == reflect.Array { + if len(a.values) > val.Cap() { + panic(fmt.Errorf("plist: attempted to unmarshal %d values into an array of size %d", len(a.values), val.Cap())) + } + } else { + panic(&incompatibleDecodeTypeError{val.Type(), a.typeName()}) + } + + // Recur to read element into slice. + for _, sval := range a.values { + p.unmarshal(sval, val.Index(n)) + n++ + } + return +} + +func (p *Decoder) unmarshalDictionary(dict *cfDictionary, val reflect.Value) { + typ := val.Type() + switch val.Kind() { + case reflect.Struct: + tinfo, err := getTypeInfo(typ) + if err != nil { + panic(err) + } + + entries := make(map[string]cfValue, len(dict.keys)) + for i, k := range dict.keys { + sval := dict.values[i] + entries[k] = sval + } + + for _, finfo := range tinfo.fields { + p.unmarshal(entries[finfo.name], finfo.value(val)) + } + case reflect.Map: + if val.IsNil() { + val.Set(reflect.MakeMap(typ)) + } + + for i, k := range dict.keys { + sval := dict.values[i] + + keyv := reflect.ValueOf(k).Convert(typ.Key()) + mapElem := val.MapIndex(keyv) + if !mapElem.IsValid() { + mapElem = reflect.New(typ.Elem()).Elem() + } + + p.unmarshal(sval, mapElem) + val.SetMapIndex(keyv, mapElem) + } + default: + panic(&incompatibleDecodeTypeError{typ, dict.typeName()}) + } +} + +/* *Interface is modelled after encoding/json */ +func (p *Decoder) valueInterface(pval cfValue) interface{} { + switch pval := pval.(type) { + case cfString: + return string(pval) + case *cfNumber: + if pval.signed { + return int64(pval.value) + } + return pval.value + case *cfReal: + if pval.wide { + return pval.value + } else { + return float32(pval.value) + } + case cfBoolean: + return bool(pval) + case *cfArray: + return p.arrayInterface(pval) + case *cfDictionary: + return p.dictionaryInterface(pval) + case cfData: + return []byte(pval) + case cfDate: + return time.Time(pval) + case cfUID: + return UID(pval) + } + return nil +} + +func (p *Decoder) arrayInterface(a *cfArray) []interface{} { + out := make([]interface{}, len(a.values)) + for i, subv := range a.values { + out[i] = p.valueInterface(subv) + } + return out +} + +func (p *Decoder) dictionaryInterface(dict *cfDictionary) map[string]interface{} { + out := make(map[string]interface{}) + for i, k := range dict.keys { + subv := dict.values[i] + out[k] = p.valueInterface(subv) + } + return out +} diff --git a/vendor/github.com/DHowett/go-plist/util.go b/vendor/howett.net/plist/util.go similarity index 100% rename from vendor/github.com/DHowett/go-plist/util.go rename to vendor/howett.net/plist/util.go diff --git a/vendor/github.com/DHowett/go-plist/xml.go b/vendor/howett.net/plist/xml.go similarity index 68% rename from vendor/github.com/DHowett/go-plist/xml.go rename to vendor/howett.net/plist/xml.go index 17090c14..91cfeaa6 100644 --- a/vendor/github.com/DHowett/go-plist/xml.go +++ b/vendor/howett.net/plist/xml.go @@ -20,7 +20,7 @@ type xmlPlistGenerator struct { xmlEncoder *xml.Encoder } -func (p *xmlPlistGenerator) generateDocument(pval *plistValue) { +func (p *xmlPlistGenerator) generateDocument(root cfValue) { io.WriteString(p.writer, xml.Header) io.WriteString(p.writer, xmlDOCTYPE) @@ -39,74 +39,98 @@ func (p *xmlPlistGenerator) generateDocument(pval *plistValue) { p.xmlEncoder.EncodeToken(plistStartElement) - p.writePlistValue(pval) + p.writePlistValue(root) p.xmlEncoder.EncodeToken(plistStartElement.End()) p.xmlEncoder.Flush() } -func (p *xmlPlistGenerator) writePlistValue(pval *plistValue) { +func (p *xmlPlistGenerator) writeDictionary(dict *cfDictionary) { + dict.sort() + startElement := xml.StartElement{Name: xml.Name{Local: "dict"}} + p.xmlEncoder.EncodeToken(startElement) + for i, k := range dict.keys { + p.xmlEncoder.EncodeElement(k, xml.StartElement{Name: xml.Name{Local: "key"}}) + p.writePlistValue(dict.values[i]) + } + p.xmlEncoder.EncodeToken(startElement.End()) +} + +func (p *xmlPlistGenerator) writeArray(a *cfArray) { + startElement := xml.StartElement{Name: xml.Name{Local: "array"}} + p.xmlEncoder.EncodeToken(startElement) + for _, v := range a.values { + p.writePlistValue(v) + } + p.xmlEncoder.EncodeToken(startElement.End()) +} + +func (p *xmlPlistGenerator) writePlistValue(pval cfValue) { if pval == nil { return } defer p.xmlEncoder.Flush() + if dict, ok := pval.(*cfDictionary); ok { + p.writeDictionary(dict) + return + } else if a, ok := pval.(*cfArray); ok { + p.writeArray(a) + return + } else if uid, ok := pval.(cfUID); ok { + p.writeDictionary(&cfDictionary{ + keys: []string{"CF$UID"}, + values: []cfValue{ + &cfNumber{ + signed: false, + value: uint64(uid), + }, + }, + }) + return + } + + // Everything here and beyond is encoded the same way: value key := "" - encodedValue := pval.value - switch pval.kind { - case Dictionary: - startElement := xml.StartElement{Name: xml.Name{Local: "dict"}} - p.xmlEncoder.EncodeToken(startElement) - dict := encodedValue.(*dictionary) - dict.populateArrays() - for i, k := range dict.keys { - p.xmlEncoder.EncodeElement(k, xml.StartElement{Name: xml.Name{Local: "key"}}) - p.writePlistValue(dict.values[i]) - } - p.xmlEncoder.EncodeToken(startElement.End()) - case Array: - startElement := xml.StartElement{Name: xml.Name{Local: "array"}} - p.xmlEncoder.EncodeToken(startElement) - values := encodedValue.([]*plistValue) - for _, v := range values { - p.writePlistValue(v) - } - p.xmlEncoder.EncodeToken(startElement.End()) - case String: + var encodedValue interface{} = pval + + switch pval := pval.(type) { + case cfString: key = "string" - case Integer: + case *cfNumber: key = "integer" - if pval.value.(signedInt).signed { - encodedValue = int64(pval.value.(signedInt).value) + if pval.signed { + encodedValue = int64(pval.value) } else { - encodedValue = pval.value.(signedInt).value + encodedValue = pval.value } - case Real: + case *cfReal: key = "real" - encodedValue = pval.value.(sizedFloat).value + encodedValue = pval.value switch { - case math.IsInf(pval.value.(sizedFloat).value, 1): + case math.IsInf(pval.value, 1): encodedValue = "inf" - case math.IsInf(pval.value.(sizedFloat).value, -1): + case math.IsInf(pval.value, -1): encodedValue = "-inf" - case math.IsNaN(pval.value.(sizedFloat).value): + case math.IsNaN(pval.value): encodedValue = "nan" } - case Boolean: + case cfBoolean: key = "false" - b := pval.value.(bool) + b := bool(pval) if b { key = "true" } encodedValue = "" - case Data: + case cfData: key = "data" - encodedValue = xml.CharData(base64.StdEncoding.EncodeToString(pval.value.([]byte))) - case Date: + encodedValue = xml.CharData(base64.StdEncoding.EncodeToString([]byte(pval))) + case cfDate: key = "date" - encodedValue = pval.value.(time.Time).In(time.UTC).Format(time.RFC3339) + encodedValue = time.Time(pval).In(time.UTC).Format(time.RFC3339) } + if key != "" { err := p.xmlEncoder.EncodeElement(encodedValue, xml.StartElement{Name: xml.Name{Local: key}}) if err != nil { @@ -131,7 +155,7 @@ type xmlPlistParser struct { ntags int } -func (p *xmlPlistParser) parseDocument() (pval *plistValue, parseError error) { +func (p *xmlPlistParser) parseDocument() (pval cfValue, parseError error) { defer func() { if r := recover(); r != nil { if _, ok := r.(runtime.Error); ok { @@ -162,7 +186,7 @@ func (p *xmlPlistParser) parseDocument() (pval *plistValue, parseError error) { } } -func (p *xmlPlistParser) parseXMLElement(element xml.StartElement) *plistValue { +func (p *xmlPlistParser) parseXMLElement(element xml.StartElement) cfValue { var charData xml.CharData switch element.Name.Local { case "plist": @@ -189,7 +213,7 @@ func (p *xmlPlistParser) parseXMLElement(element xml.StartElement) *plistValue { panic(err) } - return &plistValue{String, string(charData)} + return cfString(charData) case "integer": p.ntags++ err := p.xmlDecoder.DecodeElement(&charData, &element) @@ -205,11 +229,11 @@ func (p *xmlPlistParser) parseXMLElement(element xml.StartElement) *plistValue { if s[0] == '-' { s, base := unsignedGetBase(s[1:]) n := mustParseInt("-"+s, base, 64) - return &plistValue{Integer, signedInt{uint64(n), true}} + return &cfNumber{signed: true, value: uint64(n)} } else { s, base := unsignedGetBase(s) n := mustParseUint(s, base, 64) - return &plistValue{Integer, signedInt{n, false}} + return &cfNumber{signed: false, value: n} } case "real": p.ntags++ @@ -219,13 +243,13 @@ func (p *xmlPlistParser) parseXMLElement(element xml.StartElement) *plistValue { } n := mustParseFloat(string(charData), 64) - return &plistValue{Real, sizedFloat{n, 64}} + return &cfReal{wide: true, value: n} case "true", "false": p.ntags++ p.xmlDecoder.Skip() b := element.Name.Local == "true" - return &plistValue{Boolean, b} + return cfBoolean(b) case "date": p.ntags++ err := p.xmlDecoder.DecodeElement(&charData, &element) @@ -238,7 +262,7 @@ func (p *xmlPlistParser) parseXMLElement(element xml.StartElement) *plistValue { panic(err) } - return &plistValue{Date, t} + return cfDate(t) case "data": p.ntags++ err := p.xmlDecoder.DecodeElement(&charData, &element) @@ -255,11 +279,12 @@ func (p *xmlPlistParser) parseXMLElement(element xml.StartElement) *plistValue { panic(err) } - return &plistValue{Data, bytes[:l]} + return cfData(bytes[:l]) case "dict": p.ntags++ var key *string - var subvalues map[string]*plistValue = make(map[string]*plistValue) + keys := make([]string, 0, 32) + values := make([]cfValue, 0, 32) for { token, err := p.xmlDecoder.Token() if err != nil { @@ -282,15 +307,23 @@ func (p *xmlPlistParser) parseXMLElement(element xml.StartElement) *plistValue { if key == nil { panic(errors.New("missing key in dictionary")) } - subvalues[*key] = p.parseXMLElement(el) + keys = append(keys, *key) + values = append(values, p.parseXMLElement(el)) key = nil } } } - return &plistValue{Dictionary, &dictionary{m: subvalues}} + + if len(keys) == 1 && keys[0] == "CF$UID" && len(values) == 1 { + if integer, ok := values[0].(*cfNumber); ok { + return cfUID(integer.value) + } + } + + return &cfDictionary{keys: keys, values: values} case "array": p.ntags++ - var subvalues []*plistValue = make([]*plistValue, 0, 10) + values := make([]cfValue, 0, 10) for { token, err := p.xmlDecoder.Token() if err != nil { @@ -302,10 +335,10 @@ func (p *xmlPlistParser) parseXMLElement(element xml.StartElement) *plistValue { } if el, ok := token.(xml.StartElement); ok { - subvalues = append(subvalues, p.parseXMLElement(el)) + values = append(values, p.parseXMLElement(el)) } } - return &plistValue{Array, subvalues} + return &cfArray{values} } err := fmt.Errorf("encountered unknown element %s", element.Name.Local) if p.ntags == 0 { diff --git a/vendor/howett.net/plist/zerocopy.go b/vendor/howett.net/plist/zerocopy.go new file mode 100644 index 00000000..999f401b --- /dev/null +++ b/vendor/howett.net/plist/zerocopy.go @@ -0,0 +1,20 @@ +// +build !appengine + +package plist + +import ( + "reflect" + "unsafe" +) + +func zeroCopy8BitString(buf []byte, off int, len int) string { + if len == 0 { + return "" + } + + var s string + hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) + hdr.Data = uintptr(unsafe.Pointer(&buf[off])) + hdr.Len = len + return s +} diff --git a/vendor/howett.net/plist/zerocopy_appengine.go b/vendor/howett.net/plist/zerocopy_appengine.go new file mode 100644 index 00000000..dbd9a1ac --- /dev/null +++ b/vendor/howett.net/plist/zerocopy_appengine.go @@ -0,0 +1,7 @@ +// +build appengine + +package plist + +func zeroCopy8BitString(buf []byte, off int, len int) string { + return string(buf[off : off+len]) +}