Add version && package version support to discovery
[pandora-libraries.git] / lib / pnd_pxml.c
1
2 #include <stdio.h> /* for FILE */
3 #include <stdlib.h> /* for malloc */
4 #include <string.h> /* for string ops */
5
6 #include <sys/types.h> /* for stat */
7 #include <sys/stat.h> /* for stat */
8 #include <unistd.h> /* for stat */
9
10 #include "pnd_pxml.h"
11 #include "pnd_pathiter.h"
12 #include "pnd_tinyxml.h"
13 #include "pnd_logger.h"
14
15 pnd_pxml_handle *pnd_pxml_fetch ( char *fullpath ) {
16   FILE *f;
17   char *b;
18   unsigned int len;
19
20   f = fopen ( fullpath, "r" );
21
22   if ( ! f ) {
23     return ( 0 );
24   }
25
26   fseek ( f, 0, SEEK_END );
27
28   len = ftell ( f );
29
30   fseek ( f, 0, SEEK_SET );
31
32   if ( ! len ) {
33     return ( NULL );
34   }
35
36   b = (char*) malloc ( len + 1 );
37
38   if ( ! b ) {
39     fclose ( f );
40     return ( 0 );
41   }
42
43   fread ( b, 1, len, f );
44
45   fclose ( f );
46
47   return ( pnd_pxml_fetch_buffer ( fullpath, b ) );
48 }
49
50 pnd_pxml_handle *pnd_pxml_fetch_buffer ( char *filename, char *buffer ) {
51
52   pnd_pxml_t **p = malloc ( sizeof(pnd_pxml_t*) * PXML_MAXAPPS );
53   memset ( p, '\0', sizeof(pnd_pxml_t*) * PXML_MAXAPPS );
54
55   if ( ! pnd_pxml_parse ( filename, buffer, strlen ( buffer ), p ) ) {
56     return ( 0 );
57   }
58
59   return ( (pnd_pxml_handle*) p );
60 }
61
62 void pnd_pxml_delete ( pnd_pxml_handle h ) {
63   pnd_pxml_t *p = (pnd_pxml_t*) h;
64
65   int i;
66   if (p->titles) {
67     for (i = 0; i < p->titles_c; i++)
68     {
69       free(p->titles[i].language);
70       free(p->titles[i].string);
71     }
72     free(p->titles);
73   }
74
75   if (p->descriptions) {
76     for (i = 0; i < p->descriptions_c; i++)
77     {
78       free(p->descriptions[i].language);
79       free(p->descriptions[i].string);
80     }
81     free(p->descriptions);
82   }
83
84   if ( p -> unique_id ) {
85      free ( p -> unique_id );
86   }
87   if ( p -> package_id ) {
88      free ( p -> package_id );
89   }
90   if ( p -> standalone ) {
91     free ( p -> standalone );
92   }
93   if ( p -> icon ) {
94     free ( p -> icon );
95   }
96   if ( p -> previewpic1 ) {
97     free ( p -> previewpic1 );
98   }
99   if ( p -> previewpic2 ) {
100     free ( p -> previewpic2 );
101   }
102   if ( p -> author_name ) {
103     free ( p -> author_name );
104   }
105   if ( p -> author_website ) {
106     free ( p -> author_website );
107   }
108   if ( p -> version_major ) {
109     free ( p -> version_major );
110   }
111   if ( p -> version_minor ) {
112     free ( p -> version_minor );
113   }
114   if ( p -> version_release ) {
115     free ( p -> version_release );
116   }
117   if ( p -> version_build ) {
118     free ( p -> version_build );
119   }
120   if ( p -> exec ) {
121     free ( p -> exec );
122   }
123   if ( p -> main_category ) {
124     free ( p -> main_category );
125   }
126   if ( p -> subcategory1 ) {
127     free ( p -> subcategory1 );
128   }
129   if ( p -> subcategory2 ) {
130     free ( p -> subcategory2 );
131   }
132   if ( p -> altcategory ) {
133     free ( p -> altcategory );
134   }
135   if ( p -> altsubcategory1 ) {
136     free ( p -> altsubcategory1 );
137   }
138   if ( p -> altsubcategory2 ) {
139     free ( p -> altsubcategory2 );
140   }
141   if ( p -> osversion_major ) {
142     free ( p -> osversion_major );
143   }
144   if ( p -> osversion_minor ) {
145     free ( p -> osversion_minor );
146   }
147   if ( p -> osversion_release ) {
148     free ( p -> osversion_release );
149   }
150   if ( p -> osversion_build ) {
151     free ( p -> osversion_build );
152   }
153   if ( p -> associationitem1_name ) {
154     free ( p -> associationitem1_name );
155   }
156   if ( p -> associationitem1_filetype ) {
157     free ( p -> associationitem1_filetype );
158   }
159   if ( p -> associationitem1_parameter ) {
160     free ( p -> associationitem1_parameter );
161   }
162   if ( p -> associationitem2_name ) {
163     free ( p -> associationitem2_name );
164   }
165   if ( p -> associationitem2_filetype ) {
166     free ( p -> associationitem2_filetype );
167   }
168   if ( p -> associationitem2_parameter ) {
169     free ( p -> associationitem2_parameter );
170   }
171   if ( p -> associationitem3_name ) {
172     free ( p -> associationitem3_name );
173   }
174   if ( p -> associationitem1_filetype ) {
175     free ( p -> associationitem3_filetype );
176   }
177   if ( p -> associationitem1_parameter ) {
178     free ( p -> associationitem3_parameter );
179   }
180   if ( p -> clockspeed ) {
181     free ( p -> clockspeed );
182   }
183   if ( p -> background ) {
184     free ( p -> background );
185   }
186   if ( p -> startdir ) {
187     free ( p -> startdir );
188   }
189   if ( p -> appdata_dirname ) {
190     free ( p -> appdata_dirname );
191   }
192   if ( p -> info_name ) {
193     free ( p -> info_name );
194   }
195   if ( p -> info_filename ) {
196     free ( p -> info_filename );
197   }
198   if ( p -> info_type ) {
199     free ( p -> info_type );
200   }
201   if ( p -> exec_no_x11 ) {
202     free ( p -> exec_no_x11 );
203   }
204   if ( p -> execargs ) {
205     free ( p -> execargs );
206   }
207   if ( p -> package_version_major ) {
208     free ( p -> package_version_major );
209   }
210   if ( p -> package_version_minor ) {
211     free ( p -> package_version_minor );
212   }
213   if ( p -> package_version_release ) {
214     free ( p -> package_version_release );
215   }
216   if ( p -> package_version_build ) {
217     free ( p -> package_version_build );
218   }
219
220   free(p); /*very important!*/
221
222   return;
223 }
224
225 void pnd_pxml_set_app_name ( pnd_pxml_handle h, char *v ) {
226   /*
227    * Please do not use this function if it can be avoided; it is only here for compatibility.
228    * The function might fail on low memory, and there's no way for the user to know when this happens.
229    */
230   pnd_pxml_t *p = (pnd_pxml_t*) h;
231   char has_en_field = 0;
232   int i;
233
234   if (!v) return; /*TODO: add error information? Make it possible to set the string to NULL?*/
235
236   for (i = 0; i < p->titles_c; i++)
237   {
238     if (strncmp("en", p->titles[i].language, 2) == 0) /*strict comparison; match "en_US", "en_GB" etc... All these are set.*/
239     {
240       free(p->titles[i].string);
241       p->titles[i].string = strdup(v);
242       has_en_field = 1;
243     }
244   }
245
246   if (!has_en_field)
247   {
248     p->titles_c++;
249     if (p->titles_c > p->titles_alloc_c) //we don't have enough strings allocated
250     {
251       p->titles_alloc_c <<= 1;
252       p->titles = (pnd_localized_string_t *)realloc((void*)p->titles, p->titles_alloc_c);
253       if (!p->titles) return; //errno = ENOMEM
254     }
255     p->titles[p->titles_c - 1].language = "en_US";
256     p->titles[p->titles_c - 1].string = strdup(v);
257   }
258
259   return;
260 }
261
262 unsigned char pnd_is_pxml_valid_app ( pnd_pxml_handle h ) {
263   //pnd_pxml_t *p = (pnd_pxml_t*) h; //unused atm
264
265   // for now, lets just verify the exec-path is valid
266
267   //printf ( "exec is '%s'\n", p -> exec );
268
269   return ( 1 );
270
271   // even this is complicated by pnd_run.sh semantics .. can't check if it exists
272   // during discovery, since it is not mounted yet..
273 #if 0
274   struct stat buf;
275   if ( stat ( p -> exec, &buf ) == 0 ) {
276     return ( 1 ); // path is present
277   }
278 #endif
279
280   return ( 0 );
281 }
282
283 signed char pnd_pxml_merge_override ( pnd_pxml_handle h, char *searchpath ) {
284   // the pxml includes a unique-id; use this value to attempt to find an
285   // override in the given searchpath
286   signed char retval = 0;
287
288 #if 0 // TODO: Unfinished entirely now
289   pnd_pxml_handle mergeh;
290
291   if ( ! pnd_pxml_get_unique_id ( h ) ) {
292     return ( -1 ); // no unique-id present, so can't use it to name potential override files
293   }
294
295   SEARCHPATH_PRE
296   {
297
298     // do it
299     strncat ( buffer, "/", FILENAME_MAX );
300     strncat ( buffer, pnd_pxml_get_unique_id ( h ), FILENAME_MAX );
301     strncat ( buffer, ".xml", FILENAME_MAX );
302     //printf ( "  Path to seek merges: '%s'\n", buffer );
303
304     // TODO: handle multiple subapps!
305     mergeh = pnd_pxml_fetch ( buffer );
306
307     if ( mergeh ) {
308
309       // TODO: handle all the various data bits
310       if ( pnd_pxml_get_app_name_en ( mergeh ) ) {
311         pnd_pxml_set_app_name ( h, pnd_pxml_get_app_name_en ( mergeh ) );
312       }
313
314       pnd_pxml_delete ( mergeh );
315     }
316
317   }
318   SEARCHPATH_POST
319 #endif
320
321   return ( retval );
322 }
323
324 char *pnd_pxml_get_best_localized_string(pnd_localized_string_t strings[], int strings_c, char *iso_lang)
325 {
326   int i;
327   int similarity_weight = 0xffff; /*Set to something Really Bad in the beginning*/
328   char *best_match = NULL;
329
330   for(i = 0; i < strings_c; i++)
331   {
332     // factor in the length -- if we're given 'en' and have a string 'en_US', thats better than 'de_something'; if we don't
333     // use length, then en_US and de_FO are same to 'en'.
334     int maxcount = strlen ( strings[i].language ) < strlen ( iso_lang ) ? strlen ( strings[i].language ) : strlen ( iso_lang );
335     int new_weight = abs(strncmp(strings[i].language, iso_lang, maxcount));
336     //pnd_log ( PND_LOG_DEFAULT, "looking for lang %s, looking at lang %s (weight %d, old %d): %s\n",
337     //          iso_lang, strings [ i ].language, new_weight, similarity_weight, strings [ i ].string );
338     if (new_weight < similarity_weight)
339     {
340       similarity_weight = new_weight;
341       best_match = strings[i].string;
342     }
343   }
344
345   if ( best_match ) {
346     //pnd_log ( PND_LOG_DEFAULT, "best match: %s\n", best_match );
347     return strdup(best_match);
348   }
349
350   //pnd_log ( PND_LOG_DEFAULT, "best match: FAIL\n" );
351
352   return ( NULL );
353 }
354
355 char *pnd_pxml_get_package_id ( pnd_pxml_handle h ) {
356   pnd_pxml_t *p = (pnd_pxml_t*) h;
357   return ( p -> package_id );
358 }
359
360 char *pnd_pxml_get_app_name ( pnd_pxml_handle h, char *iso_lang ) {
361   pnd_pxml_t *p = (pnd_pxml_t *) h;
362   return pnd_pxml_get_best_localized_string(p->titles, p->titles_c, iso_lang);
363 }
364
365 char *pnd_pxml_get_app_name_en ( pnd_pxml_handle h ) {
366   return pnd_pxml_get_app_name(h, "en");
367 }
368
369 char *pnd_pxml_get_app_name_de ( pnd_pxml_handle h ) {
370   return pnd_pxml_get_app_name(h, "de");
371 }
372
373 char *pnd_pxml_get_app_name_it ( pnd_pxml_handle h ) {
374   return pnd_pxml_get_app_name(h, "it");
375 }
376
377 char *pnd_pxml_get_app_name_fr ( pnd_pxml_handle h ) {
378   return pnd_pxml_get_app_name(h, "fr");
379 }
380 char *pnd_pxml_get_unique_id ( pnd_pxml_handle h ) {
381   pnd_pxml_t *p = (pnd_pxml_t*) h;
382   return ( p -> unique_id );
383 }
384
385 char *pnd_pxml_get_appdata_dirname ( pnd_pxml_handle h ) {
386   pnd_pxml_t *p = (pnd_pxml_t*) h;
387   return ( p -> appdata_dirname );
388 }
389
390 char *pnd_pxml_get_standalone ( pnd_pxml_handle h ) {
391   pnd_pxml_t *p = (pnd_pxml_t*) h;
392   return ( p -> standalone );
393 }
394
395 char *pnd_pxml_get_icon ( pnd_pxml_handle h ) {
396   pnd_pxml_t *p = (pnd_pxml_t*) h;
397   return ( p -> icon );
398 }
399
400 // this guy's func name is 'out of sync' with the family of functions below; but since it
401 // exists, rather than just remove it and break someones code, will add in the appropriate
402 // function wrapper; the header only specifies the other guy (always did), so the header
403 // was already on the right path.
404 char *pnd_pxml_get_app_description ( pnd_pxml_handle h, char *iso_lang ) {
405   pnd_pxml_t *p = (pnd_pxml_t *) h;
406   return pnd_pxml_get_best_localized_string(p->descriptions, p->descriptions_c, iso_lang);
407 }
408
409 char *pnd_pxml_get_description_en ( pnd_pxml_handle h ) {
410   return pnd_pxml_get_app_description(h, "en");
411 }
412
413 char *pnd_pxml_get_description_de ( pnd_pxml_handle h ) {
414   return pnd_pxml_get_app_description(h, "de");
415 }
416
417 char *pnd_pxml_get_description_it ( pnd_pxml_handle h ) {
418   return pnd_pxml_get_app_description(h, "it");
419 }
420
421 char *pnd_pxml_get_description_fr ( pnd_pxml_handle h ) {
422   return pnd_pxml_get_app_description(h, "fr");
423 }
424
425 // wrapper added so family of function names is consistent; see comment for pnd_pxml_get_app_description() above
426 char *pnd_pxml_get_description ( pnd_pxml_handle h, char *iso_lang) {
427   return ( pnd_pxml_get_app_description ( h, iso_lang ) );
428 }
429
430 char *pnd_pxml_get_previewpic1 ( pnd_pxml_handle h ) {
431   pnd_pxml_t *p = (pnd_pxml_t*) h;
432   return ( p -> previewpic1 );
433 }
434
435 char *pnd_pxml_get_previewpic2 ( pnd_pxml_handle h ) {
436   pnd_pxml_t *p = (pnd_pxml_t*) h;
437   return ( p -> previewpic2 );
438 }
439
440 char *pnd_pxml_get_author_name ( pnd_pxml_handle h ) {
441   pnd_pxml_t *p = (pnd_pxml_t*) h;
442   return ( p -> author_name );
443 }
444
445 char *pnd_pxml_get_author_website ( pnd_pxml_handle h ) {
446   pnd_pxml_t *p = (pnd_pxml_t*) h;
447   return ( p -> author_website );
448 }
449
450 char *pnd_pxml_get_version_major ( pnd_pxml_handle h ) {
451   pnd_pxml_t *p = (pnd_pxml_t*) h;
452   return ( p -> version_major );
453 }
454
455 char *pnd_pxml_get_version_minor ( pnd_pxml_handle h ) {
456   pnd_pxml_t *p = (pnd_pxml_t*) h;
457   return ( p -> version_minor );
458 }
459
460 char *pnd_pxml_get_version_release ( pnd_pxml_handle h ) {
461   pnd_pxml_t *p = (pnd_pxml_t*) h;
462   return ( p -> version_release );
463 }
464
465 char *pnd_pxml_get_version_build ( pnd_pxml_handle h ) {
466   pnd_pxml_t *p = (pnd_pxml_t*) h;
467   return ( p -> version_build );
468 }
469
470 char *pnd_pxml_get_exec ( pnd_pxml_handle h ) {
471   pnd_pxml_t *p = (pnd_pxml_t*) h;
472   return ( p -> exec );
473 }
474
475 char *pnd_pxml_get_execargs ( pnd_pxml_handle h ) {
476   pnd_pxml_t *p = (pnd_pxml_t*) h;
477   return ( p -> execargs );
478 }
479
480 char *pnd_pxml_get_exec_option_no_x11 ( pnd_pxml_handle h ) {
481   pnd_pxml_t *p = (pnd_pxml_t*) h;
482   return ( p -> exec_no_x11 );
483 }
484
485 char *pnd_pxml_get_main_category ( pnd_pxml_handle h ) {
486   pnd_pxml_t *p = (pnd_pxml_t*) h;
487   return ( p -> main_category );
488 }
489
490 char *pnd_pxml_get_subcategory1 ( pnd_pxml_handle h ) {
491   pnd_pxml_t *p = (pnd_pxml_t*) h;
492   return ( p -> subcategory1 );
493 }
494
495 char *pnd_pxml_get_subcategory2 ( pnd_pxml_handle h ) {
496   pnd_pxml_t *p = (pnd_pxml_t*) h;
497   return ( p -> subcategory2 );
498 }
499
500 char *pnd_pxml_get_altcategory ( pnd_pxml_handle h ) {
501   pnd_pxml_t *p = (pnd_pxml_t*) h;
502   return ( p -> altcategory );
503 }
504
505 char *pnd_pxml_get_altsubcategory1 ( pnd_pxml_handle h ) {
506   pnd_pxml_t *p = (pnd_pxml_t*) h;
507   return ( p -> altsubcategory1 );
508 }
509
510 char *pnd_pxml_get_altsubcategory2 ( pnd_pxml_handle h ) {
511   pnd_pxml_t *p = (pnd_pxml_t*) h;
512   return ( p -> altsubcategory2 );
513 }
514
515 char *pnd_pxml_get_osversion_major ( pnd_pxml_handle h ) {
516   pnd_pxml_t *p = (pnd_pxml_t*) h;
517   return ( p -> osversion_major );
518 }
519
520 char *pnd_pxml_get_osversion_minor ( pnd_pxml_handle h ) {
521   pnd_pxml_t *p = (pnd_pxml_t*) h;
522   return ( p -> osversion_minor );
523 }
524
525 char *pnd_pxml_get_osversion_release ( pnd_pxml_handle h ) {
526   pnd_pxml_t *p = (pnd_pxml_t*) h;
527   return ( p -> osversion_release );
528 }
529
530 char *pnd_pxml_get_osversion_build ( pnd_pxml_handle h ) {
531   pnd_pxml_t *p = (pnd_pxml_t*) h;
532   return ( p -> osversion_build );
533 }
534
535 char *pnd_pxml_get_associationitem1_name ( pnd_pxml_handle h ) {
536   pnd_pxml_t *p = (pnd_pxml_t*) h;
537   return ( p -> associationitem1_name );
538 }
539
540 char *pnd_pxml_get_associationitem1_filetype ( pnd_pxml_handle h ) {
541   pnd_pxml_t *p = (pnd_pxml_t*) h;
542   return ( p -> associationitem1_filetype );
543 }
544
545 char *pnd_pxml_get_associationitem1_parameter ( pnd_pxml_handle h ) {
546   pnd_pxml_t *p = (pnd_pxml_t*) h;
547   return ( p -> associationitem1_parameter );
548 }
549
550 char *pnd_pxml_get_associationitem2_name ( pnd_pxml_handle h ) {
551   pnd_pxml_t *p = (pnd_pxml_t*) h;
552   return ( p -> associationitem2_name );
553 }
554
555 char *pnd_pxml_get_associationitem2_filetype ( pnd_pxml_handle h ) {
556   pnd_pxml_t *p = (pnd_pxml_t*) h;
557   return ( p -> associationitem2_filetype );
558 }
559
560 char *pnd_pxml_get_associationitem2_parameter ( pnd_pxml_handle h ) {
561   pnd_pxml_t *p = (pnd_pxml_t*) h;
562   return ( p -> associationitem2_parameter );
563 }
564
565 char *pnd_pxml_get_associationitem3_name ( pnd_pxml_handle h ) {
566   pnd_pxml_t *p = (pnd_pxml_t*) h;
567   return ( p -> associationitem3_name );
568 }
569
570 char *pnd_pxml_get_associationitem3_filetype ( pnd_pxml_handle h ) {
571   pnd_pxml_t *p = (pnd_pxml_t*) h;
572   return ( p -> associationitem3_filetype );
573 }
574
575 char *pnd_pxml_get_associationitem3_parameter ( pnd_pxml_handle h ) {
576   pnd_pxml_t *p = (pnd_pxml_t*) h;
577   return ( p -> associationitem3_parameter );
578 }
579
580 char *pnd_pxml_get_clockspeed ( pnd_pxml_handle h ) {
581   pnd_pxml_t *p = (pnd_pxml_t*) h;
582   return ( p -> clockspeed );
583 }
584
585 char *pnd_pxml_get_background ( pnd_pxml_handle h ) {
586   pnd_pxml_t *p = (pnd_pxml_t*) h;
587   return ( p -> background );
588 }
589
590 char *pnd_pxml_get_startdir ( pnd_pxml_handle h ) {
591   pnd_pxml_t *p = (pnd_pxml_t*) h;
592   return ( p -> startdir );
593 }
594
595 char *pnd_pxml_get_mkdir ( pnd_pxml_handle h ) {
596   pnd_pxml_t *p = (pnd_pxml_t*) h;
597   return ( p -> mkdir_sp );
598 }
599
600 char *pnd_pxml_get_package_version_major ( pnd_pxml_handle h ) {
601    pnd_pxml_t *p = (pnd_pxml_t*) h;
602    return ( p -> package_version_major );
603 }
604
605 char *pnd_pxml_get_package_version_minor ( pnd_pxml_handle h ) {
606    pnd_pxml_t *p = (pnd_pxml_t*) h;
607    return ( p -> package_version_minor );
608 }
609
610 char *pnd_pxml_get_package_version_release ( pnd_pxml_handle h ) {
611    pnd_pxml_t *p = (pnd_pxml_t*) h;
612    return ( p -> package_version_release );
613 }
614
615 char *pnd_pxml_get_package_version_build ( pnd_pxml_handle h ) {
616    pnd_pxml_t *p = (pnd_pxml_t*) h;
617    return ( p -> package_version_build );
618 }
619
620 unsigned char pnd_pxml_is_affirmative ( char *v ) {
621
622   if ( ! v ) {
623     return ( 0 );
624   }
625
626   if ( ( v [ 0 ] == 'Y' ) ||
627        ( v [ 0 ] == 'y' ) ||
628        ( v [ 0 ] == '1' ) )
629   {
630     return ( 0 );
631   }
632
633   return ( 0 );
634 }
635
636 pnd_pxml_x11_req_e pnd_pxml_get_x11 ( char *pxmlvalue ) {
637
638   if ( ! pxmlvalue ) {
639     return ( pnd_pxml_x11_ignored );
640   } else if ( strcasecmp ( pxmlvalue, "req" ) == 0 ) {
641     return ( pnd_pxml_x11_required );
642   } else if ( strcasecmp ( pxmlvalue, "stop" ) == 0 ) {
643     return ( pnd_pxml_x11_stop );
644   } else if ( strcasecmp ( pxmlvalue, "ignore" ) == 0 ) {
645     return ( pnd_pxml_x11_ignored );
646   }
647
648   return ( pnd_pxml_x11_ignored ); // default
649 }
650
651 char *pnd_pxml_get_info_name ( pnd_pxml_handle h ) {
652   pnd_pxml_t *p = (pnd_pxml_t*) h;
653   return ( p -> info_name );
654 }
655
656 char *pnd_pxml_get_info_type ( pnd_pxml_handle h ) {
657   pnd_pxml_t *p = (pnd_pxml_t*) h;
658   return ( p -> info_type );
659 }
660
661 char *pnd_pxml_get_info_src ( pnd_pxml_handle h ) {
662   pnd_pxml_t *p = (pnd_pxml_t*) h;
663   return ( p -> info_filename );
664 }