Patch from Cloudef to add very basic support for 'package' PXML tags
[pandora-libraries.git] / lib / pnd_pxml.c
index 35b21b3..4b743d4 100644 (file)
 #include "pnd_pxml.h"
 #include "pnd_pathiter.h"
 #include "pnd_tinyxml.h"
+#include "pnd_logger.h"
 
-pnd_pxml_handle pnd_pxml_fetch ( char *fullpath ) {
+pnd_pxml_handle *pnd_pxml_fetch ( char *fullpath ) {
+  FILE *f;
+  char *b;
+  unsigned int len;
 
-  pnd_pxml_t *p = malloc ( sizeof(pnd_pxml_t) );
+  f = fopen ( fullpath, "r" );
 
-  memset ( p, '\0', sizeof(pnd_pxml_t) );
+  if ( ! f ) {
+    return ( 0 );
+  }
+
+  fseek ( f, 0, SEEK_END );
+
+  len = ftell ( f );
 
-  if ( ! pnd_pxml_load ( fullpath, p ) ) {
+  fseek ( f, 0, SEEK_SET );
+
+  b = (char*) malloc ( len );
+
+  if ( ! b ) {
+    fclose ( f );
     return ( 0 );
   }
 
-  return ( p );
-}
+  fread ( b, 1, len, f );
 
-pnd_pxml_handle pnd_pxml_fetch_buffer ( char *filename, char *buffer ) {
+  fclose ( f );
 
-  pnd_pxml_t *p = malloc ( sizeof(pnd_pxml_t) );
+  return ( pnd_pxml_fetch_buffer ( fullpath, b ) );
+}
+
+pnd_pxml_handle *pnd_pxml_fetch_buffer ( char *filename, char *buffer ) {
 
-  memset ( p, '\0', sizeof(pnd_pxml_t) );
+  pnd_pxml_t **p = malloc ( sizeof(pnd_pxml_t*) * PXML_MAXAPPS );
+  memset ( p, '\0', sizeof(pnd_pxml_t*) * PXML_MAXAPPS );
 
   if ( ! pnd_pxml_parse ( filename, buffer, strlen ( buffer ), p ) ) {
     return ( 0 );
   }
 
-  return ( p );
+  return ( (pnd_pxml_handle*) p );
 }
 
 void pnd_pxml_delete ( pnd_pxml_handle h ) {
   pnd_pxml_t *p = (pnd_pxml_t*) h;
 
-  if ( p -> title_en ) {
-    free ( p -> title_en );
-  }
-  if ( p -> title_de ) {
-    free ( p -> title_de );
-  }
-  if ( p -> title_it ) {
-    free ( p -> title_it );
-  }
-  if ( p -> title_fr ) {
-    free ( p -> title_fr );
+  int i;
+  if (p->titles) {
+    for (i = 0; i < p->titles_c; i++)
+    {
+      free(p->titles[i].language);
+      free(p->titles[i].string);
+    }
+    free(p->titles);
   }
-  if ( p -> unique_id ) {
-    free ( p -> unique_id );
+
+  if (p->descriptions) {
+    for (i = 0; i < p->descriptions_c; i++)
+    {
+      free(p->descriptions[i].language);
+      free(p->descriptions[i].string);
+    }
+    free(p->descriptions);
   }
+
   if ( p -> standalone ) {
     free ( p -> standalone );
   }
   if ( p -> icon ) {
     free ( p -> icon );
   }
-  if ( p -> description_en ) {
-    free ( p -> description_en );
-  }
-  if ( p -> description_de ) {
-    free ( p -> description_de );
-  }
-  if ( p -> description_it ) {
-    free ( p -> description_it );
-  }
-  if ( p -> description_fr ) {
-    free ( p -> description_fr );
-  }
   if ( p -> previewpic1 ) {
     free ( p -> previewpic1 );
   }
@@ -166,26 +176,54 @@ void pnd_pxml_delete ( pnd_pxml_handle h ) {
   if ( p -> startdir ) {
     free ( p -> startdir );
   }
+  if ( p -> appdata_dirname ) {
+    free ( p -> appdata_dirname );
+  }
+
+  free(p); /*very important!*/
 
   return;
 }
 
 void pnd_pxml_set_app_name ( pnd_pxml_handle h, char *v ) {
+  /* 
+   * Please do not use this function if it can be avoided; it is only here for compatibility.
+   * The function might fail on low memory, and there's no way for the user to know when this happens.
+   */
   pnd_pxml_t *p = (pnd_pxml_t*) h;
-  if ( p -> title_en ) {
-    free ( p -> title_en );
-    p -> title_en = NULL;
+  char has_en_field = 0;
+  int i;
+
+  if (!v) return; /*TODO: add error information? Make it possible to set the string to NULL?*/
+
+  for (i = 0; i < p->titles_c; i++)
+  {
+    if (strncmp("en", p->titles[i].language, 2) == 0) /*strict comparison; match "en_US", "en_GB" etc... All these are set.*/
+    {
+      free(p->titles[i].string);
+      p->titles[i].string = strdup(v);
+      has_en_field = 1;
+    }
   }
 
-  if ( v ) {
-    p -> title_en = strdup ( v );
+  if (!has_en_field)
+  {
+    p->titles_c++;
+    if (p->titles_c > p->titles_alloc_c) //we don't have enough strings allocated
+    {
+      p->titles_alloc_c <<= 1;
+      p->titles = (pnd_localized_string_t *)realloc((void*)p->titles, p->titles_alloc_c);
+      if (!p->titles) return; //errno = ENOMEM
+    }
+    p->titles[p->titles_c - 1].language = "en_US";
+    p->titles[p->titles_c - 1].string = strdup(v);
   }
 
   return;
 }
 
 unsigned char pnd_is_pxml_valid_app ( pnd_pxml_handle h ) {
-  pnd_pxml_t *p = (pnd_pxml_t*) h;
+  //pnd_pxml_t *p = (pnd_pxml_t*) h; //unused atm
 
   // for now, lets just verify the exec-path is valid
 
@@ -209,8 +247,14 @@ signed char pnd_pxml_merge_override ( pnd_pxml_handle h, char *searchpath ) {
   // the pxml includes a unique-id; use this value to attempt to find an
   // override in the given searchpath
   signed char retval = 0;
+
+#if 0 // TODO: Unfinished entirely now
   pnd_pxml_handle mergeh;
 
+  if ( ! pnd_pxml_get_unique_id ( h ) ) {
+    return ( -1 ); // no unique-id present, so can't use it to name potential override files
+  }
+
   SEARCHPATH_PRE
   {
 
@@ -220,10 +264,12 @@ signed char pnd_pxml_merge_override ( pnd_pxml_handle h, char *searchpath ) {
     strncat ( buffer, ".xml", FILENAME_MAX );
     //printf ( "  Path to seek merges: '%s'\n", buffer );
 
+    // TODO: handle multiple subapps!
     mergeh = pnd_pxml_fetch ( buffer );
 
     if ( mergeh ) {
 
+      // TODO: handle all the various data bits
       if ( pnd_pxml_get_app_name_en ( mergeh ) ) {
        pnd_pxml_set_app_name ( h, pnd_pxml_get_app_name_en ( mergeh ) );
       }
@@ -233,35 +279,77 @@ signed char pnd_pxml_merge_override ( pnd_pxml_handle h, char *searchpath ) {
 
   }
   SEARCHPATH_POST
+#endif
 
   return ( retval );
 }
 
-char *pnd_pxml_get_app_name_en ( pnd_pxml_handle h ) {
+char *pnd_pxml_get_best_localized_string(pnd_localized_string_t strings[], int strings_c, char *iso_lang)
+{
+  int i;
+  int similarity_weight = 0xffff; /*Set to something Really Bad in the beginning*/
+  char *best_match = NULL;
+
+  for(i = 0; i < strings_c; i++)
+  {
+    // factor in the length -- if we're given 'en' and have a string 'en_US', thats better than 'de_something'; if we don't
+    // use length, then en_US and de_FO are same to 'en'.
+    int maxcount = strlen ( strings[i].language ) < strlen ( iso_lang ) ? strlen ( strings[i].language ) : strlen ( iso_lang );
+    int new_weight = abs(strncmp(strings[i].language, iso_lang, maxcount));
+    //pnd_log ( PND_LOG_DEFAULT, "looking for lang %s, looking at lang %s (weight %d, old %d): %s\n",
+    //          iso_lang, strings [ i ].language, new_weight, similarity_weight, strings [ i ].string );
+    if (new_weight < similarity_weight)
+    {
+      similarity_weight = new_weight;
+      best_match = strings[i].string;
+    }
+  }
+
+  if ( best_match ) {
+    //pnd_log ( PND_LOG_DEFAULT, "best match: %s\n", best_match );
+    return strdup(best_match);
+  }
+
+  //pnd_log ( PND_LOG_DEFAULT, "best match: FAIL\n" );
+
+  return ( NULL );
+}
+
+char *pnd_pxml_get_package_id ( pnd_pxml_handle h ) {
   pnd_pxml_t *p = (pnd_pxml_t*) h;
-  return ( p -> title_en );
+  return ( p -> package_id );
+}
+
+char *pnd_pxml_get_app_name ( pnd_pxml_handle h, char *iso_lang ) {
+  pnd_pxml_t *p = (pnd_pxml_t *) h;
+  return pnd_pxml_get_best_localized_string(p->titles, p->titles_c, iso_lang);
+}
+
+char *pnd_pxml_get_app_name_en ( pnd_pxml_handle h ) {
+  return pnd_pxml_get_app_name(h, "en");
 }
 
 char *pnd_pxml_get_app_name_de ( pnd_pxml_handle h ) {
-  pnd_pxml_t *p = (pnd_pxml_t*) h;
-  return ( p -> title_de );
+  return pnd_pxml_get_app_name(h, "de");
 }
 
 char *pnd_pxml_get_app_name_it ( pnd_pxml_handle h ) {
-  pnd_pxml_t *p = (pnd_pxml_t*) h;
-  return ( p -> title_it );
+  return pnd_pxml_get_app_name(h, "it");
 }
 
 char *pnd_pxml_get_app_name_fr ( pnd_pxml_handle h ) {
-  pnd_pxml_t *p = (pnd_pxml_t*) h;
-  return ( p -> title_fr );
+  return pnd_pxml_get_app_name(h, "fr");
 }
-
 char *pnd_pxml_get_unique_id ( pnd_pxml_handle h ) {
   pnd_pxml_t *p = (pnd_pxml_t*) h;
   return ( p -> unique_id );
 }
 
+char *pnd_pxml_get_appdata_dirname ( pnd_pxml_handle h ) {
+  pnd_pxml_t *p = (pnd_pxml_t*) h;
+  return ( p -> appdata_dirname );
+}
+
 char *pnd_pxml_get_standalone ( pnd_pxml_handle h ) {
   pnd_pxml_t *p = (pnd_pxml_t*) h;
   return ( p -> standalone );
@@ -272,24 +360,34 @@ char *pnd_pxml_get_icon ( pnd_pxml_handle h ) {
   return ( p -> icon );
 }
 
+// this guy's func name is 'out of sync' with the family of functions below; but since it
+// exists, rather than just remove it and break someones code, will add in the appropriate
+// function wrapper; the header only specifies the other guy (always did), so the header
+// was already on the right path.
+char *pnd_pxml_get_app_description ( pnd_pxml_handle h, char *iso_lang ) {
+  pnd_pxml_t *p = (pnd_pxml_t *) h;
+  return pnd_pxml_get_best_localized_string(p->descriptions, p->descriptions_c, iso_lang);
+}
+
 char *pnd_pxml_get_description_en ( pnd_pxml_handle h ) {
-  pnd_pxml_t *p = (pnd_pxml_t*) h;
-  return ( p -> description_en );
+  return pnd_pxml_get_app_description(h, "en");
 }
 
 char *pnd_pxml_get_description_de ( pnd_pxml_handle h ) {
-  pnd_pxml_t *p = (pnd_pxml_t*) h;
-  return ( p -> description_de );
+  return pnd_pxml_get_app_description(h, "de");
 }
 
 char *pnd_pxml_get_description_it ( pnd_pxml_handle h ) {
-  pnd_pxml_t *p = (pnd_pxml_t*) h;
-  return ( p -> description_it );
+  return pnd_pxml_get_app_description(h, "it");
 }
 
 char *pnd_pxml_get_description_fr ( pnd_pxml_handle h ) {
-  pnd_pxml_t *p = (pnd_pxml_t*) h;
-  return ( p -> description_fr );
+  return pnd_pxml_get_app_description(h, "fr");
+}
+
+// wrapper added so family of function names is consistent; see comment for pnd_pxml_get_app_description() above
+char *pnd_pxml_get_description ( pnd_pxml_handle h, char *iso_lang) {
+  return ( pnd_pxml_get_app_description ( h, iso_lang ) );
 }
 
 char *pnd_pxml_get_previewpic1 ( pnd_pxml_handle h ) {
@@ -337,6 +435,16 @@ char *pnd_pxml_get_exec ( pnd_pxml_handle h ) {
   return ( p -> exec );
 }
 
+char *pnd_pxml_get_execargs ( pnd_pxml_handle h ) {
+  pnd_pxml_t *p = (pnd_pxml_t*) h;
+  return ( p -> execargs );
+}
+
+char *pnd_pxml_get_exec_option_no_x11 ( pnd_pxml_handle h ) {
+  pnd_pxml_t *p = (pnd_pxml_t*) h;
+  return ( p -> exec_no_x11 );
+}
+
 char *pnd_pxml_get_main_category ( pnd_pxml_handle h ) {
   pnd_pxml_t *p = (pnd_pxml_t*) h;
   return ( p -> main_category );
@@ -446,3 +554,54 @@ char *pnd_pxml_get_startdir ( pnd_pxml_handle h ) {
   pnd_pxml_t *p = (pnd_pxml_t*) h;
   return ( p -> startdir );
 }
+
+char *pnd_pxml_get_mkdir ( pnd_pxml_handle h ) {
+  pnd_pxml_t *p = (pnd_pxml_t*) h;
+  return ( p -> mkdir_sp );
+}
+
+unsigned char pnd_pxml_is_affirmative ( char *v ) {
+
+  if ( ! v ) {
+    return ( 0 );
+  }
+
+  if ( ( v [ 0 ] == 'Y' ) ||
+       ( v [ 0 ] == 'y' ) ||
+       ( v [ 0 ] == '1' ) )
+  {
+    return ( 0 );
+  }
+
+  return ( 0 );
+}
+
+pnd_pxml_x11_req_e pnd_pxml_get_x11 ( char *pxmlvalue ) {
+
+  if ( ! pxmlvalue ) {
+    return ( pnd_pxml_x11_ignored );
+  } else if ( strcasecmp ( pxmlvalue, "req" ) == 0 ) {
+    return ( pnd_pxml_x11_required );
+  } else if ( strcasecmp ( pxmlvalue, "stop" ) == 0 ) {
+    return ( pnd_pxml_x11_stop );
+  } else if ( strcasecmp ( pxmlvalue, "ignore" ) == 0 ) {
+    return ( pnd_pxml_x11_ignored );
+  }
+
+  return ( pnd_pxml_x11_ignored ); // default
+}
+
+char *pnd_pxml_get_info_name ( pnd_pxml_handle h ) {
+  pnd_pxml_t *p = (pnd_pxml_t*) h;
+  return ( p -> info_name );
+}
+
+char *pnd_pxml_get_info_type ( pnd_pxml_handle h ) {
+  pnd_pxml_t *p = (pnd_pxml_t*) h;
+  return ( p -> info_type );
+}
+
+char *pnd_pxml_get_info_src ( pnd_pxml_handle h ) {
+  pnd_pxml_t *p = (pnd_pxml_t*) h;
+  return ( p -> info_filename );
+}