65df986b02c855f960b087cc84cfb78f0391ecbe — Louis Solofrizzo 4 months ago 2b4cb97
lib: Add locale support, list and k/v store

I've also added all the free routines

Signed-off-by: Louis Solofrizzo <lsolofrizzo@online.net>
3 files changed, 527 insertions(+), 106 deletions(-)

M include/desktop.h
M main.c
M src/list.c
M include/desktop.h => include/desktop.h +65 -47
@@ 7,25 7,29 @@ #include <sys/stat.h>
  #include <unistd.h>
  
- typedef struct {
-     char        **array;
-     size_t      size;
+ typedef struct dsktp_str_array_s {
+     char                        *val;
+ 
+     struct dsktp_str_array_s    *next;
+     struct dsktp_str_array_s    *prev;
  } dsktp_str_array_t;
  
  typedef struct dsktp_kv_str_s {
      char                        *key;
      char                        *value;
+ 
      struct dsktp_kv_str_s       *next;
+     struct dsktp_kv_str_s       *prev;
  } dsktp_kv_str_t;
  
  typedef struct {
-     char        *locale;
      char        *str;
+     int         score;
  } dsktp_locale_str_t;
  
- typedef struct {
-     dsktp_locale_str_t  *array;
-     size_t              size;
+ typedef struct dsktp_locale_str_s {
+     dsktp_str_array_t           *array;
+     int                         score;
  } dsktp_locale_str_array_t;
  
  typedef enum {


@@ 39,62 43,64 @@ * Application entry
   * See https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
   */
- typedef struct {
-     char                *group;            /*!< Group name of the entry */
-     dsktp_app_type_t    type;              /*!< Type of the entry. REQUIRED */
-     char                *version;          /*!< Version string */
-     dsktp_locale_str_t  name;              /*!< Name of the entry. REQUIRED */
-     dsktp_locale_str_t  generic_name;      /*!< Generic name */
-     bool                no_display;        /*!< Is the entry to be displayed ? */
-     dsktp_locale_str_t  comment;           /*!< Comment about the entry */
-     dsktp_locale_str_t  icon;              /*!< Icon of the entry */
-     dsktp_str_array_t   only_show_in;      /*!< List of whitelisted WM */
-     dsktp_str_array_t   not_show_in;       /*!< List of blacklisted WM */
-     bool                dbus_activatable;  /*!< Is Dbus activation supported ? */
-     char                *try_exec;         /*!< Path to determine if the underlying program is installed */
-     char                *exec;             /*!< Program to execute, possibly with arguments */
-     char                *path;             /*!< Running directory */
-     bool                terminal;          /*!< Is the entry to be run in a term window ? */
-     dsktp_str_array_t   actions;           /*!< Identifiers for applications actions */
-     dsktp_str_array_t   mime_types;        /*!< MIME type(s) supported by this application */
-     dsktp_str_array_t   categories;        /*!< Categories for this application */
-     dsktp_str_array_t   implements;        /*!< A list of interfaces that this application implements */
-     dsktp_str_array_t   keywords;          /*!< A list of strings which may be used to describe the entry */
-     bool                startup_notify;    /*!< See the spec */
-     char                *startup_wm_class; /*!< Window name */
-     char                *url;              /*!< URL, for DSKTP_LINK types */
- 
-     dsktp_kv_str_t      *personnal;        /*!< Personnal K/V Store */
+ typedef struct dsktp_app_entries_s {
+     char                        *group;            /*!< Group name of the entry */
+     dsktp_app_type_t            type;              /*!< Type of the entry. REQUIRED */
+     char                        *version;          /*!< Version string */
+     dsktp_locale_str_t          name;              /*!< Name of the entry. REQUIRED */
+     dsktp_locale_str_t          generic_name;      /*!< Generic name */
+     bool                        no_display;        /*!< Is the entry to be displayed ? */
+     bool                        hidden;            /*!< Hide the entry ? */
+     dsktp_locale_str_t          comment;           /*!< Comment about the entry */
+     dsktp_locale_str_t          icon;              /*!< Icon of the entry */
+     dsktp_str_array_t           *only_show_in;     /*!< List of whitelisted WM */
+     dsktp_str_array_t           *not_show_in;      /*!< List of blacklisted WM */
+     bool                        dbus_activatable;  /*!< Is Dbus activation supported ? */
+     char                        *try_exec;         /*!< Path to determine if the underlying program is installed */
+     char                        *exec;             /*!< Program to execute, possibly with arguments */
+     char                        *path;             /*!< Running directory */
+     bool                        terminal;          /*!< Is the entry to be run in a term window ? */
+     dsktp_str_array_t           *actions;          /*!< Identifiers for applications actions */
+     dsktp_str_array_t           *mime_types;       /*!< MIME type(s) supported by this application */
+     dsktp_str_array_t           *categories;       /*!< Categories for this application */
+     dsktp_str_array_t           *implements;       /*!< A list of interfaces that this application implements */
+     dsktp_locale_str_array_t    keywords;          /*!< A list of strings which may be used to describe the entry */
+     bool                        startup_notify;    /*!< See the spec */
+     char                        *startup_wm_class; /*!< Window name */
+     char                        *url;              /*!< URL, for DSKTP_LINK types */
+ 
+     dsktp_kv_str_t              *personnal;        /*!< Personnal K/V Store ('X-*' keys)*/
  
      /* Deprecated */
-     char                *encoding;
+     char                        *encoding;
  
      /* Compatibility */
-     char                *service_types;
-     char                *doc_path;
-     char                *initial_preference;
-     char                *target_environment;
+     char                        *service_types;
+     char                        *doc_path;
+     char                        *initial_preference;
+     char                        *target_environment;
  
+     struct dsktp_app_entries_s  *next;
+     struct dsktp_app_entries_s  *prev;
  } dsktp_app_entries_t;
  
  
  typedef struct dsktp_app_s {
-     dsktp_app_entries_t         main_entries;      /*!< Main entries of the app */
+     dsktp_app_entries_t main_entries;      /*!< Main entries of the app */
  
-     dsktp_app_entries_t         *others_entries;   /*!< Array of other groups */
-     size_t                      others_size;       /*!< Number of elements in other_entries */
+     dsktp_app_entries_t *others_entries;   /*!< Array of other groups */
  
-     struct timespec             creation_time;     /*!< Creation time of the app */
-     struct timespec             modification_time; /*!< Modification time of the app */
+     time_t              creation_time;     /*!< Creation time of the app */
+     time_t              modification_time; /*!< Modification time of the app */
  
-     struct dsktp_app_s          *next;
-     struct dsktp_app_s          *prev;
+     struct dsktp_app_s  *next;
+     struct dsktp_app_s  *prev;
  } dsktp_app_t;
  
  typedef struct {
      dsktp_app_t         *applications; /*!< Applications list */
      char                *locale;
-     struct timespec     last_modification;
+     time_t              last_modification;
  } dsktp_ctx_t;
  
  /*!


@@ 104,6 110,18 @@ *
   * \return true on success, false on failure
   */
- bool dsktp_get_applications(const dsktp_ctx_t *ctx);
+ bool dsktp_get_applications(dsktp_ctx_t *ctx);
+ 
+ void dsktp_free_ctx(dsktp_ctx_t *ctx);
+ 
+ #define DSKTP_LIST_FOR_EACH(i, t)       \
+     i = t;                              \
+     for (size_t __j = 0; i != NULL && (i != t || __j == 0); i = i->next, __j++)
+ 
+ #define DSKTP_LIST_FOR_EACH_SAFE(i, tmp, t)     \
+     i = t;                                      \
+     if (i != NULL) \
+         tmp = i->next; \
+     for (size_t __j = 0; i != NULL && (i != t || __j == 0); i = tmp, tmp = (tmp != NULL ? tmp->next : NULL), __j++)
  
  #endif /* DSKTP_H */

M main.c => main.c +54 -0
@@ 4,6 4,47 @@ #include <errno.h>
  #include <string.h>
  
+ static void dump_entries(const dsktp_app_entries_t *entries)
+ {
+     dsktp_kv_str_t *iter;
+     dsktp_str_array_t *tmp;
+ 
+     printf("\tName : %s\n", entries->name.str);
+     printf("\tExec : %s\n", entries->exec);
+     printf("\tIcon : %s\n", entries->icon.str);
+ 
+     printf("\tCategories :\n");
+     DSKTP_LIST_FOR_EACH(tmp, entries->categories)
+     {
+         printf("\t\t- %s\n", tmp->val);
+     }
+ 
+     printf("\tKeywords :\n");
+     DSKTP_LIST_FOR_EACH(tmp, entries->keywords.array)
+     {
+         printf("\t\t- %s\n", tmp->val);
+     }
+ 
+     DSKTP_LIST_FOR_EACH(iter, entries->personnal)
+     {
+         printf("\t%s : %s\n", iter->key, iter->value);
+     }
+ }
+ 
+ static void dump_app(const dsktp_app_t *app)
+ {
+     dsktp_app_entries_t *iter;
+ 
+     printf("Main entry:\n");
+     dump_entries(&app->main_entries);
+ 
+     DSKTP_LIST_FOR_EACH(iter, app->others_entries)
+     {
+         printf("Entry %s:\n", iter->group);
+         dump_entries(iter);
+     }
+ }
+ 
  int main(void) {
      dsktp_ctx_t  ctx = { 0 };
  


@@ 13,5 54,18 @@ return 1;
      }
  
+     dsktp_app_t *i;
+ 
+     int j =0;
+     DSKTP_LIST_FOR_EACH(i, ctx.applications)
+     {
+         dump_app(i);
+         printf("\n");
+         j++;
+     }
+ 
+     printf("End count: %d\n", j);
+     dsktp_free_ctx(&ctx);
+ 
      return 0;
  }

M src/list.c => src/list.c +408 -59
@@ 8,6 8,7 @@ #include <stdio.h>
  #include <string.h>
  #include <inttypes.h>
+ #include <locale.h>
  
  #define COUNT_OF(ptr) sizeof(ptr) / sizeof(ptr[0])
  #define STATIC_ARRAY_FOREACH(item, array) \


@@ 17,16 18,78 @@ #define APPLICATION_DIR "/usr/share/applications/"
  #define APPLICATION_EXT ".desktop"
  
+ #define ADD_TO_LIST(h, n) do {          \
+     if ((h) == NULL)                    \
+         (h) = (n);                      \
+     else                                \
+     {                                   \
+         if ((h)->prev)                  \
+         {                               \
+             (h)->prev->next = (n);      \
+             (n)->prev = (h)->prev;      \
+         }                               \
+         else {                          \
+             (n)->prev = (h);            \
+             (h)->next = (n);            \
+         }                               \
+         (n)->next = (h);                \
+         (h)->prev = (n);                \
+     }                                   \
+ } while (0)
+ 
  typedef enum {
      ENTRY_TYPE_TYPE,
      ENTRY_TYPE_STRING,
      ENTRY_TYPE_LOCALESTRING,
      ENTRY_TYPE_BOOL,
      ENTRY_TYPE_LIST,
-     ENTRY_TYPE_LOCALELIST,
-     ENTRY_TYPE_MAX
+     ENTRY_TYPE_LOCALELIST
  } entry_type_t;
  
+ typedef struct {
+     char                lang[20];
+     char                country[20];
+     char                modifier[20];
+     char                *full;
+ } dsktp_locale_t;
+ 
+ typedef struct {
+     char                *inode;
+     int                 line;
+     dsktp_app_t         *app;
+     dsktp_app_entries_t *current_entry;
+ 
+     dsktp_locale_t      locale;
+ } dsktp_parse_ctx_t;
+ 
+ static bool parse_locale(const char *in, dsktp_locale_t *out)
+ {
+     int         ret;
+ 
+     out->full = strdup(in);
+     if (out->full == NULL)
+         return false;
+ 
+     /* lang@MODIFIER format */
+     ret = sscanf(in, "%[^'@']@%s", out->lang, out->modifier);
+     if (ret == 2)
+         return true;
+ 
+     /* lang_COUNTRY */
+     ret = sscanf(in, "%[^'_']_%s", out->lang, out->country);
+     if (ret == 2)
+         return true;
+ 
+     /* lang_COUNTRY@MODIFIER format */
+     ret = sscanf(in, "%[^'_']_%[^'@']@%s", out->lang, out->country, out->modifier);
+     if (ret == 3)
+         return true;
+ 
+     /* It's a simple 'lang' */
+     strncpy(out->lang, in, 20);
+     return true;
+ }
+ 
  static bool parse_entry_type(const char *in, dsktp_app_type_t *out)
  {
      if (strcmp(in, "Application") == 0)


@@ 49,6 112,161 @@ return *out != NULL;
  }
  
+ static bool parse_entry_bool(const char *in, bool *out)
+ {
+     if (strcmp(in, "True") == 0)
+         *out = true;
+     else
+         *out = false;
+     return true;
+ }
+ 
+ static bool parse_entry_list(const char *in, dsktp_str_array_t **out)
+ {
+     dsktp_str_array_t   *tmp;
+     size_t              len = strlen(in);
+     size_t              i = 0, j = 0;
+ 
+     while (true)
+     {
+         while (i < len && in[i] != ';')
+             i++;
+ 
+         if (i == len)
+             break;
+ 
+         if (i - 1 == j)
+             continue;
+ 
+         while (in[j] == ' ')
+             j++;
+ 
+         tmp = calloc(1, sizeof(*tmp));
+         tmp->val = strndup(in + j, i - j);
+         ADD_TO_LIST(*out, tmp);
+ 
+         i++;
+         j = i;
+     }
+ 
+     if (j != 0 && i != j)
+     {
+         tmp = calloc(1, sizeof(*tmp));
+         tmp->val = strndup(in + j, i - j);
+         ADD_TO_LIST(*out, tmp);
+     }
+ 
+     return true;
+ }
+ 
+ #define EXISTS(n) n[0] != '\0'
+ #define EQUALS(n) strcmp(one->n, two->n) == 0
+ static int compute_match_locale(dsktp_locale_t *one, dsktp_locale_t *two)
+ {
+     /* Perfect match */
+     if (strcmp(one->full, two->full) == 0)
+         return 100;
+ 
+     if (EXISTS(one->lang) && EXISTS(one->country) && EXISTS(one->modifier))
+     {
+         if (EQUALS(lang) && EQUALS(country))
+             return 75;
+ 
+         if (EQUALS(lang) && EQUALS(modifier))
+             return 50;
+ 
+         if (EQUALS(lang))
+             return 25;
+     }
+ 
+     if (EXISTS(one->lang) && EXISTS(one->country))
+     {
+         if (EQUALS(lang) && EQUALS(country))
+             return 75;
+ 
+         if (EQUALS(lang))
+             return 25;
+     }
+ 
+     if (EXISTS(one->lang) && EXISTS(one->modifier))
+     {
+         if (EQUALS(lang))
+             return 25;
+     }
+ 
+     if (EXISTS(one->lang) && EQUALS(lang))
+         return 25;
+ 
+     return 0;
+ }
+ #undef EXISTS
+ #undef EQUALS
+ 
+ static bool parse_entry_locale_string(dsktp_parse_ctx_t *ctx, const char *in, const char *post, dsktp_locale_str_t *out)
+ {
+     dsktp_locale_t      locale = { 0 };
+     int                 score;
+ 
+     /* We've got no current value and it's the default key, let's save it */
+     if (out->str == NULL && post == NULL)
+     {
+         out->str = strdup(in);
+         return out->str != NULL;
+     }
+ 
+     /* We already have a value and this is the default */
+     if (post == NULL && out->str != NULL)
+         return true;
+ 
+     if (!parse_locale(post, &locale))
+         return false;
+ 
+     score = compute_match_locale(&ctx->locale, &locale);
+ 
+     if (score > out->score)
+     {
+         free(out->str);
+         out->str = strdup(in);
+         out->score = score;
+     }
+ 
+     free(locale.full);
+     return true;
+ }
+ 
+ static bool parse_entry_locale_list(dsktp_parse_ctx_t *ctx, const char *in, const char *post, dsktp_locale_str_array_t *out)
+ {
+     dsktp_locale_t      locale = { 0 };
+     int                 score;
+ 
+     /* We've got no current value and it's the default key, let's save it */
+     if (out->array == NULL && post == NULL)
+         return parse_entry_list(in, &out->array);
+ 
+     /* We already have a value and this is the default */
+     if (post == NULL && out->array != NULL)
+         return true;
+ 
+     if (!parse_locale(post, &locale))
+         return false;
+ 
+     score = compute_match_locale(&ctx->locale, &locale);
+ 
+     if (score > out->score)
+     {
+         /* XXX: free array */
+         out->score = score;
+         if (!parse_entry_list(in, &out->array))
+         {
+             free(locale.full);
+             return false;
+         }
+     }
+ 
+     free(locale.full);
+     return true;
+ }
+ 
  typedef struct {
      entry_type_t        type;
      ssize_t             offset;


@@ 63,6 281,7 @@ ADD_TOKEN("Name", ENTRY_TYPE_LOCALESTRING, name),
      ADD_TOKEN("GenericName", ENTRY_TYPE_LOCALESTRING, generic_name),
      ADD_TOKEN("NoDisplay", ENTRY_TYPE_BOOL, no_display),
+     ADD_TOKEN("Hidden", ENTRY_TYPE_BOOL, hidden),
      ADD_TOKEN("Comment", ENTRY_TYPE_LOCALESTRING, comment),
      ADD_TOKEN("Icon", ENTRY_TYPE_LOCALESTRING, icon),
      ADD_TOKEN("OnlyShowIn", ENTRY_TYPE_LIST, only_show_in),


@@ 93,53 312,40 @@ ADD_TOKEN("TargetEnvironment", ENTRY_TYPE_STRING, target_environment),
  };
  
- typedef struct {
-     char                *inode;
-     int                 line;
-     dsktp_app_t         *app;
-     dsktp_app_entries_t *current_entry;
- } dsktp_parse_ctx_t;
- 
  static bool parse_entry_group(dsktp_parse_ctx_t *ctx, char *line, ssize_t *len)
  {
-      char    *type;
-      char    *name;
-      ssize_t i = 0;
- 
-      /* Remove the brackets */
-      line[*len - 1] = 0;
-      line++;
-      (*len)--;
- 
-      /* Look for the space */
-      for (i = 0; i < *len && line[i] != ' '; i++)
-          ;
- 
-      if (i == *len)
-          return true;
- 
-      line[i] = 0;
-      type = line;
-      name = line + i + 1;
- 
-      if (strcmp(type, "Desktop") == 0)
-      {
-          if (strcmp(name, "Entry") == 0)
-          {
-              ctx->current_entry = &ctx->app->main_entries;
-              ctx->current_entry->group = strdup(name);
-          }
-          else
-          {
-              /* XXX: Support custom groups */
-              ctx->current_entry = NULL;
-          }
-      }
-      else
-      {
-          ctx->current_entry = NULL;
-          /* XXX: Support custom actions / newwindow */
-      }
+     char    *name;
+     ssize_t i = 0;
+ 
+     /* Remove the brackets */
+     line[*len - 1] = 0;
+     line++;
+     (*len)--;
+ 
+     /* Look for the space */
+     for (i = 0; i < *len && line[i] != ' '; i++)
+         ;
+ 
+     if (i == *len)
+         return true;
+ 
+     name = line + i + 1;
+ 
+     if (strcmp(name, "Entry") == 0)
+     {
+         ctx->current_entry = &ctx->app->main_entries;
+         ctx->current_entry->group = strdup(name);
+     }
+     else
+     {
+         dsktp_app_entries_t *entries = calloc(1, sizeof(*entries));
+ 
+         if (ctx->current_entry != NULL && strcmp(ctx->current_entry->group, "Entry"))
+             ADD_TO_LIST(ctx->app->others_entries, ctx->current_entry);
+ 
+         ctx->current_entry = entries;
+         ctx->current_entry->group = strdup(name);
+     }
  
      return true;
  }


@@ 147,7 353,7 @@ static bool parse_token(dsktp_parse_ctx_t *ctx, char *line)
  {
      ssize_t     len = strlen(line);
-     ssize_t      i;
+     ssize_t      i, j;
      char        *token;
      char        *value;
  


@@ 184,13 390,14 @@ /* Skip personnal use */
      if (token[0] == 'X' && token[1] == '-')
      {
-         /* XXX: Save to personnal */
-         return true;
-     }
+         dsktp_kv_str_t *ptr = calloc(1, sizeof(*ptr));
+ 
+         ptr->key = strdup(token);
+         ptr->value = strdup(value);
  
-     /* Skip files marked with Hidden=true, per specification */
-     if (strcmp(token, "Hidden") == 0 && strcmp(value, "true") == 0)
+         ADD_TO_LIST(ctx->current_entry->personnal, ptr);
          return true;
+     }
  
      STATIC_ARRAY_FOREACH(const parse_callback_t *cb, cbs)
      {


@@ 206,7 413,6 @@ uintptr_t   ptr;
  
              ptr = (uintptr_t)ctx->current_entry + (uintptr_t)cb->offset;
-             /*printf("%s:%d: [%s] %s - %s\n", ctx->inode, ctx->line, ctx->current_entry->group, token, value);*/
  
              switch (cb->type)
              {


@@ 214,8 420,46 @@ return parse_entry_string(value, (char **)ptr);
                  case ENTRY_TYPE_TYPE:
                      return parse_entry_type(value, (dsktp_app_type_t *)ptr);
-                 default:
-                     return true;
+                 case ENTRY_TYPE_BOOL:
+                     return parse_entry_bool(value, (bool *)ptr);
+                 case ENTRY_TYPE_LOCALESTRING:
+                     /* Look for the opening bracket */
+                     for (i = 0; token[i] != '\0' && token[i] != '['; i++)
+                         ;
+ 
+                     /* No bracket, we've got a global value */
+                     if (token[i] == '\0')
+                         return parse_entry_locale_string(ctx, value, NULL, (dsktp_locale_str_t *)ptr);
+ 
+                     /* Look for the closing bracket */
+                     for (j = i; token[j] != '\0' && token[j] != ']'; j++)
+                         ;
+ 
+                     if (token[j] == '\0')
+                         return false;
+ 
+                     token[j] = '\0';
+                     return parse_entry_locale_string(ctx, value, token + i + 1, (dsktp_locale_str_t *)ptr);
+                 case ENTRY_TYPE_LIST:
+                     return parse_entry_list(value, (dsktp_str_array_t **)ptr);
+                 case ENTRY_TYPE_LOCALELIST:
+                     /* Look for the opening bracket */
+                     for (i = 0; token[i] != '\0' && token[i] != '['; i++)
+                         ;
+ 
+                     /* No bracket, we've got a global value */
+                     if (token[i] == '\0')
+                         return parse_entry_locale_list(ctx, value, NULL, (dsktp_locale_str_array_t *)ptr);
+ 
+                     /* Look for the closing bracket */
+                     for (j = i; token[j] != '\0' && token[j] != ']'; j++)
+                         ;
+ 
+                     if (token[j] == '\0')
+                         return false;
+ 
+                     token[j] = '\0';
+                     return parse_entry_locale_list(ctx, value, token + i + 1, (dsktp_locale_str_array_t *)ptr);
              }
  
              return true;


@@ 226,7 470,7 @@ return false;
  }
  
- static bool parse_file(const dsktp_ctx_t *ctx, int dirfd, char *inode, struct stat *st)
+ static bool parse_file(dsktp_ctx_t *ctx, int dirfd, char *inode, struct stat *st)
  {
      int         inode_fd = openat(dirfd, inode, O_RDONLY);
      FILE        *fd = fdopen(inode_fd, "r");


@@ 249,11 493,15 @@ .app = app,
      };
  
+     /* Parse the locale */
+     if (!parse_locale(ctx->locale, &parse_ctx.locale))
+         goto end;
+ 
      while ((read = getline(&line, &len, fd)) != -1)
      {
          if (!parse_token(&parse_ctx, line))
          {
-             free(line);
+             printf("Error: %s -> %s\n", inode, line);
              free(app);
              goto end;
          }


@@ 261,14 509,28 @@ parse_ctx.line++;
      }
  
+     if (parse_ctx.current_entry != NULL && strcmp(parse_ctx.current_entry->group, "Entry"))
+         ADD_TO_LIST(parse_ctx.app->others_entries, parse_ctx.current_entry);
+ 
+     app->creation_time = st->st_ctim.tv_sec;
+     app->modification_time = st->st_mtim.tv_sec;
+ 
+     /* Save the higher modification time, for lazy-reloading */
+     if (ctx->last_modification < app->modification_time)
+         ctx->last_modification = app->modification_time;
+ 
+     ADD_TO_LIST(ctx->applications, app);
+ 
      ret = true;
  end:
+     free(line);
+     free(parse_ctx.locale.full);
      fclose(fd);
      close(inode_fd);
      return ret;
  }
  
- bool dsktp_get_applications(const dsktp_ctx_t *ctx)
+ bool dsktp_get_applications(dsktp_ctx_t *ctx)
  {
      DIR                 *dir = opendir(APPLICATION_DIR);
      struct dirent       *ptr;


@@ 278,6 540,10 @@ if (dir == NULL)
          return false;
  
+     /* Get the system locale */
+     setlocale(LC_ALL, "");
+     ctx->locale = setlocale(LC_MESSAGES, NULL);
+ 
      /* For each inode in the directory */
      while ((ptr = readdir(dir)) != NULL)
      {


@@ 307,3 573,86 @@ closedir(dir);
      return ret;
  }
+ 
+ static void dsktp_free_str_array(dsktp_str_array_t *array)
+ {
+     dsktp_str_array_t   *iter, *tmp;
+ 
+     DSKTP_LIST_FOR_EACH_SAFE(iter, tmp, array)
+     {
+         free(iter->val);
+         if (iter != array)
+             free(iter);
+     }
+ 
+     free(array);
+ }
+ 
+ static void dsktp_free_kv_str(dsktp_kv_str_t *kv)
+ {
+     dsktp_kv_str_t      *iter, *tmp;
+     DSKTP_LIST_FOR_EACH_SAFE(iter, tmp, kv)
+     {
+         free(iter->key);
+         free(iter->value);
+         if (iter != kv)
+             free(iter);
+     }
+ 
+     free(kv);
+ }
+ 
+ static void dsktp_free_app_entries(dsktp_app_entries_t *entries)
+ {
+     free(entries->group);
+     free(entries->version);
+     free(entries->try_exec);
+     free(entries->exec);
+     free(entries->path);
+     free(entries->startup_wm_class);
+     free(entries->url);
+     free(entries->encoding);
+     free(entries->service_types);
+     free(entries->doc_path);
+     free(entries->initial_preference);
+     free(entries->target_environment);
+ 
+     free(entries->name.str);
+     free(entries->generic_name.str);
+     free(entries->comment.str);
+     free(entries->icon.str);
+ 
+     dsktp_free_str_array(entries->only_show_in);
+     dsktp_free_str_array(entries->not_show_in);
+     dsktp_free_str_array(entries->actions);
+     dsktp_free_str_array(entries->mime_types);
+     dsktp_free_str_array(entries->categories);
+     dsktp_free_str_array(entries->implements);
+     dsktp_free_str_array(entries->keywords.array);
+ 
+     dsktp_free_kv_str(entries->personnal);
+ }
+ 
+ void dsktp_free_ctx(dsktp_ctx_t *ctx)
+ {
+     dsktp_app_t         *iter, *tmp;
+     dsktp_app_entries_t *ent_iter, *ent_tmp;
+ 
+     DSKTP_LIST_FOR_EACH_SAFE(iter, tmp, ctx->applications)
+     {
+         dsktp_free_app_entries(&iter->main_entries);
+         DSKTP_LIST_FOR_EACH_SAFE(ent_iter, ent_tmp, iter->others_entries)
+         {
+             dsktp_free_app_entries(ent_iter);
+             if (ent_iter != iter->others_entries)
+                 free(ent_iter);
+         }
+ 
+         free(iter->others_entries);
+ 
+         if (iter != ctx->applications)
+             free(iter);
+     }
+ 
+     free(ctx->applications);
+ }