3 #include "tinyxml/tinyxml.h"
4 #include "../include/pnd_pxml.h"
5 #include "pnd_tinyxml.h"
6 #include "pnd_logger.h"
8 //Easily change the tag names if required (globally in this file):
9 #include "pnd_pxml_names.h"
13 char *pnd_pxml_get_attribute(TiXmlElement *elem, const char *name)
15 const char *value = elem->Attribute(name);
22 unsigned char pnd_pxml_parse_titles(const TiXmlHandle hRoot, pnd_pxml_t *app) {
24 app->titles_alloc_c = 4; //TODO: adjust this based on how many titles a PXML usually has. Power of 2.
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
29 // Go through all title tags and load them.
30 // - Check if newer style titles sub-block exists; if so, use that.
31 // - if not, fall back to old style
32 // - failing that, crash earth into sun
33 if ( (pElem = hRoot.FirstChild(PND_PXML_NODENAME_TITLES).Element()) ) {
34 // newer <titles> block
36 pElem = pElem -> FirstChildElement ( PND_PXML_ENAME_TITLE );
40 // handle <title lang="en_US">Program Title</title>
43 // parse out the text and lang
46 if ( ! ( text = strdup ( pElem -> GetText() ) ) ) {
50 if ( ! ( lang = pnd_pxml_get_attribute ( pElem, PND_PXML_ATTRNAME_TITLELANG ) ) ) {
54 // increment counter; if we're running out of buffers, grow to handle the new strings
57 if ( app -> titles_c > app -> titles_alloc_c ) {
58 // we don't have enough strings allocated
59 app -> titles_alloc_c <<= 1;
60 app -> titles = (pnd_localized_string_t *)realloc((void*)app->titles, app->titles_alloc_c);
61 if (!app->titles) return (0); //errno = ENOMEM
64 // populate the stringbuf
65 pnd_localized_string_t *title = &app->titles[app->titles_c - 1];
66 title->language = lang;
70 pElem = pElem -> NextSiblingElement ( PND_PXML_ENAME_TITLE );
75 // older style <title> entry series
77 for ( pElem = hRoot.FirstChild(PND_PXML_ENAME_TITLE).Element(); pElem;
78 pElem = pElem->NextSiblingElement(PND_PXML_ENAME_TITLE))
81 if ( ! pElem->GetText() ) {
85 char *text = strdup(pElem->GetText());
88 char *lang = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_TITLELANG);
92 if (app->titles_c > app->titles_alloc_c) //we don't have enough strings allocated
94 app->titles_alloc_c <<= 1;
95 app->titles = (pnd_localized_string_t *)realloc((void*)app->titles, app->titles_alloc_c);
96 if (!app->titles) return (0); //errno = ENOMEM
99 pnd_localized_string_t *title = &app->titles[app->titles_c - 1];
100 title->language = lang;
101 title->string = text;
103 //pnd_log ( PND_LOG_DEFAULT, (char*)" Title/Lang: %s/%s\n", text, lang );
107 } // new or old style <title(s)>
112 unsigned char pnd_pxml_parse_descriptions(const TiXmlHandle hRoot, pnd_pxml_t *app) {
114 app->descriptions_alloc_c = 4; //TODO: adjust this based on how many descriptions a PXML usually has. Power of 2.
116 app->descriptions = (pnd_localized_string_t *)malloc(sizeof(pnd_localized_string_t) * app->descriptions_alloc_c);
117 if (!app->descriptions)
119 app->descriptions_alloc_c = 0;
120 return (0); //errno = NOMEM
123 // similar logic to how <titles> or <title> is parsed
124 // - if <titles> block is found, use that; otherwise fall back to <title> deprecated form
125 if ( (pElem = hRoot.FirstChild ( PND_PXML_NODENAME_DESCRIPTIONS).Element() ) ) {
126 // newer style <descriptions> block
128 pElem = pElem -> FirstChildElement ( PND_PXML_ENAME_DESCRIPTION );
134 if ( ! ( text = strdup ( pElem -> GetText() ) ) ) {
138 if ( ! ( lang = pnd_pxml_get_attribute ( pElem, PND_PXML_ATTRNAME_DESCRLANG ) ) ) {
139 if(text) free(text); text = NULL;
143 app->descriptions_c++;
144 if (app->descriptions_c > app->descriptions_alloc_c) //we don't have enough strings allocated
146 app->descriptions_alloc_c <<= 1;
147 app->descriptions = (pnd_localized_string_t*)realloc((void*)app->descriptions, app->descriptions_alloc_c * sizeof(pnd_localized_string_t) );
148 if (!app->descriptions) { if(text) free(text); if(lang) free(lang); return (0); } //errno = ENOMEM
151 pnd_localized_string_t *description = &app->descriptions[app->descriptions_c - 1];
152 description->language = lang;
153 description->string = text;
156 pElem = pElem -> NextSiblingElement ( PND_PXML_ENAME_DESCRIPTION );
161 // fallback to older approach
163 for (pElem = hRoot.FirstChild(PND_PXML_ENAME_DESCRIPTION).Element(); pElem;
164 pElem = pElem->NextSiblingElement(PND_PXML_ENAME_DESCRIPTION))
167 if ( ! pElem->GetText() ) {
171 char *text = strdup(pElem->GetText());
174 char *lang = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_DESCRLANG);
175 if (!lang) { if(text) free(text); text = NULL; continue; }
177 app->descriptions_c++;
178 if (app->descriptions_c > app->descriptions_alloc_c) //we don't have enough strings allocated
180 app->descriptions_alloc_c <<= 1;
181 app->descriptions = (pnd_localized_string_t*)realloc((void*)app->descriptions, app->descriptions_alloc_c * sizeof(pnd_localized_string_t) );
182 if (!app->descriptions) { if(text) free(text); if(lang) free(lang); return (0); } //errno = ENOMEM
185 pnd_localized_string_t *description = &app->descriptions[app->descriptions_c - 1];
186 description->language = lang;
187 description->string = text;
190 } // new form or old form?
195 unsigned char pnd_pxml_parse ( const char *pFilename, char *buffer, unsigned int length, pnd_pxml_t **apps ) {
197 //Load the XML document
201 unsigned char appwrappermode = 0; // >=1 -> using <application>...</application> wrapper
202 unsigned char appcount = 0;
203 pnd_pxml_t *app = NULL;
205 TiXmlElement *pElem = NULL;
206 TiXmlElement *appElem = NULL;
208 //Find the root element
209 TiXmlHandle hDoc(&doc);
210 TiXmlHandle hRoot(0);
212 pElem = hDoc.FirstChild("PXML").Element();
213 if (!pElem) return (0);
215 // new Strategy; really, we want multiple app support within a PXML, without the lameness of
216 // multiple <PXML>..</PXML> within a single PXML.xml file. As such, we should have an app within
217 // an <application>..</application> wrapper level, with the ID on that. Further, the icon and
218 // .desktop filenames can have a # appended which is the application-count-# within the PXML,
219 // so that they can have their own .desktop and icon without collisions, but still use the
220 // same unique-id if they want to.
221 // To avoid breaking existing PXML's (even though we're pre-launch), can detect if ID
222 // is present in PXML line or not; if not, assume application mode?
223 hRoot = TiXmlHandle(pElem);
225 // workaround for package ID's used by some package managers
226 // get the package ID and store it for each application
227 char* package_id = NULL;
228 char* package_version_major = NULL;
229 char* package_version_minor = NULL;
230 char* package_version_release = NULL;
231 char* package_version_build = NULL;
232 pElem = hRoot.FirstChild ( PND_PXML_ENAME_PACKAGE ).Element();
234 package_id = pnd_pxml_get_attribute ( pElem, PND_PXML_ATTRNAME_PACKAGE_ID );
235 TiXmlHandle pRoot = TiXmlHandle( pElem );
236 if ( (pElem = pRoot.FirstChild(PND_PXML_ENAME_VERSION).Element()) )
238 package_version_major = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERMAJOR);
239 package_version_minor = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERMINOR);
240 package_version_release = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERREL);
241 package_version_build = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERBUILD);
245 // move to applications element then
246 if ( hRoot.FirstChild(PND_PXML_APP).Element() != NULL ) {
248 appElem = hRoot.FirstChild(PND_PXML_APP).Element();
249 hRoot = TiXmlHandle ( appElem );
252 // until we run out of applications in the PXML..
255 //pnd_log ( PND_LOG_DEFAULT, (char*)" App #%u inside of PXML %s\n", appcount, pFilename );
257 // create the buffer to hold the pxml
258 apps [ appcount ] = (pnd_pxml_t*) malloc ( sizeof(pnd_pxml_t) );
259 memset ( apps [ appcount ], '\0', sizeof(pnd_pxml_t) );
261 // due to old code, just make life easier a minute..
262 app = apps [ appcount ];
263 if ( appwrappermode ) {
264 app -> subapp_number = appcount;
266 app -> subapp_number = 0;
269 // give application the package id, if there is one
271 app -> package_id = strdup(package_id);
272 if( package_version_major )
273 app -> package_version_major = strdup(package_version_major);
274 if( package_version_minor )
275 app -> package_version_minor = strdup(package_version_minor);
276 if( package_version_release )
277 app -> package_version_release = strdup(package_version_release);
278 if( package_version_build )
279 app -> package_version_build = strdup(package_version_build);
281 //Get unique ID first.
282 if ( appwrappermode ) {
283 app->unique_id = pnd_pxml_get_attribute(appElem, PND_PXML_ATTRNAME_UID);
284 //pnd_log ( PND_LOG_DEFAULT, (char*)" Subapp #%u has unique_id %s\n", appcount, app -> unique_id );
285 app->appdata_dirname = pnd_pxml_get_attribute(appElem, PND_PXML_ATTRNAME_APPDATANAME);
287 app->unique_id = pnd_pxml_get_attribute(hRoot.Element(), PND_PXML_ATTRNAME_UID);
288 //pnd_log ( PND_LOG_DEFAULT, (char*)" Only-app #%u has unique_id %s\n", appcount, app -> unique_id );
291 //Everything related to the title:
292 pnd_pxml_parse_titles(hRoot, app);
294 //Everything description-related:
295 pnd_pxml_parse_descriptions(hRoot, app);
297 //Everything launcher-related in one tag:
298 if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_EXEC).Element()) )
300 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.
301 app->standalone = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_EXECSTAL);
302 app->exec = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_EXECCMD);
303 app->startdir = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_EXECWD);
304 app->exec_no_x11 = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_EXECNOX11);
305 app->execargs = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_EXECARGS);
309 if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_ICON).Element()) ) {
310 app->icon = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_ICONSRC);
314 if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_INFO).Element()) )
316 app-> info_name = pnd_pxml_get_attribute ( pElem, PND_PXML_ATTRNAME_INFONAME );
317 app-> info_filename = pnd_pxml_get_attribute ( pElem, PND_PXML_ATTRNAME_INFOSRC );
318 app-> info_type = pnd_pxml_get_attribute ( pElem, PND_PXML_ATTRNAME_INFOTYPE );
322 if ( (pElem = hRoot.FirstChild(PND_PXML_NODENAME_PREVPICS).Element()) )
324 //TODO: Change this if pnd_pxml_t gains the feature of more pics than 2.
325 if ( (pElem = pElem->FirstChildElement(PND_PXML_ENAME_PREVPIC)) )
327 app->previewpic1 = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_PREVPICSRC);
329 if ( (pElem = pElem->NextSiblingElement(PND_PXML_ENAME_PREVPIC)) )
331 app->previewpic2 = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_PREVPICSRC);
337 if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_AUTHOR).Element()) )
339 app->author_name = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_AUTHORNAME);
340 app->author_website = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_AUTHORWWW);
341 //TODO: Uncomment this if the author gets email support.
342 //app->author_email = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_AUTHOREMAIL));
346 if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_VERSION).Element()) )
348 app->version_major = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERMAJOR);
349 app->version_minor = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERMINOR);
350 app->version_release = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERREL);
351 app->version_build = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERBUILD);
354 //The OS version info:
355 if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_OSVERSION).Element()) )
357 app->osversion_major = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_OSVERMAJOR);
358 app->osversion_minor = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_OSVERMINOR);
359 app->osversion_release = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_OSVERREL);
360 app->osversion_build = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_OSVERBUILD);
363 int i; //For now, we need to keep track of the index of categories.
365 if ( (pElem = hRoot.FirstChildElement(PND_PXML_NODENAME_CATS).Element()) ) //First, enter the "categories" node.
369 //Goes through all the top-level categories and their sub-categories. i helps limit these to 2.
370 for (pElem = pElem->FirstChildElement(PND_PXML_ENAME_CAT); pElem && i < 2;
371 pElem = pElem->NextSiblingElement(PND_PXML_ENAME_CAT), i++)
373 //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.
376 case 0: //first category counts as the main cat for now
377 app->main_category = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_CATNAME);
380 case 1: //...second as the alternative
381 app->altcategory = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_CATNAME);
384 TiXmlElement *pSubCatElem; //the sub-elements for a main category.
385 int j = 0; //the subcategory index within this category
387 //Goes through all the subcategories within this category. j helps limit these to 2.
388 for (pSubCatElem = pElem->FirstChildElement(PND_PXML_ENAME_SUBCAT); pSubCatElem && j < 2;
389 pSubCatElem = pSubCatElem->NextSiblingElement(PND_PXML_ENAME_SUBCAT), j++)
391 char *subcat = pnd_pxml_get_attribute(pSubCatElem, PND_PXML_ATTRNAME_SUBCATNAME);
392 if (!(subcat)) continue;
394 //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.
395 switch (j | (i << 1))
398 app->subcategory1 = subcat;
401 app->subcategory2 = subcat;
404 app->altsubcategory1 = subcat;
407 app->altsubcategory2 = subcat;
413 //All file associations:
415 //Step into the associations node
416 if ( (pElem = hRoot.FirstChild(PND_PXML_NODENAME_ASSOCS).Element()) )
420 //Go through all associations. i serves as index; since the format only supports 3 associations we need to keep track of the number.
421 for (pElem = pElem->FirstChildElement(PND_PXML_ENAME_ASSOC); pElem && i < 3;
422 pElem = pElem->NextSiblingElement(PND_PXML_ENAME_ASSOC), i++)
424 char *name = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_ASSOCNAME);
425 char *filetype = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_ASSOCFTYPE);
426 char *command = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_ASSOCCMD);
427 char *args = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_ASSOCARGS);
429 if ( ! ( name && filetype && command ) ) {
430 if ( name ) free(name);
431 if ( filetype ) free(filetype);
432 if ( command ) free(command);
433 if ( args ) free(args);
437 switch ( i ) { //TODO: same problem here: only 3 associations supported
439 app->associationitem1_name = strdup ( name );
440 app->associationitem1_filetype = strdup ( filetype );
441 app->associationitem1_command = strdup ( command );
442 app->associationitem1_args = strdup ( args );
443 //pnd_log ( PND_LOG_DEFAULT, (char*)" Found file association request in PXML (%d-0)\n", i );
447 app->associationitem2_name = strdup ( name );
448 app->associationitem2_filetype = strdup ( filetype );
449 app->associationitem2_command = strdup ( command );
450 app->associationitem2_args = strdup ( args );
451 //pnd_log ( PND_LOG_DEFAULT, (char*)" Found file association request in PXML (%d-1)\n", i );
455 app->associationitem3_name = strdup ( name );
456 app->associationitem3_filetype = strdup ( filetype );
457 app->associationitem3_command = strdup ( command );
458 app->associationitem3_args = strdup ( args );
459 //pnd_log ( PND_LOG_DEFAULT, (char*)" Found file association request in PXML (%d-2)\n", i );
463 if ( name ) free(name);
464 if ( filetype ) free(filetype);
465 if ( command ) free(command);
466 if ( args ) free(args);
472 //Performance related things (aka: Clockspeed XD):
473 pElem = hRoot.FirstChild(PND_PXML_ENAME_CLOCK).Element();
476 app->clockspeed = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_CLOCKFREQ);
480 pElem = hRoot.FirstChild ( PND_PXML_ENAME_PACKAGE ).Element();
482 app -> package_name = pnd_pxml_get_attribute ( pElem, PND_PXML_ATTRNAME_PACKAGE_NAME );
483 app -> package_release_date = pnd_pxml_get_attribute ( pElem, PND_PXML_ATTRNAME_PACKAGE_DATE );
487 if ( (pElem = hRoot.FirstChild(PND_PXML_NODENAME_MKDIR).Element()) ) {
490 if ( (pElem = pElem->FirstChildElement(PND_PXML_ENAME_MKDIR)) ) {
493 if ( ( t = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_MKDIRPATH) ) ) {
494 // first <dir>, so just replace it wholesale; we use strdup so we can free() easily later, consistently. Mmm, leak seems imminent.
495 app -> mkdir_sp = strdup ( t );
496 free(t); // free this attribute
499 while ( ( pElem = pElem -> NextSiblingElement ( PND_PXML_ENAME_MKDIR ) ) ) {
501 if ( ( t = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_MKDIRPATH) ) ) {
502 char *foo = (char*) malloc ( strlen ( app -> mkdir_sp ) + strlen ( t ) + 1 /*:*/ + 1 /*\0*/ );
505 sprintf ( foo, "%s:%s", app -> mkdir_sp, t );
506 free ( app -> mkdir_sp );
507 app -> mkdir_sp = foo;
508 } // assuming we got ram, lets cat it all together
510 free(t); // free this attribute
511 } // got another elem?
518 if ( app -> mkdir_sp ) {
519 printf ( "mkdir: %s\n", app -> mkdir_sp );
525 // if in <application> mode, do we find another app?
526 if ( appwrappermode ) {
527 appElem = appElem -> NextSiblingElement ( PND_PXML_APP );
529 //pnd_log ( PND_LOG_DEFAULT, (char*)" No more applications within PXML\n" );
530 break; // no more applications
532 // got another application..
533 //pnd_log ( PND_LOG_DEFAULT, " Found another applications within PXML\n" );
535 hRoot = TiXmlHandle ( appElem );
539 if ( appcount == PXML_MAXAPPS ) {
540 return ( 1 ); // thats all we can handle; we're not going to auto-extend this
544 break; // single-app old PXML
547 } // while finding apps
551 if( package_version_major )
552 free(package_version_major);
553 if( package_version_minor )
554 free(package_version_minor);
555 if( package_version_release )
556 free(package_version_release);
557 if( package_version_build )
558 free(package_version_build);