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