add some tests - webdump_tests - Testfiles for webdump
(HTM) git clone git://git.codemadness.org/webdump_tests
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
---
(DIR) commit 5a6db78a46e4f5742799fcfefc2e2580860db3ef
(DIR) parent 5f5b757714a91ec9e436533b128112fb1aca0422
(HTM) Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date: Tue, 22 Aug 2023 16:35:37 +0200
add some tests
Diffstat:
A realworld/stagit_file.html | 1106 +++++++++++++++++++++++++++++++
A tests/indent.html | 47 +++++++++++++++++++++++++++++++
M tests/pre.html | 46 ++++++++++++++++++-------------
A tests/pre_code.html | 3 +++
4 files changed, 1183 insertions(+), 19 deletions(-)
---
(DIR) diff --git a/realworld/stagit_file.html b/realworld/stagit_file.html
@@ -0,0 +1,1106 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>sfeed.c - sfeed - RSS and Atom parser
+</title>
+<link rel="icon" type="image/png" href="../favicon.png" />
+<link rel="alternate" type="application/atom+xml" title="sfeed Atom Feed" href="../atom.xml" />
+<link rel="alternate" type="application/atom+xml" title="sfeed Atom Feed (tags)" href="../tags.xml" />
+<link rel="stylesheet" type="text/css" href="../style.css" />
+</head>
+<body>
+<table><tr><td><a href="../../"><img src="../logo.png" alt="" width="32" height="32" /></a></td><td><h1>sfeed</h1><span class="desc">RSS and Atom parser
+</span></td></tr><tr class="url"><td></td><td>git clone <a href="git://git.codemadness.org/sfeed">git://git.codemadness.org/sfeed</a></td></tr><tr><td></td><td>
+<a href="../log.html">Log</a> | <a href="../files.html">Files</a> | <a href="../refs.html">Refs</a> | <a href="../file/README.html">README</a> | <a href="../file/LICENSE.html">LICENSE</a></td></tr></table>
+<hr/>
+<div id="content">
+<p> sfeed.c (30040B)</p><hr/><pre id="blob">
+<a href="#l1" class="line" id="l1"> 1</a> #include <errno.h>
+<a href="#l2" class="line" id="l2"> 2</a> #include <stdint.h>
+<a href="#l3" class="line" id="l3"> 3</a> #include <stdio.h>
+<a href="#l4" class="line" id="l4"> 4</a> #include <stdlib.h>
+<a href="#l5" class="line" id="l5"> 5</a> #include <string.h>
+<a href="#l6" class="line" id="l6"> 6</a> #include <strings.h>
+<a href="#l7" class="line" id="l7"> 7</a>
+<a href="#l8" class="line" id="l8"> 8</a> #include "util.h"
+<a href="#l9" class="line" id="l9"> 9</a> #include "xml.h"
+<a href="#l10" class="line" id="l10"> 10</a>
+<a href="#l11" class="line" id="l11"> 11</a> #define ISINCONTENT(ctx) ((ctx).iscontent && !((ctx).iscontenttag))
+<a href="#l12" class="line" id="l12"> 12</a> #define ISCONTENTTAG(ctx) (!((ctx).iscontent) && (ctx).iscontenttag)
+<a href="#l13" class="line" id="l13"> 13</a>
+<a href="#l14" class="line" id="l14"> 14</a> /* these feed fields support multiple separated values */
+<a href="#l15" class="line" id="l15"> 15</a> #define ISFEEDFIELDMULTI(t) ((t) == FeedFieldCategory)
+<a href="#l16" class="line" id="l16"> 16</a>
+<a href="#l17" class="line" id="l17"> 17</a> /* string and byte-length */
+<a href="#l18" class="line" id="l18"> 18</a> #define STRP(s) s,sizeof(s)-1
+<a href="#l19" class="line" id="l19"> 19</a>
+<a href="#l20" class="line" id="l20"> 20</a> enum FeedType {
+<a href="#l21" class="line" id="l21"> 21</a> FeedTypeNone = 0,
+<a href="#l22" class="line" id="l22"> 22</a> FeedTypeRSS = 1,
+<a href="#l23" class="line" id="l23"> 23</a> FeedTypeAtom = 2
+<a href="#l24" class="line" id="l24"> 24</a> };
+<a href="#l25" class="line" id="l25"> 25</a>
+<a href="#l26" class="line" id="l26"> 26</a> enum ContentType {
+<a href="#l27" class="line" id="l27"> 27</a> ContentTypeNone = 0,
+<a href="#l28" class="line" id="l28"> 28</a> ContentTypePlain = 1,
+<a href="#l29" class="line" id="l29"> 29</a> ContentTypeHTML = 2
+<a href="#l30" class="line" id="l30"> 30</a> };
+<a href="#l31" class="line" id="l31"> 31</a> static const char *contenttypes[] = { "", "plain", "html" };
+<a href="#l32" class="line" id="l32"> 32</a>
+<a href="#l33" class="line" id="l33"> 33</a> /* String data / memory pool */
+<a href="#l34" class="line" id="l34"> 34</a> typedef struct string {
+<a href="#l35" class="line" id="l35"> 35</a> char *data; /* data */
+<a href="#l36" class="line" id="l36"> 36</a> size_t len; /* string length */
+<a href="#l37" class="line" id="l37"> 37</a> size_t bufsiz; /* allocated size */
+<a href="#l38" class="line" id="l38"> 38</a> } String;
+<a href="#l39" class="line" id="l39"> 39</a>
+<a href="#l40" class="line" id="l40"> 40</a> /* NOTE: the order of these fields (content, date, author) indicate the
+<a href="#l41" class="line" id="l41"> 41</a> * priority to use them, from least important to high. */
+<a href="#l42" class="line" id="l42"> 42</a> enum TagId {
+<a href="#l43" class="line" id="l43"> 43</a> TagUnknown = 0,
+<a href="#l44" class="line" id="l44"> 44</a> /* RSS */
+<a href="#l45" class="line" id="l45"> 45</a> RSSTagDcdate, RSSTagPubdate, /* creation date has higher priority */
+<a href="#l46" class="line" id="l46"> 46</a> RSSTagTitle,
+<a href="#l47" class="line" id="l47"> 47</a> RSSTagMediaDescription, RSSTagDescription, RSSTagContentEncoded,
+<a href="#l48" class="line" id="l48"> 48</a> RSSTagGuid,
+<a href="#l49" class="line" id="l49"> 49</a> RSSTagGuidPermalinkFalse,
+<a href="#l50" class="line" id="l50"> 50</a> RSSTagGuidPermalinkTrue,
+<a href="#l51" class="line" id="l51"> 51</a> /* must be defined after GUID, because it can be a link (isPermaLink) */
+<a href="#l52" class="line" id="l52"> 52</a> RSSTagLink,
+<a href="#l53" class="line" id="l53"> 53</a> RSSTagEnclosure,
+<a href="#l54" class="line" id="l54"> 54</a> RSSTagAuthor, RSSTagDccreator,
+<a href="#l55" class="line" id="l55"> 55</a> RSSTagCategory,
+<a href="#l56" class="line" id="l56"> 56</a> /* Atom */
+<a href="#l57" class="line" id="l57"> 57</a> /* creation date has higher priority */
+<a href="#l58" class="line" id="l58"> 58</a> AtomTagModified, AtomTagUpdated, AtomTagIssued, AtomTagPublished,
+<a href="#l59" class="line" id="l59"> 59</a> AtomTagTitle,
+<a href="#l60" class="line" id="l60"> 60</a> AtomTagMediaDescription, AtomTagSummary, AtomTagContent,
+<a href="#l61" class="line" id="l61"> 61</a> AtomTagId,
+<a href="#l62" class="line" id="l62"> 62</a> AtomTagLink,
+<a href="#l63" class="line" id="l63"> 63</a> AtomTagLinkAlternate,
+<a href="#l64" class="line" id="l64"> 64</a> AtomTagLinkEnclosure,
+<a href="#l65" class="line" id="l65"> 65</a> AtomTagAuthor, AtomTagAuthorName,
+<a href="#l66" class="line" id="l66"> 66</a> AtomTagCategory,
+<a href="#l67" class="line" id="l67"> 67</a> TagLast
+<a href="#l68" class="line" id="l68"> 68</a> };
+<a href="#l69" class="line" id="l69"> 69</a>
+<a href="#l70" class="line" id="l70"> 70</a> typedef struct feedtag {
+<a href="#l71" class="line" id="l71"> 71</a> char *name; /* name of tag to match */
+<a href="#l72" class="line" id="l72"> 72</a> size_t len; /* len of `name` */
+<a href="#l73" class="line" id="l73"> 73</a> enum TagId id; /* unique ID */
+<a href="#l74" class="line" id="l74"> 74</a> } FeedTag;
+<a href="#l75" class="line" id="l75"> 75</a>
+<a href="#l76" class="line" id="l76"> 76</a> typedef struct field {
+<a href="#l77" class="line" id="l77"> 77</a> String str;
+<a href="#l78" class="line" id="l78"> 78</a> enum TagId tagid; /* tagid set previously, used for tag priority */
+<a href="#l79" class="line" id="l79"> 79</a> } FeedField;
+<a href="#l80" class="line" id="l80"> 80</a>
+<a href="#l81" class="line" id="l81"> 81</a> enum {
+<a href="#l82" class="line" id="l82"> 82</a> FeedFieldTime = 0, FeedFieldTitle, FeedFieldLink, FeedFieldContent,
+<a href="#l83" class="line" id="l83"> 83</a> FeedFieldId, FeedFieldAuthor, FeedFieldEnclosure, FeedFieldCategory,
+<a href="#l84" class="line" id="l84"> 84</a> FeedFieldLast
+<a href="#l85" class="line" id="l85"> 85</a> };
+<a href="#l86" class="line" id="l86"> 86</a>
+<a href="#l87" class="line" id="l87"> 87</a> typedef struct feedcontext {
+<a href="#l88" class="line" id="l88"> 88</a> String *field; /* current FeedItem field String */
+<a href="#l89" class="line" id="l89"> 89</a> FeedField fields[FeedFieldLast]; /* data for current item */
+<a href="#l90" class="line" id="l90"> 90</a> FeedTag tag; /* unique current parsed tag */
+<a href="#l91" class="line" id="l91"> 91</a> int iscontent; /* in content data */
+<a href="#l92" class="line" id="l92"> 92</a> int iscontenttag; /* in content tag */
+<a href="#l93" class="line" id="l93"> 93</a> enum ContentType contenttype; /* content-type for item */
+<a href="#l94" class="line" id="l94"> 94</a> enum FeedType feedtype;
+<a href="#l95" class="line" id="l95"> 95</a> int attrcount; /* count item HTML element attributes */
+<a href="#l96" class="line" id="l96"> 96</a> } FeedContext;
+<a href="#l97" class="line" id="l97"> 97</a>
+<a href="#l98" class="line" id="l98"> 98</a> static long long datetounix(long long, int, int, int, int, int);
+<a href="#l99" class="line" id="l99"> 99</a> static FeedTag * gettag(enum FeedType, const char *, size_t);
+<a href="#l100" class="line" id="l100"> 100</a> static long gettzoffset(const char *);
+<a href="#l101" class="line" id="l101"> 101</a> static int isattr(const char *, size_t, const char *, size_t);
+<a href="#l102" class="line" id="l102"> 102</a> static int istag(const char *, size_t, const char *, size_t);
+<a href="#l103" class="line" id="l103"> 103</a> static int parsetime(const char *, long long *);
+<a href="#l104" class="line" id="l104"> 104</a> static void printfields(void);
+<a href="#l105" class="line" id="l105"> 105</a> static void string_append(String *, const char *, size_t);
+<a href="#l106" class="line" id="l106"> 106</a> static void string_buffer_realloc(String *, size_t);
+<a href="#l107" class="line" id="l107"> 107</a> static void string_clear(String *);
+<a href="#l108" class="line" id="l108"> 108</a> static void string_print_encoded(String *);
+<a href="#l109" class="line" id="l109"> 109</a> static void string_print_timestamp(String *);
+<a href="#l110" class="line" id="l110"> 110</a> static void string_print_trimmed(String *);
+<a href="#l111" class="line" id="l111"> 111</a> static void string_print_trimmed_multi(String *);
+<a href="#l112" class="line" id="l112"> 112</a> static void string_print_uri(String *);
+<a href="#l113" class="line" id="l113"> 113</a> static void xmlattr(XMLParser *, const char *, size_t, const char *, size_t,
+<a href="#l114" class="line" id="l114"> 114</a> const char *, size_t);
+<a href="#l115" class="line" id="l115"> 115</a> static void xmlattrentity(XMLParser *, const char *, size_t, const char *,
+<a href="#l116" class="line" id="l116"> 116</a> size_t, const char *, size_t);
+<a href="#l117" class="line" id="l117"> 117</a> static void xmlattrend(XMLParser *, const char *, size_t, const char *,
+<a href="#l118" class="line" id="l118"> 118</a> size_t);
+<a href="#l119" class="line" id="l119"> 119</a> static void xmlattrstart(XMLParser *, const char *, size_t, const char *,
+<a href="#l120" class="line" id="l120"> 120</a> size_t);
+<a href="#l121" class="line" id="l121"> 121</a> static void xmldata(XMLParser *, const char *, size_t);
+<a href="#l122" class="line" id="l122"> 122</a> static void xmldataentity(XMLParser *, const char *, size_t);
+<a href="#l123" class="line" id="l123"> 123</a> static void xmltagend(XMLParser *, const char *, size_t, int);
+<a href="#l124" class="line" id="l124"> 124</a> static void xmltagstart(XMLParser *, const char *, size_t);
+<a href="#l125" class="line" id="l125"> 125</a> static void xmltagstartparsed(XMLParser *, const char *, size_t, int);
+<a href="#l126" class="line" id="l126"> 126</a>
+<a href="#l127" class="line" id="l127"> 127</a> /* map tag name to TagId type */
+<a href="#l128" class="line" id="l128"> 128</a> /* RSS, must be alphabetical order */
+<a href="#l129" class="line" id="l129"> 129</a> static const FeedTag rsstags[] = {
+<a href="#l130" class="line" id="l130"> 130</a> { STRP("author"), RSSTagAuthor },
+<a href="#l131" class="line" id="l131"> 131</a> { STRP("category"), RSSTagCategory },
+<a href="#l132" class="line" id="l132"> 132</a> { STRP("content:encoded"), RSSTagContentEncoded },
+<a href="#l133" class="line" id="l133"> 133</a> { STRP("dc:creator"), RSSTagDccreator },
+<a href="#l134" class="line" id="l134"> 134</a> { STRP("dc:date"), RSSTagDcdate },
+<a href="#l135" class="line" id="l135"> 135</a> { STRP("description"), RSSTagDescription },
+<a href="#l136" class="line" id="l136"> 136</a> /* RSS: <enclosure url="" />, Atom has <link rel="enclosure" /> */
+<a href="#l137" class="line" id="l137"> 137</a> { STRP("enclosure"), RSSTagEnclosure },
+<a href="#l138" class="line" id="l138"> 138</a> { STRP("guid"), RSSTagGuid },
+<a href="#l139" class="line" id="l139"> 139</a> { STRP("link"), RSSTagLink },
+<a href="#l140" class="line" id="l140"> 140</a> { STRP("media:description"), RSSTagMediaDescription },
+<a href="#l141" class="line" id="l141"> 141</a> { STRP("pubdate"), RSSTagPubdate },
+<a href="#l142" class="line" id="l142"> 142</a> { STRP("title"), RSSTagTitle }
+<a href="#l143" class="line" id="l143"> 143</a> };
+<a href="#l144" class="line" id="l144"> 144</a>
+<a href="#l145" class="line" id="l145"> 145</a> /* Atom, must be alphabetical order */
+<a href="#l146" class="line" id="l146"> 146</a> static const FeedTag atomtags[] = {
+<a href="#l147" class="line" id="l147"> 147</a> { STRP("author"), AtomTagAuthor },
+<a href="#l148" class="line" id="l148"> 148</a> { STRP("category"), AtomTagCategory },
+<a href="#l149" class="line" id="l149"> 149</a> { STRP("content"), AtomTagContent },
+<a href="#l150" class="line" id="l150"> 150</a> { STRP("id"), AtomTagId },
+<a href="#l151" class="line" id="l151"> 151</a> { STRP("issued"), AtomTagIssued }, /* Atom 0.3 */
+<a href="#l152" class="line" id="l152"> 152</a> /* Atom: <link href="" />, RSS has <link></link> */
+<a href="#l153" class="line" id="l153"> 153</a> { STRP("link"), AtomTagLink },
+<a href="#l154" class="line" id="l154"> 154</a> { STRP("media:description"), AtomTagMediaDescription },
+<a href="#l155" class="line" id="l155"> 155</a> { STRP("modified"), AtomTagModified }, /* Atom 0.3 */
+<a href="#l156" class="line" id="l156"> 156</a> { STRP("published"), AtomTagPublished },
+<a href="#l157" class="line" id="l157"> 157</a> { STRP("summary"), AtomTagSummary },
+<a href="#l158" class="line" id="l158"> 158</a> { STRP("title"), AtomTagTitle },
+<a href="#l159" class="line" id="l159"> 159</a> { STRP("updated"), AtomTagUpdated }
+<a href="#l160" class="line" id="l160"> 160</a> };
+<a href="#l161" class="line" id="l161"> 161</a>
+<a href="#l162" class="line" id="l162"> 162</a> /* special case: nested <author><name> */
+<a href="#l163" class="line" id="l163"> 163</a> static const FeedTag atomtagauthor = { STRP("author"), AtomTagAuthor };
+<a href="#l164" class="line" id="l164"> 164</a> static const FeedTag atomtagauthorname = { STRP("name"), AtomTagAuthorName };
+<a href="#l165" class="line" id="l165"> 165</a>
+<a href="#l166" class="line" id="l166"> 166</a> /* reference to no / unknown tag */
+<a href="#l167" class="line" id="l167"> 167</a> static const FeedTag notag = { STRP(""), TagUnknown };
+<a href="#l168" class="line" id="l168"> 168</a>
+<a href="#l169" class="line" id="l169"> 169</a> /* map TagId type to RSS/Atom field, all tags must be defined */
+<a href="#l170" class="line" id="l170"> 170</a> static const int fieldmap[TagLast] = {
+<a href="#l171" class="line" id="l171"> 171</a> [TagUnknown] = -1,
+<a href="#l172" class="line" id="l172"> 172</a> /* RSS */
+<a href="#l173" class="line" id="l173"> 173</a> [RSSTagDcdate] = FeedFieldTime,
+<a href="#l174" class="line" id="l174"> 174</a> [RSSTagPubdate] = FeedFieldTime,
+<a href="#l175" class="line" id="l175"> 175</a> [RSSTagTitle] = FeedFieldTitle,
+<a href="#l176" class="line" id="l176"> 176</a> [RSSTagMediaDescription] = FeedFieldContent,
+<a href="#l177" class="line" id="l177"> 177</a> [RSSTagDescription] = FeedFieldContent,
+<a href="#l178" class="line" id="l178"> 178</a> [RSSTagContentEncoded] = FeedFieldContent,
+<a href="#l179" class="line" id="l179"> 179</a> [RSSTagGuid] = -1,
+<a href="#l180" class="line" id="l180"> 180</a> [RSSTagGuidPermalinkFalse] = FeedFieldId,
+<a href="#l181" class="line" id="l181"> 181</a> [RSSTagGuidPermalinkTrue] = FeedFieldId, /* special-case: both a link and an id */
+<a href="#l182" class="line" id="l182"> 182</a> [RSSTagLink] = FeedFieldLink,
+<a href="#l183" class="line" id="l183"> 183</a> [RSSTagEnclosure] = FeedFieldEnclosure,
+<a href="#l184" class="line" id="l184"> 184</a> [RSSTagAuthor] = FeedFieldAuthor,
+<a href="#l185" class="line" id="l185"> 185</a> [RSSTagDccreator] = FeedFieldAuthor,
+<a href="#l186" class="line" id="l186"> 186</a> [RSSTagCategory] = FeedFieldCategory,
+<a href="#l187" class="line" id="l187"> 187</a> /* Atom */
+<a href="#l188" class="line" id="l188"> 188</a> [AtomTagModified] = FeedFieldTime,
+<a href="#l189" class="line" id="l189"> 189</a> [AtomTagUpdated] = FeedFieldTime,
+<a href="#l190" class="line" id="l190"> 190</a> [AtomTagIssued] = FeedFieldTime,
+<a href="#l191" class="line" id="l191"> 191</a> [AtomTagPublished] = FeedFieldTime,
+<a href="#l192" class="line" id="l192"> 192</a> [AtomTagTitle] = FeedFieldTitle,
+<a href="#l193" class="line" id="l193"> 193</a> [AtomTagMediaDescription] = FeedFieldContent,
+<a href="#l194" class="line" id="l194"> 194</a> [AtomTagSummary] = FeedFieldContent,
+<a href="#l195" class="line" id="l195"> 195</a> [AtomTagContent] = FeedFieldContent,
+<a href="#l196" class="line" id="l196"> 196</a> [AtomTagId] = FeedFieldId,
+<a href="#l197" class="line" id="l197"> 197</a> [AtomTagLink] = -1,
+<a href="#l198" class="line" id="l198"> 198</a> [AtomTagLinkAlternate] = FeedFieldLink,
+<a href="#l199" class="line" id="l199"> 199</a> [AtomTagLinkEnclosure] = FeedFieldEnclosure,
+<a href="#l200" class="line" id="l200"> 200</a> [AtomTagAuthor] = -1,
+<a href="#l201" class="line" id="l201"> 201</a> [AtomTagAuthorName] = FeedFieldAuthor,
+<a href="#l202" class="line" id="l202"> 202</a> [AtomTagCategory] = FeedFieldCategory
+<a href="#l203" class="line" id="l203"> 203</a> };
+<a href="#l204" class="line" id="l204"> 204</a>
+<a href="#l205" class="line" id="l205"> 205</a> static const int FieldSeparator = '\t';
+<a href="#l206" class="line" id="l206"> 206</a> /* separator for multiple values in a field, separator should be 1 byte */
+<a href="#l207" class="line" id="l207"> 207</a> static const char FieldMultiSeparator[] = "|";
+<a href="#l208" class="line" id="l208"> 208</a> static struct uri baseuri;
+<a href="#l209" class="line" id="l209"> 209</a> static const char *baseurl;
+<a href="#l210" class="line" id="l210"> 210</a>
+<a href="#l211" class="line" id="l211"> 211</a> static FeedContext ctx;
+<a href="#l212" class="line" id="l212"> 212</a> static XMLParser parser; /* XML parser state */
+<a href="#l213" class="line" id="l213"> 213</a> static String attrispermalink, attrrel, attrtype, tmpstr;
+<a href="#l214" class="line" id="l214"> 214</a>
+<a href="#l215" class="line" id="l215"> 215</a> static int
+<a href="#l216" class="line" id="l216"> 216</a> tagcmp(const void *v1, const void *v2)
+<a href="#l217" class="line" id="l217"> 217</a> {
+<a href="#l218" class="line" id="l218"> 218</a> return strcasecmp(((FeedTag *)v1)->name, ((FeedTag *)v2)->name);
+<a href="#l219" class="line" id="l219"> 219</a> }
+<a href="#l220" class="line" id="l220"> 220</a>
+<a href="#l221" class="line" id="l221"> 221</a> /* Unique tagid for parsed tag name. */
+<a href="#l222" class="line" id="l222"> 222</a> static FeedTag *
+<a href="#l223" class="line" id="l223"> 223</a> gettag(enum FeedType feedtype, const char *name, size_t namelen)
+<a href="#l224" class="line" id="l224"> 224</a> {
+<a href="#l225" class="line" id="l225"> 225</a> FeedTag f, *r = NULL;
+<a href="#l226" class="line" id="l226"> 226</a>
+<a href="#l227" class="line" id="l227"> 227</a> f.name = (char *)name;
+<a href="#l228" class="line" id="l228"> 228</a>
+<a href="#l229" class="line" id="l229"> 229</a> switch (feedtype) {
+<a href="#l230" class="line" id="l230"> 230</a> case FeedTypeRSS:
+<a href="#l231" class="line" id="l231"> 231</a> r = bsearch(&f, rsstags, sizeof(rsstags) / sizeof(rsstags[0]),
+<a href="#l232" class="line" id="l232"> 232</a> sizeof(rsstags[0]), tagcmp);
+<a href="#l233" class="line" id="l233"> 233</a> break;
+<a href="#l234" class="line" id="l234"> 234</a> case FeedTypeAtom:
+<a href="#l235" class="line" id="l235"> 235</a> r = bsearch(&f, atomtags, sizeof(atomtags) / sizeof(atomtags[0]),
+<a href="#l236" class="line" id="l236"> 236</a> sizeof(atomtags[0]), tagcmp);
+<a href="#l237" class="line" id="l237"> 237</a> break;
+<a href="#l238" class="line" id="l238"> 238</a> default:
+<a href="#l239" class="line" id="l239"> 239</a> break;
+<a href="#l240" class="line" id="l240"> 240</a> }
+<a href="#l241" class="line" id="l241"> 241</a>
+<a href="#l242" class="line" id="l242"> 242</a> return r;
+<a href="#l243" class="line" id="l243"> 243</a> }
+<a href="#l244" class="line" id="l244"> 244</a>
+<a href="#l245" class="line" id="l245"> 245</a> static char *
+<a href="#l246" class="line" id="l246"> 246</a> ltrim(const char *s)
+<a href="#l247" class="line" id="l247"> 247</a> {
+<a href="#l248" class="line" id="l248"> 248</a> for (; ISSPACE((unsigned char)*s); s++)
+<a href="#l249" class="line" id="l249"> 249</a> ;
+<a href="#l250" class="line" id="l250"> 250</a> return (char *)s;
+<a href="#l251" class="line" id="l251"> 251</a> }
+<a href="#l252" class="line" id="l252"> 252</a>
+<a href="#l253" class="line" id="l253"> 253</a> static char *
+<a href="#l254" class="line" id="l254"> 254</a> rtrim(const char *s)
+<a href="#l255" class="line" id="l255"> 255</a> {
+<a href="#l256" class="line" id="l256"> 256</a> const char *e;
+<a href="#l257" class="line" id="l257"> 257</a>
+<a href="#l258" class="line" id="l258"> 258</a> for (e = s + strlen(s); e > s && ISSPACE((unsigned char)*(e - 1)); e--)
+<a href="#l259" class="line" id="l259"> 259</a> ;
+<a href="#l260" class="line" id="l260"> 260</a> return (char *)e;
+<a href="#l261" class="line" id="l261"> 261</a> }
+<a href="#l262" class="line" id="l262"> 262</a>
+<a href="#l263" class="line" id="l263"> 263</a> /* Clear string only; don't free, prevents unnecessary reallocation. */
+<a href="#l264" class="line" id="l264"> 264</a> static void
+<a href="#l265" class="line" id="l265"> 265</a> string_clear(String *s)
+<a href="#l266" class="line" id="l266"> 266</a> {
+<a href="#l267" class="line" id="l267"> 267</a> if (s->data)
+<a href="#l268" class="line" id="l268"> 268</a> s->data[0] = '\0';
+<a href="#l269" class="line" id="l269"> 269</a> s->len = 0;
+<a href="#l270" class="line" id="l270"> 270</a> }
+<a href="#l271" class="line" id="l271"> 271</a>
+<a href="#l272" class="line" id="l272"> 272</a> static void
+<a href="#l273" class="line" id="l273"> 273</a> string_buffer_realloc(String *s, size_t newlen)
+<a href="#l274" class="line" id="l274"> 274</a> {
+<a href="#l275" class="line" id="l275"> 275</a> size_t alloclen;
+<a href="#l276" class="line" id="l276"> 276</a>
+<a href="#l277" class="line" id="l277"> 277</a> if (newlen > SIZE_MAX / 2) {
+<a href="#l278" class="line" id="l278"> 278</a> alloclen = SIZE_MAX;
+<a href="#l279" class="line" id="l279"> 279</a> } else {
+<a href="#l280" class="line" id="l280"> 280</a> for (alloclen = 64; alloclen <= newlen; alloclen *= 2)
+<a href="#l281" class="line" id="l281"> 281</a> ;
+<a href="#l282" class="line" id="l282"> 282</a> }
+<a href="#l283" class="line" id="l283"> 283</a> if (!(s->data = realloc(s->data, alloclen)))
+<a href="#l284" class="line" id="l284"> 284</a> err(1, "realloc");
+<a href="#l285" class="line" id="l285"> 285</a> s->bufsiz = alloclen;
+<a href="#l286" class="line" id="l286"> 286</a> }
+<a href="#l287" class="line" id="l287"> 287</a>
+<a href="#l288" class="line" id="l288"> 288</a> /* Append data to String, s->data and data may not overlap. */
+<a href="#l289" class="line" id="l289"> 289</a> static void
+<a href="#l290" class="line" id="l290"> 290</a> string_append(String *s, const char *data, size_t len)
+<a href="#l291" class="line" id="l291"> 291</a> {
+<a href="#l292" class="line" id="l292"> 292</a> if (!len)
+<a href="#l293" class="line" id="l293"> 293</a> return;
+<a href="#l294" class="line" id="l294"> 294</a>
+<a href="#l295" class="line" id="l295"> 295</a> if (s->len >= SIZE_MAX - len) {
+<a href="#l296" class="line" id="l296"> 296</a> errno = ENOMEM;
+<a href="#l297" class="line" id="l297"> 297</a> err(1, "realloc");
+<a href="#l298" class="line" id="l298"> 298</a> }
+<a href="#l299" class="line" id="l299"> 299</a>
+<a href="#l300" class="line" id="l300"> 300</a> /* check if allocation is necessary, never shrink the buffer. */
+<a href="#l301" class="line" id="l301"> 301</a> if (s->len + len >= s->bufsiz)
+<a href="#l302" class="line" id="l302"> 302</a> string_buffer_realloc(s, s->len + len + 1);
+<a href="#l303" class="line" id="l303"> 303</a> memcpy(s->data + s->len, data, len);
+<a href="#l304" class="line" id="l304"> 304</a> s->len += len;
+<a href="#l305" class="line" id="l305"> 305</a> s->data[s->len] = '\0';
+<a href="#l306" class="line" id="l306"> 306</a> }
+<a href="#l307" class="line" id="l307"> 307</a>
+<a href="#l308" class="line" id="l308"> 308</a> /* Print text, encode TABs, newlines and '\', remove other whitespace.
+<a href="#l309" class="line" id="l309"> 309</a> * Remove leading and trailing whitespace. */
+<a href="#l310" class="line" id="l310"> 310</a> static void
+<a href="#l311" class="line" id="l311"> 311</a> string_print_encoded(String *s)
+<a href="#l312" class="line" id="l312"> 312</a> {
+<a href="#l313" class="line" id="l313"> 313</a> const char *p, *e;
+<a href="#l314" class="line" id="l314"> 314</a>
+<a href="#l315" class="line" id="l315"> 315</a> if (!s->data || !s->len)
+<a href="#l316" class="line" id="l316"> 316</a> return;
+<a href="#l317" class="line" id="l317"> 317</a>
+<a href="#l318" class="line" id="l318"> 318</a> p = ltrim(s->data);
+<a href="#l319" class="line" id="l319"> 319</a> e = rtrim(p);
+<a href="#l320" class="line" id="l320"> 320</a>
+<a href="#l321" class="line" id="l321"> 321</a> for (; *p && p != e; p++) {
+<a href="#l322" class="line" id="l322"> 322</a> switch (*p) {
+<a href="#l323" class="line" id="l323"> 323</a> case '\n': putchar('\\'); putchar('n'); break;
+<a href="#l324" class="line" id="l324"> 324</a> case '\\': putchar('\\'); putchar('\\'); break;
+<a href="#l325" class="line" id="l325"> 325</a> case '\t': putchar('\\'); putchar('t'); break;
+<a href="#l326" class="line" id="l326"> 326</a> default:
+<a href="#l327" class="line" id="l327"> 327</a> /* ignore control chars */
+<a href="#l328" class="line" id="l328"> 328</a> if (!ISCNTRL((unsigned char)*p))
+<a href="#l329" class="line" id="l329"> 329</a> putchar(*p);
+<a href="#l330" class="line" id="l330"> 330</a> break;
+<a href="#l331" class="line" id="l331"> 331</a> }
+<a href="#l332" class="line" id="l332"> 332</a> }
+<a href="#l333" class="line" id="l333"> 333</a> }
+<a href="#l334" class="line" id="l334"> 334</a>
+<a href="#l335" class="line" id="l335"> 335</a> static void
+<a href="#l336" class="line" id="l336"> 336</a> printtrimmed(const char *s)
+<a href="#l337" class="line" id="l337"> 337</a> {
+<a href="#l338" class="line" id="l338"> 338</a> char *p, *e;
+<a href="#l339" class="line" id="l339"> 339</a>
+<a href="#l340" class="line" id="l340"> 340</a> p = ltrim(s);
+<a href="#l341" class="line" id="l341"> 341</a> e = rtrim(p);
+<a href="#l342" class="line" id="l342"> 342</a> for (; *p && p != e; p++) {
+<a href="#l343" class="line" id="l343"> 343</a> if (ISSPACE((unsigned char)*p))
+<a href="#l344" class="line" id="l344"> 344</a> putchar(' '); /* any whitespace to space */
+<a href="#l345" class="line" id="l345"> 345</a> else if (!ISCNTRL((unsigned char)*p))
+<a href="#l346" class="line" id="l346"> 346</a> /* ignore other control chars */
+<a href="#l347" class="line" id="l347"> 347</a> putchar(*p);
+<a href="#l348" class="line" id="l348"> 348</a> }
+<a href="#l349" class="line" id="l349"> 349</a> }
+<a href="#l350" class="line" id="l350"> 350</a>
+<a href="#l351" class="line" id="l351"> 351</a> /* Print text, replace TABs, carriage return and other whitespace with ' '.
+<a href="#l352" class="line" id="l352"> 352</a> * Other control chars are removed. Remove leading and trailing whitespace. */
+<a href="#l353" class="line" id="l353"> 353</a> static void
+<a href="#l354" class="line" id="l354"> 354</a> string_print_trimmed(String *s)
+<a href="#l355" class="line" id="l355"> 355</a> {
+<a href="#l356" class="line" id="l356"> 356</a> if (!s->data || !s->len)
+<a href="#l357" class="line" id="l357"> 357</a> return;
+<a href="#l358" class="line" id="l358"> 358</a>
+<a href="#l359" class="line" id="l359"> 359</a> printtrimmed(s->data);
+<a href="#l360" class="line" id="l360"> 360</a> }
+<a href="#l361" class="line" id="l361"> 361</a>
+<a href="#l362" class="line" id="l362"> 362</a> /* Print each field with trimmed whitespace, separated by '|'. */
+<a href="#l363" class="line" id="l363"> 363</a> static void
+<a href="#l364" class="line" id="l364"> 364</a> string_print_trimmed_multi(String *s)
+<a href="#l365" class="line" id="l365"> 365</a> {
+<a href="#l366" class="line" id="l366"> 366</a> char *p, *e;
+<a href="#l367" class="line" id="l367"> 367</a> int c;
+<a href="#l368" class="line" id="l368"> 368</a>
+<a href="#l369" class="line" id="l369"> 369</a> if (!s->data || !s->len)
+<a href="#l370" class="line" id="l370"> 370</a> return;
+<a href="#l371" class="line" id="l371"> 371</a>
+<a href="#l372" class="line" id="l372"> 372</a> for (p = s->data; ; p = e + 1) {
+<a href="#l373" class="line" id="l373"> 373</a> if ((e = strstr(p, FieldMultiSeparator))) {
+<a href="#l374" class="line" id="l374"> 374</a> c = *e;
+<a href="#l375" class="line" id="l375"> 375</a> *e = '\0';
+<a href="#l376" class="line" id="l376"> 376</a> printtrimmed(p);
+<a href="#l377" class="line" id="l377"> 377</a> *e = c; /* restore NUL byte to original character */
+<a href="#l378" class="line" id="l378"> 378</a> fputs(FieldMultiSeparator, stdout);
+<a href="#l379" class="line" id="l379"> 379</a> } else {
+<a href="#l380" class="line" id="l380"> 380</a> printtrimmed(p);
+<a href="#l381" class="line" id="l381"> 381</a> break;
+<a href="#l382" class="line" id="l382"> 382</a> }
+<a href="#l383" class="line" id="l383"> 383</a> }
+<a href="#l384" class="line" id="l384"> 384</a> }
+<a href="#l385" class="line" id="l385"> 385</a>
+<a href="#l386" class="line" id="l386"> 386</a> /* Print URL, if it is a relative URL then it uses the global `baseurl`. */
+<a href="#l387" class="line" id="l387"> 387</a> static void
+<a href="#l388" class="line" id="l388"> 388</a> printuri(char *s)
+<a href="#l389" class="line" id="l389"> 389</a> {
+<a href="#l390" class="line" id="l390"> 390</a> char link[4096], *p, *e;
+<a href="#l391" class="line" id="l391"> 391</a> struct uri newuri, olduri;
+<a href="#l392" class="line" id="l392"> 392</a> int c, r = -1;
+<a href="#l393" class="line" id="l393"> 393</a>
+<a href="#l394" class="line" id="l394"> 394</a> p = ltrim(s);
+<a href="#l395" class="line" id="l395"> 395</a> e = rtrim(p);
+<a href="#l396" class="line" id="l396"> 396</a> c = *e;
+<a href="#l397" class="line" id="l397"> 397</a> *e = '\0';
+<a href="#l398" class="line" id="l398"> 398</a>
+<a href="#l399" class="line" id="l399"> 399</a> if (baseurl && !uri_hasscheme(p) &&
+<a href="#l400" class="line" id="l400"> 400</a> uri_parse(p, &olduri) != -1 && !olduri.proto[0] &&
+<a href="#l401" class="line" id="l401"> 401</a> uri_makeabs(&newuri, &olduri, &baseuri) != -1 && newuri.proto[0])
+<a href="#l402" class="line" id="l402"> 402</a> r = uri_format(link, sizeof(link), &newuri);
+<a href="#l403" class="line" id="l403"> 403</a>
+<a href="#l404" class="line" id="l404"> 404</a> if (r >= 0 && (size_t)r < sizeof(link))
+<a href="#l405" class="line" id="l405"> 405</a> printtrimmed(link);
+<a href="#l406" class="line" id="l406"> 406</a> else
+<a href="#l407" class="line" id="l407"> 407</a> printtrimmed(p);
+<a href="#l408" class="line" id="l408"> 408</a>
+<a href="#l409" class="line" id="l409"> 409</a> *e = c; /* restore NUL byte to original character */
+<a href="#l410" class="line" id="l410"> 410</a> }
+<a href="#l411" class="line" id="l411"> 411</a>
+<a href="#l412" class="line" id="l412"> 412</a> /* Print URL, if it is a relative URL then it uses the global `baseurl`. */
+<a href="#l413" class="line" id="l413"> 413</a> static void
+<a href="#l414" class="line" id="l414"> 414</a> string_print_uri(String *s)
+<a href="#l415" class="line" id="l415"> 415</a> {
+<a href="#l416" class="line" id="l416"> 416</a> if (!s->data || !s->len)
+<a href="#l417" class="line" id="l417"> 417</a> return;
+<a href="#l418" class="line" id="l418"> 418</a>
+<a href="#l419" class="line" id="l419"> 419</a> printuri(s->data);
+<a href="#l420" class="line" id="l420"> 420</a> }
+<a href="#l421" class="line" id="l421"> 421</a>
+<a href="#l422" class="line" id="l422"> 422</a> /* Print as UNIX timestamp, print nothing if the time is empty or invalid. */
+<a href="#l423" class="line" id="l423"> 423</a> static void
+<a href="#l424" class="line" id="l424"> 424</a> string_print_timestamp(String *s)
+<a href="#l425" class="line" id="l425"> 425</a> {
+<a href="#l426" class="line" id="l426"> 426</a> long long t;
+<a href="#l427" class="line" id="l427"> 427</a>
+<a href="#l428" class="line" id="l428"> 428</a> if (!s->data || !s->len)
+<a href="#l429" class="line" id="l429"> 429</a> return;
+<a href="#l430" class="line" id="l430"> 430</a>
+<a href="#l431" class="line" id="l431"> 431</a> if (parsetime(s->data, &t) != -1)
+<a href="#l432" class="line" id="l432"> 432</a> printf("%lld", t);
+<a href="#l433" class="line" id="l433"> 433</a> }
+<a href="#l434" class="line" id="l434"> 434</a>
+<a href="#l435" class="line" id="l435"> 435</a> /* Convert time fields. Returns a signed (at least) 64-bit UNIX timestamp.
+<a href="#l436" class="line" id="l436"> 436</a> Parameters should be passed as they are in a struct tm:
+<a href="#l437" class="line" id="l437"> 437</a> that is: year = year - 1900, month = month - 1. */
+<a href="#l438" class="line" id="l438"> 438</a> static long long
+<a href="#l439" class="line" id="l439"> 439</a> datetounix(long long year, int mon, int day, int hour, int min, int sec)
+<a href="#l440" class="line" id="l440"> 440</a> {
+<a href="#l441" class="line" id="l441"> 441</a> /* seconds in a month in a regular (non-leap) year */
+<a href="#l442" class="line" id="l442"> 442</a> static const long secs_through_month[] = {
+<a href="#l443" class="line" id="l443"> 443</a> 0, 31 * 86400, 59 * 86400, 90 * 86400,
+<a href="#l444" class="line" id="l444"> 444</a> 120 * 86400, 151 * 86400, 181 * 86400, 212 * 86400,
+<a href="#l445" class="line" id="l445"> 445</a> 243 * 86400, 273 * 86400, 304 * 86400, 334 * 86400 };
+<a href="#l446" class="line" id="l446"> 446</a> int is_leap = 0, cycles, centuries = 0, leaps = 0, rem;
+<a href="#l447" class="line" id="l447"> 447</a> long long t;
+<a href="#l448" class="line" id="l448"> 448</a>
+<a href="#l449" class="line" id="l449"> 449</a> /* optimization: handle common range year 1902 up to and including 2038 */
+<a href="#l450" class="line" id="l450"> 450</a> if (year - 2ULL <= 136) {
+<a href="#l451" class="line" id="l451"> 451</a> /* amount of leap days relative to 1970: every 4 years */
+<a href="#l452" class="line" id="l452"> 452</a> leaps = (year - 68) >> 2;
+<a href="#l453" class="line" id="l453"> 453</a> if (!((year - 68) & 3)) {
+<a href="#l454" class="line" id="l454"> 454</a> leaps--;
+<a href="#l455" class="line" id="l455"> 455</a> is_leap = 1;
+<a href="#l456" class="line" id="l456"> 456</a> } else {
+<a href="#l457" class="line" id="l457"> 457</a> is_leap = 0;
+<a href="#l458" class="line" id="l458"> 458</a> }
+<a href="#l459" class="line" id="l459"> 459</a> t = 31536000 * (year - 70) + (86400 * leaps); /* 365 * 86400 = 31536000 */
+<a href="#l460" class="line" id="l460"> 460</a> } else {
+<a href="#l461" class="line" id="l461"> 461</a> /* general leap year calculation:
+<a href="#l462" class="line" id="l462"> 462</a> leap years occur mostly every 4 years but every 100 years
+<a href="#l463" class="line" id="l463"> 463</a> a leap year is skipped unless the year is divisible by 400 */
+<a href="#l464" class="line" id="l464"> 464</a> cycles = (year - 100) / 400;
+<a href="#l465" class="line" id="l465"> 465</a> rem = (year - 100) % 400;
+<a href="#l466" class="line" id="l466"> 466</a> if (rem < 0) {
+<a href="#l467" class="line" id="l467"> 467</a> cycles--;
+<a href="#l468" class="line" id="l468"> 468</a> rem += 400;
+<a href="#l469" class="line" id="l469"> 469</a> }
+<a href="#l470" class="line" id="l470"> 470</a> if (!rem) {
+<a href="#l471" class="line" id="l471"> 471</a> is_leap = 1;
+<a href="#l472" class="line" id="l472"> 472</a> } else {
+<a href="#l473" class="line" id="l473"> 473</a> if (rem >= 300) {
+<a href="#l474" class="line" id="l474"> 474</a> centuries = 3;
+<a href="#l475" class="line" id="l475"> 475</a> rem -= 300;
+<a href="#l476" class="line" id="l476"> 476</a> } else if (rem >= 200) {
+<a href="#l477" class="line" id="l477"> 477</a> centuries = 2;
+<a href="#l478" class="line" id="l478"> 478</a> rem -= 200;
+<a href="#l479" class="line" id="l479"> 479</a> } else if (rem >= 100) {
+<a href="#l480" class="line" id="l480"> 480</a> centuries = 1;
+<a href="#l481" class="line" id="l481"> 481</a> rem -= 100;
+<a href="#l482" class="line" id="l482"> 482</a> }
+<a href="#l483" class="line" id="l483"> 483</a> if (rem) {
+<a href="#l484" class="line" id="l484"> 484</a> leaps = rem / 4U;
+<a href="#l485" class="line" id="l485"> 485</a> rem %= 4U;
+<a href="#l486" class="line" id="l486"> 486</a> is_leap = !rem;
+<a href="#l487" class="line" id="l487"> 487</a> }
+<a href="#l488" class="line" id="l488"> 488</a> }
+<a href="#l489" class="line" id="l489"> 489</a> leaps += (97 * cycles) + (24 * centuries) - is_leap;
+<a href="#l490" class="line" id="l490"> 490</a>
+<a href="#l491" class="line" id="l491"> 491</a> /* adjust 8 leap days from 1970 up to and including 2000:
+<a href="#l492" class="line" id="l492"> 492</a> ((30 * 365) + 8) * 86400 = 946771200 */
+<a href="#l493" class="line" id="l493"> 493</a> t = ((year - 100) * 31536000LL) + (leaps * 86400LL) + 946771200LL;
+<a href="#l494" class="line" id="l494"> 494</a> }
+<a href="#l495" class="line" id="l495"> 495</a> t += secs_through_month[mon];
+<a href="#l496" class="line" id="l496"> 496</a> if (is_leap && mon >= 2)
+<a href="#l497" class="line" id="l497"> 497</a> t += 86400;
+<a href="#l498" class="line" id="l498"> 498</a> t += 86400LL * (day - 1);
+<a href="#l499" class="line" id="l499"> 499</a> t += 3600LL * hour;
+<a href="#l500" class="line" id="l500"> 500</a> t += 60LL * min;
+<a href="#l501" class="line" id="l501"> 501</a> t += sec;
+<a href="#l502" class="line" id="l502"> 502</a>
+<a href="#l503" class="line" id="l503"> 503</a> return t;
+<a href="#l504" class="line" id="l504"> 504</a> }
+<a href="#l505" class="line" id="l505"> 505</a>
+<a href="#l506" class="line" id="l506"> 506</a> /* Get timezone from string, return time offset in seconds from UTC.
+<a href="#l507" class="line" id="l507"> 507</a> * NOTE: only parses timezones in RFC 822, many other timezone names are
+<a href="#l508" class="line" id="l508"> 508</a> * ambiguous anyway.
+<a href="#l509" class="line" id="l509"> 509</a> * ANSI and military zones are defined wrong in RFC 822 and are unsupported,
+<a href="#l510" class="line" id="l510"> 510</a> * see note on RFC 2822 4.3 page 32. */
+<a href="#l511" class="line" id="l511"> 511</a> static long
+<a href="#l512" class="line" id="l512"> 512</a> gettzoffset(const char *s)
+<a href="#l513" class="line" id="l513"> 513</a> {
+<a href="#l514" class="line" id="l514"> 514</a> static const struct {
+<a href="#l515" class="line" id="l515"> 515</a> char *name;
+<a href="#l516" class="line" id="l516"> 516</a> int offhour;
+<a href="#l517" class="line" id="l517"> 517</a> } tzones[] = {
+<a href="#l518" class="line" id="l518"> 518</a> { "CDT", -5 * 3600 },
+<a href="#l519" class="line" id="l519"> 519</a> { "CST", -6 * 3600 },
+<a href="#l520" class="line" id="l520"> 520</a> { "EDT", -4 * 3600 },
+<a href="#l521" class="line" id="l521"> 521</a> { "EST", -5 * 3600 },
+<a href="#l522" class="line" id="l522"> 522</a> { "MDT", -6 * 3600 },
+<a href="#l523" class="line" id="l523"> 523</a> { "MST", -7 * 3600 },
+<a href="#l524" class="line" id="l524"> 524</a> { "PDT", -7 * 3600 },
+<a href="#l525" class="line" id="l525"> 525</a> { "PST", -8 * 3600 },
+<a href="#l526" class="line" id="l526"> 526</a> };
+<a href="#l527" class="line" id="l527"> 527</a> const char *p;
+<a href="#l528" class="line" id="l528"> 528</a> long tzhour = 0, tzmin = 0;
+<a href="#l529" class="line" id="l529"> 529</a> size_t i;
+<a href="#l530" class="line" id="l530"> 530</a>
+<a href="#l531" class="line" id="l531"> 531</a> for (; ISSPACE((unsigned char)*s); s++)
+<a href="#l532" class="line" id="l532"> 532</a> ;
+<a href="#l533" class="line" id="l533"> 533</a> switch (*s) {
+<a href="#l534" class="line" id="l534"> 534</a> case '-': /* offset */
+<a href="#l535" class="line" id="l535"> 535</a> case '+':
+<a href="#l536" class="line" id="l536"> 536</a> for (i = 0, p = s + 1; i < 2 && ISDIGIT((unsigned char)*p); i++, p++)
+<a href="#l537" class="line" id="l537"> 537</a> tzhour = (tzhour * 10) + (*p - '0');
+<a href="#l538" class="line" id="l538"> 538</a> if (*p == ':')
+<a href="#l539" class="line" id="l539"> 539</a> p++;
+<a href="#l540" class="line" id="l540"> 540</a> for (i = 0; i < 2 && ISDIGIT((unsigned char)*p); i++, p++)
+<a href="#l541" class="line" id="l541"> 541</a> tzmin = (tzmin * 10) + (*p - '0');
+<a href="#l542" class="line" id="l542"> 542</a> return ((tzhour * 3600) + (tzmin * 60)) * (s[0] == '-' ? -1 : 1);
+<a href="#l543" class="line" id="l543"> 543</a> default: /* timezone name */
+<a href="#l544" class="line" id="l544"> 544</a> for (i = 0; ISALPHA((unsigned char)s[i]); i++)
+<a href="#l545" class="line" id="l545"> 545</a> ;
+<a href="#l546" class="line" id="l546"> 546</a> if (i != 3)
+<a href="#l547" class="line" id="l547"> 547</a> return 0;
+<a href="#l548" class="line" id="l548"> 548</a> /* compare timezone and adjust offset relative to UTC */
+<a href="#l549" class="line" id="l549"> 549</a> for (i = 0; i < sizeof(tzones) / sizeof(*tzones); i++) {
+<a href="#l550" class="line" id="l550"> 550</a> if (!memcmp(s, tzones[i].name, 3))
+<a href="#l551" class="line" id="l551"> 551</a> return tzones[i].offhour;
+<a href="#l552" class="line" id="l552"> 552</a> }
+<a href="#l553" class="line" id="l553"> 553</a> }
+<a href="#l554" class="line" id="l554"> 554</a> return 0;
+<a href="#l555" class="line" id="l555"> 555</a> }
+<a href="#l556" class="line" id="l556"> 556</a>
+<a href="#l557" class="line" id="l557"> 557</a> /* Parse time string `s` into the UNIX timestamp `tp`.
+<a href="#l558" class="line" id="l558"> 558</a> Returns 0 on success or -1 on failure. */
+<a href="#l559" class="line" id="l559"> 559</a> static int
+<a href="#l560" class="line" id="l560"> 560</a> parsetime(const char *s, long long *tp)
+<a href="#l561" class="line" id="l561"> 561</a> {
+<a href="#l562" class="line" id="l562"> 562</a> static const struct {
+<a href="#l563" class="line" id="l563"> 563</a> char *name;
+<a href="#l564" class="line" id="l564"> 564</a> int len;
+<a href="#l565" class="line" id="l565"> 565</a> } mons[] = {
+<a href="#l566" class="line" id="l566"> 566</a> { STRP("January"), },
+<a href="#l567" class="line" id="l567"> 567</a> { STRP("February"), },
+<a href="#l568" class="line" id="l568"> 568</a> { STRP("March"), },
+<a href="#l569" class="line" id="l569"> 569</a> { STRP("April"), },
+<a href="#l570" class="line" id="l570"> 570</a> { STRP("May"), },
+<a href="#l571" class="line" id="l571"> 571</a> { STRP("June"), },
+<a href="#l572" class="line" id="l572"> 572</a> { STRP("July"), },
+<a href="#l573" class="line" id="l573"> 573</a> { STRP("August"), },
+<a href="#l574" class="line" id="l574"> 574</a> { STRP("September"), },
+<a href="#l575" class="line" id="l575"> 575</a> { STRP("October"), },
+<a href="#l576" class="line" id="l576"> 576</a> { STRP("November"), },
+<a href="#l577" class="line" id="l577"> 577</a> { STRP("December"), },
+<a href="#l578" class="line" id="l578"> 578</a> };
+<a href="#l579" class="line" id="l579"> 579</a> int va[6] = { 0 }, i, j, v, vi;
+<a href="#l580" class="line" id="l580"> 580</a> size_t m;
+<a href="#l581" class="line" id="l581"> 581</a>
+<a href="#l582" class="line" id="l582"> 582</a> for (; ISSPACE((unsigned char)*s); s++)
+<a href="#l583" class="line" id="l583"> 583</a> ;
+<a href="#l584" class="line" id="l584"> 584</a> if (!ISDIGIT((unsigned char)*s) && !ISALPHA((unsigned char)*s))
+<a href="#l585" class="line" id="l585"> 585</a> return -1;
+<a href="#l586" class="line" id="l586"> 586</a>
+<a href="#l587" class="line" id="l587"> 587</a> if (ISDIGIT((unsigned char)s[0]) &&
+<a href="#l588" class="line" id="l588"> 588</a> ISDIGIT((unsigned char)s[1]) &&
+<a href="#l589" class="line" id="l589"> 589</a> ISDIGIT((unsigned char)s[2]) &&
+<a href="#l590" class="line" id="l590"> 590</a> ISDIGIT((unsigned char)s[3])) {
+<a href="#l591" class="line" id="l591"> 591</a> /* formats "%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%S" or "%Y%m%d%H%M%S" */
+<a href="#l592" class="line" id="l592"> 592</a> vi = 0;
+<a href="#l593" class="line" id="l593"> 593</a> } else {
+<a href="#l594" class="line" id="l594"> 594</a> /* format: "[%a, ]%d %b %Y %H:%M:%S" */
+<a href="#l595" class="line" id="l595"> 595</a> /* parse "[%a, ]%d %b %Y " part, then use time parsing as above */
+<a href="#l596" class="line" id="l596"> 596</a> for (; ISALPHA((unsigned char)*s); s++)
+<a href="#l597" class="line" id="l597"> 597</a> ;
+<a href="#l598" class="line" id="l598"> 598</a> for (; ISSPACE((unsigned char)*s); s++)
+<a href="#l599" class="line" id="l599"> 599</a> ;
+<a href="#l600" class="line" id="l600"> 600</a> if (*s == ',')
+<a href="#l601" class="line" id="l601"> 601</a> s++;
+<a href="#l602" class="line" id="l602"> 602</a> for (; ISSPACE((unsigned char)*s); s++)
+<a href="#l603" class="line" id="l603"> 603</a> ;
+<a href="#l604" class="line" id="l604"> 604</a> for (v = 0, i = 0; i < 2 && ISDIGIT((unsigned char)*s); s++, i++)
+<a href="#l605" class="line" id="l605"> 605</a> v = (v * 10) + (*s - '0');
+<a href="#l606" class="line" id="l606"> 606</a> va[2] = v; /* day */
+<a href="#l607" class="line" id="l607"> 607</a> for (; ISSPACE((unsigned char)*s); s++)
+<a href="#l608" class="line" id="l608"> 608</a> ;
+<a href="#l609" class="line" id="l609"> 609</a> /* end of word month */
+<a href="#l610" class="line" id="l610"> 610</a> for (j = 0; ISALPHA((unsigned char)s[j]); j++)
+<a href="#l611" class="line" id="l611"> 611</a> ;
+<a href="#l612" class="line" id="l612"> 612</a> /* check month name */
+<a href="#l613" class="line" id="l613"> 613</a> if (j < 3 || j > 9)
+<a href="#l614" class="line" id="l614"> 614</a> return -1; /* month cannot match */
+<a href="#l615" class="line" id="l615"> 615</a> for (m = 0; m < sizeof(mons) / sizeof(*mons); m++) {
+<a href="#l616" class="line" id="l616"> 616</a> /* abbreviation (3 length) or long name */
+<a href="#l617" class="line" id="l617"> 617</a> if ((j == 3 || j == mons[m].len) &&
+<a href="#l618" class="line" id="l618"> 618</a> !strncasecmp(mons[m].name, s, j)) {
+<a href="#l619" class="line" id="l619"> 619</a> va[1] = m + 1;
+<a href="#l620" class="line" id="l620"> 620</a> s += j;
+<a href="#l621" class="line" id="l621"> 621</a> break;
+<a href="#l622" class="line" id="l622"> 622</a> }
+<a href="#l623" class="line" id="l623"> 623</a> }
+<a href="#l624" class="line" id="l624"> 624</a> if (m >= 12)
+<a href="#l625" class="line" id="l625"> 625</a> return -1; /* no month found */
+<a href="#l626" class="line" id="l626"> 626</a> for (; ISSPACE((unsigned char)*s); s++)
+<a href="#l627" class="line" id="l627"> 627</a> ;
+<a href="#l628" class="line" id="l628"> 628</a> for (v = 0, i = 0; i < 4 && ISDIGIT((unsigned char)*s); s++, i++)
+<a href="#l629" class="line" id="l629"> 629</a> v = (v * 10) + (*s - '0');
+<a href="#l630" class="line" id="l630"> 630</a> /* obsolete short year: RFC 2822 4.3 */
+<a href="#l631" class="line" id="l631"> 631</a> if (i == 2 || i == 3)
+<a href="#l632" class="line" id="l632"> 632</a> v += (i == 2 && v >= 0 && v <= 49) ? 2000 : 1900;
+<a href="#l633" class="line" id="l633"> 633</a> va[0] = v; /* year */
+<a href="#l634" class="line" id="l634"> 634</a> for (; ISSPACE((unsigned char)*s); s++)
+<a href="#l635" class="line" id="l635"> 635</a> ;
+<a href="#l636" class="line" id="l636"> 636</a> /* parse only regular time part, see below */
+<a href="#l637" class="line" id="l637"> 637</a> vi = 3;
+<a href="#l638" class="line" id="l638"> 638</a> }
+<a href="#l639" class="line" id="l639"> 639</a>
+<a href="#l640" class="line" id="l640"> 640</a> /* parse time parts (and possibly remaining date parts) */
+<a href="#l641" class="line" id="l641"> 641</a> for (; *s && vi < 6; vi++) {
+<a href="#l642" class="line" id="l642"> 642</a> for (i = 0, v = 0; i < ((vi == 0) ? 4 : 2) &&
+<a href="#l643" class="line" id="l643"> 643</a> ISDIGIT((unsigned char)*s); s++, i++) {
+<a href="#l644" class="line" id="l644"> 644</a> v = (v * 10) + (*s - '0');
+<a href="#l645" class="line" id="l645"> 645</a> }
+<a href="#l646" class="line" id="l646"> 646</a> va[vi] = v;
+<a href="#l647" class="line" id="l647"> 647</a>
+<a href="#l648" class="line" id="l648"> 648</a> if ((vi < 2 && *s == '-') ||
+<a href="#l649" class="line" id="l649"> 649</a> (vi == 2 && (*s == 'T' || ISSPACE((unsigned char)*s))) ||
+<a href="#l650" class="line" id="l650"> 650</a> (vi > 2 && *s == ':'))
+<a href="#l651" class="line" id="l651"> 651</a> s++;
+<a href="#l652" class="line" id="l652"> 652</a> }
+<a href="#l653" class="line" id="l653"> 653</a>
+<a href="#l654" class="line" id="l654"> 654</a> /* skip milliseconds in for example: "%Y-%m-%dT%H:%M:%S.000Z" */
+<a href="#l655" class="line" id="l655"> 655</a> if (*s == '.') {
+<a href="#l656" class="line" id="l656"> 656</a> for (s++; ISDIGIT((unsigned char)*s); s++)
+<a href="#l657" class="line" id="l657"> 657</a> ;
+<a href="#l658" class="line" id="l658"> 658</a> }
+<a href="#l659" class="line" id="l659"> 659</a>
+<a href="#l660" class="line" id="l660"> 660</a> /* invalid range */
+<a href="#l661" class="line" id="l661"> 661</a> if (va[0] < 0 || va[0] > 9999 ||
+<a href="#l662" class="line" id="l662"> 662</a> va[1] < 1 || va[1] > 12 ||
+<a href="#l663" class="line" id="l663"> 663</a> va[2] < 1 || va[2] > 31 ||
+<a href="#l664" class="line" id="l664"> 664</a> va[3] < 0 || va[3] > 23 ||
+<a href="#l665" class="line" id="l665"> 665</a> va[4] < 0 || va[4] > 59 ||
+<a href="#l666" class="line" id="l666"> 666</a> va[5] < 0 || va[5] > 60) /* allow leap second */
+<a href="#l667" class="line" id="l667"> 667</a> return -1;
+<a href="#l668" class="line" id="l668"> 668</a>
+<a href="#l669" class="line" id="l669"> 669</a> *tp = datetounix(va[0] - 1900, va[1] - 1, va[2], va[3], va[4], va[5]) -
+<a href="#l670" class="line" id="l670"> 670</a> gettzoffset(s);
+<a href="#l671" class="line" id="l671"> 671</a>
+<a href="#l672" class="line" id="l672"> 672</a> return 0;
+<a href="#l673" class="line" id="l673"> 673</a> }
+<a href="#l674" class="line" id="l674"> 674</a>
+<a href="#l675" class="line" id="l675"> 675</a> static void
+<a href="#l676" class="line" id="l676"> 676</a> printfields(void)
+<a href="#l677" class="line" id="l677"> 677</a> {
+<a href="#l678" class="line" id="l678"> 678</a> string_print_timestamp(&ctx.fields[FeedFieldTime].str);
+<a href="#l679" class="line" id="l679"> 679</a> putchar(FieldSeparator);
+<a href="#l680" class="line" id="l680"> 680</a> string_print_trimmed(&ctx.fields[FeedFieldTitle].str);
+<a href="#l681" class="line" id="l681"> 681</a> putchar(FieldSeparator);
+<a href="#l682" class="line" id="l682"> 682</a> string_print_uri(&ctx.fields[FeedFieldLink].str);
+<a href="#l683" class="line" id="l683"> 683</a> putchar(FieldSeparator);
+<a href="#l684" class="line" id="l684"> 684</a> string_print_encoded(&ctx.fields[FeedFieldContent].str);
+<a href="#l685" class="line" id="l685"> 685</a> putchar(FieldSeparator);
+<a href="#l686" class="line" id="l686"> 686</a> fputs(contenttypes[ctx.contenttype], stdout);
+<a href="#l687" class="line" id="l687"> 687</a> putchar(FieldSeparator);
+<a href="#l688" class="line" id="l688"> 688</a> string_print_trimmed(&ctx.fields[FeedFieldId].str);
+<a href="#l689" class="line" id="l689"> 689</a> putchar(FieldSeparator);
+<a href="#l690" class="line" id="l690"> 690</a> string_print_trimmed(&ctx.fields[FeedFieldAuthor].str);
+<a href="#l691" class="line" id="l691"> 691</a> putchar(FieldSeparator);
+<a href="#l692" class="line" id="l692"> 692</a> string_print_uri(&ctx.fields[FeedFieldEnclosure].str);
+<a href="#l693" class="line" id="l693"> 693</a> putchar(FieldSeparator);
+<a href="#l694" class="line" id="l694"> 694</a> string_print_trimmed_multi(&ctx.fields[FeedFieldCategory].str);
+<a href="#l695" class="line" id="l695"> 695</a> putchar('\n');
+<a href="#l696" class="line" id="l696"> 696</a>
+<a href="#l697" class="line" id="l697"> 697</a> if (ferror(stdout)) /* check for errors but do not flush */
+<a href="#l698" class="line" id="l698"> 698</a> checkfileerror(stdout, "<stdout>", 'w');
+<a href="#l699" class="line" id="l699"> 699</a> }
+<a href="#l700" class="line" id="l700"> 700</a>
+<a href="#l701" class="line" id="l701"> 701</a> static int
+<a href="#l702" class="line" id="l702"> 702</a> istag(const char *name, size_t len, const char *name2, size_t len2)
+<a href="#l703" class="line" id="l703"> 703</a> {
+<a href="#l704" class="line" id="l704"> 704</a> return (len == len2 && !strcasecmp(name, name2));
+<a href="#l705" class="line" id="l705"> 705</a> }
+<a href="#l706" class="line" id="l706"> 706</a>
+<a href="#l707" class="line" id="l707"> 707</a> static int
+<a href="#l708" class="line" id="l708"> 708</a> isattr(const char *name, size_t len, const char *name2, size_t len2)
+<a href="#l709" class="line" id="l709"> 709</a> {
+<a href="#l710" class="line" id="l710"> 710</a> return (len == len2 && !strcasecmp(name, name2));
+<a href="#l711" class="line" id="l711"> 711</a> }
+<a href="#l712" class="line" id="l712"> 712</a>
+<a href="#l713" class="line" id="l713"> 713</a> static void
+<a href="#l714" class="line" id="l714"> 714</a> xmlattr(XMLParser *p, const char *t, size_t tl, const char *n, size_t nl,
+<a href="#l715" class="line" id="l715"> 715</a> const char *v, size_t vl)
+<a href="#l716" class="line" id="l716"> 716</a> {
+<a href="#l717" class="line" id="l717"> 717</a> /* handles transforming inline XML to data */
+<a href="#l718" class="line" id="l718"> 718</a> if (ISINCONTENT(ctx)) {
+<a href="#l719" class="line" id="l719"> 719</a> if (ctx.contenttype == ContentTypeHTML)
+<a href="#l720" class="line" id="l720"> 720</a> xmldata(p, v, vl);
+<a href="#l721" class="line" id="l721"> 721</a> return;
+<a href="#l722" class="line" id="l722"> 722</a> }
+<a href="#l723" class="line" id="l723"> 723</a>
+<a href="#l724" class="line" id="l724"> 724</a> if (!ctx.tag.id)
+<a href="#l725" class="line" id="l725"> 725</a> return;
+<a href="#l726" class="line" id="l726"> 726</a>
+<a href="#l727" class="line" id="l727"> 727</a> /* content-type may be for Atom: text, xhtml, html or a mime-type.
+<a href="#l728" class="line" id="l728"> 728</a> for MRSS (media:description): plain, html. */
+<a href="#l729" class="line" id="l729"> 729</a> if (ISCONTENTTAG(ctx)) {
+<a href="#l730" class="line" id="l730"> 730</a> if (isattr(n, nl, STRP("type")))
+<a href="#l731" class="line" id="l731"> 731</a> string_append(&attrtype, v, vl);
+<a href="#l732" class="line" id="l732"> 732</a> return;
+<a href="#l733" class="line" id="l733"> 733</a> }
+<a href="#l734" class="line" id="l734"> 734</a>
+<a href="#l735" class="line" id="l735"> 735</a> if (ctx.feedtype == FeedTypeRSS) {
+<a href="#l736" class="line" id="l736"> 736</a> if (ctx.tag.id == RSSTagEnclosure &&
+<a href="#l737" class="line" id="l737"> 737</a> isattr(n, nl, STRP("url"))) {
+<a href="#l738" class="line" id="l738"> 738</a> string_append(&tmpstr, v, vl);
+<a href="#l739" class="line" id="l739"> 739</a> } else if (ctx.tag.id == RSSTagGuid &&
+<a href="#l740" class="line" id="l740"> 740</a> isattr(n, nl, STRP("ispermalink"))) {
+<a href="#l741" class="line" id="l741"> 741</a> string_append(&attrispermalink, v, vl);
+<a href="#l742" class="line" id="l742"> 742</a> }
+<a href="#l743" class="line" id="l743"> 743</a> } else if (ctx.feedtype == FeedTypeAtom) {
+<a href="#l744" class="line" id="l744"> 744</a> if (ctx.tag.id == AtomTagLink) {
+<a href="#l745" class="line" id="l745"> 745</a> if (isattr(n, nl, STRP("rel"))) {
+<a href="#l746" class="line" id="l746"> 746</a> string_append(&attrrel, v, vl);
+<a href="#l747" class="line" id="l747"> 747</a> } else if (isattr(n, nl, STRP("href"))) {
+<a href="#l748" class="line" id="l748"> 748</a> string_append(&tmpstr, v, vl);
+<a href="#l749" class="line" id="l749"> 749</a> }
+<a href="#l750" class="line" id="l750"> 750</a> } else if (ctx.tag.id == AtomTagCategory &&
+<a href="#l751" class="line" id="l751"> 751</a> isattr(n, nl, STRP("term"))) {
+<a href="#l752" class="line" id="l752"> 752</a> string_append(&tmpstr, v, vl);
+<a href="#l753" class="line" id="l753"> 753</a> }
+<a href="#l754" class="line" id="l754"> 754</a> }
+<a href="#l755" class="line" id="l755"> 755</a> }
+<a href="#l756" class="line" id="l756"> 756</a>
+<a href="#l757" class="line" id="l757"> 757</a> static void
+<a href="#l758" class="line" id="l758"> 758</a> xmlattrentity(XMLParser *p, const char *t, size_t tl, const char *n, size_t nl,
+<a href="#l759" class="line" id="l759"> 759</a> const char *data, size_t datalen)
+<a href="#l760" class="line" id="l760"> 760</a> {
+<a href="#l761" class="line" id="l761"> 761</a> char buf[8];
+<a href="#l762" class="line" id="l762"> 762</a> int len;
+<a href="#l763" class="line" id="l763"> 763</a>
+<a href="#l764" class="line" id="l764"> 764</a> /* handles transforming inline XML to data */
+<a href="#l765" class="line" id="l765"> 765</a> if (ISINCONTENT(ctx)) {
+<a href="#l766" class="line" id="l766"> 766</a> if (ctx.contenttype == ContentTypeHTML)
+<a href="#l767" class="line" id="l767"> 767</a> xmldata(p, data, datalen);
+<a href="#l768" class="line" id="l768"> 768</a> return;
+<a href="#l769" class="line" id="l769"> 769</a> }
+<a href="#l770" class="line" id="l770"> 770</a>
+<a href="#l771" class="line" id="l771"> 771</a> if (!ctx.tag.id)
+<a href="#l772" class="line" id="l772"> 772</a> return;
+<a href="#l773" class="line" id="l773"> 773</a>
+<a href="#l774" class="line" id="l774"> 774</a> /* try to translate entity, else just pass as data to
+<a href="#l775" class="line" id="l775"> 775</a> * xmlattr handler. */
+<a href="#l776" class="line" id="l776"> 776</a> if ((len = xml_entitytostr(data, buf, sizeof(buf))) > 0)
+<a href="#l777" class="line" id="l777"> 777</a> xmlattr(p, t, tl, n, nl, buf, (size_t)len);
+<a href="#l778" class="line" id="l778"> 778</a> else
+<a href="#l779" class="line" id="l779"> 779</a> xmlattr(p, t, tl, n, nl, data, datalen);
+<a href="#l780" class="line" id="l780"> 780</a> }
+<a href="#l781" class="line" id="l781"> 781</a>
+<a href="#l782" class="line" id="l782"> 782</a> static void
+<a href="#l783" class="line" id="l783"> 783</a> xmlattrend(XMLParser *p, const char *t, size_t tl, const char *n, size_t nl)
+<a href="#l784" class="line" id="l784"> 784</a> {
+<a href="#l785" class="line" id="l785"> 785</a> if (ISINCONTENT(ctx)) {
+<a href="#l786" class="line" id="l786"> 786</a> if (ctx.contenttype == ContentTypeHTML) {
+<a href="#l787" class="line" id="l787"> 787</a> /* handles transforming inline XML to data */
+<a href="#l788" class="line" id="l788"> 788</a> xmldata(p, "\"", 1);
+<a href="#l789" class="line" id="l789"> 789</a> ctx.attrcount = 0;
+<a href="#l790" class="line" id="l790"> 790</a> }
+<a href="#l791" class="line" id="l791"> 791</a> return;
+<a href="#l792" class="line" id="l792"> 792</a> }
+<a href="#l793" class="line" id="l793"> 793</a> }
+<a href="#l794" class="line" id="l794"> 794</a>
+<a href="#l795" class="line" id="l795"> 795</a> static void
+<a href="#l796" class="line" id="l796"> 796</a> xmlattrstart(XMLParser *p, const char *t, size_t tl, const char *n, size_t nl)
+<a href="#l797" class="line" id="l797"> 797</a> {
+<a href="#l798" class="line" id="l798"> 798</a> if (ISINCONTENT(ctx)) {
+<a href="#l799" class="line" id="l799"> 799</a> if (ctx.contenttype == ContentTypeHTML) {
+<a href="#l800" class="line" id="l800"> 800</a> /* handles transforming inline XML to data */
+<a href="#l801" class="line" id="l801"> 801</a> if (!ctx.attrcount)
+<a href="#l802" class="line" id="l802"> 802</a> xmldata(p, " ", 1);
+<a href="#l803" class="line" id="l803"> 803</a> ctx.attrcount++;
+<a href="#l804" class="line" id="l804"> 804</a> xmldata(p, n, nl);
+<a href="#l805" class="line" id="l805"> 805</a> xmldata(p, "=\"", 2);
+<a href="#l806" class="line" id="l806"> 806</a> }
+<a href="#l807" class="line" id="l807"> 807</a> return;
+<a href="#l808" class="line" id="l808"> 808</a> }
+<a href="#l809" class="line" id="l809"> 809</a>
+<a href="#l810" class="line" id="l810"> 810</a> if (attrispermalink.len && isattr(n, nl, STRP("ispermalink")))
+<a href="#l811" class="line" id="l811"> 811</a> string_clear(&attrispermalink);
+<a href="#l812" class="line" id="l812"> 812</a> else if (attrrel.len && isattr(n, nl, STRP("rel")))
+<a href="#l813" class="line" id="l813"> 813</a> string_clear(&attrrel);
+<a href="#l814" class="line" id="l814"> 814</a> else if (attrtype.len && isattr(n, nl, STRP("type")))
+<a href="#l815" class="line" id="l815"> 815</a> string_clear(&attrtype);
+<a href="#l816" class="line" id="l816"> 816</a> else if (tmpstr.len &&
+<a href="#l817" class="line" id="l817"> 817</a> (isattr(n, nl, STRP("href")) ||
+<a href="#l818" class="line" id="l818"> 818</a> isattr(n, nl, STRP("term")) ||
+<a href="#l819" class="line" id="l819"> 819</a> isattr(n, nl, STRP("url"))))
+<a href="#l820" class="line" id="l820"> 820</a> string_clear(&tmpstr); /* use the last value for multiple attribute values */
+<a href="#l821" class="line" id="l821"> 821</a> }
+<a href="#l822" class="line" id="l822"> 822</a>
+<a href="#l823" class="line" id="l823"> 823</a> static void
+<a href="#l824" class="line" id="l824"> 824</a> xmldata(XMLParser *p, const char *s, size_t len)
+<a href="#l825" class="line" id="l825"> 825</a> {
+<a href="#l826" class="line" id="l826"> 826</a> if (!ctx.field)
+<a href="#l827" class="line" id="l827"> 827</a> return;
+<a href="#l828" class="line" id="l828"> 828</a>
+<a href="#l829" class="line" id="l829"> 829</a> if (ISFEEDFIELDMULTI(fieldmap[ctx.tag.id]))
+<a href="#l830" class="line" id="l830"> 830</a> string_append(&tmpstr, s, len);
+<a href="#l831" class="line" id="l831"> 831</a> else
+<a href="#l832" class="line" id="l832"> 832</a> string_append(ctx.field, s, len);
+<a href="#l833" class="line" id="l833"> 833</a> }
+<a href="#l834" class="line" id="l834"> 834</a>
+<a href="#l835" class="line" id="l835"> 835</a> static void
+<a href="#l836" class="line" id="l836"> 836</a> xmldataentity(XMLParser *p, const char *data, size_t datalen)
+<a href="#l837" class="line" id="l837"> 837</a> {
+<a href="#l838" class="line" id="l838"> 838</a> char buf[8];
+<a href="#l839" class="line" id="l839"> 839</a> int len;
+<a href="#l840" class="line" id="l840"> 840</a>
+<a href="#l841" class="line" id="l841"> 841</a> if (!ctx.field)
+<a href="#l842" class="line" id="l842"> 842</a> return;
+<a href="#l843" class="line" id="l843"> 843</a>
+<a href="#l844" class="line" id="l844"> 844</a> /* try to translate entity, else just pass as data to
+<a href="#l845" class="line" id="l845"> 845</a> * xmldata handler. */
+<a href="#l846" class="line" id="l846"> 846</a> if ((len = xml_entitytostr(data, buf, sizeof(buf))) > 0)
+<a href="#l847" class="line" id="l847"> 847</a> xmldata(p, buf, (size_t)len);
+<a href="#l848" class="line" id="l848"> 848</a> else
+<a href="#l849" class="line" id="l849"> 849</a> xmldata(p, data, datalen);
+<a href="#l850" class="line" id="l850"> 850</a> }
+<a href="#l851" class="line" id="l851"> 851</a>
+<a href="#l852" class="line" id="l852"> 852</a> static void
+<a href="#l853" class="line" id="l853"> 853</a> xmltagstart(XMLParser *p, const char *t, size_t tl)
+<a href="#l854" class="line" id="l854"> 854</a> {
+<a href="#l855" class="line" id="l855"> 855</a> const FeedTag *f;
+<a href="#l856" class="line" id="l856"> 856</a>
+<a href="#l857" class="line" id="l857"> 857</a> if (ISINCONTENT(ctx)) {
+<a href="#l858" class="line" id="l858"> 858</a> if (ctx.contenttype == ContentTypeHTML) {
+<a href="#l859" class="line" id="l859"> 859</a> ctx.attrcount = 0;
+<a href="#l860" class="line" id="l860"> 860</a> xmldata(p, "<", 1);
+<a href="#l861" class="line" id="l861"> 861</a> xmldata(p, t, tl);
+<a href="#l862" class="line" id="l862"> 862</a> }
+<a href="#l863" class="line" id="l863"> 863</a> return;
+<a href="#l864" class="line" id="l864"> 864</a> }
+<a href="#l865" class="line" id="l865"> 865</a>
+<a href="#l866" class="line" id="l866"> 866</a> /* start of RSS or Atom item / entry */
+<a href="#l867" class="line" id="l867"> 867</a> if (ctx.feedtype == FeedTypeNone) {
+<a href="#l868" class="line" id="l868"> 868</a> if (istag(t, tl, STRP("entry")))
+<a href="#l869" class="line" id="l869"> 869</a> ctx.feedtype = FeedTypeAtom;
+<a href="#l870" class="line" id="l870"> 870</a> else if (istag(t, tl, STRP("item")))
+<a href="#l871" class="line" id="l871"> 871</a> ctx.feedtype = FeedTypeRSS;
+<a href="#l872" class="line" id="l872"> 872</a> return;
+<a href="#l873" class="line" id="l873"> 873</a> }
+<a href="#l874" class="line" id="l874"> 874</a>
+<a href="#l875" class="line" id="l875"> 875</a> /* field tagid already set or nested tags. */
+<a href="#l876" class="line" id="l876"> 876</a> if (ctx.tag.id) {
+<a href="#l877" class="line" id="l877"> 877</a> /* nested <author><name> for Atom */
+<a href="#l878" class="line" id="l878"> 878</a> if (ctx.tag.id == AtomTagAuthor &&
+<a href="#l879" class="line" id="l879"> 879</a> istag(t, tl, STRP("name"))) {
+<a href="#l880" class="line" id="l880"> 880</a> memcpy(&(ctx.tag), &atomtagauthorname, sizeof(ctx.tag));
+<a href="#l881" class="line" id="l881"> 881</a> } else {
+<a href="#l882" class="line" id="l882"> 882</a> return; /* other nested tags are not allowed: return */
+<a href="#l883" class="line" id="l883"> 883</a> }
+<a href="#l884" class="line" id="l884"> 884</a> }
+<a href="#l885" class="line" id="l885"> 885</a>
+<a href="#l886" class="line" id="l886"> 886</a> /* in item */
+<a href="#l887" class="line" id="l887"> 887</a> if (ctx.tag.id == TagUnknown) {
+<a href="#l888" class="line" id="l888"> 888</a> if (!(f = gettag(ctx.feedtype, t, tl)))
+<a href="#l889" class="line" id="l889"> 889</a> f = &notag;
+<a href="#l890" class="line" id="l890"> 890</a> memcpy(&(ctx.tag), f, sizeof(ctx.tag));
+<a href="#l891" class="line" id="l891"> 891</a> }
+<a href="#l892" class="line" id="l892"> 892</a>
+<a href="#l893" class="line" id="l893"> 893</a> ctx.iscontenttag = (fieldmap[ctx.tag.id] == FeedFieldContent);
+<a href="#l894" class="line" id="l894"> 894</a> string_clear(&attrispermalink);
+<a href="#l895" class="line" id="l895"> 895</a> string_clear(&attrrel);
+<a href="#l896" class="line" id="l896"> 896</a> string_clear(&attrtype);
+<a href="#l897" class="line" id="l897"> 897</a> }
+<a href="#l898" class="line" id="l898"> 898</a>
+<a href="#l899" class="line" id="l899"> 899</a> static void
+<a href="#l900" class="line" id="l900"> 900</a> xmltagstartparsed(XMLParser *p, const char *t, size_t tl, int isshort)
+<a href="#l901" class="line" id="l901"> 901</a> {
+<a href="#l902" class="line" id="l902"> 902</a> enum TagId tagid;
+<a href="#l903" class="line" id="l903"> 903</a>
+<a href="#l904" class="line" id="l904"> 904</a> if (ISINCONTENT(ctx)) {
+<a href="#l905" class="line" id="l905"> 905</a> if (ctx.contenttype == ContentTypeHTML) {
+<a href="#l906" class="line" id="l906"> 906</a> if (isshort)
+<a href="#l907" class="line" id="l907"> 907</a> xmldata(p, "/>", 2);
+<a href="#l908" class="line" id="l908"> 908</a> else
+<a href="#l909" class="line" id="l909"> 909</a> xmldata(p, ">", 1);
+<a href="#l910" class="line" id="l910"> 910</a> }
+<a href="#l911" class="line" id="l911"> 911</a> return;
+<a href="#l912" class="line" id="l912"> 912</a> }
+<a href="#l913" class="line" id="l913"> 913</a>
+<a href="#l914" class="line" id="l914"> 914</a> /* set tag type based on its attribute value */
+<a href="#l915" class="line" id="l915"> 915</a> if (ctx.tag.id == RSSTagGuid) {
+<a href="#l916" class="line" id="l916"> 916</a> /* if empty the default is "true" */
+<a href="#l917" class="line" id="l917"> 917</a> if (!attrispermalink.len ||
+<a href="#l918" class="line" id="l918"> 918</a> isattr(attrispermalink.data, attrispermalink.len, STRP("true")))
+<a href="#l919" class="line" id="l919"> 919</a> ctx.tag.id = RSSTagGuidPermalinkTrue;
+<a href="#l920" class="line" id="l920"> 920</a> else
+<a href="#l921" class="line" id="l921"> 921</a> ctx.tag.id = RSSTagGuidPermalinkFalse;
+<a href="#l922" class="line" id="l922"> 922</a> } else if (ctx.tag.id == AtomTagLink) {
+<a href="#l923" class="line" id="l923"> 923</a> /* empty or "alternate": other types could be
+<a href="#l924" class="line" id="l924"> 924</a> "enclosure", "related", "self" or "via" */
+<a href="#l925" class="line" id="l925"> 925</a> if (!attrrel.len || isattr(attrrel.data, attrrel.len, STRP("alternate")))
+<a href="#l926" class="line" id="l926"> 926</a> ctx.tag.id = AtomTagLinkAlternate;
+<a href="#l927" class="line" id="l927"> 927</a> else if (isattr(attrrel.data, attrrel.len, STRP("enclosure")))
+<a href="#l928" class="line" id="l928"> 928</a> ctx.tag.id = AtomTagLinkEnclosure;
+<a href="#l929" class="line" id="l929"> 929</a> else
+<a href="#l930" class="line" id="l930"> 930</a> ctx.tag.id = AtomTagLink; /* unknown */
+<a href="#l931" class="line" id="l931"> 931</a> }
+<a href="#l932" class="line" id="l932"> 932</a>
+<a href="#l933" class="line" id="l933"> 933</a> tagid = ctx.tag.id;
+<a href="#l934" class="line" id="l934"> 934</a>
+<a href="#l935" class="line" id="l935"> 935</a> /* map tag type to field: unknown or lesser priority is ignored,
+<a href="#l936" class="line" id="l936"> 936</a> when tags of the same type are repeated only the first is used. */
+<a href="#l937" class="line" id="l937"> 937</a> if (fieldmap[tagid] == -1 ||
+<a href="#l938" class="line" id="l938"> 938</a> (!ISFEEDFIELDMULTI(fieldmap[tagid]) &&
+<a href="#l939" class="line" id="l939"> 939</a> tagid <= ctx.fields[fieldmap[tagid]].tagid)) {
+<a href="#l940" class="line" id="l940"> 940</a> return;
+<a href="#l941" class="line" id="l941"> 941</a> }
+<a href="#l942" class="line" id="l942"> 942</a>
+<a href="#l943" class="line" id="l943"> 943</a> if (ctx.iscontenttag) {
+<a href="#l944" class="line" id="l944"> 944</a> ctx.iscontent = 1;
+<a href="#l945" class="line" id="l945"> 945</a> ctx.iscontenttag = 0;
+<a href="#l946" class="line" id="l946"> 946</a>
+<a href="#l947" class="line" id="l947"> 947</a> /* detect content-type based on type attribute */
+<a href="#l948" class="line" id="l948"> 948</a> if (attrtype.len) {
+<a href="#l949" class="line" id="l949"> 949</a> if (isattr(attrtype.data, attrtype.len, STRP("html")) ||
+<a href="#l950" class="line" id="l950"> 950</a> isattr(attrtype.data, attrtype.len, STRP("xhtml")) ||
+<a href="#l951" class="line" id="l951"> 951</a> isattr(attrtype.data, attrtype.len, STRP("text/html")) ||
+<a href="#l952" class="line" id="l952"> 952</a> isattr(attrtype.data, attrtype.len, STRP("text/xhtml")) ||
+<a href="#l953" class="line" id="l953"> 953</a> isattr(attrtype.data, attrtype.len, STRP("application/xhtml+xml")))
+<a href="#l954" class="line" id="l954"> 954</a> ctx.contenttype = ContentTypeHTML;
+<a href="#l955" class="line" id="l955"> 955</a> else /* unknown: handle as base64 text data */
+<a href="#l956" class="line" id="l956"> 956</a> ctx.contenttype = ContentTypePlain;
+<a href="#l957" class="line" id="l957"> 957</a> } else {
+<a href="#l958" class="line" id="l958"> 958</a> /* default content-type */
+<a href="#l959" class="line" id="l959"> 959</a> if (tagid == RSSTagContentEncoded || tagid == RSSTagDescription)
+<a href="#l960" class="line" id="l960"> 960</a> ctx.contenttype = ContentTypeHTML;
+<a href="#l961" class="line" id="l961"> 961</a> else
+<a href="#l962" class="line" id="l962"> 962</a> ctx.contenttype = ContentTypePlain;
+<a href="#l963" class="line" id="l963"> 963</a> }
+<a href="#l964" class="line" id="l964"> 964</a> }
+<a href="#l965" class="line" id="l965"> 965</a>
+<a href="#l966" class="line" id="l966"> 966</a> ctx.field = &(ctx.fields[fieldmap[tagid]].str);
+<a href="#l967" class="line" id="l967"> 967</a> ctx.fields[fieldmap[tagid]].tagid = tagid;
+<a href="#l968" class="line" id="l968"> 968</a>
+<a href="#l969" class="line" id="l969"> 969</a> /* clear field if it is overwritten (with a priority order) for the new
+<a href="#l970" class="line" id="l970"> 970</a> value, if the field can have multiple values then do not clear it. */
+<a href="#l971" class="line" id="l971"> 971</a> if (!ISFEEDFIELDMULTI(fieldmap[ctx.tag.id]))
+<a href="#l972" class="line" id="l972"> 972</a> string_clear(ctx.field);
+<a href="#l973" class="line" id="l973"> 973</a> }
+<a href="#l974" class="line" id="l974"> 974</a>
+<a href="#l975" class="line" id="l975"> 975</a> static void
+<a href="#l976" class="line" id="l976"> 976</a> xmltagend(XMLParser *p, const char *t, size_t tl, int isshort)
+<a href="#l977" class="line" id="l977"> 977</a> {
+<a href="#l978" class="line" id="l978"> 978</a> size_t i;
+<a href="#l979" class="line" id="l979"> 979</a>
+<a href="#l980" class="line" id="l980"> 980</a> if (ctx.feedtype == FeedTypeNone)
+<a href="#l981" class="line" id="l981"> 981</a> return;
+<a href="#l982" class="line" id="l982"> 982</a>
+<a href="#l983" class="line" id="l983"> 983</a> if (ISINCONTENT(ctx)) {
+<a href="#l984" class="line" id="l984"> 984</a> /* not a closed content field */
+<a href="#l985" class="line" id="l985"> 985</a> if (!istag(ctx.tag.name, ctx.tag.len, t, tl)) {
+<a href="#l986" class="line" id="l986"> 986</a> if (!isshort && ctx.contenttype == ContentTypeHTML) {
+<a href="#l987" class="line" id="l987"> 987</a> xmldata(p, "</", 2);
+<a href="#l988" class="line" id="l988"> 988</a> xmldata(p, t, tl);
+<a href="#l989" class="line" id="l989"> 989</a> xmldata(p, ">", 1);
+<a href="#l990" class="line" id="l990"> 990</a> }
+<a href="#l991" class="line" id="l991"> 991</a> return;
+<a href="#l992" class="line" id="l992"> 992</a> }
+<a href="#l993" class="line" id="l993"> 993</a> } else if (ctx.tag.id && istag(ctx.tag.name, ctx.tag.len, t, tl)) {
+<a href="#l994" class="line" id="l994"> 994</a> /* matched tag end: close it */
+<a href="#l995" class="line" id="l995"> 995</a> /* copy also to the link field if the attribute isPermaLink="true"
+<a href="#l996" class="line" id="l996"> 996</a> and it is not set by a tag with higher priority. */
+<a href="#l997" class="line" id="l997"> 997</a> if (ctx.tag.id == RSSTagGuidPermalinkTrue && ctx.field &&
+<a href="#l998" class="line" id="l998"> 998</a> ctx.tag.id > ctx.fields[FeedFieldLink].tagid) {
+<a href="#l999" class="line" id="l999"> 999</a> string_clear(&ctx.fields[FeedFieldLink].str);
+<a href="#l1000" class="line" id="l1000"> 1000</a> string_append(&ctx.fields[FeedFieldLink].str,
+<a href="#l1001" class="line" id="l1001"> 1001</a> ctx.field->data, ctx.field->len);
+<a href="#l1002" class="line" id="l1002"> 1002</a> ctx.fields[FeedFieldLink].tagid = ctx.tag.id;
+<a href="#l1003" class="line" id="l1003"> 1003</a> }
+<a href="#l1004" class="line" id="l1004"> 1004</a> } else if (!ctx.tag.id && ((ctx.feedtype == FeedTypeAtom &&
+<a href="#l1005" class="line" id="l1005"> 1005</a> istag(t, tl, STRP("entry"))) || /* Atom */
+<a href="#l1006" class="line" id="l1006"> 1006</a> (ctx.feedtype == FeedTypeRSS &&
+<a href="#l1007" class="line" id="l1007"> 1007</a> istag(t, tl, STRP("item"))))) /* RSS */
+<a href="#l1008" class="line" id="l1008"> 1008</a> {
+<a href="#l1009" class="line" id="l1009"> 1009</a> /* end of RSS or Atom entry / item */
+<a href="#l1010" class="line" id="l1010"> 1010</a> printfields();
+<a href="#l1011" class="line" id="l1011"> 1011</a>
+<a href="#l1012" class="line" id="l1012"> 1012</a> /* clear strings */
+<a href="#l1013" class="line" id="l1013"> 1013</a> for (i = 0; i < FeedFieldLast; i++) {
+<a href="#l1014" class="line" id="l1014"> 1014</a> string_clear(&ctx.fields[i].str);
+<a href="#l1015" class="line" id="l1015"> 1015</a> ctx.fields[i].tagid = TagUnknown;
+<a href="#l1016" class="line" id="l1016"> 1016</a> }
+<a href="#l1017" class="line" id="l1017"> 1017</a> ctx.contenttype = ContentTypeNone;
+<a href="#l1018" class="line" id="l1018"> 1018</a> /* allow parsing of Atom and RSS concatenated in one XML stream. */
+<a href="#l1019" class="line" id="l1019"> 1019</a> ctx.feedtype = FeedTypeNone;
+<a href="#l1020" class="line" id="l1020"> 1020</a> } else {
+<a href="#l1021" class="line" id="l1021"> 1021</a> return; /* not end of field */
+<a href="#l1022" class="line" id="l1022"> 1022</a> }
+<a href="#l1023" class="line" id="l1023"> 1023</a>
+<a href="#l1024" class="line" id="l1024"> 1024</a> /* temporary string: for fields that cannot be processed
+<a href="#l1025" class="line" id="l1025"> 1025</a> directly and need more context, for example by its tag
+<a href="#l1026" class="line" id="l1026"> 1026</a> attributes, like the Atom link rel="alternate|enclosure". */
+<a href="#l1027" class="line" id="l1027"> 1027</a> if (tmpstr.len && ctx.field) {
+<a href="#l1028" class="line" id="l1028"> 1028</a> if (ISFEEDFIELDMULTI(fieldmap[ctx.tag.id])) {
+<a href="#l1029" class="line" id="l1029"> 1029</a> if (ctx.field->len)
+<a href="#l1030" class="line" id="l1030"> 1030</a> string_append(ctx.field, FieldMultiSeparator, 1);
+<a href="#l1031" class="line" id="l1031"> 1031</a> string_append(ctx.field, tmpstr.data, tmpstr.len);
+<a href="#l1032" class="line" id="l1032"> 1032</a> } else {
+<a href="#l1033" class="line" id="l1033"> 1033</a> string_clear(ctx.field);
+<a href="#l1034" class="line" id="l1034"> 1034</a> string_append(ctx.field, tmpstr.data, tmpstr.len);
+<a href="#l1035" class="line" id="l1035"> 1035</a> }
+<a href="#l1036" class="line" id="l1036"> 1036</a> }
+<a href="#l1037" class="line" id="l1037"> 1037</a>
+<a href="#l1038" class="line" id="l1038"> 1038</a> /* close field */
+<a href="#l1039" class="line" id="l1039"> 1039</a> string_clear(&tmpstr); /* reuse and clear temporary string */
+<a href="#l1040" class="line" id="l1040"> 1040</a>
+<a href="#l1041" class="line" id="l1041"> 1041</a> if (ctx.tag.id == AtomTagAuthorName)
+<a href="#l1042" class="line" id="l1042"> 1042</a> memcpy(&(ctx.tag), &atomtagauthor, sizeof(ctx.tag)); /* outer tag */
+<a href="#l1043" class="line" id="l1043"> 1043</a> else
+<a href="#l1044" class="line" id="l1044"> 1044</a> memcpy(&(ctx.tag), &notag, sizeof(ctx.tag));
+<a href="#l1045" class="line" id="l1045"> 1045</a>
+<a href="#l1046" class="line" id="l1046"> 1046</a> ctx.iscontent = 0;
+<a href="#l1047" class="line" id="l1047"> 1047</a> ctx.field = NULL;
+<a href="#l1048" class="line" id="l1048"> 1048</a> }
+<a href="#l1049" class="line" id="l1049"> 1049</a>
+<a href="#l1050" class="line" id="l1050"> 1050</a> int
+<a href="#l1051" class="line" id="l1051"> 1051</a> main(int argc, char *argv[])
+<a href="#l1052" class="line" id="l1052"> 1052</a> {
+<a href="#l1053" class="line" id="l1053"> 1053</a> if (pledge("stdio", NULL) == -1)
+<a href="#l1054" class="line" id="l1054"> 1054</a> err(1, "pledge");
+<a href="#l1055" class="line" id="l1055"> 1055</a>
+<a href="#l1056" class="line" id="l1056"> 1056</a> if (argc > 1) {
+<a href="#l1057" class="line" id="l1057"> 1057</a> if (uri_parse(argv[1], &baseuri) != -1 && baseuri.proto[0])
+<a href="#l1058" class="line" id="l1058"> 1058</a> baseurl = argv[1];
+<a href="#l1059" class="line" id="l1059"> 1059</a> else
+<a href="#l1060" class="line" id="l1060"> 1060</a> errx(1, "baseurl incorrect or too long");
+<a href="#l1061" class="line" id="l1061"> 1061</a> }
+<a href="#l1062" class="line" id="l1062"> 1062</a>
+<a href="#l1063" class="line" id="l1063"> 1063</a> memcpy(&(ctx.tag), &notag, sizeof(ctx.tag));
+<a href="#l1064" class="line" id="l1064"> 1064</a>
+<a href="#l1065" class="line" id="l1065"> 1065</a> parser.xmlattr = xmlattr;
+<a href="#l1066" class="line" id="l1066"> 1066</a> parser.xmlattrentity = xmlattrentity;
+<a href="#l1067" class="line" id="l1067"> 1067</a> parser.xmlattrend = xmlattrend;
+<a href="#l1068" class="line" id="l1068"> 1068</a> parser.xmlattrstart = xmlattrstart;
+<a href="#l1069" class="line" id="l1069"> 1069</a> parser.xmlcdata = xmldata;
+<a href="#l1070" class="line" id="l1070"> 1070</a> parser.xmldata = xmldata;
+<a href="#l1071" class="line" id="l1071"> 1071</a> parser.xmldataentity = xmldataentity;
+<a href="#l1072" class="line" id="l1072"> 1072</a> parser.xmltagend = xmltagend;
+<a href="#l1073" class="line" id="l1073"> 1073</a> parser.xmltagstart = xmltagstart;
+<a href="#l1074" class="line" id="l1074"> 1074</a> parser.xmltagstartparsed = xmltagstartparsed;
+<a href="#l1075" class="line" id="l1075"> 1075</a>
+<a href="#l1076" class="line" id="l1076"> 1076</a> /* NOTE: GETNEXT is defined in xml.h for inline optimization */
+<a href="#l1077" class="line" id="l1077"> 1077</a> xml_parse(&parser);
+<a href="#l1078" class="line" id="l1078"> 1078</a>
+<a href="#l1079" class="line" id="l1079"> 1079</a> checkfileerror(stdin, "<stdin>", 'r');
+<a href="#l1080" class="line" id="l1080"> 1080</a> checkfileerror(stdout, "<stdout>", 'w');
+<a href="#l1081" class="line" id="l1081"> 1081</a>
+<a href="#l1082" class="line" id="l1082"> 1082</a> return 0;
+<a href="#l1083" class="line" id="l1083"> 1083</a> }
+</pre>
+</div>
+</body>
+</html>
(DIR) diff --git a/tests/indent.html b/tests/indent.html
@@ -0,0 +1,47 @@
+<ul>
+<li>test</li>
+<li>test2</li>
+<li>test3</li>
+</ul>
+
+<ol>
+<li>test</li>
+<li>test2</li>
+<li>test3</li>
+<li>test</li>
+<li>test</li>
+<li>test</li>
+<li>test</li>
+<li>test</li>
+<li>test</li>
+<li>test</li>
+<li>test</li>
+<li>test</li>
+<li>test</li>
+</ol>
+
+<ul>
+<li><div>test</div><div>another line</div></li>
+<li><div>test</div><div>another line</div><div>and another</div></li>
+<li><div>test</div><div>another line</div></li>
+</ul>
+
+<h1>header 1</h1>
+<p>some text</p>
+<p>some text</p>
+
+<table>
+<tr><td>text</td><td><hr/></td></tr>
+<tr><td>text</td><td><hr/>some text</td></tr>
+<tr><td>text</td><td><div><hr/></div></td></tr>
+<tr><td>text</td><td><div><hr/>some text</div></td></tr>
+</table>
+
+<ul>
+<li>test<hr/></li>
+<li>test2<hr/>text</li>
+<li>test3</li>
+<li><img alt="alt text" /></li>
+</ul>
+
+<p><img alt="alt text" /></p>
(DIR) diff --git a/tests/pre.html b/tests/pre.html
@@ -1,19 +1,27 @@
-<p>pre:</p>
-
-<pre>a
-b
-c</pre>
-
-
-<p>code:</p>
-
-<code>a
-b
-c</code>
-
-
-<p>code, pre:</p>
-
-<code><pre>a
-b
-c</pre></code>
+<pre id="blob">
+<a href="#l1" class="line" id="l1"> 1</a> #include <errno.h>
+<a href="#l2" class="line" id="l2"> 2</a> #include <stdint.h>
+<a href="#l3" class="line" id="l3"> 3</a> #include <stdio.h>
+<a href="#l4" class="line" id="l4"> 4</a> #include <stdlib.h>
+<a href="#l5" class="line" id="l5"> 5</a> #include <string.h>
+<a href="#l6" class="line" id="l6"> 6</a> #include <strings.h>
+<a href="#l7" class="line" id="l7"> 7</a>
+<a href="#l8" class="line" id="l8"> 8</a> #include "util.h"
+<a href="#l9" class="line" id="l9"> 9</a> #include "xml.h"
+<a href="#l10" class="line" id="l10"> 10</a>
+<a href="#l11" class="line" id="l11"> 11</a> #define ISINCONTENT(ctx) ((ctx).iscontent && !((ctx).iscontenttag))
+<a href="#l12" class="line" id="l12"> 12</a> #define ISCONTENTTAG(ctx) (!((ctx).iscontent) && (ctx).iscontenttag)
+<a href="#l13" class="line" id="l13"> 13</a>
+<a href="#l14" class="line" id="l14"> 14</a> /* these feed fields support multiple separated values */
+<a href="#l15" class="line" id="l15"> 15</a> #define ISFEEDFIELDMULTI(t) ((t) == FeedFieldCategory)
+<a href="#l16" class="line" id="l16"> 16</a>
+<a href="#l17" class="line" id="l17"> 17</a> /* string and byte-length */
+<a href="#l18" class="line" id="l18"> 18</a> #define STRP(s) s,sizeof(s)-1
+<a href="#l19" class="line" id="l19"> 19</a>
+<a href="#l20" class="line" id="l20"> 20</a> enum FeedType {
+<a href="#l21" class="line" id="l21"> 21</a> FeedTypeNone = 0,
+<a href="#l22" class="line" id="l22"> 22</a> FeedTypeRSS = 1,
+<a href="#l23" class="line" id="l23"> 23</a> FeedTypeAtom = 2
+<a href="#l24" class="line" id="l24"> 24</a> };
+<a href="#l25" class="line" id="l25"> 25</a>
+</pre>
(DIR) diff --git a/tests/pre_code.html b/tests/pre_code.html
@@ -0,0 +1,3 @@
+<pre><code>test
+test2
+test3</code></pre>