jf2atom.c - jfconvert - JSON Feed (subset) to sfeed or Atom converter | |
git clone git://git.codemadness.org/jfconvert | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
jf2atom.c (7078B) | |
--- | |
1 #include <errno.h> | |
2 #include <stdio.h> | |
3 #include <stdlib.h> | |
4 #include <string.h> | |
5 | |
6 #ifdef __OpenBSD__ | |
7 #include <unistd.h> | |
8 #else | |
9 #define pledge(a,b) 0 | |
10 #endif | |
11 | |
12 #include "json.h" | |
13 | |
14 /* control-character in the ASCII range 0-127: compatible with UTF-8 */ | |
15 #define ISCNTRL(c) ((c) < ' ' || (c) == 0x7f) | |
16 | |
17 /* compare attributes case-sensitively */ | |
18 #define attrcmp strcmp | |
19 | |
20 static int itemisopen = 0, enclosureisopen = 0; | |
21 | |
22 /* Escape characters below as HTML 2.0 / XML 1.0. */ | |
23 void | |
24 xmlencode(const char *s, FILE *fp) | |
25 { | |
26 for (; *s; ++s) { | |
27 switch (*s) { | |
28 case '<': fputs("<", fp); break; | |
29 case '>': fputs(">", fp); break; | |
30 case '\'': fputs("'", fp); break; | |
31 case '&': fputs("&", fp); break; | |
32 case '"': fputs(""", fp); break; | |
33 default: putc(*s, fp); | |
34 } | |
35 } | |
36 } | |
37 | |
38 void | |
39 processnode(struct json_node *nodes, size_t depth, const char *value, si… | |
40 { | |
41 const char *outtag, *outtype, *outhref; | |
42 | |
43 /* feed / channel */ | |
44 if (depth == 2) { | |
45 if (nodes[0].type == JSON_TYPE_OBJECT) { | |
46 if (nodes[1].type == JSON_TYPE_STRING) { | |
47 if (!attrcmp(nodes[1].name, "title")) { | |
48 fputs("<title type=\"text\">", s… | |
49 xmlencode(value, stdout); | |
50 fputs("</title>\n", stdout); | |
51 } else if (!attrcmp(nodes[1].name, "home… | |
52 fputs("<link rel=\"alternate\" t… | |
53 xmlencode(value, stdout); | |
54 fputs("\" />\n", stdout); | |
55 } else if (!attrcmp(nodes[1].name, "desc… | |
56 fputs("<subtitle>", stdout); | |
57 xmlencode(value, stdout); | |
58 fputs("</subtitle>\n", stdout); | |
59 } | |
60 } | |
61 } | |
62 } | |
63 | |
64 /* item */ | |
65 if (depth == 3) { | |
66 if (nodes[0].type == JSON_TYPE_OBJECT && | |
67 nodes[1].type == JSON_TYPE_ARRAY && | |
68 nodes[2].type == JSON_TYPE_OBJECT && | |
69 !attrcmp(nodes[1].name, "items")) { | |
70 if (enclosureisopen) { | |
71 fputs(" />\n", stdout); | |
72 enclosureisopen = 0; | |
73 } | |
74 if (itemisopen) | |
75 fputs("</entry>\n", stdout); | |
76 fputs("<entry>\n", stdout); | |
77 itemisopen = 1; | |
78 } | |
79 } | |
80 | |
81 /* item attributes */ | |
82 if (depth == 4) { | |
83 if (nodes[0].type == JSON_TYPE_OBJECT && | |
84 nodes[1].type == JSON_TYPE_ARRAY && | |
85 nodes[2].type == JSON_TYPE_OBJECT && | |
86 !attrcmp(nodes[1].name, "items")) { | |
87 outtag = NULL; | |
88 outtype = NULL; | |
89 outhref = NULL; | |
90 | |
91 if (!attrcmp(nodes[3].name, "content_html")) { | |
92 outtag = "content"; | |
93 outtype = "html"; | |
94 } else if (!attrcmp(nodes[3].name, "content_text… | |
95 outtag = "content"; | |
96 outtype = "text"; | |
97 } else if (!attrcmp(nodes[3].name, "date_publish… | |
98 outtag = "published"; | |
99 } else if (!attrcmp(nodes[3].name, "date_modifie… | |
100 outtag = "updated"; | |
101 } else if (!attrcmp(nodes[3].name, "id")) { | |
102 outtag = "id"; | |
103 } else if (!attrcmp(nodes[3].name, "summary")) { | |
104 outtag = "summary"; | |
105 } else if (!attrcmp(nodes[3].name, "title")) { | |
106 outtag = "title"; | |
107 } else if (!attrcmp(nodes[3].name, "url")) { | |
108 outtag = "link"; | |
109 outhref = value; | |
110 value = NULL; | |
111 } | |
112 | |
113 if (outtag) { | |
114 fputs("\t<", stdout); | |
115 fputs(outtag, stdout); | |
116 if (outhref) { | |
117 fputs(" href=\"", stdout); | |
118 xmlencode(outhref, stdout); | |
119 fputs("\"", stdout); | |
120 } | |
121 if (outtype) { | |
122 fputs(" type=\"", stdout); | |
123 xmlencode(outtype, stdout); | |
124 fputs("\"", stdout); | |
125 } | |
126 fputs(">", stdout); | |
127 if (value) | |
128 xmlencode(value, stdout); | |
129 fputs("</", stdout); | |
130 fputs(outtag, stdout); | |
131 fputs(">\n", stdout); | |
132 } | |
133 } | |
134 } | |
135 | |
136 if (depth == 5) { | |
137 /* 1.0 author name */ | |
138 if (nodes[0].type == JSON_TYPE_OBJECT && | |
139 nodes[1].type == JSON_TYPE_ARRAY && | |
140 nodes[2].type == JSON_TYPE_OBJECT && | |
141 nodes[3].type == JSON_TYPE_OBJECT && | |
142 nodes[4].type == JSON_TYPE_STRING && | |
143 !attrcmp(nodes[1].name, "items") && | |
144 !attrcmp(nodes[3].name, "author") && | |
145 !attrcmp(nodes[4].name, "name")) { | |
146 fputs("\t<author><name>", stdout); | |
147 xmlencode(value, stdout); | |
148 fputs("</name></author>\n", stdout); | |
149 } | |
150 | |
151 /* tags / categories */ | |
152 if (nodes[0].type == JSON_TYPE_OBJECT && | |
153 nodes[1].type == JSON_TYPE_ARRAY && | |
154 nodes[2].type == JSON_TYPE_OBJECT && | |
155 nodes[3].type == JSON_TYPE_ARRAY && | |
156 nodes[4].type == JSON_TYPE_STRING && | |
157 !attrcmp(nodes[1].name, "items") && | |
158 !attrcmp(nodes[3].name, "tags")) { | |
159 fputs("\t<category term=\"", stdout); | |
160 xmlencode(value, stdout); | |
161 fputs("\" />\n", stdout); | |
162 } | |
163 | |
164 /* enclosure */ | |
165 if (nodes[0].type == JSON_TYPE_OBJECT && | |
166 nodes[1].type == JSON_TYPE_ARRAY && | |
167 nodes[2].type == JSON_TYPE_OBJECT && | |
168 nodes[3].type == JSON_TYPE_ARRAY && | |
169 nodes[4].type == JSON_TYPE_OBJECT && | |
170 !attrcmp(nodes[1].name, "items") && | |
171 !attrcmp(nodes[3].name, "attachments")) { | |
172 if (enclosureisopen) | |
173 fputs(" />\n", stdout); | |
174 fputs("\t<link rel=\"enclosure\"", stdout); | |
175 enclosureisopen = 1; | |
176 } | |
177 } | |
178 | |
179 if (depth == 6) { | |
180 /* 1.1 author name */ | |
181 if (nodes[0].type == JSON_TYPE_OBJECT && | |
182 nodes[1].type == JSON_TYPE_ARRAY && | |
183 nodes[2].type == JSON_TYPE_OBJECT && | |
184 nodes[3].type == JSON_TYPE_ARRAY && | |
185 nodes[4].type == JSON_TYPE_OBJECT && | |
186 nodes[5].type == JSON_TYPE_STRING && | |
187 !attrcmp(nodes[1].name, "items") && | |
188 !attrcmp(nodes[3].name, "authors") && | |
189 !attrcmp(nodes[5].name, "name")) { | |
190 fputs("\t<author><name>", stdout); | |
191 xmlencode(value, stdout); | |
192 fputs("</name></author>\n", stdout); | |
193 } | |
194 | |
195 /* enclosure attributes */ | |
196 if (nodes[0].type == JSON_TYPE_OBJECT && | |
197 nodes[1].type == JSON_TYPE_ARRAY && | |
198 nodes[2].type == JSON_TYPE_OBJECT && | |
199 nodes[3].type == JSON_TYPE_ARRAY && | |
200 nodes[4].type == JSON_TYPE_OBJECT && | |
201 (nodes[5].type == JSON_TYPE_STRING || nodes[5].type … | |
202 !attrcmp(nodes[1].name, "items") && | |
203 !attrcmp(nodes[3].name, "attachments")) { | |
204 if (!attrcmp(nodes[5].name, "url")) { | |
205 fputs(" href=\"", stdout); | |
206 xmlencode(value, stdout); | |
207 fputs("\"", stdout); | |
208 } else if (!attrcmp(nodes[5].name, "mime_type"))… | |
209 fputs(" type=\"", stdout); | |
210 xmlencode(value, stdout); | |
211 fputs("\"", stdout); | |
212 } else if (!attrcmp(nodes[5].name, "size_in_byte… | |
213 fputs(" length=\"", stdout); | |
214 xmlencode(value, stdout); | |
215 fputs("\"", stdout); | |
216 } | |
217 } | |
218 } | |
219 | |
220 if (ferror(stdout)) { | |
221 fprintf(stderr, "write error: <stdout>\n"); | |
222 exit(2); | |
223 } | |
224 } | |
225 | |
226 int | |
227 main(int argc, char *argv[]) | |
228 { | |
229 if (pledge("stdio", NULL) == -1) { | |
230 fprintf(stderr, "pledge stdio: %s\n", strerror(errno)); | |
231 return 1; | |
232 } | |
233 | |
234 fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" | |
235 "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n", stdout); | |
236 | |
237 switch (parsejson(processnode)) { | |
238 case JSON_ERROR_MEM: | |
239 fputs("error: cannot allocate enough memory\n", stderr); | |
240 return 2; | |
241 case JSON_ERROR_INVALID: | |
242 fputs("error: invalid JSON\n", stderr); | |
243 return 1; | |
244 } | |
245 | |
246 if (enclosureisopen) | |
247 fputs(" />\n", stdout); | |
248 if (itemisopen) | |
249 fputs("</entry>\n", stdout); | |
250 fputs("</feed>\n", stdout); | |
251 | |
252 if (ferror(stdin)) { | |
253 fprintf(stderr, "read error: <stdin>\n"); | |
254 return 2; | |
255 } | |
256 if (fflush(stdout) || ferror(stdout)) { | |
257 fprintf(stderr, "write error: <stdout>\n"); | |
258 return 2; | |
259 } | |
260 | |
261 return 0; | |
262 } |