package mmark import ( "bytes" "fmt" "sort" "strconv" "time" ) // xml2rfc.go contains common code and variables that is shared // between xml2rfcv[23].go. var ( // These have been known to change, these are the current ones (2015-08-27). CitationsBase = "https://xml2rfc.tools.ietf.org/public/rfc/" // CitationsRFC is the URL where the citations for RFCs are. CitationsRFC = CitationsBase + "bibxml/" // CitationsANSI is the URL where the citations for ANSI documents are. CitationsANSI = CitationsBase + "bibxml2/" // CitationsID is the URL where the citations for I-Ds are. CitationsID = CitationsBase + "bibxml3/" // CitationsW3C is the URL where the citations for W3C documents are. CitationsW3C = CitationsBase + "bibxml4/" ) const ( referenceRFC = "reference.RFC." referenceID = "reference.I-D.draft-" referenceIDLatest = "reference.I-D." referenceW3C = "reference." referenceANSI = "reference." ext = ".xml" ) // referenceFile creates a .xml filename for the citation c. // For I-D references like '[@?I-D.ietf-dane-openpgpkey#02]' it will // create http:///reference.I-D.draft-ietf-dane-openpgpkey-02.xml // without an sequence number it becomes: // http:///reference.I-D.ietf-dane-openpgpkey.xml func referenceFile(c *citation) string { if len(c.link) < 4 { return "" } switch { case bytes.HasPrefix(c.link, []byte("RFC")): return CitationsRFC + referenceRFC + string(c.link[3:]) + ext case bytes.HasPrefix(c.link, []byte("I-D")): seq := "" if c.seq != -1 { seq = "-" + fmt.Sprintf("%02d", c.seq) return CitationsID + referenceID + string(c.link[4:]) + seq + ext } return CitationsID + referenceIDLatest + string(c.link[4:]) + ext case bytes.HasPrefix(c.link, []byte("W3C")): return CitationsW3C + referenceW3C + string(c.link) + ext case bytes.HasPrefix(c.link, []byte("ANSI")): fallthrough case bytes.HasPrefix(c.link, []byte("CCITT")): fallthrough case bytes.HasPrefix(c.link, []byte("FIPS")): fallthrough case bytes.HasPrefix(c.link, []byte("IEEE")): fallthrough case bytes.HasPrefix(c.link, []byte("ISO")): fallthrough case bytes.HasPrefix(c.link, []byte("ITU")): fallthrough case bytes.HasPrefix(c.link, []byte("PKCS")): return CitationsANSI + referenceANSI + string(c.link) + ext } return "" } // countCitationsAndSort returns the number of informative and normative // references and a string slice with the sorted keys. func countCitationsAndSort(citations map[string]*citation) (int, int, []string) { keys := make([]string, 0, len(citations)) refi, refn := 0, 0 for k, c := range citations { if c.typ == 'i' { refi++ } if c.typ == 'n' { refn++ } keys = append(keys, k) } sort.Strings(keys) return refi, refn, keys } var entityConvert = map[byte][]byte{ '<': []byte("<"), '>': []byte(">"), '&': []byte("&"), // '\'': []byte("'"), // '"': []byte("""), } func writeEntity(out *bytes.Buffer, text []byte) { for i := 0; i < len(text); i++ { if s, ok := entityConvert[text[i]]; ok { out.Write(s) continue } out.WriteByte(text[i]) } } // sanitizeXML strips XML from a string. func sanitizeXML(s []byte) []byte { inTag := false j := 0 for i := 0; i < len(s); i++ { if s[i] == '<' { inTag = true continue } if s[i] == '>' { inTag = false continue } if !inTag { s[j] = s[i] j++ } } return s[:j] } // writeSanitizeXML strips XML from a string and writes // to out. func writeSanitizeXML(out *bytes.Buffer, s []byte) { inTag := false for i := 0; i < len(s); i++ { if s[i] == '<' { inTag = true continue } if s[i] == '>' { inTag = false continue } if !inTag { out.WriteByte(s[i]) } } } // titleBlockTOMLAuthor outputs the author from the TOML title block. func titleBlockTOMLAuthor(out *bytes.Buffer, a author) { out.WriteString("\n") abbrev := "" if a.OrganizationAbbrev != "" { abbrev = " abbrev=\"" + a.OrganizationAbbrev + "\"" } out.WriteString("") writeEntity(out, []byte(a.Organization)) out.WriteString("\n") out.WriteString("
\n") out.WriteString("\n") out.WriteString("") writeEntity(out, []byte(a.Address.Postal.Street)) out.WriteString("\n") for _, street := range a.Address.Postal.Streets { out.WriteString("") writeEntity(out, []byte(street)) out.WriteString("\n") } out.WriteString("") writeEntity(out, []byte(a.Address.Postal.City)) out.WriteString("\n") for _, city := range a.Address.Postal.Cities { out.WriteString("") writeEntity(out, []byte(city)) out.WriteString("\n") } out.WriteString("") writeEntity(out, []byte(a.Address.Postal.Code)) out.WriteString("\n") for _, code := range a.Address.Postal.Codes { out.WriteString("") writeEntity(out, []byte(code)) out.WriteString("\n") } out.WriteString("") writeEntity(out, []byte(a.Address.Postal.Country)) out.WriteString("\n") for _, country := range a.Address.Postal.Countries { out.WriteString("") writeEntity(out, []byte(country)) out.WriteString("\n") } out.WriteString("") writeEntity(out, []byte(a.Address.Postal.Region)) out.WriteString("\n") for _, region := range a.Address.Postal.Regions { out.WriteString("") writeEntity(out, []byte(region)) out.WriteString("\n") } out.WriteString("\n") out.WriteString("" + a.Address.Phone + "\n") out.WriteString("" + a.Address.Email + "\n") out.WriteString("" + a.Address.Uri + "\n") out.WriteString("
\n") out.WriteString("\n") } // titleBlockTOMLDate outputs the date from the TOML title block. func titleBlockTOMLDate(out *bytes.Buffer, d time.Time) { year := "" if d.Year() > 0 { year = " year=\"" + strconv.Itoa(d.Year()) + "\"" } month := "" if d.Month() > 0 { month = " month=\"" + time.Month(d.Month()).String() + "\"" } day := "" if d.Day() > 0 { day = " day=\"" + strconv.Itoa(d.Day()) + "\"" } out.WriteString("\n\n") } // titleBlockTOMLKeyword outputs the keywords from the TOML title block. func titleBlockTOMLKeyword(out *bytes.Buffer, keywords []string) { for _, k := range keywords { out.WriteString("" + k + "\n") } } // titleBlockTOMLPI returns "yes" or "no" or a stringified number // for use as process instruction. If version is 3 they are returned // as attributes for use *inside* the tag. func titleBlockTOMLPI(pi pi, name string, version int) string { if version == 2 { switch name { case "toc": return "\n" case "symrefs": return "\n" case "sortrefs": return "\n" case "compact": return "\n" case "topblock": return "\n" case "comments": return "\n" case "subcompact": return "\n" case "private": return "\n" case "header": if pi.Header == piNotSet { return "" } return "\n" case "footer": if pi.Footer == piNotSet { return "" } return "\n" default: printf(nil, "unhandled or unknown PI seen: %s", name) return "" } } // version 3 return "" } func yesno(s, def string) string { if s == "" { return def } if s == "yes" { return "yes" } return "no" }