jf2atom.c - jfconvert - JSON Feed (subset) to sfeed or Atom converter
(HTM) git clone git://git.codemadness.org/jfconvert
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) 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, size_t valuelen)
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\">", stdout);
49 xmlencode(value, stdout);
50 fputs("</title>\n", stdout);
51 } else if (!attrcmp(nodes[1].name, "home_page_url")) {
52 fputs("<link rel=\"alternate\" type=\"text/html\" href=\"", stdout);
53 xmlencode(value, stdout);
54 fputs("\" />\n", stdout);
55 } else if (!attrcmp(nodes[1].name, "description")) {
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_published")) {
98 outtag = "published";
99 } else if (!attrcmp(nodes[3].name, "date_modified")) {
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 == JSON_TYPE_NUMBER) &&
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_bytes")) {
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 }