Reformatted all code
[monky] / src / prss.c
1 /* $Id$
2  *
3  * Copyright (c) 2007 Mikko Sysikaski <mikko.sysikaski@gmail.com>
4  *                                        Toni Spets <toni.spets@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
17
18 #include <libxml/parser.h>
19 #include <libxml/tree.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include "prss.h"
24 #include "config.h"
25
26 #ifndef PARSE_OPTIONS
27 #define PARSE_OPTIONS 0
28 #endif
29
30 PRSS *prss_parse_doc(xmlDocPtr doc);
31
32 PRSS *prss_parse_data(const char *xml_data)
33 {
34         xmlDocPtr doc = xmlReadMemory(xml_data, strlen(xml_data), "", NULL,
35                 PARSE_OPTIONS);
36
37         if (!doc) {
38                 return NULL;
39         }
40
41         return prss_parse_doc(doc);
42 }
43
44 PRSS *prss_parse_file(const char *xml_file)
45 {
46         xmlDocPtr doc = xmlReadFile(xml_file, NULL, PARSE_OPTIONS);
47
48         if (!doc) {
49                 return NULL;
50         }
51
52         return prss_parse_doc(doc);
53 }
54
55 void prss_free(PRSS *data)
56 {
57         if (!data) {
58                 return;
59         }
60         xmlFreeDoc(data->_data);
61         free(data->version);
62         free(data->items);
63         free(data);
64 }
65
66 static inline void prss_null(PRSS *p)
67 {
68         memset(p, 0, sizeof(PRSS));
69 }
70 static inline void prss_null_item(PRSS_Item *i)
71 {
72         memset(i, 0, sizeof(PRSS_Item));
73 }
74
75 static inline void read_item(PRSS_Item *res, xmlNodePtr data)
76 {
77         prss_null_item(res);
78
79         res->title = res->link = res->description = NULL;
80         for (; data; data = data->next) {
81                 if (data->type != XML_ELEMENT_NODE) {
82                         continue;
83                 }
84                 xmlNodePtr child = data->children;
85
86                 if (!child) {
87                         continue;
88                 }
89
90                 if (!strcasecmp((char *) data->name, "title")) {
91                         res->title = (char *) child->content;
92                 } else if (!strcasecmp((char *) data->name, "link")) {
93                         res->link = (char *) child->content;
94                 } else if (!strcasecmp((char *) data->name, "description")) {
95                         res->description = (char *) child->content;
96                 } else if (!strcasecmp((char *) data->name, "category")) {
97                         res->category = (char *) child->content;
98                 } else if (!strcasecmp((char *) data->name, "pubDate")) {
99                         res->pubdate = (char *) child->content;
100                 } else if (!strcasecmp((char *) data->name, "guid")) {
101                         res->guid = (char *) child->content;
102                 }
103         }
104 }
105 static inline void read_element(PRSS *res, xmlNodePtr n)
106 {
107         if (n->type != XML_ELEMENT_NODE) {
108                 return;
109         }
110         xmlNodePtr child = n->children;
111
112         if (!child) {
113                 return;
114         }
115
116         if (!strcasecmp((char *) n->name, "title")) {
117                 res->title = (char *) child->content;
118         } else if (!strcasecmp((char *) n->name, "link")) {
119                 res->link = (char *) child->content;
120         } else if (!strcasecmp((char *) n->name, "description")) {
121                 res->description = (char *) child->content;
122         } else if (!strcasecmp((char *) n->name, "language")) {
123                 res->language = (char *) child->content;
124         } else if (!strcasecmp((char *) n->name, "pubDate")) {
125                 res->pubdate = (char *) child->content;
126         } else if (!strcasecmp((char *) n->name, "lastBuildDate")) {
127                 res->lastbuilddate = (char *) child->content;
128         } else if (!strcasecmp((char *) n->name, "generator")) {
129                 res->generator = (char *) child->content;
130         } else if (!strcasecmp((char *) n->name, "docs")) {
131                 res->docs = (char *) child->content;
132         } else if (!strcasecmp((char *) n->name, "managingEditor")) {
133                 res->managingeditor = (char *) child->content;
134         } else if (!strcasecmp((char *) n->name, "webMaster")) {
135                 res->webmaster = (char *) child->content;
136         } else if (!strcasecmp((char *) n->name, "copyright")) {
137                 res->copyright = (char *) child->content;
138         } else if (!strcasecmp((char *) n->name, "ttl")) {
139                 res->ttl = (char *) child->content;
140         } else if (!strcasecmp((char *) n->name, "item")) {
141                 read_item(&res->items[res->item_count++], n->children);
142         }
143 }
144
145 static inline int parse_rss_2_0(PRSS *res, xmlNodePtr root)
146 {
147         xmlNodePtr channel = root->children;
148
149         while (channel && (channel->type != XML_ELEMENT_NODE
150                         || strcmp((char *) channel->name, "channel"))) {
151                 channel = channel->next;
152         }
153         if (!channel) {
154                 return 0;
155         }
156
157         int items = 0;
158         xmlNodePtr n;
159
160         for (n = channel->children; n; n = n->next) {
161                 if (n->type == XML_ELEMENT_NODE && !strcmp((char *) n->name, "item")) {
162                         ++items;
163                 }
164         }
165
166         res->version = strdup("2.0");
167         res->items = malloc(items * sizeof(PRSS_Item));
168         res->item_count = 0;
169
170         for (n = channel->children; n; n = n->next) {
171                 read_element(res, n);
172         }
173
174         return 1;
175 }
176 static inline int parse_rss_1_0(PRSS *res, xmlNodePtr root)
177 {
178         int items = 0;
179         xmlNodePtr n;
180
181         for (n = root->children; n; n = n->next) {
182                 if (n->type == XML_ELEMENT_NODE) {
183                         if (!strcmp((char *) n->name, "item")) {
184                                 ++items;
185                         } else if (!strcmp((char *) n->name, "channel")) {
186                                 xmlNodePtr i;
187
188                                 for (i = n->children; i; i = i->next) {
189                                         read_element(res, i);
190                                 }
191                         }
192                 }
193         }
194
195         res->version = strdup("1.0");
196         res->items = malloc(items * sizeof(PRSS_Item));
197         res->item_count = 0;
198
199         for (n = root->children; n; n = n->next) {
200                 if (n->type == XML_ELEMENT_NODE && !strcmp((char *) n->name, "item")) {
201                         read_item(&res->items[res->item_count++], n->children);
202                 }
203         }
204
205         return 1;
206 }
207 static inline int parse_rss_0_9x(PRSS *res, xmlNodePtr root)
208 {
209         // almost same...
210         return parse_rss_2_0(res, root);
211 }
212
213 PRSS *prss_parse_doc(xmlDocPtr doc)
214 {
215         /* FIXME: doc shouldn't be freed after failure when called explicitly from
216          * program! */
217
218         xmlNodePtr root = xmlDocGetRootElement(doc);
219         PRSS *result = malloc(sizeof(PRSS));
220
221         prss_null(result);
222         result->_data = doc;
223         do {
224                 if (root->type == XML_ELEMENT_NODE) {
225                         if (!strcmp((char *) root->name, "RDF")) {
226                                 // RSS 1.0 document
227                                 if (!parse_rss_1_0(result, root)) {
228                                         free(result);
229                                         xmlFreeDoc(doc);
230                                         return NULL;
231                                 }
232                                 return result;
233                         } else if (!strcmp((char *) root->name, "rss")) {
234                                 // RSS 2.0 or <1.0 document
235                                 if (!parse_rss_2_0(result, root)) {
236                                         free(result);
237                                         xmlFreeDoc(doc);
238                                         return NULL;
239                                 }
240                                 return result;
241                         }
242                 }
243                 root = root->next;
244         } while (root);
245         free(result);
246         return NULL;
247 }