Add vim modelines.
[monky] / src / prss.c
1 /*
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 <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         xmlDocPtr doc = xmlReadMemory(xml_data, strlen(xml_data), "", NULL,
37                 PARSE_OPTIONS);
38
39         if (!doc) {
40                 return;
41         }
42
43         prss_parse_doc(data, doc);
44 }
45
46 void prss_free(PRSS *data)
47 {
48         if (!data) {
49                 return;
50         }
51         xmlFreeDoc(data->_data);
52         free(data->version);
53         free(data->items);
54 }
55
56 static inline void prss_null(PRSS *p)
57 {
58         memset(p, 0, sizeof(PRSS));
59 }
60 static inline void prss_null_item(PRSS_Item *i)
61 {
62         memset(i, 0, sizeof(PRSS_Item));
63 }
64
65 static inline void read_item(PRSS_Item *res, xmlNodePtr data)
66 {
67         prss_null_item(res);
68
69         res->title = res->link = res->description = NULL;
70         for (; data; data = data->next) {
71                 xmlNodePtr child;
72                 const char *name;
73
74                 if (data->type != XML_ELEMENT_NODE) {
75                         continue;
76                 }
77                 child = data->children;
78
79                 if (!child) {
80                         continue;
81                 }
82
83                 name = (const char *)data->name;
84                 if (!strcasecmp(name, "title")) {
85                         res->title = (char *) child->content;
86                 } else if (!strcasecmp(name, "link")) {
87                         res->link = (char *) child->content;
88                 } else if (!strcasecmp(name, "description")) {
89                         res->description = (char *) child->content;
90                 } else if (!strcasecmp(name, "category")) {
91                         res->category = (char *) child->content;
92                 } else if (!strcasecmp(name, "pubDate")) {
93                         res->pubdate = (char *) child->content;
94                 } else if (!strcasecmp(name, "guid")) {
95                         res->guid = (char *) child->content;
96                 }
97         }
98 }
99 static inline void read_element(PRSS *res, xmlNodePtr n)
100 {
101         xmlNodePtr child;
102         const char *name;
103
104         if (n->type != XML_ELEMENT_NODE) {
105                 return;
106         }
107         child = n->children;
108
109         if (!child) {
110                 return;
111         }
112
113         name = (const char *)n->name;
114         if (!strcasecmp(name, "title")) {
115                 res->title = (char *) child->content;
116         } else if (!strcasecmp(name, "link")) {
117                 res->link = (char *) child->content;
118         } else if (!strcasecmp(name, "description")) {
119                 res->description = (char *) child->content;
120         } else if (!strcasecmp(name, "language")) {
121                 res->language = (char *) child->content;
122         } else if (!strcasecmp(name, "pubDate")) {
123                 res->pubdate = (char *) child->content;
124         } else if (!strcasecmp(name, "lastBuildDate")) {
125                 res->lastbuilddate = (char *) child->content;
126         } else if (!strcasecmp(name, "generator")) {
127                 res->generator = (char *) child->content;
128         } else if (!strcasecmp(name, "docs")) {
129                 res->docs = (char *) child->content;
130         } else if (!strcasecmp(name, "managingEditor")) {
131                 res->managingeditor = (char *) child->content;
132         } else if (!strcasecmp(name, "webMaster")) {
133                 res->webmaster = (char *) child->content;
134         } else if (!strcasecmp(name, "copyright")) {
135                 res->copyright = (char *) child->content;
136         } else if (!strcasecmp(name, "ttl")) {
137                 res->ttl = (char *) child->content;
138         } else if (!strcasecmp(name, "item")) {
139                 read_item(&res->items[res->item_count++], n->children);
140         }
141 }
142
143 static inline int parse_rss_2_0(PRSS *res, xmlNodePtr root)
144 {
145         xmlNodePtr channel = root->children;
146         xmlNodePtr n;
147         int items = 0;
148
149         while (channel && (channel->type != XML_ELEMENT_NODE
150                         || strcmp((const char *) channel->name, "channel"))) {
151                 channel = channel->next;
152         }
153         if (!channel) {
154                 return 0;
155         }
156
157         for (n = channel->children; n; n = n->next) {
158                 if (n->type == XML_ELEMENT_NODE &&
159                                 !strcmp((const char *) n->name, "item")) {
160                         ++items;
161                 }
162         }
163
164         res->version = strndup("2.0", text_buffer_size);
165         if (res->items) free(res->items);
166         res->items = malloc(items * sizeof(PRSS_Item));
167         res->item_count = 0;
168
169         for (n = channel->children; n; n = n->next) {
170                 read_element(res, n);
171         }
172
173         return 1;
174 }
175 static inline int parse_rss_1_0(PRSS *res, xmlNodePtr root)
176 {
177         int items = 0;
178         xmlNodePtr n;
179
180         for (n = root->children; n; n = n->next) {
181                 if (n->type == XML_ELEMENT_NODE) {
182                         if (!strcmp((const char *) n->name, "item")) {
183                                 ++items;
184                         } else if (!strcmp((const char *) n->name, "channel")) {
185                                 xmlNodePtr i;
186
187                                 for (i = n->children; i; i = i->next) {
188                                         read_element(res, i);
189                                 }
190                         }
191                 }
192         }
193
194         res->version = strndup("1.0", text_buffer_size);
195         if (res->items) free(res->items);
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 &&
201                                 !strcmp((const char *) n->name, "item")) {
202                         read_item(&res->items[res->item_count++], n->children);
203                 }
204         }
205
206         return 1;
207 }
208 static inline int parse_rss_0_9x(PRSS *res, xmlNodePtr root)
209 {
210         // almost same...
211         return parse_rss_2_0(res, root);
212 }
213
214 void prss_parse_doc(PRSS *result, xmlDocPtr doc)
215 {
216         /* FIXME: doc shouldn't be freed after failure when called explicitly from
217          * program! */
218
219         xmlNodePtr root = xmlDocGetRootElement(doc);
220
221         prss_null(result);
222         result->_data = doc;
223         do {
224                 if (root->type == XML_ELEMENT_NODE) {
225                         if (!strcmp((const char *) root->name, "RDF")) {
226                                 // RSS 1.0 document
227                                 parse_rss_1_0(result, root);
228                                 return;
229                         } else if (!strcmp((const char *) root->name, "rss")) {
230                                 // RSS 2.0 or <1.0 document
231                                 parse_rss_2_0(result, root);
232                                 return;
233                         }
234                 }
235                 root = root->next;
236         } while (root);
237         return;
238 }