2c6d4e9f236c60d8934affe6f22edf223e371ce8
[pandora-libraries.git] / lib / pnd_tinyxml.cpp
1
2 #include <stdio.h>
3 #include "tinyxml/tinyxml.h"
4 #include "../include/pnd_pxml.h"
5 #include "pnd_tinyxml.h"
6 #include "pnd_logger.h"
7
8 //Easily change the tag names if required (globally in this file):
9 #include "pnd_pxml_names.h"
10
11 extern "C" {
12
13 char *pnd_pxml_get_attribute(TiXmlElement *elem, const char *name)
14 {
15         const char *value = elem->Attribute(name);
16         if (value)
17                 return strdup(value);
18         else
19                 return NULL;
20 }
21
22 unsigned char pnd_pxml_parse_titles(const TiXmlHandle hRoot, pnd_pxml_t *app) {
23   TiXmlElement *pElem;
24   app->titles_alloc_c = 4;  //TODO: adjust this based on how many titles a PXML usually has. Power of 2.
25
26   app->titles = (pnd_localized_string_t *)malloc(sizeof(pnd_localized_string_t) * app->titles_alloc_c);
27   if (!app->titles) return (0); //errno = NOMEM
28
29   //Go through all title tags and load them.
30   for (pElem = hRoot.FirstChild(PND_PXML_ENAME_TITLE).Element(); pElem;
31        pElem = pElem->NextSiblingElement(PND_PXML_ENAME_TITLE))
32   {
33
34     if ( ! pElem->GetText() ) {
35       continue;
36     }
37
38     char *text = strdup(pElem->GetText());
39     if (!text) continue;
40
41     char *lang = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_TITLELANG);
42     if (!lang) continue;
43
44     app->titles_c++;
45     if (app->titles_c > app->titles_alloc_c) //we don't have enough strings allocated
46     {
47       app->titles_alloc_c <<= 1;
48       app->titles = (pnd_localized_string_t *)realloc((void*)app->titles, app->titles_alloc_c);
49       if (!app->titles) return (0); //errno = ENOMEM
50     }
51
52     pnd_localized_string_t *title = &app->titles[app->titles_c - 1];
53     title->language = lang;
54     title->string = text;
55
56     //pnd_log ( PND_LOG_DEFAULT, (char*)"    Title/Lang: %s/%s\n", text, lang );
57
58   }
59
60   return ( 1 );
61 }
62
63 unsigned char pnd_pxml_parse_descriptions(const TiXmlHandle hRoot, pnd_pxml_t *app)
64 {
65         TiXmlElement *pElem;
66         app->descriptions_alloc_c = 4; //TODO: adjust this based on how many descriptions a PXML usually has. Power of 2.
67
68         app->descriptions = (pnd_localized_string_t *)malloc(sizeof(pnd_localized_string_t) * app->descriptions_alloc_c);
69         if (!app->descriptions) 
70         {
71                 app->descriptions_alloc_c = 0;
72                 return (0); //errno = NOMEM
73         }
74
75         for (pElem = hRoot.FirstChild(PND_PXML_ENAME_DESCRIPTION).Element(); pElem; 
76                 pElem = pElem->NextSiblingElement(PND_PXML_ENAME_DESCRIPTION))
77         {
78
79           if ( ! pElem->GetText() ) {
80             continue;
81           }
82
83           char *text = strdup(pElem->GetText());
84           if (!text) continue;
85
86           char *lang = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_DESCRLANG);
87           if (!lang) continue;
88
89           app->descriptions_c++;
90           if (app->descriptions_c > app->descriptions_alloc_c) //we don't have enough strings allocated
91           {
92             app->descriptions_alloc_c <<= 1;
93             app->descriptions = (pnd_localized_string_t*)realloc((void*)app->descriptions, app->descriptions_alloc_c * sizeof(pnd_localized_string_t) );
94             if (!app->descriptions) return (0); //errno = ENOMEM
95           }
96
97           pnd_localized_string_t *description = &app->descriptions[app->descriptions_c - 1];
98           description->language = lang;
99           description->string = text;
100         }
101
102         return (1);
103 }
104
105 unsigned char pnd_pxml_parse ( const char *pFilename, char *buffer, unsigned int length, pnd_pxml_t **apps ) {
106
107   //Load the XML document
108   TiXmlDocument doc;
109   doc.Parse(buffer);
110
111   unsigned char appwrappermode = 0; // >=1 -> using <application>...</application> wrapper
112   unsigned char appcount = 0;
113   pnd_pxml_t *app = NULL;
114
115   TiXmlElement *pElem = NULL;
116   TiXmlElement *appElem = NULL;
117
118   //Find the root element
119   TiXmlHandle hDoc(&doc);
120   TiXmlHandle hRoot(0);
121
122   pElem = hDoc.FirstChild("PXML").Element();
123   if (!pElem) return (0);
124
125   // new Strategy; really, we want multiple app support within a PXML, without the lameness of
126   // multiple <PXML>..</PXML> within a single PXML.xml file. As such, we should have an app within
127   // an <application>..</application> wrapper level, with the ID on that. Further, the icon and
128   // .desktop filenames can have a # appended which is the application-count-# within the PXML,
129   // so that they can have their own .desktop and icon without collisions, but still use the
130   // same unique-id if they want to.
131   //   To avoid breaking existing PXML's (even though we're pre-launch), can detect if ID
132   // is present in PXML line or not; if not, assume application mode?
133   hRoot = TiXmlHandle(pElem);
134
135   if ( hRoot.FirstChild(PND_PXML_APP).Element() != NULL ) {
136     appwrappermode = 1;
137     appElem = hRoot.FirstChild(PND_PXML_APP).Element();
138     hRoot = TiXmlHandle ( appElem );
139   }
140
141   // until we run out of applications in the PXML..
142   while ( 1 ) {
143
144     //pnd_log ( PND_LOG_DEFAULT, (char*)"  App #%u inside of PXML %s\n", appcount, pFilename );
145
146     // create the buffer to hold the pxml
147     apps [ appcount ] = (pnd_pxml_t*) malloc ( sizeof(pnd_pxml_t) );
148     memset ( apps [ appcount ], '\0', sizeof(pnd_pxml_t) );
149
150     // due to old code, just make life easier a minute..
151     app = apps [ appcount ];
152     if ( appwrappermode ) {
153       app -> subapp_number = appcount;
154     } else {
155       app -> subapp_number = 0;
156     }
157
158     //Get unique ID first.
159     if ( appwrappermode ) {
160       app->unique_id = pnd_pxml_get_attribute(appElem, PND_PXML_ATTRNAME_UID);
161       //pnd_log ( PND_LOG_DEFAULT, (char*)"  Subapp #%u has unique_id %s\n", appcount, app -> unique_id );
162       app->appdata_dirname = pnd_pxml_get_attribute(appElem, PND_PXML_ATTRNAME_APPDATANAME);
163     } else {
164       app->unique_id = pnd_pxml_get_attribute(hRoot.Element(), PND_PXML_ATTRNAME_UID);
165       //pnd_log ( PND_LOG_DEFAULT, (char*)"  Only-app #%u has unique_id %s\n", appcount, app -> unique_id );
166     }
167
168     //Everything related to the title:
169     pnd_pxml_parse_titles(hRoot, app);
170
171     //Everything description-related:
172     pnd_pxml_parse_descriptions(hRoot, app);
173
174     //Everything launcher-related in one tag:
175     if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_EXEC).Element()) )
176      {
177        app->background = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_EXECBG); //if this returns NULL, the struct is filled with NULL. No need to check.
178        app->standalone = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_EXECSTAL);
179        app->exec       = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_EXECCMD);
180        app->startdir   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_EXECWD);
181        app->exec_no_x11     = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_EXECNOX11);
182        app->execargs   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_EXECARGS);
183      }
184
185     //The app icon:
186     if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_ICON).Element()) ) {
187       app->icon       = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_ICONSRC);
188     }
189
190     // <info>
191     if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_INFO).Element()) )
192      {
193        app-> info_name = pnd_pxml_get_attribute ( pElem, PND_PXML_ATTRNAME_INFONAME );
194        app-> info_filename = pnd_pxml_get_attribute ( pElem, PND_PXML_ATTRNAME_INFOSRC );
195        app-> info_type = pnd_pxml_get_attribute ( pElem, PND_PXML_ATTRNAME_INFOTYPE );
196      }
197
198     //The preview pics:
199     if ( (pElem = hRoot.FirstChild(PND_PXML_NODENAME_PREVPICS).Element()) )
200     {
201       //TODO: Change this if pnd_pxml_t gains the feature of more pics than 2.
202       if ( (pElem = pElem->FirstChildElement(PND_PXML_ENAME_PREVPIC)) )
203       {
204         app->previewpic1 = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_PREVPICSRC);
205
206         if ( (pElem = pElem->NextSiblingElement(PND_PXML_ENAME_PREVPIC)) )
207         {
208           app->previewpic2 = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_PREVPICSRC);
209         }
210       } 
211     } //previewpic
212
213     //The author info:
214     if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_AUTHOR).Element()) )
215     {
216       app->author_name    = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_AUTHORNAME);
217       app->author_website = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_AUTHORWWW);
218       //TODO: Uncomment this if the author gets email support.
219       //app->author_email = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_AUTHOREMAIL));
220     }
221
222     //The version info:
223     if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_VERSION).Element()) )
224     {
225       app->version_major   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERMAJOR);
226       app->version_minor   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERMINOR);
227       app->version_release = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERREL);
228       app->version_build   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERBUILD);
229     }
230
231     //The OS version info:
232     if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_OSVERSION).Element()) )
233     {
234       app->osversion_major   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_OSVERMAJOR);
235       app->osversion_minor   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_OSVERMINOR);
236       app->osversion_release = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_OSVERREL);
237       app->osversion_build   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_OSVERBUILD);
238     }
239
240     int i; //For now, we need to keep track of the index of categories.
241     //Categories:
242     if ( (pElem = hRoot.FirstChildElement(PND_PXML_NODENAME_CATS).Element()) ) //First, enter the "categories" node.
243     {
244       i = 0;
245
246       //Goes through all the top-level categories and their sub-categories. i helps limit these to 2.
247       for (pElem = pElem->FirstChildElement(PND_PXML_ENAME_CAT); pElem && i < 2; 
248            pElem = pElem->NextSiblingElement(PND_PXML_ENAME_CAT), i++)
249       {
250         //TODO: Fix pnd_pxml_t so that there can be more than 2 category 'trees' and more than 2 subcategories. Then this can be removed.
251         switch (i)
252         {
253         case 0: //first category counts as the main cat for now
254           app->main_category = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_CATNAME);
255           break;
256
257         case 1: //...second as the alternative
258           app->altcategory = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_CATNAME);
259         }
260
261         TiXmlElement *pSubCatElem; //the sub-elements for a main category.
262         int j = 0; //the subcategory index within this category
263
264         //Goes through all the subcategories within this category. j helps limit these to 2.
265         for (pSubCatElem = pElem->FirstChildElement(PND_PXML_ENAME_SUBCAT); pSubCatElem && j < 2;
266              pSubCatElem = pSubCatElem->NextSiblingElement(PND_PXML_ENAME_SUBCAT), j++)
267         {
268           char *subcat = pnd_pxml_get_attribute(pSubCatElem, PND_PXML_ATTRNAME_SUBCATNAME);
269           if (!(subcat)) continue;
270
271           //TODO: This is ugly. Fix pnd_pxml_t so that there can be more than 2 category 'trees' and more than 2 subcategories. Then this can be removed.
272           switch (j | (i << 1))
273           {
274           case 0:
275             app->subcategory1 = subcat;
276             break;
277           case 1:
278             app->subcategory2 = subcat;
279             break;
280           case 2:
281             app->altsubcategory1 = subcat;
282             break;
283           case 3:
284             app->altsubcategory2 = subcat;
285           }
286         }
287       }
288     }
289
290     //All file associations:
291     //Step into the associations node
292     if ( (pElem = hRoot.FirstChild(PND_PXML_NODENAME_ASSOCS).Element()) )
293     {
294       i = 0;
295       //Go through all associations. i serves as index; since the format only supports 3 associations we need to keep track of the number.
296       for (pElem = pElem->FirstChildElement(PND_PXML_ENAME_ASSOC); pElem && i < 3; 
297            pElem = pElem->NextSiblingElement(PND_PXML_ENAME_ASSOC), i++)
298       {
299         char *name = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_ASSOCNAME);
300         char *filetype = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_ASSOCFTYPE);
301         char *paramter = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_ASSOCARGS);
302
303         if (!(name && filetype && paramter)) continue;
304
305         switch(i) //TODO: same problem here: only 3 associations supported
306         {
307         case 0:
308         {
309           app->associationitem1_name      = name;
310           app->associationitem1_filetype  = filetype;
311           app->associationitem1_parameter = paramter;
312           break;
313         }
314         case 1:
315         {
316           app->associationitem2_name      = name;
317           app->associationitem2_filetype  = filetype;
318           app->associationitem2_parameter = paramter;
319           break;
320         }
321         case 2:
322         {
323           app->associationitem3_name      = name;
324           app->associationitem3_filetype  = filetype;
325           app->associationitem3_parameter = paramter;
326         }
327         }
328       }
329     }
330
331     //Performance related things (aka: Clockspeed XD):
332     pElem = hRoot.FirstChild(PND_PXML_ENAME_CLOCK).Element();
333     if (pElem)
334     {   
335       app->clockspeed = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_CLOCKFREQ);
336     }
337
338     // Package
339     pElem = hRoot.FirstChild ( PND_PXML_ENAME_PACKAGE ).Element();
340     if ( pElem ) {      
341       app -> package_name = pnd_pxml_get_attribute ( pElem, PND_PXML_ATTRNAME_PACKAGE_NAME );
342       app -> package_release_date = pnd_pxml_get_attribute ( pElem, PND_PXML_ATTRNAME_PACKAGE_DATE );
343     }
344
345     // mkdir request
346     if ( (pElem = hRoot.FirstChild(PND_PXML_NODENAME_MKDIR).Element()) ) {
347
348       // seek <dir>
349       if ( (pElem = pElem->FirstChildElement(PND_PXML_ENAME_MKDIR)) ) {
350         char *t;
351
352         if ( ( t = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_MKDIRPATH) ) ) {
353           // first <dir>, so just replace it wholesale; we use strdup so we can free() easily later, consistently. Mmm, leak seems imminent.
354           app -> mkdir_sp = strdup ( t );
355         }
356
357         while ( ( pElem = pElem -> NextSiblingElement ( PND_PXML_ENAME_MKDIR ) ) ) {
358               
359           if ( ( t = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_MKDIRPATH) ) ) {
360             char *foo = (char*) malloc ( strlen ( app -> mkdir_sp ) + strlen ( t ) + 1 /*:*/ + 1 /*\0*/ );
361
362             if ( foo ) {
363               sprintf ( foo, "%s:%s", app -> mkdir_sp, t );
364               free ( app -> mkdir_sp );
365               app -> mkdir_sp = foo;
366             } // assuming we got ram, lets cat it all together
367
368           } // got another elem?
369
370         } // while
371
372       } // found a <dir>
373
374 #if 0
375       if ( app -> mkdir_sp ) {
376         printf ( "mkdir: %s\n", app -> mkdir_sp );
377       }
378 #endif
379
380     } // mkdir
381
382     // if in <application> mode, do we find another app?
383     if ( appwrappermode ) {
384       appElem = appElem -> NextSiblingElement ( PND_PXML_APP );
385       if ( ! appElem ) {
386         //pnd_log ( PND_LOG_DEFAULT, (char*)"  No more applications within PXML\n" );
387         break; // no more applications
388       }
389       // got another application..
390       //pnd_log ( PND_LOG_DEFAULT, "  Found another applications within PXML\n" );
391       appwrappermode++;
392       hRoot = TiXmlHandle ( appElem );
393
394       appcount++;
395
396       if ( appcount == PXML_MAXAPPS ) {
397         return ( 1 ); // thats all we can handle; we're not going to auto-extend this
398       }
399
400     } else {
401       break; // single-app old PXML
402     }
403
404   } // while finding apps
405
406   return (1);
407 }
408
409 } // extern C