fix compilation error due to initial declaration in for loop being not compliant...
[monky] / src / prss.c
1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
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  * vim: ts=4 sw=4 noet ai cindent syntax=c
19  *
20  */
21
22 #include "conky.h"
23 #include "prss.h"
24 #include "logging.h"
25 #include <libxml/parser.h>
26 #include <libxml/tree.h>
27
28 #ifndef PARSE_OPTIONS
29 #define PARSE_OPTIONS 0
30 #endif
31
32 void prss_parse_doc(PRSS *result, xmlDocPtr doc);
33
34 void prss_parse_data(void *result, const char *xml_data)
35 {
36         PRSS *data = (PRSS*)result;
37
38         xmlDocPtr doc = xmlReadMemory(xml_data, strlen(xml_data), "", NULL,
39                 PARSE_OPTIONS);
40
41         if (!doc) {
42                 return;
43         }
44
45         prss_parse_doc(data, doc);
46         xmlFreeDoc(doc);
47 }
48
49 void free_rss_items(PRSS *data)
50 {
51         int i;
52
53         for (i = 0; i < data->item_count; i++) {
54 #define CLEAR(a) if (data->items[i].a) { free(data->items[i].a); data->items[i].a = 0; }
55                 CLEAR(title);
56                 CLEAR(link);
57                 CLEAR(description);
58                 CLEAR(category);
59                 CLEAR(pubDate);
60                 CLEAR(guid);
61 #undef CLEAR
62         }
63         free(data->items);
64         data->item_count = 0;
65         data->items = 0;
66 }
67
68 void prss_free(PRSS *data)
69 {
70         if (!data) {
71                 return;
72         }
73         if (data->version) {
74                 free(data->version);
75                 data->version = 0;
76         }
77         if (data->items) {
78                 free_rss_items(data);
79         }
80         data->version = 0;
81         data->items = 0;
82 #define CLEAR(a) if (data->a) { free(data->a); data->a = 0; }
83         CLEAR(title);
84         CLEAR(link);
85         CLEAR(description);
86         CLEAR(language);
87         CLEAR(pubDate);
88         CLEAR(lastBuildDate);
89         CLEAR(generator);
90         CLEAR(docs);
91         CLEAR(managingEditor);
92         CLEAR(webMaster);
93         CLEAR(copyright);
94         CLEAR(ttl);
95 #undef CLEAR
96 }
97
98 static inline void prss_null_item(PRSS_Item *i)
99 {
100         memset(i, 0, sizeof(PRSS_Item));
101 }
102
103 static inline void read_item(PRSS_Item *res, xmlNodePtr data)
104 {
105         prss_null_item(res);
106
107         for (; data; data = data->next) {
108                 xmlNodePtr child;
109
110                 if (data->type != XML_ELEMENT_NODE) {
111                         continue;
112                 }
113                 child = data->children;
114
115                 if (!child) {
116                         continue;
117                 }
118
119 #define ASSIGN(a) if (strcasecmp((const char*)data->name, #a) == EQUAL) { \
120                         if (res->a) free(res->a); \
121                         res->a = strdup((const char*)child->content); \
122                         continue; \
123                 }
124                 ASSIGN(title);
125                 ASSIGN(link);
126                 ASSIGN(description);
127                 ASSIGN(category);
128                 ASSIGN(pubDate);
129                 ASSIGN(guid);
130 #undef ASSIGN
131         }
132 }
133 static inline void read_element(PRSS *res, xmlNodePtr n)
134 {
135         xmlNodePtr child;
136
137         if (n->type != XML_ELEMENT_NODE) {
138                 return;
139         }
140         child = n->children;
141
142         if (!child) {
143                 return;
144         }
145
146 #define ASSIGN(a) if (strcasecmp((const char*)n->name, #a) == EQUAL) { \
147                 if (res->a) free(res->a); \
148                 res->a = strdup((const char*)child->content); \
149                 return; \
150         }
151         ASSIGN(title);
152         ASSIGN(link);
153         ASSIGN(description);
154         ASSIGN(language);
155         ASSIGN(pubDate);
156         ASSIGN(lastBuildDate);
157         ASSIGN(generator);
158         ASSIGN(docs);
159         ASSIGN(managingEditor);
160         ASSIGN(webMaster);
161         ASSIGN(copyright);
162         ASSIGN(ttl);
163 #undef ASSIGN
164         if (!strcasecmp((const char*)n->name, "item")) {
165                 read_item(&res->items[res->item_count++], n->children);
166         }
167 }
168
169 static inline int parse_rss_2_0(PRSS *res, xmlNodePtr root)
170 {
171         xmlNodePtr channel = root->children;
172         xmlNodePtr n;
173         int items = 0;
174
175         DBGP("parsing rss 2.0 or <1 doc");
176
177         while (channel && (channel->type != XML_ELEMENT_NODE
178                                 || strcmp((const char *) channel->name, "channel"))) {
179                 channel = channel->next;
180         }
181         if (!channel) {
182                 return 0;
183         }
184
185         for (n = channel->children; n; n = n->next) {
186                 if (n->type == XML_ELEMENT_NODE &&
187                                 !strcmp((const char *) n->name, "item")) {
188                         ++items;
189                 }
190         }
191
192         if (res->version) free(res->version);
193         res->version = strndup("2.0", text_buffer_size);
194         if (res->items) free_rss_items(res);
195         res->items = malloc(items * sizeof(PRSS_Item));
196         res->item_count = 0;
197
198         for (n = channel->children; n; n = n->next) {
199                 read_element(res, n);
200         }
201
202         return 1;
203 }
204 static inline int parse_rss_1_0(PRSS *res, xmlNodePtr root)
205 {
206         int items = 0;
207         xmlNodePtr n;
208
209         DBGP("parsing rss 1.0 doc");
210
211         for (n = root->children; n; n = n->next) {
212                 if (n->type == XML_ELEMENT_NODE) {
213                         if (!strcmp((const char *) n->name, "item")) {
214                                 ++items;
215                         } else if (!strcmp((const char *) n->name, "channel")) {
216                                 xmlNodePtr i;
217
218                                 for (i = n->children; i; i = i->next) {
219                                         read_element(res, i);
220                                 }
221                         }
222                 }
223         }
224
225         if (res->version) free(res->version);
226         res->version = strndup("1.0", text_buffer_size);
227         if (res->items) free_rss_items(res);
228         res->items = malloc(items * sizeof(PRSS_Item));
229         res->item_count = 0;
230
231         for (n = root->children; n; n = n->next) {
232                 if (n->type == XML_ELEMENT_NODE &&
233                                 !strcmp((const char *) n->name, "item")) {
234                         read_item(&res->items[res->item_count++], n->children);
235                 }
236         }
237
238         return 1;
239 }
240
241 void prss_parse_doc(PRSS *result, xmlDocPtr doc)
242 {
243         xmlNodePtr root = xmlDocGetRootElement(doc);
244
245         do {
246                 if (root->type == XML_ELEMENT_NODE) {
247                         if (!strcmp((const char *) root->name, "RDF")) {
248                                 // RSS 1.0 document
249                                 parse_rss_1_0(result, root);
250                                 return;
251                         } else if (!strcmp((const char *) root->name, "rss")) {
252                                 // RSS 2.0 or <1.0 document
253                                 parse_rss_2_0(result, root);
254                                 return;
255                         }
256                 }
257                 root = root->next;
258         } while (root);
259         return;
260 }