Project: Removed librest dependencies
[maevies] / examples / gmovies.c
1 /* Queries Google movies for the theaters of a city and parses
2  * the response using libxml2.
3  */
4
5 #include <libxml/HTMLparser.h>
6 #include <libxml/tree.h>
7 #include <libsoup/soup.h>
8 #include <glib.h>
9 #include <stdio.h>
10 #include <unistd.h>
11
12 #define BASE_URL "http://www.google.com/movies?near=%s"
13
14 typedef struct struct_theater
15 {
16         gchar* name;
17         gchar* address;
18         xmlNodePtr data; //points to the tree node with the theater info, including movies
19 } Theater;
20
21
22 typedef struct struct_showtime
23 {
24         gchar* time;
25 } ShowTime;
26
27
28 typedef struct struct_movie
29 {
30         gchar* title;
31         int rating;
32         gchar* info;
33         GList* showTimes;
34 } Movie;
35
36
37 /** Search for a sibling node by the name of the sibling node */
38 xmlNodePtr getSiblingByName(xmlNodePtr node, xmlChar* name, int nameLen)
39 {
40
41         xmlNodePtr sibling = node->next;
42         while((sibling != NULL) && (strncmp(sibling->name, name, nameLen) != 0)) {
43                 sibling = sibling->next;
44         }
45
46         return sibling;
47
48 }
49
50 /** Search a child node by its node name */
51 xmlNodePtr getChildByName(xmlNodePtr node, xmlChar* name, int nameLen)
52 {
53         return getSiblingByName(node->children, name, nameLen);
54 }
55
56 /** Search the first sibling node that has an attribute 'attr'
57  * with the value 'attrValue' */
58 xmlNodePtr getFirstSiblingByAttributeValue(
59         xmlNodePtr sibling, xmlChar* attr, xmlChar * attrValue, int attrValueLen)
60 {
61         xmlNodePtr tempNode = sibling;
62         xmlNodePtr result = NULL;
63
64         while ((tempNode != NULL) && (result == NULL)) {
65                 xmlChar* value = xmlGetProp(tempNode, attr);
66                 if (value != NULL) {
67                         if (strncmp(value, attrValue, attrValueLen) == 0) {
68                                 result = tempNode;
69                         }
70                         free(value);
71                 }
72                 tempNode = tempNode->next;
73         }
74
75         return result;
76 }
77
78 /** Search the first child node that has an attribute 'attr' with
79  * value 'attrValue' */
80 xmlNodePtr getFirstChildByAttributeValue(
81         xmlNodePtr node, xmlChar* attr, xmlChar * attrValue, int attrValueLen)
82 {
83         return getFirstSiblingByAttributeValue(node->children, attr, attrValue, attrValueLen);
84 }
85
86 /** Advances N sibling nodes in the node list */
87 xmlNodePtr jumpXSiblings(xmlNodePtr node, int siblings)
88 {
89         xmlNodePtr r = node;
90
91         int i = 0;
92         for(; i<siblings; i++) {
93                 r = r->next;
94         }
95
96         return r;
97 }
98
99
100 int childrenCount(xmlNodePtr node)
101 {
102         int i=0;
103         xmlNodePtr nav = node->children;
104         while(nav != NULL) {
105                 i++;
106                 nav = nav->next;
107         }
108
109         return i;
110 }
111
112
113 /** Search the <div> with the results and returns it, or NULL
114  * if it couldn't be found */
115 xmlNodePtr getMovieResultsDiv(xmlNodePtr root)
116 {
117         //<body>
118         xmlNodePtr body = getSiblingByName(root->children, "body", 4);
119
120         //<div id="results">
121         xmlNodePtr tempNode = getFirstChildByAttributeValue(body, "id", "results", 8);
122
123         if (tempNode == NULL) {
124                 //no results
125                 return NULL;
126         }
127
128         //<div id="movie_results">
129         tempNode = getFirstChildByAttributeValue(tempNode, "id", "movie_results", 14);
130
131         if (tempNode == NULL) {
132                 //no results
133                 return NULL;
134         }
135
136         //<div class="movie_results">
137         tempNode = getFirstChildByAttributeValue(tempNode, "class", "movie_results", 14);
138
139         if (tempNode == NULL) {
140                 //no results
141                 return NULL;
142         }
143
144         return tempNode;
145
146 }
147
148
149 /** Parses the results and returns a list with all the theaters.
150  * Theater info is parsed and returned as Theater 'objects", the movie
151  * info is not parsed */
152 GList* getTheaterList(xmlNodePtr movieResults)
153 {
154         //<div class="movie_results"><div class="theater"/><div class="theater"/>...
155         xmlNodePtr nav = movieResults->children;
156         xmlNodePtr tmp1, tmp2, tmp3 = NULL;
157
158         GList* resultList = NULL;
159
160         while(nav != NULL) {
161                 tmp1 = getFirstSiblingByAttributeValue(nav, "class", "theater", 7);
162
163                 /*
164                  *  <div class="theater">
165                  *              <div class="desc">
166                  *                      <div class="name"/><div class="info"/>
167                  *              </div>
168                  *              <div class="showtimes"/>
169                  * </div>
170                  */
171
172                 if (tmp1 != NULL) { //its theater data
173                         Theater* t = malloc(sizeof(Theater));
174                         tmp2 = getFirstChildByAttributeValue(tmp1, "class", "desc", 4);
175                         if (tmp2 != NULL) { //has desc
176                                 tmp3 = getFirstChildByAttributeValue(tmp2, "class", "name", 4);
177                                 if ((tmp3 != NULL) && (tmp3->children != NULL) && (tmp3->children->children != NULL)) { //<div class="name"><a><span/>
178                                         t->name = xmlNodeGetContent(tmp3->children->children);
179                                 }
180                                 tmp3 = getFirstChildByAttributeValue(tmp2, "class", "info", 4);
181                                 if (tmp3 != NULL) { //<div class="info">
182                                         t->address = xmlNodeGetContent(tmp3);
183                                 }
184                         }
185                         t->data = tmp1;
186                         resultList = g_list_append(resultList, t);
187                 }
188                 nav = nav->next;
189         }
190
191         return resultList;
192
193 }
194
195 GList* getShowtimes(gchar* times)
196 {
197         GList* resultList = NULL;
198
199         gchar** timesArray = g_strsplit(times, " ", -1);
200
201         int i = 0;
202         for(i=0; timesArray[i] != NULL; i++) {
203                 ShowTime* st = malloc(sizeof(ShowTime));
204                 st->time = g_strndup(timesArray[i], 5);
205                 resultList = g_list_append(resultList, st);
206         }
207
208         g_strfreev(timesArray);
209
210         return resultList;
211 }
212
213
214 GList* getMovieList(xmlNodePtr movieSideDiv)
215 {
216
217         xmlNodePtr nav = movieSideDiv->children;
218         xmlNodePtr tmp1, tmp2, tmp3 = NULL;
219
220         GList* resultList = NULL;
221
222         while(nav != NULL) {
223                 tmp1 = getFirstSiblingByAttributeValue(nav, "class", "movie", 5);
224                 if (tmp1 != NULL) { //is a movie
225                         Movie* m = malloc(sizeof(Movie));
226                         tmp2 = getFirstChildByAttributeValue(tmp1, "class", "name", 4);
227                         if ((tmp2 != NULL) && (tmp2->children != NULL) && (tmp2->children->children != NULL)) { //<div class="name"><a><span/>
228                                 m->title = xmlNodeGetContent(tmp2->children->children);
229                         }
230                         tmp2 = getFirstChildByAttributeValue(tmp1, "class", "info", 4);
231                         if (tmp2 != NULL) {
232                                 m->info = xmlNodeGetContent(tmp2);
233                         }
234                         tmp2 = getFirstChildByAttributeValue(tmp1, "class", "times", 5);
235                         if (tmp2 != NULL) {
236                                 gchar* showtimesStr = xmlNodeGetContent(tmp2);
237                                 GList* showtimes = getShowtimes(showtimesStr);
238                                 free(showtimesStr);
239                                 m->showTimes = showtimes;
240                         }
241                         resultList = g_list_append(resultList, m);
242                 }
243
244                 nav = nav->next;
245         }
246
247         return resultList;
248 }
249
250 GList* getTheaterMovies(Theater* t)
251 {
252
253         /*
254          * <div class="theater">
255          *              <div class="desc"/>
256          *              <div class="showtimes">
257          *                      <div class="show_left">
258          *                              <div class="movie/>
259          *                              ...
260          *                      </div>
261          *                      <div class="show_right">
262          *                              <div class="movie"/>
263          *                              ...
264          *                      </div>
265          *              </div>
266          * </div>
267          */
268
269         GList* left = NULL;
270         GList* right = NULL;
271         xmlNodePtr showtimesDiv = getFirstChildByAttributeValue(t->data, "class", "showtimes", 9);
272         if (showtimesDiv != NULL) {
273
274                 xmlNodePtr showLeft = getFirstChildByAttributeValue(showtimesDiv, "class", "show_left", 9);
275                 if (showLeft != NULL)
276                         left = getMovieList(showLeft);
277
278                 xmlNodePtr showRight = getFirstChildByAttributeValue(showtimesDiv, "class", "show_right", 10);
279                 if (showRight != NULL)
280                         right = getMovieList(showRight);
281
282                 return g_list_concat(left, right);
283
284         }
285
286
287 }
288
289
290 void deleteShowTime(ShowTime* st)
291 {
292         free(st->time);
293         free(st);
294 }
295
296
297 void deleteShowTimeList(GList * showTimeList)
298 {
299         g_list_foreach(showTimeList, (GFunc) deleteShowTime, NULL);
300         g_list_free(showTimeList);
301 }
302
303
304 void deleteMovie(Movie* m)
305 {
306         free(m->title);
307         free(m->info);
308         deleteShowTimeList(m->showTimes);
309         free(m);
310 }
311
312
313 void deleteMovieList(GList* movieList)
314 {
315         g_list_foreach(movieList, (GFunc) deleteMovie, NULL);
316         g_list_free(movieList);
317 }
318
319
320 void deleteTheater(Theater* t)
321 {
322         free(t->name);
323         free(t->address);
324         free(t);
325 }
326
327 void deleteTheaterList(GList* theaterList)
328 {
329         g_list_foreach(theaterList, (GFunc) deleteTheater, NULL);
330         g_list_free(theaterList);
331 }
332
333
334 void showTime(ShowTime* st, gpointer nothing)
335 {
336         printf("%s ", st->time);
337 }
338
339 void showMovie(Movie* m, gpointer nothing)
340 {
341         printf("        Title = %s\n", m->title);
342         printf("        Info = %s\n", m->info);
343         printf("        Schedule = ");
344         g_list_foreach(m->showTimes, (GFunc) showTime, NULL);
345         printf("\n");
346 }
347
348
349 void showTheater(Theater * t, gpointer nothing)
350 {
351         printf("Name = %s\n", t->name);
352         printf("Info = %s\n", t->address);
353         GList* movieList = getTheaterMovies(t);
354         g_list_foreach(movieList, (GFunc) showMovie, NULL);
355         deleteMovieList(movieList);
356         printf("\n");
357 }
358
359 static char*
360 get_query_uri (const char *city)
361 {
362         char *query_uri = g_strdup_printf (BASE_URL,
363                         city);
364         g_message ("%s\n", query_uri);
365
366         return query_uri;
367 }
368
369 int main (int argc, char ** argv)
370 {
371
372         if (argc != 2) {
373                 printf("usage: gmovies city_name\n");
374                 exit(-1);
375         }
376
377         SoupSession *session;
378         SoupMessage *message;
379         char *query_uri;
380         const gchar *payload;
381         const char *city = argv[1];
382         gssize len;
383
384         g_thread_init(NULL);
385         g_type_init();
386
387         session = soup_session_async_new ();
388         query_uri = get_query_uri (city);
389         message = soup_message_new ("GET", query_uri);
390
391         soup_session_send_message (session, message);
392
393         payload = message->response_body->data;
394         len = message->response_body->length;
395
396         htmlDocPtr doc = htmlReadMemory(payload, len, "http://movies.google.com", "UTF-8", HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING);
397
398         xmlNodePtr root = xmlDocGetRootElement(doc);//html
399
400         //get the body node
401         xmlNodePtr movieResults = getMovieResultsDiv(root);
402
403         if (movieResults == NULL) {
404                 printf("NO RESULTS.\n");
405                 exit(-1);
406         }
407
408         GList* theaterList = getTheaterList(movieResults);
409
410         g_list_foreach(theaterList, (GFunc) showTheater, NULL);
411
412         deleteTheaterList(theaterList);
413
414         xmlFreeDoc(doc);
415
416         g_free(query_uri);
417         g_object_unref(session);
418
419         exit(0);
420
421 }
422