123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- package plist
- import (
- "bufio"
- "encoding/base64"
- "encoding/xml"
- "io"
- "math"
- "strconv"
- "time"
- )
- const (
- xmlHEADER string = `<?xml version="1.0" encoding="UTF-8"?>` + "\n"
- xmlDOCTYPE = `<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">` + "\n"
- xmlArrayTag = "array"
- xmlDataTag = "data"
- xmlDateTag = "date"
- xmlDictTag = "dict"
- xmlFalseTag = "false"
- xmlIntegerTag = "integer"
- xmlKeyTag = "key"
- xmlPlistTag = "plist"
- xmlRealTag = "real"
- xmlStringTag = "string"
- xmlTrueTag = "true"
- // magic value used in the XML encoding of UIDs
- // (stored as a dictionary mapping CF$UID->integer)
- xmlCFUIDMagic = "CF$UID"
- )
- func formatXMLFloat(f float64) string {
- switch {
- case math.IsInf(f, 1):
- return "inf"
- case math.IsInf(f, -1):
- return "-inf"
- case math.IsNaN(f):
- return "nan"
- }
- return strconv.FormatFloat(f, 'g', -1, 64)
- }
- type xmlPlistGenerator struct {
- *bufio.Writer
- indent string
- depth int
- putNewline bool
- }
- func (p *xmlPlistGenerator) generateDocument(root cfValue) {
- p.WriteString(xmlHEADER)
- p.WriteString(xmlDOCTYPE)
- p.openTag(`plist version="1.0"`)
- p.writePlistValue(root)
- p.closeTag(xmlPlistTag)
- p.Flush()
- }
- func (p *xmlPlistGenerator) openTag(n string) {
- p.writeIndent(1)
- p.WriteByte('<')
- p.WriteString(n)
- p.WriteByte('>')
- }
- func (p *xmlPlistGenerator) closeTag(n string) {
- p.writeIndent(-1)
- p.WriteString("</")
- p.WriteString(n)
- p.WriteByte('>')
- }
- func (p *xmlPlistGenerator) element(n string, v string) {
- p.writeIndent(0)
- if len(v) == 0 {
- p.WriteByte('<')
- p.WriteString(n)
- p.WriteString("/>")
- } else {
- p.WriteByte('<')
- p.WriteString(n)
- p.WriteByte('>')
- err := xml.EscapeText(p.Writer, []byte(v))
- if err != nil {
- panic(err)
- }
- p.WriteString("</")
- p.WriteString(n)
- p.WriteByte('>')
- }
- }
- func (p *xmlPlistGenerator) writeDictionary(dict *cfDictionary) {
- dict.sort()
- p.openTag(xmlDictTag)
- for i, k := range dict.keys {
- p.element(xmlKeyTag, k)
- p.writePlistValue(dict.values[i])
- }
- p.closeTag(xmlDictTag)
- }
- func (p *xmlPlistGenerator) writeArray(a *cfArray) {
- p.openTag(xmlArrayTag)
- for _, v := range a.values {
- p.writePlistValue(v)
- }
- p.closeTag(xmlArrayTag)
- }
- func (p *xmlPlistGenerator) writePlistValue(pval cfValue) {
- if pval == nil {
- return
- }
- switch pval := pval.(type) {
- case cfString:
- p.element(xmlStringTag, string(pval))
- case *cfNumber:
- if pval.signed {
- p.element(xmlIntegerTag, strconv.FormatInt(int64(pval.value), 10))
- } else {
- p.element(xmlIntegerTag, strconv.FormatUint(pval.value, 10))
- }
- case *cfReal:
- p.element(xmlRealTag, formatXMLFloat(pval.value))
- case cfBoolean:
- if bool(pval) {
- p.element(xmlTrueTag, "")
- } else {
- p.element(xmlFalseTag, "")
- }
- case cfData:
- p.element(xmlDataTag, base64.StdEncoding.EncodeToString([]byte(pval)))
- case cfDate:
- p.element(xmlDateTag, time.Time(pval).In(time.UTC).Format(time.RFC3339))
- case *cfDictionary:
- p.writeDictionary(pval)
- case *cfArray:
- p.writeArray(pval)
- case cfUID:
- p.openTag(xmlDictTag)
- p.element(xmlKeyTag, xmlCFUIDMagic)
- p.element(xmlIntegerTag, strconv.FormatUint(uint64(pval), 10))
- p.closeTag(xmlDictTag)
- }
- }
- func (p *xmlPlistGenerator) writeIndent(delta int) {
- if len(p.indent) == 0 {
- return
- }
- if delta < 0 {
- p.depth--
- }
- if p.putNewline {
- // from encoding/xml/marshal.go; it seems to be intended
- // to suppress the first newline.
- p.WriteByte('\n')
- } else {
- p.putNewline = true
- }
- for i := 0; i < p.depth; i++ {
- p.WriteString(p.indent)
- }
- if delta > 0 {
- p.depth++
- }
- }
- func (p *xmlPlistGenerator) Indent(i string) {
- p.indent = i
- }
- func newXMLPlistGenerator(w io.Writer) *xmlPlistGenerator {
- return &xmlPlistGenerator{Writer: bufio.NewWriter(w)}
- }
|