Added in rudimentary category support for dotdesktop file emitting; ie: PXML's catego...
authorskeezix <skeezix@flotsam-vm.(none)>
Sat, 10 Oct 2009 08:50:37 +0000 (04:50 -0400)
committerskeezix <skeezix@flotsam-vm.(none)>
Sat, 10 Oct 2009 08:50:37 +0000 (04:50 -0400)
Also included some possibly sensible defaults for categories
Fixed a hard to spot parsing bug in pnd_tinyxml (& instead of |, bad dflem ;) so categories are pulled in correctly now
- really, we should fix up the pxml struct (and disco struct) to have trees instead of fixedcount fields
- really really, we should fix disco-t to be derived from pxml-t to avoid the duplication; when it was simpler I thought it was a good idea to separate disco and pxml structures, but they're too fat now :/

deployment/etc/pandora/conf/categories [new file with mode: 0644]
include/pnd_conf.h
include/pnd_desktop.h
include/pnd_discovery.h
lib/pnd_conf.c
lib/pnd_desktop.c
lib/pnd_discovery.c
lib/pnd_tinyxml.cpp
testdata/apps/sampleapp1/PXML.xml
testdata/conf/categories [new file with mode: 0644]

diff --git a/deployment/etc/pandora/conf/categories b/deployment/etc/pandora/conf/categories
new file mode 100644 (file)
index 0000000..1291793
--- /dev/null
@@ -0,0 +1,27 @@
+
+# Open Pandora
+# dotdesktop configuration
+
+# this config file maps 'PXML' categories to free-desktop standard categories
+# ie: category 'Foo' could map to more standard 'Utility', thus making .desktop file
+# emitting a more useful thing
+
+# the standard listing of categories is:
+# http://standards.freedesktop.org/menu-spec/latest/apa.html
+
+# note that 'map' section in the config is _required_ for a match to be found; this
+# is done to separate categories from (future) top-level directives
+
+default        Application;Utility;Network;
+
+[map]
+Development    Development
+Education      Education
+Games  Game
+Graphics       Graphics
+Internet       Network
+Multimedia     AudioVideo
+Office Office
+Settings       Settings
+System System
+Utilities      Utility
index 0332c7c..6cd18f5 100644 (file)
@@ -67,6 +67,7 @@ typedef enum {
   pnd_conf_apps,           // provides application search-path, pxml override location, etc.
   pnd_conf_startup,        // provides list of startup applications, basic shell application, etc.
   pnd_conf_desktop,        // provides settings for the launchers
   pnd_conf_apps,           // provides application search-path, pxml override location, etc.
   pnd_conf_startup,        // provides list of startup applications, basic shell application, etc.
   pnd_conf_desktop,        // provides settings for the launchers
+  pnd_conf_categories,     // provides mapping from PXML category to dot-desktop category
 } pnd_conf_filename_e;
 
 typedef struct {
 } pnd_conf_filename_e;
 
 typedef struct {
@@ -114,7 +115,7 @@ pnd_conf_handle pnd_conf_fetch_by_path ( char *fullpath );
  * config file accessor functions public API
  */
 
  * config file accessor functions public API
  */
 
-/* get_as_char() will attempt to locate the specified key string (of format section,key) in the
+/* get_as_char() will attempt to locate the specified key string (of format section.key) in the
  * provided config handle. Do not free up this value, it is considered read only.
  * Returns NULL on error, otherwise a READ ONLY char* reference to the value.
  */
  * provided config handle. Do not free up this value, it is considered read only.
  * Returns NULL on error, otherwise a READ ONLY char* reference to the value.
  */
index 7e0b2ee..a6aba00 100644 (file)
@@ -20,6 +20,21 @@ unsigned char pnd_emit_dotdesktop ( char *targetpath, char *pndrun, pnd_disco_t
 // to the given directory; returns 1 on sucess, otherwise is a fail.
 unsigned char pnd_emit_icon ( char *targetpath, pnd_disco_t *p );
 
 // to the given directory; returns 1 on sucess, otherwise is a fail.
 unsigned char pnd_emit_icon ( char *targetpath, pnd_disco_t *p );
 
+// pnd_map_dotdesktop_categories() will attempt to find an appropriate standard .desktop category(s) based
+// on the provided PXML-style category.
+//   In essence, the PXML top-level (and alternate) category will be used, but if (for example) the top
+// level primary cat is no good, then it will descend into the alternates for that to find the mapping. 
+//   NOTE: PXML has a prime and alt category, with sub-category; the standard only supports a flat list of
+// categories to show an entry in (though any number of 'alternates.' so the formats are not directly
+// compatible.
+//   Pass in the PXML handle, and the target buffer will be filled up
+//   Returns the number of successful mappings on success, or -1 for error (0 for no matches of course.)
+// QUESTION: It possible makes sense to just copy over verbatim any categories that are unmapped, as a 'best guess'
+//   scenario, but it currently is not coded like that.
+int pnd_map_dotdesktop_categories ( pnd_conf_handle c, char *target_buffer, unsigned short int len, pnd_disco_t *d ); // <---
+char *pnd_map_dotdesktop_category ( pnd_conf_handle c, char *single_category ); // not likely needed by anyone
+// this default is only used if the conf file's "default" key cannot be found, and the category cannot be mapped
+#define PND_DOTDESKTOP_DEFAULT_CATEGORY "Application;Utility;"
 
 #ifdef __cplusplus
 } /* "C" */
 
 #ifdef __cplusplus
 } /* "C" */
index 17d10a7..97d8ebc 100644 (file)
@@ -47,15 +47,20 @@ typedef struct {
   char *object_path;           // directory containing pnd or PXML.xml (does not include filename)
   char *object_filename;       // filename within object_path of the app: the PXML.xml or awesomeapp.pnd file itself
   unsigned int pnd_icon_pos;   // offset to the byte after end of PXML in a pnd file (should be icon if present)
   char *object_path;           // directory containing pnd or PXML.xml (does not include filename)
   char *object_filename;       // filename within object_path of the app: the PXML.xml or awesomeapp.pnd file itself
   unsigned int pnd_icon_pos;   // offset to the byte after end of PXML in a pnd file (should be icon if present)
-  // strdup'd from PXML
+  // strdup'd from PXML -- hey, who was the idiot who thought it was a reat idea not to just re-use the pxml-struct?
   char *title_en;
   char *unique_id;
   char *icon;
   char *exec;
   char *title_en;
   char *unique_id;
   char *icon;
   char *exec;
-  char *main_category;
   char *clockspeed;
   char *startdir;
   char *option_no_x11;
   char *clockspeed;
   char *startdir;
   char *option_no_x11;
+  char *main_category;
+  char *main_category1;
+  char *main_category2;
+  char *alt_category;
+  char *alt_category1;
+  char *alt_category2;
 } pnd_disco_t;
 
 void pnd_disco_destroy ( pnd_disco_t *p ); // a function name that simply could not be avoided
 } pnd_disco_t;
 
 void pnd_disco_destroy ( pnd_disco_t *p ); // a function name that simply could not be avoided
index 10dc9d1..e3077c2 100644 (file)
@@ -13,6 +13,7 @@ pnd_conf_filename_t pnd_conf_filenames[] = {
   { pnd_conf_apps,         "apps" },
   { pnd_conf_startup,      "startup" },
   { pnd_conf_desktop,      "desktop" },
   { pnd_conf_apps,         "apps" },
   { pnd_conf_startup,      "startup" },
   { pnd_conf_desktop,      "desktop" },
+  { pnd_conf_categories,   "categories" },
   { pnd_conf_nil,          NULL },
 };
 
   { pnd_conf_nil,          NULL },
 };
 
index bac1634..b5b9a40 100644 (file)
@@ -2,12 +2,15 @@
 #include <stdio.h> /* for FILE etc */
 #include <string.h>
 #include <unistd.h> /* for unlink */
 #include <stdio.h> /* for FILE etc */
 #include <string.h>
 #include <unistd.h> /* for unlink */
+#include <stdlib.h> /* for free */
 
 #include "pnd_apps.h"
 #include "pnd_container.h"
 #include "pnd_pxml.h"
 #include "pnd_discovery.h"
 #include "pnd_pndfiles.h"
 
 #include "pnd_apps.h"
 #include "pnd_container.h"
 #include "pnd_pxml.h"
 #include "pnd_discovery.h"
 #include "pnd_pndfiles.h"
+#include "pnd_conf.h"
+#include "pnd_desktop.h"
 
 unsigned char pnd_emit_dotdesktop ( char *targetpath, char *pndrun, pnd_disco_t *p ) {
   char filename [ FILENAME_MAX ];
 
 unsigned char pnd_emit_dotdesktop ( char *targetpath, char *pndrun, pnd_disco_t *p ) {
   char filename [ FILENAME_MAX ];
@@ -109,9 +112,49 @@ unsigned char pnd_emit_dotdesktop ( char *targetpath, char *pndrun, pnd_disco_t
     fprintf ( f, "%s", buffer );
   }
 
     fprintf ( f, "%s", buffer );
   }
 
-#if 1 // categories
-  fprintf ( f, "%s\n", "Categories=Application;Network;" );
-#endif
+  // categories
+  {
+    char cats [ 512 ] = "";
+    int n;
+    pnd_conf_handle c;
+    char *confpath;
+
+    // uuuuh, defaults?
+    // "Application" used to be in the standard and is commonly supported still
+    // Utility and Network should ensure the app is visible 'somewhere' :/
+    char *defaults = PND_DOTDESKTOP_DEFAULT_CATEGORY;
+
+    // determine searchpath (for conf, not for apps)
+    confpath = pnd_conf_query_searchpath();
+
+    // inhale the conf file
+    c = pnd_conf_fetch_by_id ( pnd_conf_categories, confpath );
+
+    // if we can find a default category set, pull it in; otherwise assume
+    // the hardcoded one
+    if ( pnd_conf_get_as_char ( c, "default" ) ) {
+      defaults = pnd_conf_get_as_char ( c, "default" );
+    }
+
+    // ditch the confpath
+    free ( confpath );
+
+    // attempt mapping
+    if ( c ) {
+
+      n = pnd_map_dotdesktop_categories ( c, cats, 511, p );
+
+      if ( n ) {
+       fprintf ( f, "Categories=%s\n", cats );
+      } else {
+       fprintf ( f, "Categories=%s\n", defaults );
+      }
+
+    } else {
+      fprintf ( f, "Categories=%s\n", defaults );
+    }
+
+  }
 
   fprintf ( f, "%s\n", PND_DOTDESKTOP_SOURCE ); // should we need to know 'who' created the file during trimming
 
 
   fprintf ( f, "%s\n", PND_DOTDESKTOP_SOURCE ); // should we need to know 'who' created the file during trimming
 
@@ -199,3 +242,142 @@ unsigned char pnd_emit_icon ( char *targetpath, pnd_disco_t *p ) {
 
   return ( 1 );
 }
 
   return ( 1 );
 }
+
+//int pnd_map_dotdesktop_categories ( pnd_conf_handle c, char *target_buffer, unsigned short int len, pnd_pxml_handle h ) {
+int pnd_map_dotdesktop_categories ( pnd_conf_handle c, char *target_buffer, unsigned short int len, pnd_disco_t *d ) {
+  unsigned short int n = 0; // no. matches
+  char *t;
+  char *match;
+
+  // clear target so we can easily append
+  memset ( target_buffer, '\0', len );
+
+  /* attempt primary category chain
+   */
+  match = NULL;
+
+  if ( ( t = d -> main_category ) ) {
+    match = pnd_map_dotdesktop_category ( c, t );
+  }
+
+  if ( ( ! match ) &&
+       ( t = d -> main_category1 ) )
+  {
+    match = pnd_map_dotdesktop_category ( c, t );
+  }
+
+  if ( ( ! match ) &&
+       ( t = d -> main_category2 ) )
+  {
+    match = pnd_map_dotdesktop_category ( c, t );
+  }
+  
+  if ( match ) {
+    strncat ( target_buffer, match, len );
+    len -= strlen ( target_buffer );
+    n += 1;
+  }
+
+  /* attempt secondary category chain
+   */
+  match = NULL;
+
+  if ( ( t = d -> alt_category ) ) {
+    match = pnd_map_dotdesktop_category ( c, t );
+  }
+
+  if ( ( ! match ) &&
+       ( t = d -> alt_category1 ) )
+  {
+    match = pnd_map_dotdesktop_category ( c, t );
+  }
+
+  if ( ( ! match ) &&
+       ( t = d -> alt_category2 ) )
+  {
+    match = pnd_map_dotdesktop_category ( c, t );
+  }
+  
+  if ( match ) {
+    if ( target_buffer [ 0 ] != '\0' && len > 0 ) {
+      strcat ( target_buffer, ";" );
+      len -= 1;
+    }
+    strncat ( target_buffer, match, len );
+    len -= strlen ( target_buffer );
+    n += 1;
+  }
+
+#if 0 // doh, originally I was using pxml-t till I realized I pre-boned myself on that one
+  match = NULL;
+
+  if ( ( t = pnd_pxml_get_main_category ( h ) ) ) {
+    match = pnd_map_dotdesktop_category ( c, t );
+  }
+
+  if ( ( ! match ) &&
+       ( t = pnd_pxml_get_subcategory1 ( h ) ) )
+  {
+    match = pnd_map_dotdesktop_category ( c, t );
+  }
+
+  if ( ( ! match ) &&
+       ( t = pnd_pxml_get_subcategory2 ( h ) ) )
+  {
+    match = pnd_map_dotdesktop_category ( c, t );
+  }
+  
+  if ( match ) {
+    strncat ( target_buffer, match, len );
+    len -= strlen ( target_buffer );
+    n += 1;
+  }
+
+  /* attempt secondary category chain
+   */
+  match = NULL;
+
+  if ( ( t = pnd_pxml_get_altcategory ( h ) ) ) {
+    match = pnd_map_dotdesktop_category ( c, t );
+  }
+
+  if ( ( ! match ) &&
+       ( t = pnd_pxml_get_altsubcategory1 ( h ) ) )
+  {
+    match = pnd_map_dotdesktop_category ( c, t );
+  }
+
+  if ( ( ! match ) &&
+       ( t = pnd_pxml_get_altsubcategory2 ( h ) ) )
+  {
+    match = pnd_map_dotdesktop_category ( c, t );
+  }
+  
+  if ( match ) {
+    if ( target_buffer [ 0 ] != '\0' && len > 0 ) {
+      strcat ( target_buffer, ";" );
+      len -= 1;
+    }
+    strncat ( target_buffer, match, len );
+    len -= strlen ( target_buffer );
+    n += 1;
+  }
+#endif
+
+  if ( n && len ) {
+    strcat ( target_buffer, ";" );
+  }
+
+  return ( n );
+}
+
+// given category 'foo', look it up in the provided config map. return the char* reference, or NULL
+char *pnd_map_dotdesktop_category ( pnd_conf_handle c, char *single_category ) {
+  char *key;
+
+  key = malloc ( strlen ( single_category ) + 4 + 1 );
+
+  sprintf ( key, "map.%s", single_category );
+
+  return ( pnd_conf_get_as_char ( c, key ) );
+}
index df0aa7e..c150f9f 100644 (file)
@@ -23,29 +23,19 @@ static char *disco_overrides = NULL;
 
 void pnd_disco_destroy ( pnd_disco_t *p ) {
 
 
 void pnd_disco_destroy ( pnd_disco_t *p ) {
 
-  if ( p -> title_en ) {
-    free ( p -> title_en );
-  }
-
-  if ( p -> icon ) {
-    free ( p -> icon );
-  }
-
-  if ( p -> exec ) {
-    free ( p -> exec );
-  }
-
-  if ( p -> unique_id ) {
-    free ( p -> unique_id );
-  }
-
-  if ( p -> main_category ) {
-    free ( p -> main_category );
-  }
-
-  if ( p -> clockspeed ) {
-    free ( p -> clockspeed );
-  }
+  if ( p -> title_en ) {       free ( p -> title_en );    }
+  if ( p -> unique_id ) {      free ( p -> unique_id );   }
+  if ( p -> icon )     {       free ( p -> icon );        }
+  if ( p -> exec )     {       free ( p -> exec );        }
+  if ( p -> clockspeed ) {     free ( p -> clockspeed );  }
+  if ( p -> startdir ) {       free ( p -> startdir );    }
+  if ( p -> option_no_x11 ) {  free ( p -> option_no_x11 );  }
+  if ( p -> main_category ) {  free ( p -> main_category );  }
+  if ( p -> main_category1 ) { free ( p -> main_category1 ); }
+  if ( p -> main_category2 ) { free ( p -> main_category2 ); }
+  if ( p -> alt_category ) {   free ( p -> alt_category );   }
+  if ( p -> alt_category1 ) {  free ( p -> alt_category1 );  }
+  if ( p -> alt_category2 ) {  free ( p -> alt_category2 );  }
 
   return;
 }
 
   return;
 }
@@ -187,15 +177,31 @@ static int pnd_disco_callback ( const char *fpath, const struct stat *sb,
       if ( pnd_pxml_get_unique_id ( pxmlh ) ) {
        p -> unique_id = strdup ( pnd_pxml_get_unique_id ( pxmlh ) );
       }
       if ( pnd_pxml_get_unique_id ( pxmlh ) ) {
        p -> unique_id = strdup ( pnd_pxml_get_unique_id ( pxmlh ) );
       }
-      if ( pnd_pxml_get_main_category ( pxmlh ) ) {
-       p -> main_category = strdup ( pnd_pxml_get_main_category ( pxmlh ) );
-      }
       if ( pnd_pxml_get_clockspeed ( pxmlh ) ) {
        p -> clockspeed = strdup ( pnd_pxml_get_clockspeed ( pxmlh ) ); 
       }
       if ( pnd_pxml_get_startdir ( pxmlh ) ) {
        p -> startdir = strdup ( pnd_pxml_get_startdir ( pxmlh ) ); 
       }
       if ( pnd_pxml_get_clockspeed ( pxmlh ) ) {
        p -> clockspeed = strdup ( pnd_pxml_get_clockspeed ( pxmlh ) ); 
       }
       if ( pnd_pxml_get_startdir ( pxmlh ) ) {
        p -> startdir = strdup ( pnd_pxml_get_startdir ( pxmlh ) ); 
       }
+      // category kruft
+      if ( pnd_pxml_get_main_category ( pxmlh ) ) {
+       p -> main_category = strdup ( pnd_pxml_get_main_category ( pxmlh ) );
+      }
+      if ( pnd_pxml_get_subcategory1 ( pxmlh ) ) {
+       p -> main_category1 = strdup ( pnd_pxml_get_subcategory1 ( pxmlh ) );
+      }
+      if ( pnd_pxml_get_subcategory2 ( pxmlh ) ) {
+       p -> main_category2 = strdup ( pnd_pxml_get_subcategory2 ( pxmlh ) );
+      }
+      if ( pnd_pxml_get_altcategory ( pxmlh ) ) {
+       p -> alt_category = strdup ( pnd_pxml_get_altcategory ( pxmlh ) );
+      }
+      if ( pnd_pxml_get_altsubcategory1 ( pxmlh ) ) {
+       p -> alt_category1 = strdup ( pnd_pxml_get_altsubcategory1 ( pxmlh ) );
+      }
+      if ( pnd_pxml_get_altsubcategory2 ( pxmlh ) ) {
+       p -> alt_category2 = strdup ( pnd_pxml_get_altsubcategory2 ( pxmlh ) );
+      }
 
     } else {
       //printf ( "Invalid PXML; skipping.\n" );
 
     } else {
       //printf ( "Invalid PXML; skipping.\n" );
index 5e53ab9..6a7e2e3 100644 (file)
@@ -242,7 +242,7 @@ unsigned char pnd_pxml_parse ( const char *pFilename, char *buffer, unsigned int
                                if (!(subcat)) continue;
 
                                //TODO: This is ugly. Fix pnd_pxml_t so that there can be more than 2 category 'trees' and more than 2 subcategories. Then this can be removed.
                                if (!(subcat)) continue;
 
                                //TODO: This is ugly. Fix pnd_pxml_t so that there can be more than 2 category 'trees' and more than 2 subcategories. Then this can be removed.
-                               switch (j & (i << 1))
+                               switch (j | (i << 1))
                                {
                                case 0:
                                        app->subcategory1 = subcat;
                                {
                                case 0:
                                        app->subcategory1 = subcat;
index 893c1cd..e761151 100644 (file)
@@ -20,12 +20,12 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="jeff.sample.1" xsi:noN
   <osversion major="1" minor="0" release="0" build="0"/><!--The minimum OS version required-->
 
   <categories>
   <osversion major="1" minor="0" release="0" build="0"/><!--The minimum OS version required-->
 
   <categories>
-    <category name="Main category"><!--category like "Games", "Graphics", "Internet" etc-->
-    <subcategory name="Subcategory 1"/><!--subcategory, like "Board Games", "Strategy", "First Person Shooters"-->
-    <subcategory name="Subcategory 2"/>
+    <category name="MainCategory"><!--category like "Games", "Graphics", "Internet" etc-->
+      <subcategory name="Subcategory1"/><!--subcategory, like "Board Games", "Strategy", "First Person Shooters"-->
+      <subcategory name="Subcategory2"/>
     </category>
     </category>
-    <category name="Alternative category">
-      <subcategory name="Alternative Subcategory 1"/>
+    <category name="AlternativeCategory">
+      <subcategory name="AlternativeSubcategory1"/>
     </category>
   </categories>
 
     </category>
   </categories>
 
diff --git a/testdata/conf/categories b/testdata/conf/categories
new file mode 100644 (file)
index 0000000..1d09696
--- /dev/null
@@ -0,0 +1,28 @@
+
+# Open Pandora
+# dotdesktop configuration
+
+# this config file maps 'PXML' categories to free-desktop standard categories
+# ie: category 'Foo' could map to more standard 'Utility', thus making .desktop file
+# emitting a more useful thing
+
+# the standard listing of categories is:
+# http://standards.freedesktop.org/menu-spec/latest/apa.html
+
+# note that 'map' section in the config is _required_ for a match to be found; this
+# is done to separate categories from (future) top-level directives
+
+default        Application;Utility;Network;
+
+[map]
+Subcategory1   Graphics
+Development    Development
+Education      Education
+Games  Game
+Graphics       Graphics
+Internet       Network
+Multimedia     AudioVideo
+Office Office
+Settings       Settings
+System System
+Utilities      Utility