decode.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. package plist
  2. import (
  3. "bytes"
  4. "io"
  5. "reflect"
  6. "runtime"
  7. )
  8. type parser interface {
  9. parseDocument() (cfValue, error)
  10. }
  11. // A Decoder reads a property list from an input stream.
  12. type Decoder struct {
  13. // the format of the most-recently-decoded property list
  14. Format int
  15. reader io.ReadSeeker
  16. lax bool
  17. }
  18. // Decode works like Unmarshal, except it reads the decoder stream to find property list elements.
  19. //
  20. // After Decoding, the Decoder's Format field will be set to one of the plist format constants.
  21. func (p *Decoder) Decode(v interface{}) (err error) {
  22. defer func() {
  23. if r := recover(); r != nil {
  24. if _, ok := r.(runtime.Error); ok {
  25. panic(r)
  26. }
  27. err = r.(error)
  28. }
  29. }()
  30. header := make([]byte, 6)
  31. p.reader.Read(header)
  32. p.reader.Seek(0, 0)
  33. var ps parser
  34. var pval cfValue
  35. if bytes.Equal(header, []byte("bplist")) {
  36. ps = newBplistParser(p.reader)
  37. pval, err = ps.parseDocument()
  38. if err != nil {
  39. // Had a bplist header, but still got an error: we have to die here.
  40. return err
  41. }
  42. p.Format = BinaryFormat
  43. } else {
  44. ps = newXMLPlistParser(p.reader)
  45. pval, err = ps.parseDocument()
  46. if _, ok := err.(invalidPlistError); ok {
  47. // Rewind: the XML ps might have exhausted the file.
  48. p.reader.Seek(0, 0)
  49. // We don't use ps here because we want the textPlistParser type
  50. tp := newTextPlistParser(p.reader)
  51. pval, err = tp.parseDocument()
  52. if err != nil {
  53. return err
  54. }
  55. p.Format = tp.format
  56. if p.Format == OpenStepFormat {
  57. // OpenStep property lists can only store strings,
  58. // so we have to turn on lax mode here for the unmarshal step later.
  59. p.lax = true
  60. }
  61. } else {
  62. if err != nil {
  63. return err
  64. }
  65. p.Format = XMLFormat
  66. }
  67. }
  68. p.unmarshal(pval, reflect.ValueOf(v))
  69. return
  70. }
  71. // NewDecoder returns a Decoder that reads property list elements from a stream reader, r.
  72. // NewDecoder requires a Seekable stream for the purposes of file type detection.
  73. func NewDecoder(r io.ReadSeeker) *Decoder {
  74. return &Decoder{Format: InvalidFormat, reader: r, lax: false}
  75. }
  76. // Unmarshal parses a property list document and stores the result in the value pointed to by v.
  77. //
  78. // Unmarshal uses the inverse of the type encodings that Marshal uses, allocating heap-borne types as necessary.
  79. //
  80. // When given a nil pointer, Unmarshal allocates a new value for it to point to.
  81. //
  82. // To decode property list values into an interface value, Unmarshal decodes the property list into the concrete value contained
  83. // in the interface value. If the interface value is nil, Unmarshal stores one of the following in the interface value:
  84. //
  85. // string, bool, uint64, float64
  86. // plist.UID for "CoreFoundation Keyed Archiver UIDs" (convertible to uint64)
  87. // []byte, for plist data
  88. // []interface{}, for plist arrays
  89. // map[string]interface{}, for plist dictionaries
  90. //
  91. // If a property list value is not appropriate for a given value type, Unmarshal aborts immediately and returns an error.
  92. //
  93. // As Go does not support 128-bit types, and we don't want to pretend we're giving the user integer types (as opposed to
  94. // secretly passing them structs), Unmarshal will drop the high 64 bits of any 128-bit integers encoded in binary property lists.
  95. // (This is important because CoreFoundation serializes some large 64-bit values as 128-bit values with an empty high half.)
  96. //
  97. // When Unmarshal encounters an OpenStep property list, it will enter a relaxed parsing mode: OpenStep property lists can only store
  98. // plain old data as strings, so we will attempt to recover integer, floating-point, boolean and date values wherever they are necessary.
  99. // (for example, if Unmarshal attempts to unmarshal an OpenStep property list into a time.Time, it will try to parse the string it
  100. // receives as a time.)
  101. //
  102. // Unmarshal returns the detected property list format and an error, if any.
  103. func Unmarshal(data []byte, v interface{}) (format int, err error) {
  104. r := bytes.NewReader(data)
  105. dec := NewDecoder(r)
  106. err = dec.Decode(v)
  107. format = dec.Format
  108. return
  109. }