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