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