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