summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Untz <vuntz@gnome.org>2007-03-15 22:14:06 +0000
committerVincent Untz <vuntz@gnome.org>2007-03-15 22:14:06 +0000
commit4aab76b0fb470a1fcda17a6458ce210d44548478 (patch)
treed7ad4d7122b041ccb130e488d16375384fff6f4d
parent818536c358d110f695c68566a23d393a19d71904 (diff)
remove mention of desktop-menu-tool kill, was useless and deprecated
2007-03-15 Vincent Untz <vuntz@gnome.org> * README: remove mention of desktop-menu-tool * acconfig.h: kill, was useless and deprecated * src/eggintl.h: kill, was useless since quite some time * autogen.sh: * configure.in: updated because of src/desktop_file.h removal * src/Makefile.am: updated for file removals/additions * src/desktop_file.[ch]: removed. We don't use this anymore (it was based on GnomeDesktopItem which nobody maintains and is too complex for what we need) * keyfileutils.[ch]: new, contains some useful functions based on GKeyFile * src/install.c: updated for changes (GnomeDesktopFile -> GKeyFile) (process_one_file): ditto also, improves a bit the --help output * src/validate.[ch]: pretty much a rewrite. This is based on GKeyFile for now, but it'll be moved to a small parser soon, so we are not limited because of the GKeyFile parser. The validator verifies more things, warns about usage of deprecated stuff, and contains some other nice improvements. It probably contains some bugs, though. * src/validator.c: updated (well, rewritten, since it's only the main() function). We also now have some command line arguments: --warn-kde to warn about usage of KDE reserved stuff --no-warn-deprecated to not warn about usage of deprecated stuff
-rw-r--r--ChangeLog29
-rw-r--r--README2
-rw-r--r--acconfig.h10
-rwxr-xr-xautogen.sh2
-rw-r--r--configure.in4
-rw-r--r--src/Makefile.am8
-rw-r--r--src/desktop_file.c1729
-rw-r--r--src/desktop_file.h163
-rw-r--r--src/eggintl.h8
-rw-r--r--src/install.c147
-rw-r--r--src/keyfileutils.c214
-rw-r--r--src/keyfileutils.h47
-rw-r--r--src/validate.c2580
-rw-r--r--src/validate.h47
-rw-r--r--src/validator.c92
15 files changed, 2237 insertions, 2845 deletions
diff --git a/ChangeLog b/ChangeLog
index 69dfd14..223683d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,32 @@
+2007-03-15 Vincent Untz <vuntz@gnome.org>
+
+ * README: remove mention of desktop-menu-tool
+ * acconfig.h: kill, was useless and deprecated
+
+ * src/eggintl.h: kill, was useless since quite some time
+
+ * autogen.sh:
+ * configure.in: updated because of src/desktop_file.h removal
+
+ * src/Makefile.am: updated for file removals/additions
+ * src/desktop_file.[ch]: removed. We don't use this anymore (it was
+ based on GnomeDesktopItem which nobody maintains and is too complex
+ for what we need)
+ * keyfileutils.[ch]: new, contains some useful functions based on
+ GKeyFile
+ * src/install.c: updated for changes (GnomeDesktopFile -> GKeyFile)
+ (process_one_file): ditto
+ also, improves a bit the --help output
+ * src/validate.[ch]: pretty much a rewrite. This is based on GKeyFile
+ for now, but it'll be moved to a small parser soon, so we are not
+ limited because of the GKeyFile parser. The validator verifies more
+ things, warns about usage of deprecated stuff, and contains some other
+ nice improvements. It probably contains some bugs, though.
+ * src/validator.c: updated (well, rewritten, since it's only the
+ main() function). We also now have some command line arguments:
+ --warn-kde to warn about usage of KDE reserved stuff
+ --no-warn-deprecated to not warn about usage of deprecated stuff
+
2006-11-08 Ray Strode <rstrode@redhat.com>
* configure.in: post-release bump to 0.13.
diff --git a/README b/README
index 3a1eeb6..600c5de 100644
--- a/README
+++ b/README
@@ -4,5 +4,3 @@ desktop-file-validate: validates a desktop file and prints warnings/errors about
desktop-file-install: installs a desktop file to the applications directory, optionally munging
it a bit in transit.
-
-desktop-menu-tool: loads .vfolder-info and .menu files and does stuff with them
diff --git a/acconfig.h b/acconfig.h
deleted file mode 100644
index e100ec2..0000000
--- a/acconfig.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#undef PACKAGE
-#undef VERSION
-#undef HAVE_CATGETS
-#undef HAVE_GETTEXT
-#undef HAVE_LC_MESSAGES
-#undef HAVE_STPCPY
-#undef ENABLE_NLS
-#undef HAVE_PTHREAD_H
-#undef GETTEXT_PACKAGE
-#undef SANE_MALLOC_PROTOS
diff --git a/autogen.sh b/autogen.sh
index eceaa5f..209b1f8 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -9,7 +9,7 @@ cd $srcdir
PROJECT=desktop-file-utils
TEST_TYPE=-f
-FILE=src/desktop_file.h
+FILE=src/validate.h
DIE=0
diff --git a/configure.in b/configure.in
index 4e06b71..f168076 100644
--- a/configure.in
+++ b/configure.in
@@ -1,6 +1,6 @@
AC_INIT([desktop-file-utils], [0.13],
- [http://www.freedesktop.org/Software/desktop-file-utils])
-AC_CONFIG_SRCDIR(src/desktop_file.h)
+ [https://bugs.freedesktop.org/enter_bug.cgi?product=desktop-file-utils])
+AC_CONFIG_SRCDIR(src/validate.h)
AM_INIT_AUTOMAKE
AM_CONFIG_HEADER(config.h)
diff --git a/src/Makefile.am b/src/Makefile.am
index 868bde7..1e242da 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -10,15 +10,15 @@ bin_PROGRAMS = \
update-desktop-database
desktop_file_validate_SOURCES= \
- desktop_file.c \
- desktop_file.h \
+ keyfileutils.c \
+ keyfileutils.h \
validate.c \
validate.h \
validator.c
desktop_file_install_SOURCES= \
- desktop_file.c \
- desktop_file.h \
+ keyfileutils.c \
+ keyfileutils.h \
validate.c \
validate.h \
install.c
diff --git a/src/desktop_file.c b/src/desktop_file.c
deleted file mode 100644
index 05f03cd..0000000
--- a/src/desktop_file.c
+++ /dev/null
@@ -1,1729 +0,0 @@
-#include <config.h>
-
-#include <string.h>
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <locale.h>
-#include "desktop_file.h"
-
-
-#include <libintl.h>
-#define _(x) gettext ((x))
-#define N_(x) x
-
-
-typedef struct _GnomeDesktopFileSection GnomeDesktopFileSection;
-typedef struct _GnomeDesktopFileLine GnomeDesktopFileLine;
-typedef struct _GnomeDesktopFileParser GnomeDesktopFileParser;
-
-struct _GnomeDesktopFileSection {
- GQuark section_name; /* 0 means just a comment block (before any section) */
- gint n_lines;
- GnomeDesktopFileLine *lines;
- gint n_allocated_lines;
-};
-
-struct _GnomeDesktopFileLine {
- GQuark key; /* 0 means comment or blank line in value */
- char *locale;
- gchar *value;
-};
-
-struct _GnomeDesktopFile {
- gint n_sections;
- GnomeDesktopFileSection *sections;
- gint n_allocated_sections;
- gint main_section;
- GnomeDesktopFileEncoding encoding;
-};
-
-struct _GnomeDesktopFileParser {
- GnomeDesktopFile *df;
- gint current_section;
- gint line_nr;
- char *line;
-};
-
-#define VALID_KEY_CHAR 1
-#define VALID_LOCALE_CHAR 2
-guchar valid[256] = {
- /* 0 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
- /* 16 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
- /* 32 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x3 , 0x2 , 0x0 ,
- /* 48 */ 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
- /* 64 */ 0x2 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 ,
- /* 80 */ 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x2 ,
- /* 96 */ 0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 ,
- /* 112 */ 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
- /* 128 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
- /* 144 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
- /* 160 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
- /* 176 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
- /* 192 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
- /* 208 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
- /* 224 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
- /* 240 */ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0
-};
-
-static void report_error (GnomeDesktopFileParser *parser,
- char *message,
- GnomeDesktopParseError error_code,
- GError **error);
-static GnomeDesktopFileSection *lookup_section (GnomeDesktopFile *df,
- const char *section);
-static GnomeDesktopFileLine * lookup_line (GnomeDesktopFile *df,
- GnomeDesktopFileSection *section,
- const char *keyname,
- const char *locale);
-
-
-
-
-GQuark
-gnome_desktop_parse_error_quark (void)
-{
- static GQuark quark;
- if (!quark)
- quark = g_quark_from_static_string ("g_desktop_parse_error");
-
- return quark;
-}
-
-static void
-parser_free (GnomeDesktopFileParser *parser)
-{
- gnome_desktop_file_free (parser->df);
-}
-
-static void
-gnome_desktop_file_line_free (GnomeDesktopFileLine *line)
-{
- g_free (line->locale);
- g_free (line->value);
-}
-
-static void
-gnome_desktop_file_section_free (GnomeDesktopFileSection *section)
-{
- int i;
-
- for (i = 0; i < section->n_lines; i++)
- gnome_desktop_file_line_free (&section->lines[i]);
-
- g_free (section->lines);
-}
-
-void
-gnome_desktop_file_free (GnomeDesktopFile *df)
-{
- int i;
-
- for (i = 0; i < df->n_sections; i++)
- gnome_desktop_file_section_free (&df->sections[i]);
- g_free (df->sections);
-
- g_free (df);
-}
-
-static void
-grow_lines_in_section (GnomeDesktopFileSection *section)
-{
- int new_n_lines;
-
- if (section->n_allocated_lines == 0)
- new_n_lines = 1;
- else
- new_n_lines = section->n_allocated_lines*2;
-
- section->lines = g_realloc (section->lines,
- sizeof (GnomeDesktopFileLine) * new_n_lines);
- section->n_allocated_lines = new_n_lines;
-}
-
-static void
-grow_sections (GnomeDesktopFile *df)
-{
- int new_n_sections;
-
- if (df->n_allocated_sections == 0)
- new_n_sections = 1;
- else
- new_n_sections = df->n_allocated_sections*2;
-
- df->sections = g_realloc (df->sections,
- sizeof (GnomeDesktopFileSection) * new_n_sections);
- df->n_allocated_sections = new_n_sections;
-}
-
-static gchar *
-unescape_string (gchar *str, gint len)
-{
- gchar *res;
- gchar *p, *q;
- gchar *end;
-
- /* len + 1 is enough, because unescaping never makes the
- * string longer */
- res = g_new (gchar, len + 1);
- p = str;
- q = res;
- end = str + len;
-
- while (p < end)
- {
- if (*p == 0)
- {
- /* Found an embedded null */
- g_free (res);
- return NULL;
- }
- if (*p == '\\')
- {
- p++;
- if (p >= end)
- {
- /* Escape at end of string */
- g_free (res);
- return NULL;
- }
-
- switch (*p)
- {
- case 's':
- *q++ = ' ';
- break;
- case 't':
- *q++ = '\t';
- break;
- case 'n':
- *q++ = '\n';
- break;
- case 'r':
- *q++ = '\r';
- break;
- case '\\':
- *q++ = '\\';
- break;
- default:
- /* Invalid escape code */
- g_free (res);
- return NULL;
- }
- p++;
- }
- else
- *q++ = *p++;
- }
- *q = 0;
-
- return res;
-}
-
-static gchar *
-escape_string (const gchar *str, gboolean escape_first_space)
-{
- gchar *res;
- char *q;
- const gchar *p;
- const gchar *end;
-
- /* len + 1 is enough, because unescaping never makes the
- * string longer */
- res = g_new (gchar, strlen (str)*2 + 1);
-
- p = str;
- q = res;
- end = str + strlen (str);
-
- while (*p)
- {
- if (*p == ' ')
- {
- if (escape_first_space && p == str)
- {
- *q++ = '\\';
- *q++ = 's';
- }
- else
- *q++ = ' ';
- }
- else if (*p == '\\')
- {
- *q++ = '\\';
- *q++ = '\\';
- }
- else if (*p == '\t')
- {
- *q++ = '\\';
- *q++ = 't';
- }
- else if (*p == '\n')
- {
- *q++ = '\\';
- *q++ = 'n';
- }
- else if (*p == '\r')
- {
- *q++ = '\\';
- *q++ = 'r';
- }
- else
- *q++ = *p;
- p++;
- }
- *q = 0;
-
- return res;
-}
-
-
-static GnomeDesktopFileSection*
-new_section (GnomeDesktopFile *df,
- const char *name,
- GError **error)
-{
- int n;
- gboolean is_main = FALSE;
-
- if (name &&
- (strcmp (name, "Desktop Entry") == 0 ||
- strcmp (name, "KDE Desktop Entry") == 0))
- is_main = TRUE;
-
- if (is_main &&
- df->main_section >= 0)
- {
- g_set_error (error,
- GNOME_DESKTOP_PARSE_ERROR,
- GNOME_DESKTOP_PARSE_ERROR_INVALID_SYNTAX,
- "Two [Desktop Entry] or [KDE Desktop Entry] sections seen");
-
- return NULL;
- }
-
- if (df->n_allocated_sections == df->n_sections)
- grow_sections (df);
-
- if (df->n_sections == 1 &&
- df->sections[0].section_name == 0 &&
- df->sections[0].n_lines == 0)
- {
- if (!name)
- g_warning ("non-initial NULL section\n");
-
- /* The initial section was empty. Piggyback on it. */
- df->sections[0].section_name = g_quark_from_string (name);
-
- if (is_main)
- df->main_section = 0;
-
- return &df->sections[0];
- }
-
- n = df->n_sections++;
-
- if (is_main)
- df->main_section = n;
-
- if (name)
- df->sections[n].section_name = g_quark_from_string (name);
- else
- df->sections[n].section_name = 0;
-
- df->sections[n].n_lines = 0;
- df->sections[n].lines = NULL;
- df->sections[n].n_allocated_lines = 0;
-
- grow_lines_in_section (&df->sections[n]);
-
- return &df->sections[n];
-}
-
-static GnomeDesktopFileSection*
-open_section (GnomeDesktopFileParser *parser,
- char *name,
- GError **error)
-{
- GnomeDesktopFileSection *section;
-
- section = new_section (parser->df, name, error);
- if (section == NULL)
- return NULL;
-
- parser->current_section = parser->df->n_sections - 1;
- g_assert (&parser->df->sections[parser->current_section] == section);
-
- return section;
-}
-
-static GnomeDesktopFileLine*
-new_line_in_section (GnomeDesktopFileSection *section)
-{
- GnomeDesktopFileLine *line;
-
- if (section->n_allocated_lines == section->n_lines)
- grow_lines_in_section (section);
-
- line = &section->lines[section->n_lines++];
-
- memset (line, 0, sizeof (GnomeDesktopFileLine));
-
- return line;
-}
-
-static GnomeDesktopFileLine *
-new_line (GnomeDesktopFileParser *parser)
-{
- GnomeDesktopFileSection *section;
-
- section = &parser->df->sections[parser->current_section];
-
- return new_line_in_section (section);
-}
-
-static gboolean
-is_blank_line (GnomeDesktopFileParser *parser)
-{
- gchar *p;
-
- p = parser->line;
-
- while (*p && *p != '\n')
- {
- if (!g_ascii_isspace (*p))
- return FALSE;
-
- p++;
- }
- return TRUE;
-}
-
-static void
-parse_comment_or_blank (GnomeDesktopFileParser *parser)
-{
- GnomeDesktopFileLine *line;
- gchar *line_end;
-
- line_end = strchr (parser->line, '\n');
- if (line_end == NULL)
- line_end = parser->line + strlen (parser->line);
-
- line = new_line (parser);
-
- line->value = g_strndup (parser->line, line_end - parser->line);
-
- if (*line_end == '\n')
- ++line_end;
- else if (*line_end == '\0')
- line_end = NULL;
-
- parser->line = line_end;
-
- parser->line_nr++;
-}
-
-static gboolean
-is_valid_section_name (const char *name)
-{
- /* 5. Group names may contain all ASCII characters except for control characters and '[' and ']'. */
-
- while (*name)
- {
- if (!(g_ascii_isprint (*name) || *name == '\n' || *name == '\t'))
- return FALSE;
-
- name++;
- }
-
- return TRUE;
-}
-
-static gboolean
-parse_section_start (GnomeDesktopFileParser *parser, GError **error)
-{
- gchar *line_end;
- gchar *section_name;
-
- line_end = strchr (parser->line, '\n');
- if (line_end == NULL)
- line_end = parser->line + strlen (parser->line);
-
- if (line_end - parser->line <= 2 ||
- line_end[-1] != ']')
- {
- report_error (parser, "Invalid syntax for section header", GNOME_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
- parser_free (parser);
- return FALSE;
- }
-
- section_name = unescape_string (parser->line + 1, line_end - parser->line - 2);
-
- if (section_name == NULL)
- {
- report_error (parser, "Invalid escaping in section name", GNOME_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error);
- parser_free (parser);
- return FALSE;
- }
-
- if (!is_valid_section_name (section_name))
- {
- report_error (parser, "Invalid characters in section name", GNOME_DESKTOP_PARSE_ERROR_INVALID_CHARS, error);
- parser_free (parser);
- g_free (section_name);
- return FALSE;
- }
-
- if (open_section (parser, section_name, error) == NULL)
- {
- g_free (section_name);
- return FALSE;
- }
-
- if (*line_end == '\n')
- ++line_end;
- else if (*line_end == '\0')
- line_end = NULL;
-
- parser->line = line_end;
-
- parser->line_nr++;
-
- g_free (section_name);
-
- return TRUE;
-}
-
-static gboolean
-parse_key_value (GnomeDesktopFileParser *parser, GError **error)
-{
- GnomeDesktopFileLine *line;
- gchar *line_end;
- gchar *key_start;
- gchar *key_end;
- gchar *locale_start = NULL;
- gchar *locale_end = NULL;
- gchar *value_start;
- gchar *value;
- gchar *p;
- char *key;
-
- line_end = strchr (parser->line, '\n');
- if (line_end == NULL)
- line_end = parser->line + strlen (parser->line);
-
- p = parser->line;
- key_start = p;
- while (p < line_end &&
- (valid[(guchar)*p] & VALID_KEY_CHAR))
- p++;
- key_end = p;
-
- if (key_start == key_end)
- {
- report_error (parser, "Empty key name", GNOME_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
- parser_free (parser);
- return FALSE;
- }
-
- if (p < line_end && *p == '[')
- {
- p++;
- locale_start = p;
- while (p < line_end &&
- (valid[(guchar)*p] & VALID_LOCALE_CHAR))
- p++;
- locale_end = p;
-
- if (p == line_end)
- {
- report_error (parser, "Unterminated locale specification in key", GNOME_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
- parser_free (parser);
- return FALSE;
- }
-
- if (*p != ']')
- {
- report_error (parser, "Invalid characters in locale name", GNOME_DESKTOP_PARSE_ERROR_INVALID_CHARS, error);
- parser_free (parser);
- return FALSE;
- }
-
- if (locale_start == locale_end)
- {
- report_error (parser, "Empty locale name", GNOME_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
- parser_free (parser);
- return FALSE;
- }
- p++;
- }
-
- /* Skip space before '=' */
- while (p < line_end && *p == ' ')
- p++;
-
- if (p < line_end && *p != '=')
- {
- report_error (parser, "Invalid characters in key name", GNOME_DESKTOP_PARSE_ERROR_INVALID_CHARS, error);
- parser_free (parser);
- return FALSE;
- }
-
- if (p == line_end)
- {
- report_error (parser, "No '=' in key/value pair", GNOME_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
- parser_free (parser);
- return FALSE;
- }
-
- /* Skip the '=' */
- p++;
-
- /* Skip space after '=' */
- while (p < line_end && *p == ' ')
- p++;
-
- value_start = p;
-
- value = unescape_string (value_start, line_end - value_start);
- if (value == NULL)
- {
- report_error (parser, "Invalid escaping in value", GNOME_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error);
- parser_free (parser);
- return FALSE;
- }
-
- line = new_line (parser);
- key = g_strndup (key_start, key_end - key_start);
- line->key = g_quark_try_string (key);
- if (line->key == 0)
- line->key = g_quark_from_static_string (key);
- else
- g_free (key);
- if (locale_start)
- line->locale = g_strndup (locale_start, locale_end - locale_start);
- line->value = value;
-
- if (*line_end == '\n')
- ++line_end;
- else if (*line_end == '\0')
- line_end = NULL;
-
- parser->line = line_end;
- parser->line_nr++;
-
- return TRUE;
-}
-
-
-static void
-report_error (GnomeDesktopFileParser *parser,
- char *message,
- GnomeDesktopParseError error_code,
- GError **error)
-{
- GnomeDesktopFileSection *section;
- const gchar *section_name = NULL;
-
- section = &parser->df->sections[parser->current_section];
-
- if (section->section_name)
- section_name = g_quark_to_string (section->section_name);
-
- if (error)
- {
- if (section_name)
- *error = g_error_new (GNOME_DESKTOP_PARSE_ERROR,
- error_code,
- "Error in section %s at line %d: %s", section_name, parser->line_nr, message);
- else
- *error = g_error_new (GNOME_DESKTOP_PARSE_ERROR,
- error_code,
- "Error at line %d: %s", parser->line_nr, message);
- }
-}
-
-
-GnomeDesktopFile *
-gnome_desktop_file_new_from_string (char *data,
- GError **error)
-{
- GnomeDesktopFileParser parser;
- GnomeDesktopFileLine *line;
-
- parser.df = g_new0 (GnomeDesktopFile, 1);
- parser.df->main_section = -1;
- parser.current_section = -1;
-
- parser.line_nr = 1;
-
- parser.line = data;
-
- /* Put any initial comments in a NULL segment */
- open_section (&parser, NULL, NULL);
-
- while (parser.line && *parser.line)
- {
- if (*parser.line == '[') {
- if (!parse_section_start (&parser, error))
- return NULL;
- } else if (is_blank_line (&parser) ||
- *parser.line == '#')
- parse_comment_or_blank (&parser);
- else
- {
- if (!parse_key_value (&parser, error))
- return NULL;
- }
- }
-
- if (parser.df->main_section >= 0)
- {
- line = lookup_line (parser.df,
- &parser.df->sections[parser.df->main_section],
- "Encoding", NULL);
- if (line)
- {
- if (strcmp (line->value, "UTF-8") == 0)
- parser.df->encoding = GNOME_DESKTOP_FILE_ENCODING_UTF8;
- else if (strcmp (line->value, "Legacy-Mixed") == 0)
- parser.df->encoding = GNOME_DESKTOP_FILE_ENCODING_LEGACY;
- else
- parser.df->encoding = GNOME_DESKTOP_FILE_ENCODING_UNKNOWN;
- }
- else
- {
- /* No encoding specified. We have to guess
- * If the whole file validates as UTF-8 it's probably UTF-8.
- * Otherwise we guess it's a Legacy-Mixed
- */
- if (g_utf8_validate (data, -1, NULL))
- parser.df->encoding = GNOME_DESKTOP_FILE_ENCODING_UTF8;
- else
- parser.df->encoding = GNOME_DESKTOP_FILE_ENCODING_LEGACY;
- }
-
- }
- else
- parser.df->encoding = GNOME_DESKTOP_FILE_ENCODING_UNKNOWN;
-
- return parser.df;
-}
-
-GnomeDesktopFileEncoding
-gnome_desktop_file_get_encoding (GnomeDesktopFile *df)
-{
- return df->encoding;
-}
-
-char *
-gnome_desktop_file_to_string (GnomeDesktopFile *df)
-{
- GnomeDesktopFileSection *section;
- GnomeDesktopFileLine *line;
- GString *str;
- char *s;
- int i, j;
-
- str = g_string_sized_new (800);
-
- for (i = 0; i < df->n_sections; i ++)
- {
- section = &df->sections[i];
-
- if (section->section_name)
- {
- g_string_append_c (str, '[');
- s = escape_string (g_quark_to_string (section->section_name), FALSE);
- g_string_append (str, s);
- g_free (s);
- g_string_append (str, "]\n");
- }
-
- for (j = 0; j < section->n_lines; j++)
- {
- line = &section->lines[j];
-
- if (line->key == 0)
- {
- g_string_append (str, line->value);
- g_string_append_c (str, '\n');
- }
- else
- {
- g_string_append (str, g_quark_to_string (line->key));
- if (line->locale)
- {
- g_string_append_c (str, '[');
- g_string_append (str, line->locale);
- g_string_append_c (str, ']');
- }
- g_string_append_c (str, '=');
- s = escape_string (line->value, TRUE);
- g_string_append (str, s);
- g_free (s);
- g_string_append_c (str, '\n');
- }
- }
- }
-
- return g_string_free (str, FALSE);
-}
-
-static GnomeDesktopFileSection *
-lookup_section (GnomeDesktopFile *df,
- const char *section_name)
-{
- GnomeDesktopFileSection *section;
- GQuark section_quark;
- int i;
-
- if (section_name == NULL)
- {
- if (df->main_section < 0)
- return NULL;
- else
- return &df->sections[df->main_section];
- }
-
- section_quark = g_quark_try_string (section_name);
- if (section_quark == 0)
- return NULL;
-
- for (i = 0; i < df->n_sections; i ++)
- {
- section = &df->sections[i];
-
- if (section->section_name == section_quark)
- return section;
- }
- return NULL;
-}
-
-static GnomeDesktopFileLine *
-lookup_line (GnomeDesktopFile *df,
- GnomeDesktopFileSection *section,
- const char *keyname,
- const char *locale)
-{
- GnomeDesktopFileLine *line;
- GQuark key_quark;
- int i;
-
- key_quark = g_quark_try_string (keyname);
- if (key_quark == 0)
- return NULL;
-
- for (i = 0; i < section->n_lines; i++)
- {
- line = &section->lines[i];
-
- if (line->key == key_quark &&
- ((locale == NULL && line->locale == NULL) ||
- (locale != NULL && line->locale != NULL && strcmp (locale, line->locale) == 0)))
- return line;
- }
-
- return NULL;
-}
-
-gboolean
-gnome_desktop_file_get_raw (GnomeDesktopFile *df,
- const char *section_name,
- const char *keyname,
- const char *locale,
- const char **val)
-{
- GnomeDesktopFileSection *section;
- GnomeDesktopFileLine *line;
-
- *val = NULL;
-
- section = lookup_section (df, section_name);
- if (!section)
- return FALSE;
-
- line = lookup_line (df,
- section,
- keyname,
- locale);
-
- if (!line)
- return FALSE;
-
- *val = line->value;
-
- return TRUE;
-}
-
-void
-gnome_desktop_file_foreach_section (GnomeDesktopFile *df,
- GnomeDesktopFileSectionFunc func,
- gpointer user_data)
-{
- GnomeDesktopFileSection *section;
- int i;
-
- for (i = 0; i < df->n_sections; i ++)
- {
- section = &df->sections[i];
-
- (*func) (df, g_quark_to_string (section->section_name), user_data);
- }
- return;
-}
-
-void
-gnome_desktop_file_foreach_key (GnomeDesktopFile *df,
- const char *section_name,
- gboolean include_localized,
- GnomeDesktopFileLineFunc func,
- gpointer user_data)
-{
- GnomeDesktopFileSection *section;
- GnomeDesktopFileLine *line;
- int i;
-
- section = lookup_section (df, section_name);
- if (!section)
- return;
-
- for (i = 0; i < section->n_lines; i++)
- {
- line = &section->lines[i];
-
- (*func) (df, g_quark_to_string (line->key), line->locale, line->value, user_data);
- }
-
- return;
-}
-
-void
-gnome_desktop_file_rename_section (GnomeDesktopFile *df,
- const char *old_name,
- const char *new_name)
-{
- GnomeDesktopFileSection *section;
-
- g_return_if_fail (new_name != NULL);
-
- if (old_name == NULL &&
- df->main_section < 0)
- old_name = "Desktop Entry";
-
- section = lookup_section (df, old_name);
- if (section == NULL)
- return;
-
- section->section_name = g_quark_from_string (new_name);
-}
-
-gboolean
-gnome_desktop_file_has_section (GnomeDesktopFile *df,
- const char *name)
-{
- GnomeDesktopFileSection *section;
-
- g_return_val_if_fail (name != NULL, FALSE);
-
- section = lookup_section (df, name);
-
- return section != NULL;
-}
-
-GnomeDesktopFile*
-gnome_desktop_file_load (const char *filename,
- GError **error)
-{
- char *contents;
- GnomeDesktopFile *df;
-
- if (!g_file_get_contents (filename, &contents,
- NULL, error))
- return NULL;
-
- df = gnome_desktop_file_new_from_string (contents, error);
-
- g_free (contents);
-
- return df;
-}
-
-
-gboolean
-gnome_desktop_file_save (GnomeDesktopFile *df,
- const char *path,
- int mode,
- GError **error)
-{
- char *str;
- FILE *f;
- int fd;
- char *tmp_filename;
-
- tmp_filename = g_strconcat (path, ".new-tmp", NULL);
-
- f = fopen (tmp_filename, "w");
- if (f == NULL)
- {
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (errno),
- _("Failed to open \"%s\": %s"),
- tmp_filename, g_strerror (errno));
- g_free (tmp_filename);
- return FALSE;
- }
-
- fd = fileno (f);
-
- if (fchmod (fd, mode) < 0)
- {
- g_set_error (error, G_FILE_ERROR,
- g_file_error_from_errno (errno),
- _("Failed to set permissions %o on \"%s\": %s"),
- mode, tmp_filename, g_strerror (errno));
-
- fclose (f);
- unlink (tmp_filename);
- g_free (tmp_filename);
-
- return FALSE;
- }
-
- str = gnome_desktop_file_to_string (df);
-
- if (fputs (str, f) < 0)
- {
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (errno),
- _("Failed to write to \"%s\": %s"),
- tmp_filename, g_strerror (errno));
-
- fclose (f);
- unlink (tmp_filename);
- g_free (str);
- g_free (tmp_filename);
-
- return FALSE;
- }
-
- g_free (str);
-
- if (fclose (f) < 0)
- {
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (errno),
- _("Failed to close \"%s\": %s"),
- tmp_filename, g_strerror (errno));
-
- unlink (tmp_filename);
- g_free (tmp_filename);
-
- return FALSE;
- }
-
- if (rename (tmp_filename, path) < 0)
- {
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (errno),
- _("Failed to rename \"%s\" to \"%s\": %s"),
- tmp_filename, path, g_strerror (errno));
- unlink (tmp_filename);
- g_free (tmp_filename);
-
- return FALSE;
- }
-
- g_free (tmp_filename);
-
- return TRUE;
-}
-
-/* Mask for components of locale spec. The ordering here is from
- * least significant to most significant
- */
-enum
-{
- COMPONENT_CODESET = 1 << 0,
- COMPONENT_TERRITORY = 1 << 1,
- COMPONENT_MODIFIER = 1 << 2
-};
-
-/* Break an X/Open style locale specification into components
- */
-static guint
-explode_locale (const gchar *locale,
- gchar **language,
- gchar **territory,
- gchar **codeset,
- gchar **modifier)
-{
- const gchar *uscore_pos;
- const gchar *at_pos;
- const gchar *dot_pos;
-
- guint mask = 0;
-
- uscore_pos = strchr (locale, '_');
- dot_pos = strchr (uscore_pos ? uscore_pos : locale, '.');
- at_pos = strchr (dot_pos ? dot_pos : (uscore_pos ? uscore_pos : locale), '@');
-
- if (at_pos)
- {
- mask |= COMPONENT_MODIFIER;
- *modifier = g_strdup (at_pos);
- }
- else
- at_pos = locale + strlen (locale);
-
- if (dot_pos)
- {
- mask |= COMPONENT_CODESET;
- *codeset = g_new (gchar, 1 + at_pos - dot_pos);
- strncpy (*codeset, dot_pos, at_pos - dot_pos);
- (*codeset)[at_pos - dot_pos] = '\0';
- }
- else
- dot_pos = at_pos;
-
- if (uscore_pos)
- {
- mask |= COMPONENT_TERRITORY;
- *territory = g_new (gchar, 1 + dot_pos - uscore_pos);
- strncpy (*territory, uscore_pos, dot_pos - uscore_pos);
- (*territory)[dot_pos - uscore_pos] = '\0';
- }
- else
- uscore_pos = dot_pos;
-
- *language = g_new (gchar, 1 + uscore_pos - locale);
- strncpy (*language, locale, uscore_pos - locale);
- (*language)[uscore_pos - locale] = '\0';
-
- return mask;
-}
-
-gboolean
-gnome_desktop_file_get_locale_string (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- char **val)
-{
- const char *raw;
- char *lang, *territory, *codeset, *modifier;
- const char *locale;
- char *with_territory;
- char *used_locale;
- GError *error;
-
- *val = NULL;
-
- lang = NULL;
- territory = NULL;
- codeset = NULL;
- modifier = NULL;
- used_locale = NULL;
-
- locale = setlocale (LC_MESSAGES, NULL);
- if (locale != NULL)
- explode_locale (locale, &lang, &territory, &codeset, &modifier);
-
- if (territory)
- with_territory = g_strconcat (lang, "_", territory, NULL);
- else
- with_territory = NULL;
-
- if (with_territory == NULL ||
- !gnome_desktop_file_get_raw (df, section, keyname, with_territory, &raw))
- {
- if (lang == NULL ||
- !gnome_desktop_file_get_raw (df, section, keyname, lang, &raw))
- {
- gnome_desktop_file_get_raw (df, section, keyname, NULL, &raw);
- }
- else
- {
- used_locale = lang;
- lang = NULL;
- }
- }
- else
- {
- used_locale = with_territory;
- with_territory = NULL;
- }
-
- g_free (lang);
- g_free (territory);
- g_free (codeset);
- g_free (modifier);
- g_free (with_territory);
-
- if (raw == NULL)
- {
- g_free (used_locale);
- return FALSE;
- }
-
- if (gnome_desktop_file_get_encoding (df) == GNOME_DESKTOP_FILE_ENCODING_UTF8)
- {
- g_free (used_locale);
- *val = g_strdup (raw);
- return TRUE;
- }
- else if (gnome_desktop_file_get_encoding (df) == GNOME_DESKTOP_FILE_ENCODING_LEGACY)
- {
- if (used_locale)
- {
- const char *encoding;
-
- encoding = desktop_file_get_encoding_for_locale (used_locale);
-
- if (encoding)
- {
- char *res;
-
- g_free (used_locale);
- error = NULL;
- res = g_convert (raw, -1,
- "UTF-8",
- encoding,
- NULL,
- NULL,
- &error);
-
- if (res == NULL)
- {
- g_printerr ("Error converting from UTF-8 to %s for key %s: %s\n",
- encoding, keyname, error->message);
- g_error_free (error);
- }
-
- *val = res;
-
- return *val != NULL;
- }
- else
- {
- g_printerr ("Don't know encoding for desktop file field %s with locale \"%s\"\n",
- keyname, used_locale);
- g_free (used_locale);
- return FALSE;
- }
- }
- else
- {
- /* this is just ASCII, hopefully OK, though it's really a bit
- * broken to return it
- */
- *val = g_strdup (raw);
- return TRUE;
- }
- }
- else
- {
- g_printerr ("Desktop file doesn't have its encoding marked, can't parse it.\n");
- g_free (used_locale);
- return FALSE;
- }
-}
-
-gboolean
-gnome_desktop_file_get_string (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- char **val)
-{
- const char *raw;
-
- *val = NULL;
-
- if (!gnome_desktop_file_get_raw (df, section, keyname, NULL, &raw))
- return FALSE;
-
- *val = g_strdup (raw);
-
- return TRUE;
-}
-
-gboolean
-gnome_desktop_file_get_strings (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- const char *locale,
- char ***vals,
- int *len)
-{
- const char *raw;
- char **retval;
- int i;
-
- if (vals)
- *vals = NULL;
- if (len)
- *len = 0;
-
- if (!gnome_desktop_file_get_raw (df, section, keyname, locale, &raw))
- return FALSE;
-
- retval = g_strsplit (raw, ";", G_MAXINT);
-
- i = 0;
- while (retval[i])
- ++i;
-
- /* Drop the empty string g_strsplit leaves in the vector since
- * our list of strings ends in ";"
- */
- --i;
- g_free (retval[i]);
- retval[i] = NULL;
-
- if (vals)
- *vals = retval;
- else
- g_strfreev (retval);
-
- if (len)
- *len = i;
-
- return TRUE;
-}
-
-void
-gnome_desktop_file_set_raw (GnomeDesktopFile *df,
- const char *section_name,
- const char *keyname,
- const char *locale,
- const char *value)
-{
- GnomeDesktopFileSection *section;
- GnomeDesktopFileLine *line;
-
-
- if (section_name == NULL &&
- df->main_section < 0)
- section_name = "Desktop Entry";
-
- section = lookup_section (df, section_name);
- if (section == NULL)
- {
- section = new_section (df, section_name, NULL);
- g_assert (section);
- }
-
- line = lookup_line (df,
- section,
- keyname,
- locale);
-
- if (line == NULL)
- line = new_line_in_section (section);
-
- line->key = g_quark_from_string (keyname);
- g_free (line->value);
- g_free (line->locale);
- line->value = g_strdup (value);
- line->locale = g_strdup (locale);
-}
-
-void
-gnome_desktop_file_set_strings (GnomeDesktopFile *df,
- const char *section_name,
- const char *keyname,
- const char *locale,
- const char **value)
-{
- char *str;
- char *tmp;
-
- tmp = g_strjoinv (";", (char**)value);
- str = g_strconcat (tmp, ";", NULL);
- g_free (tmp);
- gnome_desktop_file_set_raw (df, section_name, keyname, locale, str);
- g_free (str);
-}
-
-
-void
-gnome_desktop_file_merge_string_into_list (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- const char *locale,
- const char *value)
-{
- char **values;
- int n_values;
- const char *raw;
-
- if (gnome_desktop_file_get_strings (df, section, keyname, locale,
- &values, &n_values))
- {
- /* Look for a duplicate */
- int i;
- gboolean found;
-
- found = FALSE;
-
- i = 0;
- while (i < n_values)
- {
- if (strcmp (values[i], value) == 0)
- {
- found = TRUE;
- break;
- }
-
- ++i;
- }
-
- g_strfreev (values);
-
- if (found)
- return; /* nothing to do */
- }
-
- gnome_desktop_file_get_raw (df, section, keyname, locale, &raw);
-
- {
- /* Append to current list */
- char *str;
- if (raw)
- {
- int len;
- len = strlen (raw);
- /* handle broken file with no ';' after the list */
- if (len > 0 && raw[len-1] != ';')
- str = g_strconcat (raw, ";", value, ";", NULL);
- else
- str = g_strconcat (raw, value, ";", NULL);
- }
- else
- str = g_strconcat (value, ";", NULL);
-
- gnome_desktop_file_set_raw (df, section, keyname, locale, str);
- g_free (str);
- }
-}
-
-void
-gnome_desktop_file_remove_string_from_list (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- const char *locale,
- const char *value)
-{
- char **values;
- int n_values;
-
- if (gnome_desktop_file_get_strings (df, section, keyname, locale,
- &values, &n_values))
- {
- int i, j;
- int n_found;
-
- n_found = 0;
-
- i = 0;
- while (i < n_values)
- {
- if (values[i] == NULL)
- continue;
-
- if (strcmp (values[i], value) == 0)
- {
- g_free (values[i]);
- values[i] = NULL;
- ++n_found;
- }
-
- ++i;
- }
-
- i = j = 0;
- while (i < n_values)
- {
- j = MAX (i + 1, j);
- if (values[i] == NULL)
- {
- while (j < n_values && values[j] == NULL)
- j++;
-
- if (j < n_values)
- {
- values[i] = values[j];
- values[j] = NULL;
- }
- }
-
- ++i;
- }
-
- if (n_found == n_values)
- gnome_desktop_file_unset (df, section, keyname);
- else if (n_found > 0)
- gnome_desktop_file_set_strings (df, section, keyname, locale,
- (const char**) values);
-
- g_strfreev (values);
- }
-}
-
-#define N_LANG 30
-
-/* #define VERIFY_CANONICAL_ENCODING_NAME */
-
-struct {
- const char *encoding;
- const char *langs[N_LANG];
-} known_encodings[] = {
- {"ARMSCII-8", {"hy"}},
- {"BIG5", {"zh_TW"}},
- {"CP1251", {"be", "bg"}},
- {"EUC-CN", {"zh_CN"}},
- {"EUC-JP", {"ja"}},
- {"EUC-KR", {"ko"}},
- {"GEORGIAN-ACADEMY", {}},
- {"GEORGIAN-PS", {"ka"}},
- {"ISO-8859-1", {"br", "ca", "da", "de", "en", "es", "eu", "fi", "fr", "gl", "it", "nl", "no", "pt", "sv", "wa" }},
- {"ISO-8859-2", {"cs", "hr", "hu", "pl", "ro", "sk", "sl", "sq", "sr"}},
- {"ISO-8859-3", {"eo"}},
- {"ISO-8859-5", {"mk", "sp"}},
- {"ISO-8859-7", {"el"}},
- {"ISO-8859-9", {"tr"}},
- {"ISO-8859-13", {"lv", "lt", "mi"}},
- {"ISO-8859-14", {"ga", "cy"}},
- {"ISO-8859-15", {"et"}},
- {"KOI8-R", {"ru"}},
- {"KOI8-U", {"uk"}},
- {"TCVN-5712", {"vi"}},
- {"TIS-620", {"th"}},
- {"VISCII", {}},
-};
-
-struct {
- const char *alias;
- const char *value;
-} enc_aliases[] = {
- {"GB2312", "EUC-CN"},
- {"TCVN", "TCVN-5712" }
-};
-
-static gboolean
-aliases_equal (const char *enc1, const char *enc2)
-{
- while (*enc1 && *enc2)
- {
- while (*enc1 == '-' ||
- *enc1 == '.' ||
- *enc1 == '_')
- enc1++;
-
- while (*enc2 == '-' ||
- *enc2 == '.' ||
- *enc2 == '_')
- enc2++;
-
- if (g_ascii_tolower (*enc1) != g_ascii_tolower (*enc2))
- return FALSE;
- enc1++;
- enc2++;
- }
-
- while (*enc1 == '-' ||
- *enc1 == '.' ||
- *enc1 == '_')
- enc1++;
-
- while (*enc2 == '-' ||
- *enc2 == '.' ||
- *enc2 == '_')
- enc2++;
-
- if (*enc1 || *enc2)
- return FALSE;
-
- return TRUE;
-}
-
-static const char *
-get_canonical_encoding (const char *encoding)
-{
- int i;
- for (i = 0; i < (int) G_N_ELEMENTS (enc_aliases); i++)
- {
- if (aliases_equal (enc_aliases[i].alias, encoding))
- return enc_aliases[i].value;
- }
-
- for (i = 0; i < (int) G_N_ELEMENTS (known_encodings); i++)
- {
- if (aliases_equal (known_encodings[i].encoding, encoding))
- return known_encodings[i].encoding;
- }
-
- return encoding;
-}
-
-static gboolean
-lang_tag_matches (const char *l, const char *spec)
-{
- char *l2;
-
- if (strcmp (l, spec) == 0)
- return TRUE;
-
- l2 = strchr (l, '_');
-
- if (l2 && strchr (spec, '_') == NULL &&
- strncmp (l, spec, l2 - l) == 0)
- return TRUE;
-
- return FALSE;
-}
-
-static const char *
-get_encoding_from_lang (const char *lang)
-{
- int i, j;
-
- for (i = 0; i < (int) G_N_ELEMENTS (known_encodings); i++)
- {
- for (j = 0; j < N_LANG; j++)
- {
- if (known_encodings[i].langs[j] && lang_tag_matches (lang, known_encodings[i].langs[j]))
- return known_encodings[i].encoding;
- }
- }
- return NULL;
-}
-
-const char*
-desktop_file_get_encoding_for_locale (const char *locale)
-{
- char *encoding;
-
- encoding = strchr(locale, '.');
-
- if (encoding)
- {
- encoding++;
-
- return get_canonical_encoding (encoding);
- }
-
- return get_encoding_from_lang (locale);
-}
-
-static void
-gnome_desktop_file_unset_internal (GnomeDesktopFile *df,
- const char *section_name,
- const char *keyname,
- const char *locale,
- gboolean all_locales)
-{
- GnomeDesktopFileLine *line;
- GnomeDesktopFileSection *section;
- GQuark key_quark;
- int i;
-
- section = lookup_section (df, section_name);
- if (section == NULL)
- return;
-
- key_quark = g_quark_try_string (keyname);
- if (key_quark == 0)
- return;
-
- i = 0;
- while (i < section->n_lines)
- {
- line = &section->lines[i];
-
- if (line->key == key_quark &&
- (all_locales ||
- ((locale == NULL && line->locale == NULL) ||
- (locale != NULL && line->locale != NULL && strcmp (locale, line->locale) == 0))))
- {
- g_free (line->locale);
- g_free (line->value);
-
- if ((i+1) < section->n_lines)
- g_memmove (&section->lines[i], &section->lines[i+1],
- (section->n_lines - i - 1) * sizeof (section->lines[0]));
-
- section->n_lines -= 1;
- }
- else
- {
- ++i;
- }
- }
-}
-
-void
-gnome_desktop_file_unset (GnomeDesktopFile *df,
- const char *section,
- const char *keyname)
-{
- gnome_desktop_file_unset_internal (df, section, keyname, NULL, TRUE);
-}
-
-void
-gnome_desktop_file_unset_for_locale (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- const char *locale)
-{
- gnome_desktop_file_unset_internal (df, section, keyname, locale, FALSE);
-}
-
-void
-gnome_desktop_file_copy_key (GnomeDesktopFile *df,
- const char *section_name,
- const char *source_key,
- const char *dest_key)
-{
- GnomeDesktopFileLine *line;
- GnomeDesktopFileSection *section;
- GQuark key_quark;
- int i;
-
- g_return_if_fail (source_key != NULL);
- g_return_if_fail (dest_key != NULL);
- g_return_if_fail (strcmp (source_key, dest_key) != 0);
-
- gnome_desktop_file_unset (df, section_name, dest_key);
-
- section = lookup_section (df, section_name);
- if (section == NULL)
- return;
-
- key_quark = g_quark_try_string (source_key);
- if (key_quark == 0)
- return;
-
- i = 0;
- while (i < section->n_lines)
- {
- line = &section->lines[i];
-
- if (line->key == key_quark)
- {
- /* Mmmm, modify the array we're iterating over...
- * should be safe as it just appends.
- */
-
- gnome_desktop_file_set_raw (df, section_name, dest_key,
- line->locale, line->value);
- }
-
- ++i;
- }
-}
diff --git a/src/desktop_file.h b/src/desktop_file.h
deleted file mode 100644
index 70dd852..0000000
--- a/src/desktop_file.h
+++ /dev/null
@@ -1,163 +0,0 @@
-#include <glib.h>
-
-#ifndef DESKTOP_FILE_H
-#define DESKTOP_FILE_H
-
-typedef struct _GnomeDesktopFile GnomeDesktopFile;
-typedef enum _GnomeDesktopFileEncoding GnomeDesktopFileEncoding;
-
-typedef void (* GnomeDesktopFileSectionFunc) (GnomeDesktopFile *df,
- const char *name,
- gpointer data);
-typedef void (* GnomeDesktopFileLineFunc) (GnomeDesktopFile *df,
- const char *key, /* If NULL, value is comment line */
- const char *locale,
- const char *value, /* This is raw unescaped data */
- gpointer data);
-
-enum _GnomeDesktopFileEncoding {
- GNOME_DESKTOP_FILE_ENCODING_UTF8,
- GNOME_DESKTOP_FILE_ENCODING_LEGACY,
- GNOME_DESKTOP_FILE_ENCODING_UNKNOWN
-};
-
-typedef enum
-{
- GNOME_DESKTOP_PARSE_ERROR_INVALID_SYNTAX,
- GNOME_DESKTOP_PARSE_ERROR_INVALID_ESCAPES,
- GNOME_DESKTOP_PARSE_ERROR_INVALID_CHARS
-} GnomeDesktopParseError;
-
-#define GNOME_DESKTOP_PARSE_ERROR gnome_desktop_parse_error_quark()
-GQuark gnome_desktop_parse_error_quark (void);
-
-GnomeDesktopFile *gnome_desktop_file_new (GnomeDesktopFileEncoding encoding);
-GnomeDesktopFile *gnome_desktop_file_new_from_string (char *data,
- GError **error);
-GnomeDesktopFile *gnome_desktop_file_load (const char *filename,
- GError **error);
-gboolean gnome_desktop_file_save (GnomeDesktopFile *df,
- const char *path,
- int mode,
- GError **error);
-char * gnome_desktop_file_to_string (GnomeDesktopFile *df);
-void gnome_desktop_file_free (GnomeDesktopFile *df);
-void gnome_desktop_file_launch (GnomeDesktopFile *df,
- char **argv,
- int argc,
- GError **error);
-
-
-GnomeDesktopFileEncoding gnome_desktop_file_get_encoding (GnomeDesktopFile *df);
-void gnome_desktop_file_foreach_section (GnomeDesktopFile *df,
- GnomeDesktopFileSectionFunc func,
- gpointer user_data);
-void gnome_desktop_file_foreach_key (GnomeDesktopFile *df,
- const char *section,
- gboolean include_localized,
- GnomeDesktopFileLineFunc func,
- gpointer user_data);
-gboolean gnome_desktop_file_add_section (GnomeDesktopFile *df,
- const char *name);
-gboolean gnome_desktop_file_remove_section (GnomeDesktopFile *df,
- const char *name);
-void gnome_desktop_file_rename_section (GnomeDesktopFile *df,
- const char *old_name,
- const char *new_name);
-gboolean gnome_desktop_file_has_section (GnomeDesktopFile *df,
- const char *name);
-
-/* Gets the raw text of the key, unescaped */
-gboolean gnome_desktop_file_get_raw (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- const char *locale,
- const char **val);
-gboolean gnome_desktop_file_get_boolean (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- gboolean *val);
-gboolean gnome_desktop_file_get_number (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- double *val);
-gboolean gnome_desktop_file_get_string (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- char **val);
-gboolean gnome_desktop_file_get_locale_string (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- char **val);
-gboolean gnome_desktop_file_get_regexp (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- char **val);
-
-gboolean gnome_desktop_file_get_booleans (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- gboolean **vals,
- int *len);
-gboolean gnome_desktop_file_get_numbers (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- double **vals,
- int *len);
-gboolean gnome_desktop_file_get_strings (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- const char *locale,
- char ***vals,
- int *len);
-gboolean gnome_desktop_file_get_regexps (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- char ***vals,
- int *len);
-
-void gnome_desktop_file_set_raw (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- const char *locale,
- const char *value);
-gboolean gnome_desktop_file_set_string (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- const char *value);
-void gnome_desktop_file_set_strings (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- const char *locale,
- const char **value);
-
-void gnome_desktop_file_merge_string_into_list (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- const char *locale,
- const char *value);
-void gnome_desktop_file_remove_string_from_list (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- const char *locale,
- const char *value);
-
-
-const char* desktop_file_get_encoding_for_locale (const char *locale);
-
-void gnome_desktop_file_unset (GnomeDesktopFile *df,
- const char *section,
- const char *keyname);
-void gnome_desktop_file_unset_for_locale (GnomeDesktopFile *df,
- const char *section,
- const char *keyname,
- const char *locale);
-
-void gnome_desktop_file_copy_key (GnomeDesktopFile *df,
- const char *section,
- const char *source_key,
- const char *dest_key);
-
-/* ... More setters ... */
-
-#endif
diff --git a/src/eggintl.h b/src/eggintl.h
deleted file mode 100644
index a427185..0000000
--- a/src/eggintl.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef __EGG_INTL_H__
-#define __EGG_INTL_H__
-
-/* We don't support gettext yet, dunno if we should /Anders */
-#define _(x) (x)
-#define N_(x) (x)
-
-#endif /* __EGG_INTL_H__ */
diff --git a/src/install.c b/src/install.c
index ef9d7dd..0a0ea0e 100644
--- a/src/install.c
+++ b/src/install.c
@@ -2,11 +2,9 @@
#include <config.h>
#include <glib.h>
+#include <glib/gstdio.h>
#include <glib/gi18n.h>
-#include "desktop_file.h"
-#include "validate.h"
-
#include <stdlib.h>
#include <string.h>
#include <errno.h>
@@ -14,6 +12,9 @@
#include <sys/stat.h>
#include <locale.h>
+#include "keyfileutils.h"
+#include "validate.h"
+
static const char** args = NULL;
static gboolean delete_original = FALSE;
static gboolean copy_generic_name_to_name = FALSE;
@@ -92,55 +93,47 @@ process_one_file (const char *filename,
char *new_filename;
char *dirname;
char *basename;
- GnomeDesktopFile *df = NULL;
+ GKeyFile *kf = NULL;
GError *rebuild_error;
GSList *tmp;
g_assert (vendor_name);
- dirname = g_path_get_dirname (filename);
- basename = g_path_get_basename (filename);
-
- if (!g_str_has_prefix (basename, vendor_name))
- {
- char *new_base;
- new_base = g_strconcat (vendor_name, "-", basename, NULL);
- new_filename = g_build_filename (target_dir, new_base, NULL);
- g_free (new_base);
- }
- else
- {
- new_filename = g_build_filename (target_dir, basename, NULL);
- }
-
- g_free (dirname);
- g_free (basename);
-
- df = gnome_desktop_file_load (filename, err);
- if (df == NULL)
- goto cleanup;
+ kf = g_key_file_new ();
+ if (!g_key_file_load_from_file (kf, filename,
+ G_KEY_FILE_KEEP_COMMENTS|
+ G_KEY_FILE_KEEP_TRANSLATIONS,
+ NULL)) {
+ g_key_file_free (kf);
+ return;
+ }
- if (!desktop_file_fixup (df, filename))
+ if (!desktop_file_fixup (kf, filename)) {
+ g_key_file_free (kf);
exit (1);
+ }
if (copy_name_to_generic_name)
- gnome_desktop_file_copy_key (df, NULL, "Name", "GenericName");
+ dfu_key_file_copy_key (kf, GROUP_DESKTOP_ENTRY, "Name",
+ GROUP_DESKTOP_ENTRY, "GenericName");
if (copy_generic_name_to_name)
- gnome_desktop_file_copy_key (df, NULL, "GenericName", "Name");
+ dfu_key_file_copy_key (kf, GROUP_DESKTOP_ENTRY, "GenericName",
+ GROUP_DESKTOP_ENTRY, "Name");
/* Mark file as having been processed by us, so automated
* tools can check that desktop files went through our
* munging
*/
- gnome_desktop_file_set_raw (df, NULL, "X-Desktop-File-Install-Version", NULL, VERSION);
+ g_key_file_set_string (kf, GROUP_DESKTOP_ENTRY,
+ "X-Desktop-File-Install-Version", VERSION);
/* Add categories */
tmp = added_categories;
while (tmp != NULL)
{
- gnome_desktop_file_merge_string_into_list (df, NULL, "Categories",
- NULL, tmp->data);
+ dfu_key_file_merge_list (kf, GROUP_DESKTOP_ENTRY,
+ "Categories", tmp->data);
tmp = tmp->next;
}
@@ -149,8 +142,8 @@ process_one_file (const char *filename,
tmp = removed_categories;
while (tmp != NULL)
{
- gnome_desktop_file_remove_string_from_list (df, NULL, "Categories",
- NULL, tmp->data);
+ dfu_key_file_remove_list (kf, GROUP_DESKTOP_ENTRY,
+ "Categories", tmp->data);
tmp = tmp->next;
}
@@ -159,8 +152,8 @@ process_one_file (const char *filename,
tmp = added_only_show_in;
while (tmp != NULL)
{
- gnome_desktop_file_merge_string_into_list (df, NULL, "OnlyShowIn",
- NULL, tmp->data);
+ dfu_key_file_merge_list (kf, GROUP_DESKTOP_ENTRY,
+ "OnlyShowIn", tmp->data);
tmp = tmp->next;
}
@@ -169,8 +162,8 @@ process_one_file (const char *filename,
tmp = removed_only_show_in;
while (tmp != NULL)
{
- gnome_desktop_file_remove_string_from_list (df, NULL, "OnlyShowIn",
- NULL, tmp->data);
+ dfu_key_file_remove_list (kf, GROUP_DESKTOP_ENTRY,
+ "OnlyShowIn", tmp->data);
tmp = tmp->next;
}
@@ -179,8 +172,8 @@ process_one_file (const char *filename,
tmp = removed_keys;
while (tmp != NULL)
{
- gnome_desktop_file_unset (df, NULL, tmp->data);
-
+ g_key_file_remove_key (kf, GROUP_DESKTOP_ENTRY, tmp->data, NULL);
+
tmp = tmp->next;
}
@@ -188,8 +181,8 @@ process_one_file (const char *filename,
tmp = added_mime_types;
while (tmp != NULL)
{
- gnome_desktop_file_merge_string_into_list (df, NULL, "MimeType",
- NULL, tmp->data);
+ dfu_key_file_merge_list (kf, GROUP_DESKTOP_ENTRY,
+ "MimeType", tmp->data);
tmp = tmp->next;
}
@@ -198,36 +191,64 @@ process_one_file (const char *filename,
tmp = removed_mime_types;
while (tmp != NULL)
{
- gnome_desktop_file_remove_string_from_list (df, NULL, "MimeType",
- NULL, tmp->data);
+ dfu_key_file_remove_list (kf, GROUP_DESKTOP_ENTRY,
+ "MimeType", tmp->data);
tmp = tmp->next;
}
+ dirname = g_path_get_dirname (filename);
+ basename = g_path_get_basename (filename);
+
+ if (!g_str_has_prefix (basename, vendor_name))
+ {
+ char *new_base;
+ new_base = g_strconcat (vendor_name, "-", basename, NULL);
+ new_filename = g_build_filename (target_dir, new_base, NULL);
+ g_free (new_base);
+ }
+ else
+ {
+ new_filename = g_build_filename (target_dir, basename, NULL);
+ }
+
+ g_free (dirname);
+ g_free (basename);
- if (!gnome_desktop_file_save (df, new_filename,
- permissions, err))
- goto cleanup;
+ if (!dfu_key_file_to_file (kf, new_filename, err)) {
+ g_key_file_free (kf);
+ g_free (new_filename);
+ return;
+ }
+ g_key_file_free (kf);
+
+ if (g_chmod (new_filename, permissions) < 0)
+ {
+ g_set_error (err, G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ _("Failed to set permissions %o on \"%s\": %s"),
+ permissions, new_filename, g_strerror (errno));
+
+ g_unlink (new_filename);
+ g_free (new_filename);
+ return;
+ }
+
if (delete_original &&
!files_are_the_same (filename, new_filename))
{
- if (unlink (filename) < 0)
+ if (g_unlink (filename) < 0)
g_printerr (_("Error removing original file \"%s\": %s\n"),
filename, g_strerror (errno));
}
- gnome_desktop_file_free (df);
-
/* Load and validate the file we just wrote */
- df = gnome_desktop_file_load (new_filename, err);
- if (df == NULL)
- goto cleanup;
-
- if (!desktop_file_validate (df, new_filename))
+ if (!desktop_file_validate (new_filename, FALSE, TRUE))
{
g_printerr (_("desktop-file-install created an invalid desktop file!\n"));
+ g_free (new_filename);
exit (1);
}
@@ -240,11 +261,7 @@ process_one_file (const char *filename,
g_propagate_error (err, rebuild_error);
}
- cleanup:
g_free (new_filename);
-
- if (df)
- gnome_desktop_file_free (df);
}
static gboolean parse_options_callback (const gchar *option_name,
@@ -323,7 +340,7 @@ static const GOptionEntry edit_options[] = {
G_OPTION_ARG_CALLBACK,
parse_options_callback,
N_("Specify a category to be added to the Categories field."),
- NULL
+ N_("CATEGORY")
},
{
#define OPTION_REMOVE_CATEGORY "remove-category"
@@ -333,7 +350,7 @@ static const GOptionEntry edit_options[] = {
G_OPTION_ARG_CALLBACK,
parse_options_callback,
N_("Specify a category to be removed from the Categories field."),
- NULL
+ N_("CATEGORY")
},
{
#define OPTION_ADD_ONLY_SHOW_IN "add-only-show-in"
@@ -343,7 +360,7 @@ static const GOptionEntry edit_options[] = {
G_OPTION_ARG_CALLBACK,
parse_options_callback,
N_("Specify a product name to be added to the OnlyShowIn field."),
- NULL
+ N_("PRODUCT")
},
{
#define OPTION_REMOVE_ONLY_SHOW_IN "remove-only-show-in"
@@ -353,7 +370,7 @@ static const GOptionEntry edit_options[] = {
G_OPTION_ARG_CALLBACK,
parse_options_callback,
N_("Specify a product name to be removed from the OnlyShowIn field."),
- NULL
+ N_("PRODUCT")
},
{
"copy-name-to-generic-name",
@@ -381,7 +398,7 @@ static const GOptionEntry edit_options[] = {
G_OPTION_ARG_CALLBACK,
parse_options_callback,
N_("Specify a field to be removed from the desktop file."),
- NULL
+ N_("KEY")
},
{
#define OPTION_ADD_MIME_TYPE "add-mime-type"
@@ -391,7 +408,7 @@ static const GOptionEntry edit_options[] = {
G_OPTION_ARG_CALLBACK,
parse_options_callback,
N_("Specify a mime-type to be added to the MimeType field."),
- NULL
+ N_("MIME-TYPE")
},
{
#define OPTION_REMOVE_MIME_TYPE "remove-mime-type"
@@ -401,7 +418,7 @@ static const GOptionEntry edit_options[] = {
G_OPTION_ARG_CALLBACK,
parse_options_callback,
N_("Specify a mime-type to be removed from the MimeType field."),
- NULL
+ N_("MIME-TYPE")
},
{
NULL
diff --git a/src/keyfileutils.c b/src/keyfileutils.c
new file mode 100644
index 0000000..dee9aa5
--- /dev/null
+++ b/src/keyfileutils.c
@@ -0,0 +1,214 @@
+/* keyfileutils.c: useful functions for GKeyFile
+ *
+ * Copyright (C) 2007 Vincent Untz <vuntz@gnome.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <string.h>
+
+#include "keyfileutils.h"
+
+gboolean
+dfu_key_file_rename_group (GKeyFile *keyfile,
+ const char *oldgroup,
+ const char *newgroup)
+{
+ char **keys;
+ char *value;
+ unsigned int i;
+
+ g_return_val_if_fail (keyfile != NULL, FALSE);
+
+ if (!g_key_file_has_group (keyfile, oldgroup))
+ return TRUE;
+
+ keys = g_key_file_get_keys (keyfile, oldgroup, NULL, NULL);
+ for (i = 0; keys[i] != NULL; i++) {
+ value = g_key_file_get_value (keyfile, oldgroup, keys[i], NULL);
+ g_key_file_set_value (keyfile, newgroup, keys[i], value);
+ g_free (value);
+
+ value = g_key_file_get_comment (keyfile, oldgroup, keys[i], NULL);
+ if (value) {
+ g_key_file_set_comment (keyfile, newgroup, keys[i], value, NULL);
+ g_free (value);
+ }
+ }
+ g_strfreev (keys);
+
+ value = g_key_file_get_comment (keyfile, oldgroup, NULL, NULL);
+ if (value) {
+ g_key_file_set_comment (keyfile, newgroup, NULL, value, NULL);
+ g_free (value);
+ }
+
+ g_key_file_remove_group (keyfile, oldgroup, NULL);
+
+ return TRUE;
+}
+
+gboolean
+dfu_key_file_copy_key (GKeyFile *keyfile,
+ const char *fromgroup,
+ const char *fromkey,
+ const char *togroup,
+ const char *tokey)
+{
+ char *value;
+
+ g_return_val_if_fail (keyfile != NULL, FALSE);
+
+ if (!g_key_file_has_group (keyfile, fromgroup))
+ return FALSE;
+
+ value = g_key_file_get_value (keyfile, fromgroup, fromkey, NULL);
+ if (!value)
+ return FALSE;
+
+ g_key_file_set_value (keyfile, togroup, tokey, value);
+
+ g_free (value);
+
+ return TRUE;
+}
+
+void
+dfu_key_file_merge_list (GKeyFile *keyfile,
+ const char *group,
+ const char *key,
+ const char *to_merge)
+{
+ char **values;
+ char *value;
+ char *str;
+ int i;
+
+ g_return_if_fail (keyfile != NULL);
+
+ values = g_key_file_get_string_list (keyfile, group, key, NULL, NULL);
+
+ if (values) {
+ for (i = 0; values[i] != NULL; i++) {
+ if (!strcmp (values[i], to_merge)) {
+ g_strfreev (values);
+ return;
+ }
+ }
+
+ g_strfreev (values);
+ }
+
+ value = g_key_file_get_value (keyfile, group, key, NULL);
+
+ if (value)
+ str = g_strconcat (value, to_merge, ";", NULL);
+ else
+ str = g_strconcat (to_merge, ";", NULL);
+
+ g_key_file_set_value (keyfile, group, key, str);
+
+ g_free (value);
+ g_free (str);
+}
+
+void
+dfu_key_file_remove_list (GKeyFile *keyfile,
+ const char *group,
+ const char *key,
+ const char *to_remove)
+{
+ char **values;
+ GString *value;
+ gboolean found;
+ int i;
+
+ g_return_if_fail (keyfile != NULL);
+
+ found = FALSE;
+
+ value = g_string_new ("");
+ values = g_key_file_get_string_list (keyfile, group, key, NULL, NULL);
+
+ if (values) {
+ for (i = 0; values[i] != NULL; i++) {
+ if (!strcmp (values[i], to_remove))
+ found = TRUE;
+ else
+ g_string_append_printf (value, "%s;", values[i]);
+ }
+
+ g_strfreev (values);
+ }
+
+ if (!found) {
+ g_string_free (value, TRUE);
+ return;
+ }
+
+ if (!value->str || value->str[0] == '\0')
+ g_key_file_remove_key (keyfile, group, key, NULL);
+ else
+ g_key_file_set_value (keyfile, group, key, value->str);
+
+ g_string_free (value, TRUE);
+}
+
+//FIXME: kill this when bug #309224 is fixed
+gboolean
+dfu_key_file_to_file (GKeyFile *keyfile,
+ const char *file,
+ GError **error)
+{
+ char *filename;
+ GError *write_error;
+ char *data;
+ gsize length;
+ gboolean res;
+
+ g_return_val_if_fail (keyfile != NULL, FALSE);
+ g_return_val_if_fail (file != NULL, FALSE);
+
+ write_error = NULL;
+ data = g_key_file_to_data (keyfile, &length, &write_error);
+ if (write_error) {
+ g_propagate_error (error, write_error);
+ return FALSE;
+ }
+
+ if (!g_path_is_absolute (file))
+ filename = g_filename_from_uri (file, NULL, &write_error);
+ else
+ filename = g_filename_from_utf8 (file, -1, NULL, NULL, &write_error);
+
+ if (write_error) {
+ g_propagate_error (error, write_error);
+ g_free (data);
+ return FALSE;
+ }
+
+ res = g_file_set_contents (filename, data, length, &write_error);
+ g_free (filename);
+
+ if (write_error) {
+ g_propagate_error (error, write_error);
+ g_free (data);
+ return FALSE;
+ }
+
+ g_free (data);
+ return res;
+}
diff --git a/src/keyfileutils.h b/src/keyfileutils.h
new file mode 100644
index 0000000..fbc3f2a
--- /dev/null
+++ b/src/keyfileutils.h
@@ -0,0 +1,47 @@
+/* keyfileutils.h: useful functions for GKeyFile
+ *
+ * Copyright (C) 2007 Vincent Untz <vuntz@gnome.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <glib.h>
+
+#define GROUP_DESKTOP_ENTRY "Desktop Entry"
+
+gboolean dfu_key_file_rename_group (GKeyFile *keyfile,
+ const char *oldgroup,
+ const char *newgroup);
+
+gboolean dfu_key_file_copy_key (GKeyFile *keyfile,
+ const char *fromgroup,
+ const char *fromkey,
+ const char *togroup,
+ const char *tokey);
+
+void dfu_key_file_merge_list (GKeyFile *keyfile,
+ const char *group,
+ const char *key,
+ const char *to_merge);
+
+void dfu_key_file_remove_list (GKeyFile *keyfile,
+ const char *group,
+ const char *key,
+ const char *to_remove);
+
+gboolean dfu_key_file_to_file (GKeyFile *keyfile,
+ const char *file,
+ GError **error);
diff --git a/src/validate.c b/src/validate.c
index bb5dbbd..173c77f 100644
--- a/src/validate.c
+++ b/src/validate.c
@@ -1,1009 +1,1939 @@
-#include <stdlib.h>
+/* validate.c: validate a desktop entry file
+ *
+ * Copyright (C) 2007 Vincent Untz <vuntz@gnome.org>
+ *
+ * A really small portion of this code comes from the old validate.c.
+ * Authors of the old validate.c are:
+ * Mark McLoughlin
+ * Havoc Pennington
+ * Ray Strode
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
#include <stdio.h>
#include <string.h>
-#include "desktop_file.h"
+
+#include "keyfileutils.h"
#include "validate.h"
-#include <libintl.h>
-#define _(x) gettext ((x))
-#define N_(x) x
+/* We're trying to not use GKeyFile APIs as much as possible, so we don't
+ * have to "trust" what GKeyFile is doing, and GKeyFile can be less
+ * strict than this validator.
+ * FIXME: maybe it's a good idea to just not use GKeyFile and do all the
+ * parsing ourselves. This way, we know we're doing exactly what we want.
+ */
+
+//FIXME: document where GKeyFile is stricter than the spec
+// * only UTF-8 (so no Legacy-Mixed encoding)
+// * "Historically lists have been comma separated."
+// Add this information to --usage
+
+/*TODO:
+ * + Desktop entry files are encoded as lines of 8-bit characters separated by
+ * LF characters.
+ * + Multiple groups may not have the same name.
+ * + Lecagy-Mixed Encoding (annexe D)
+ * + The escape sequences \s, \n, \t, \r, and \\ are supported for values of
+ * type string and localestring, meaning ASCII space, newline, tab, carriage
+ * return, and backslash, respectively.
+ * GKeyFile handles this, but we don't.
+ */
+
+typedef enum {
+ INVALID_TYPE = 0,
+
+ APPLICATION_TYPE,
+ LINK_TYPE,
+ DIRECTORY_TYPE,
+
+ /* Types reserved for KDE */
+ /* since 0.9.4 */
+ SERVICE_TYPE,
+ SERVICE_TYPE_TYPE,
+ /* since 0.9.6 */
+ FSDEVICE_TYPE,
+
+ /* Deprecated types */
+ /* since 0.9.4 */
+ MIMETYPE_TYPE,
+
+ LAST_TYPE
+} DesktopType;
+
+typedef enum {
+ DESKTOP_STRING_TYPE,
+ DESKTOP_LOCALESTRING_TYPE,
+ DESKTOP_BOOLEAN_TYPE,
+ DESKTOP_NUMERIC_TYPE,
+ DESKTOP_STRING_LIST_TYPE,
+ /* Deprecated types */
+ /* since 0.9.6 */
+ DESKTOP_REGEXP_LIST_TYPE
+} DesktopKeyType;
+
+typedef struct _kf_validator kf_validator;
+
+struct _kf_validator {
+ const char *filename;
+ GKeyFile *keyfile;
+
+ gboolean kde_reserved_warnings;
+ gboolean no_deprecated_warnings;
+
+ char *main_group;
+ DesktopType type;
+ char *type_string;
+
+ gboolean show_in;
+ GList *application_keys;
+ GList *link_keys;
+ GList *fsdevice_keys;
+ GList *mimetype_keys;
+
+ GHashTable *action_values;
+ GHashTable *action_groups;
+
+ gboolean fatal_error;
+};
+static gboolean
+validate_string_key (kf_validator *kf,
+ const char *key,
+ const char *locale,
+ const char *value);
+static gboolean
+validate_localestring_key (kf_validator *kf,
+ const char *key,
+ const char *locale,
+ const char *value);
+static gboolean
+validate_boolean_key (kf_validator *kf,
+ const char *key,
+ const char *locale,
+ const char *value);
+static gboolean
+validate_numeric_key (kf_validator *kf,
+ const char *key,
+ const char *locale,
+ const char *value);
+static gboolean
+validate_string_list_key (kf_validator *kf,
+ const char *key,
+ const char *locale,
+ const char *value);
+static gboolean
+validate_regexp_list_key (kf_validator *kf,
+ const char *key,
+ const char *locale,
+ const char *value);
+
+static gboolean
+handle_type_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value);
+static gboolean
+handle_version_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value);
+static gboolean
+handle_comment_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value);
+static gboolean
+handle_show_in_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value);
+static gboolean
+handle_exec_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value);
+static gboolean
+handle_path_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value);
+static gboolean
+handle_mime_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value);
+static gboolean
+handle_categories_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value);
+static gboolean
+handle_actions_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value);
+static gboolean
+handle_dev_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value);
+static gboolean
+handle_mountpoint_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value);
+static gboolean
+handle_encoding_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value);
+static gboolean
+handle_key_for_application (kf_validator *kf,
+ const char *locale_key,
+ const char *value);
+static gboolean
+handle_key_for_link (kf_validator *kf,
+ const char *locale_key,
+ const char *value);
+static gboolean
+handle_key_for_fsdevice (kf_validator *kf,
+ const char *locale_key,
+ const char *value);
+static gboolean
+handle_key_for_mimetype (kf_validator *kf,
+ const char *locale_key,
+ const char *value);
-struct KeyHashData {
- gboolean has_non_translated;
- gboolean has_translated;
+struct {
+ DesktopType type;
+ char *name;
+ gboolean kde_reserved;
+ gboolean deprecated;
+} registered_types[] = {
+ { APPLICATION_TYPE, "Application", FALSE, FALSE },
+ { LINK_TYPE, "Link", FALSE, FALSE },
+ { DIRECTORY_TYPE, "Directory", FALSE, FALSE },
+ { SERVICE_TYPE, "Service", TRUE, FALSE },
+ { SERVICE_TYPE_TYPE, "ServiceType", TRUE, FALSE },
+ { FSDEVICE_TYPE, "FSDevice", TRUE, FALSE },
+ { MIMETYPE_TYPE, "MimeType", FALSE, TRUE }
};
-struct KeyData {
- GHashTable *hash;
- const char *filename;
- gboolean deprecated;
+struct {
+ DesktopKeyType type;
+ gboolean (* validate) (kf_validator *kf,
+ const char *key,
+ const char *locale,
+ const char *value);
+} validate_for_type[] = {
+ { DESKTOP_STRING_TYPE, validate_string_key },
+ { DESKTOP_LOCALESTRING_TYPE, validate_localestring_key },
+ { DESKTOP_BOOLEAN_TYPE, validate_boolean_key },
+ { DESKTOP_NUMERIC_TYPE, validate_numeric_key },
+ { DESKTOP_STRING_LIST_TYPE, validate_string_list_key },
+ { DESKTOP_REGEXP_LIST_TYPE, validate_regexp_list_key }
};
-static gboolean fatal_error_occurred = FALSE;
+struct {
+ DesktopKeyType type;
+ char *name;
+ gboolean required;
+ gboolean deprecated;
+ gboolean kde_reserved;
+ gboolean (* handle_and_validate) (kf_validator *kf,
+ const char *locale_key,
+ const char *value);
+} registered_desktop_keys[] = {
+ { DESKTOP_STRING_TYPE, "Type", TRUE, FALSE, FALSE, handle_type_key },
+ /* it is numeric according to the spec, but it's not true in previous
+ * versions of the spec. handle_version_key() will manage this */
+ { DESKTOP_STRING_TYPE, "Version", FALSE, FALSE, FALSE, handle_version_key },
+ { DESKTOP_LOCALESTRING_TYPE, "Name", TRUE, FALSE, FALSE, NULL },
+ { DESKTOP_LOCALESTRING_TYPE, "GenericName", FALSE, FALSE, FALSE, NULL },
+ { DESKTOP_BOOLEAN_TYPE, "NoDisplay", FALSE, FALSE, FALSE, NULL },
+ { DESKTOP_LOCALESTRING_TYPE, "Comment", FALSE, FALSE, FALSE, handle_comment_key },
+ { DESKTOP_LOCALESTRING_TYPE, "Icon", FALSE, FALSE, FALSE, NULL },
+ { DESKTOP_BOOLEAN_TYPE, "Hidden", FALSE, FALSE, FALSE, NULL },
+ { DESKTOP_STRING_LIST_TYPE, "OnlyShowIn", FALSE, FALSE, FALSE, handle_show_in_key },
+ { DESKTOP_STRING_LIST_TYPE, "NotShowIn", FALSE, FALSE, FALSE, handle_show_in_key },
+ { DESKTOP_STRING_TYPE, "TryExec", FALSE, FALSE, FALSE, handle_key_for_application },
+ { DESKTOP_STRING_TYPE, "Exec", FALSE, FALSE, FALSE, handle_exec_key },
+ { DESKTOP_STRING_TYPE, "Path", FALSE, FALSE, FALSE, handle_path_key },
+ { DESKTOP_BOOLEAN_TYPE, "Terminal", FALSE, FALSE, FALSE, handle_key_for_application },
+ { DESKTOP_STRING_LIST_TYPE, "MimeType", FALSE, FALSE, FALSE, handle_mime_key },
+ { DESKTOP_STRING_LIST_TYPE, "Categories", FALSE, FALSE, FALSE, handle_categories_key },
+ { DESKTOP_BOOLEAN_TYPE, "StartupNotify", FALSE, FALSE, FALSE, handle_key_for_application },
+ { DESKTOP_STRING_TYPE, "StartupWMClass", FALSE, FALSE, FALSE, handle_key_for_application },
+ { DESKTOP_STRING_TYPE, "URL", FALSE, FALSE, FALSE, handle_key_for_link },
+
+ //FIXME: it's not deprecated, but got removed from the spec temporarly
+ { DESKTOP_STRING_LIST_TYPE, "Actions", FALSE, FALSE, FALSE, handle_actions_key },
+
+ /* Keys reserved for KDE */
+
+ /* since 0.9.4 */
+ { DESKTOP_STRING_TYPE, "ServiceTypes", FALSE, FALSE, TRUE, NULL },
+ { DESKTOP_STRING_TYPE, "DocPath", FALSE, FALSE, TRUE, NULL },
+ { DESKTOP_LOCALESTRING_TYPE, "Keywords", FALSE, FALSE, TRUE, NULL },
+ { DESKTOP_STRING_TYPE, "InitialPreference", FALSE, FALSE, TRUE, NULL },
+ /* since 0.9.6 */
+ { DESKTOP_STRING_TYPE, "Dev", FALSE, FALSE, TRUE, handle_dev_key },
+ { DESKTOP_STRING_TYPE, "FSType", FALSE, FALSE, TRUE, handle_key_for_fsdevice },
+ { DESKTOP_STRING_TYPE, "MountPoint", FALSE, FALSE, TRUE, handle_mountpoint_key },
+ { DESKTOP_BOOLEAN_TYPE, "ReadOnly", FALSE, FALSE, TRUE, handle_key_for_fsdevice },
+ { DESKTOP_STRING_TYPE, "UnmountIcon", FALSE, FALSE, TRUE, handle_key_for_fsdevice },
+
+ /* Deprecated keys */
+
+ /* since 0.9.3 */
+ { DESKTOP_STRING_TYPE, "Protocols", FALSE, TRUE, FALSE, NULL },
+ { DESKTOP_STRING_TYPE, "Extensions", FALSE, TRUE, FALSE, NULL },
+ { DESKTOP_STRING_TYPE, "BinaryPattern", FALSE, TRUE, FALSE, NULL },
+ { DESKTOP_STRING_TYPE, "MapNotify", FALSE, TRUE, FALSE, NULL },
+ /* since 0.9.4 */
+ { DESKTOP_REGEXP_LIST_TYPE, "Patterns", FALSE, TRUE, FALSE, handle_key_for_mimetype },
+ { DESKTOP_STRING_TYPE, "DefaultApp", FALSE, TRUE, FALSE, handle_key_for_mimetype },
+ { DESKTOP_STRING_TYPE, "MiniIcon", FALSE, TRUE, FALSE, NULL },
+ { DESKTOP_STRING_TYPE, "TerminalOptions", FALSE, TRUE, FALSE, NULL },
+ /* since 0.9.5 */
+ { DESKTOP_STRING_TYPE, "Encoding", FALSE, TRUE, FALSE, handle_encoding_key },
+ { DESKTOP_LOCALESTRING_TYPE, "SwallowTitle", FALSE, TRUE, FALSE, NULL },
+ { DESKTOP_STRING_TYPE, "SwallowExec", FALSE, TRUE, FALSE, NULL },
+ /* since 0.9.6 */
+ { DESKTOP_STRING_LIST_TYPE, "SortOrder", FALSE, TRUE, FALSE, NULL },
+ { DESKTOP_REGEXP_LIST_TYPE, "FilePattern", FALSE, TRUE, FALSE, NULL }
+};
+static const char *show_in_registered[] = {
+ "KDE", "GNOME", "ROX", "XFCE", "Old"
+};
+
+static const char *main_categories_registered[] = {
+ "AudioVideo", "Audio", "Video", "Development", "Education", "Game",
+ "Graphics", "Network", "Office", "Settings", "System", "Utility"
+};
+
+static const char *additional_categories_registered[] = {
+ "Building", "Debugger", "IDE", "GUIDesigner", "Profiling", "RevisionControl",
+ "Translation", "Calendar", "ContactManagement", "Database", "Dictionary",
+ "Chart", "Email", "Finance", "FlowChart", "PDA", "ProjectManagement",
+ "Presentation", "Spreadsheet", "WordProcessor", "2DGraphics",
+ "VectorGraphics", "RasterGraphics", "3DGraphics", "Scanning", "OCR",
+ "Photography", "Publishing", "Viewer", "TextTools", "DesktopSettings",
+ "HardwareSettings", "Printing", "PackageManager", "Dialup",
+ "InstantMessaging", "Chat", "IRCClient", "FileTransfer", "HamRadio", "News",
+ "P2P", "RemoteAccess", "Telephony", "TelephonyTools", "VideoConference",
+ "WebBrowser", "WebDevelopment", "Midi", "Mixer", "Sequencer", "Tuner", "TV",
+ "AudioVideoEditing", "Player", "Recorder", "DiscBurning", "ActionGame",
+ "AdventureGame", "ArcadeGame", "BoardGame", "BlocksGame", "CardGame",
+ "KidsGame", "LogicGame", "RolePlaying", "Simulation", "SportsGame",
+ "StrategyGame", "Art", "Construction", "Music", "Languages", "Science",
+ "ArtificialIntelligence", "Astronomy", "Biology", "Chemistry",
+ "ComputerScience", "DataVisualization", "Economy", "Electricity",
+ "Geography", "Geology", "Geoscience", "History", "ImageProcessing",
+ "Literature", "Math", "NumericalAnalysis", "MedicalSoftware", "Physics",
+ "Robotics", "Sports", "ParallelComputing", "Amusement", "Archiving",
+ "Compression", "Electronics", "Emulator", "Engineering", "FileTools",
+ "FileManager", "TerminalEmulator", "Filesystem", "Monitor", "Security",
+ "Accessibility", "Calculator", "Clock", "TextEditor", "Documentation",
+ "Core", "KDE", "GNOME", "GTK", "Qt", "Motif", "Java", "ConsoleOnly"
+};
+
+static const char *reserved_categories_registered[] = {
+ "Screensaver", "TrayIcon", "Applet", "Shell"
+};
+
+static const char *deprecated_categories_registered[] = {
+ "Application", "Applications"
+};
static void
-print_fatal (const char *filename, const char *format, ...)
+print_fatal (kf_validator *kf, const char *format, ...)
{
va_list args;
gchar *str;
- g_return_if_fail (filename != NULL && format != NULL);
+ g_return_if_fail (kf != NULL && format != NULL);
+
+ kf->fatal_error = TRUE;
- fputs (filename, stdout);
- fputs (": error: ", stdout);
-
va_start (args, format);
str = g_strdup_vprintf (format, args);
va_end (args);
- fputs (str, stdout);
-
- fflush (stdout);
+ g_print ("%s: error: %s", kf->filename, str);
g_free (str);
-
- fatal_error_occurred = TRUE;
}
static void
-print_warning (const char* filename, const char *format, ...)
+print_warning (kf_validator *kf, const char *format, ...)
{
va_list args;
gchar *str;
- g_return_if_fail (filename != NULL && format != NULL);
-
- fputs (filename, stdout);
- fputs (": warning: ", stdout);
+ g_return_if_fail (kf != NULL && format != NULL);
va_start (args, format);
str = g_strdup_vprintf (format, args);
va_end (args);
- fputs (str, stdout);
+ g_print ("%s: warning: %s", kf->filename, str);
- fflush (stdout);
-
g_free (str);
}
-static void
-validate_string (const char *value, const char *key, const char *locale, const char *filename, GnomeDesktopFile *df)
-{
- const char *p;
- gboolean ok = TRUE;
- char *k;
-
- p = value;
- while (*p)
- {
- if (!(g_ascii_isprint (*p) || *p == '\n' || *p == '\t'))
- {
- ok = FALSE;
- break;
- }
-
- p++;
+/* + Values of type string may contain all ASCII characters except for control
+ * characters.
+ * Checked.
+ */
+static gboolean
+validate_string_key (kf_validator *kf,
+ const char *key,
+ const char *locale,
+ const char *value)
+{
+ int i;
+ gboolean error;
+
+ error = FALSE;
+
+ for (i = 0; value[i] != '\0'; i++) {
+ if (!g_ascii_isprint (value[i])) {
+ error = TRUE;
+ break;
}
+ }
- if (!ok)
- {
- if (locale)
- k = g_strdup_printf ("%s[%s]", key, locale);
- else
- k = g_strdup_printf ("%s", key);
- print_fatal (filename, "invalid characters in value of key \"%s\", keys of type string may contain ASCII characters except control characters\n", k);
- g_free (k);
+ if (error) {
+ print_fatal (kf, "value \"%s\" for string key \"%s\" in group \"%s\" "
+ "contains invalid characters, string values may contain "
+ "all ASCII characters except for control characters\n",
+ value, key, kf->main_group);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* + Values of type localestring are user displayable, and are encoded in
+ * UTF-8.
+ * Checked.
+ * + If a postfixed key occurs, the same key must be also present without the
+ * postfix.
+ * Checked.
+ */
+static gboolean
+validate_localestring_key (kf_validator *kf,
+ const char *key,
+ const char *locale,
+ const char *value)
+{
+ char *locale_key;
+
+ if (locale)
+ locale_key = g_strdup_printf ("%s[%s]", key, locale);
+ else
+ locale_key = g_strdup_printf ("%s", key);
+
+ if (!g_utf8_validate (value, -1, NULL)) {
+ print_fatal (kf, "value \"%s\" for locale string key \"%s\" in group "
+ "\"%s\" contains invalid UTF-8 characters, locale string "
+ "values should be encoded in UTF-8\n",
+ value, locale_key, kf->main_group);
+ g_free (locale_key);
+
+ return FALSE;
+ }
+
+ if (!g_key_file_has_key (kf->keyfile, kf->main_group, key, NULL)) {
+ print_fatal (kf, "key \"%s\" in group \"%s\" is a localized key, but "
+ "there is no non-localized key \"%s\"\n",
+ locale_key, kf->main_group, key);
+ g_free (locale_key);
+
+ return FALSE;
+ }
+
+ g_free (locale_key);
+
+ return TRUE;
+}
+
+/* + Values of type boolean must either be the string true or false.
+ * Checked.
+ * + Historically some booleans have been represented by the numeric entries 0
+ * or 1. With this version of the standard they are now to be represented as
+ * a boolean string. However, if an implementation is reading a pre-1.0
+ * desktop entry, it should interpret 0 and 1 as false and true,
+ * respectively.
+ * Checked.
+ */
+static gboolean
+validate_boolean_key (kf_validator *kf,
+ const char *key,
+ const char *locale,
+ const char *value)
+{
+ if (strcmp (value, "true") && strcmp (value, "false") &&
+ strcmp (value, "0") && strcmp (value, "1")) {
+ print_fatal (kf, "value \"%s\" for boolean key \"%s\" in group \"%s\" "
+ "contains invalid characters, boolean values must be "
+ "\"false\" or \"true\"\n",
+ value, key, kf->main_group);
+ return FALSE;
+ }
+
+ if (!kf->no_deprecated_warnings &&
+ (!strcmp (value, "0") || !strcmp (value, "1")))
+ print_warning (kf, "boolean key \"%s\" in group \"%s\" has value \"%s\", "
+ "which is deprecated: boolean values should be "
+ "\"false\" or \"true\"\n",
+ key, kf->main_group, value);
+
+ return TRUE;
+}
+
+/* + Values of type numeric must be a valid floating point number as recognized
+ * by the %f specifier for scanf.
+ * Checked.
+ */
+static gboolean
+validate_numeric_key (kf_validator *kf,
+ const char *key,
+ const char *locale,
+ const char *value)
+{
+ float d;
+ int res;
+
+ res = sscanf (value, "%f", &d);
+ if (res == 0) {
+ print_fatal (kf, "value \"%s\" for numeric key \"%s\" in group \"%s\" "
+ "contains invalid characters, numeric values must be "
+ "valid floating point numbers\n",
+ value, key, kf->main_group);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* + Values of type string may contain all ASCII characters except for control
+ * characters.
+ * Checked.
+ * + The multiple values should be separated by a semicolon. Those keys which
+ * have several values should have a semicolon as the trailing character.
+ * Checked.
+ * + FIXME: how should an empty list be handled?
+ */
+static gboolean
+validate_string_regexp_list_key (kf_validator *kf,
+ const char *key,
+ const char *locale,
+ const char *value,
+ const char *type)
+{
+ int i;
+ gboolean error;
+
+ error = FALSE;
+
+ for (i = 0; value[i] != '\0'; i++) {
+ if (!g_ascii_isprint (value[i])) {
+ error = TRUE;
+ break;
}
+ }
+
+ if (error) {
+ print_fatal (kf, "value \"%s\" for %s list key \"%s\" in group \"%s\" "
+ "contains invalid character '%c', %s list values may "
+ "contain all ASCII characters except for control "
+ "characters\n",
+ value, type, key, kf->main_group, value[i], type);
+
+ return FALSE;
+ }
+
+ if (i > 0 && value[i - 1] != ';') {
+ print_fatal (kf, "value \"%s\" for %s list key \"%s\" in group \"%s\" "
+ "does not have a semicolon (';') as trailing "
+ "character\n",
+ value, type, key, kf->main_group);
+
+ return FALSE;
+ }
+
+ if (i > 1 && value[i - 1] == ';' && value[i - 2] == '\\' &&
+ (i < 3 || value[i - 3] != '\\')) {
+ print_fatal (kf, "value \"%s\" for %s list key \"%s\" in group \"%s\" "
+ "has an escaped semicolon (';') as trailing character\n",
+ value, type, key, kf->main_group);
+
+ return FALSE;
+ }
+
+ return TRUE;
}
-static void
-validate_strings (const char *value, const char *key, const char *locale, const char *filename, GnomeDesktopFile *df)
-{
- const char *p;
- gboolean ok = TRUE;
- char *k;
-
- p = value;
- while (*p)
- {
- if (!(g_ascii_isprint (*p) || *p == '\n' || *p == '\t'))
- {
- ok = FALSE;
- break;
- }
-
- p++;
+static gboolean
+validate_string_list_key (kf_validator *kf,
+ const char *key,
+ const char *locale,
+ const char *value)
+{
+ return validate_string_regexp_list_key (kf, key, locale, value, "string");
+}
+
+static gboolean
+validate_regexp_list_key (kf_validator *kf,
+ const char *key,
+ const char *locale,
+ const char *value)
+{
+ return validate_string_regexp_list_key (kf, key, locale, value, "regexp");
+}
+
+/* + This specification defines 3 types of desktop entries: Application
+ * (type 1), Link (type 2) and Directory (type 3). To allow the addition of
+ * new types in the future, implementations should ignore desktop entries
+ * with an unknown type.
+ * Checked.
+ * + KDE specific types: ServiceType, Service and FSDevice
+ * Checked.
+ */
+static gboolean
+handle_type_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value)
+{
+ unsigned int i;
+
+ for (i = 0; i < G_N_ELEMENTS (registered_types); i++) {
+ if (!strcmp (value, registered_types[i].name))
+ break;
+ }
+
+ if (i == G_N_ELEMENTS (registered_types)) {
+ /* force the type, since the key might be present multiple times... */
+ kf->type = INVALID_TYPE;
+
+ print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "is not a registered type value (\"Application\", "
+ "\"Link\" and \"Directory\")\n",
+ value, locale_key, kf->main_group);
+ return FALSE;
+ }
+
+ if (registered_types[i].kde_reserved && kf->kde_reserved_warnings)
+ print_warning (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "is a reserved value for KDE\n",
+ value, locale_key, kf->main_group);
+
+ if (registered_types[i].deprecated && !kf->no_deprecated_warnings)
+ print_warning (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "is deprecated\n",
+ value, locale_key, kf->main_group);
+
+ kf->type = registered_types[i].type;
+ kf->type_string = registered_types[i].name;
+
+ return TRUE;
+}
+
+/* + Entries that confirm with this version of the specification should use
+ * 1.0.
+ * Checked.
+ * + Previous versions of the spec: 0.9.x where 3 <= x <= 8
+ * Checked.
+ */
+static gboolean
+handle_version_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value)
+{
+ if (!strcmp (value, "1.0"))
+ return TRUE;
+
+ if (!strncmp (value, "0.9.", strlen ("0.9."))) {
+ char c;
+
+ c = value[strlen ("0.9.")];
+ if ('3' <= c && c <= '8' && value[strlen ("0.9.") + 1] == '\0')
+ return TRUE;
+ }
+
+ print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "is not a known version\n",
+ value, locale_key, kf->main_group);
+ return FALSE;
+}
+
+/* + Tooltip for the entry, for example "View sites on the Internet", should
+ * not be redundant with Name or GenericName.
+ * FIXME
+ */
+static gboolean
+handle_comment_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value)
+{
+ return TRUE;
+}
+
+/* + Only one of these keys, either OnlyShowIn or NotShowIn, may appear in a
+ * group.
+ * Checked.
+ * + (for possible values see the Desktop Menu Specification)
+ * Checked.
+ * FIXME: this is not perfect because it could fail if a new value with
+ * a semicolon is registered.
+ */
+static gboolean
+handle_show_in_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value)
+{
+ gboolean retval;
+ char **show;
+ GHashTable *hashtable;
+ int i;
+ unsigned int j;
+
+ retval = TRUE;
+
+ if (kf->show_in) {
+ print_fatal (kf, "only one of \"OnlyShowIn\" and \"NotShowInkey\" keys "
+ "may appear in group \"%s\"\n",
+ kf->main_group);
+ retval = FALSE;
+ }
+ kf->show_in = TRUE;
+
+ hashtable = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+ show = g_strsplit (value, ";", 0);
+
+ for (i = 0; show[i]; i++) {
+ /* since the value ends with a semicolon, we'll have an empty string
+ * at the end */
+ if (*show[i] == '\0' && show[i + 1] == NULL)
+ break;
+
+ if (g_hash_table_lookup (hashtable, show[i])) {
+ print_warning (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "contains \"%s\" more than once\n",
+ value, locale_key, kf->main_group, show[i]);
+ continue;
}
- if (!ok)
- {
- if (locale)
- k = g_strdup_printf ("%s[%s]", key, locale);
- else
- k = g_strdup_printf ("%s", key);
- print_fatal (filename, "invalid characters in value of key \"%s\", keys of type strings may contain ASCII characters except control characters\n", k);
- g_free (k);
+ g_hash_table_insert (hashtable, show[i], show[i]);
+
+ for (j = 0; j < G_N_ELEMENTS (show_in_registered); j++) {
+ if (!strcmp (show[i], show_in_registered[j]))
+ break;
}
- /* Check that we end in a semicolon */
- if (p != value)
- {
- --p;
- if (*p != ';')
- {
- if (locale)
- k = g_strdup_printf ("%s[%s]", key, locale);
- else
- k = g_strdup_printf ("%s", key);
-
- print_fatal (filename, "value of key \"%s\" is a list of strings and must end with a semicolon\n", k);
- g_free (k);
- }
+ if (j == G_N_ELEMENTS (show_in_registered)) {
+ print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "contains an unregistered value \"%s\"\n",
+ value, locale_key, kf->main_group, show[i]);
+ retval = FALSE;
}
+ }
+
+ g_strfreev (show);
+ g_hash_table_destroy (hashtable);
+
+ return retval;
}
-static void
-validate_categories (const char *value, const char *key, const char *locale, const char *filename, GnomeDesktopFile *df)
-{
- #define MAIN_CATEGORIES \
- "AudioVideo", "Audio", "Video", "Development", "Education", "Game", \
- "Graphics", "Network", "Office", "Settings", "System", "Utility"
-
- #define ADDITIONAL_CATEGORIES \
- "Building", "Debugger", "IDE", "GUIDesigner", "Profiling", \
- "RevisionControl", "Translation", "Calendar", "ContactManagement", \
- "Database", "Dictionary", "Chart", "Email", "Finance", "FlowChart", "PDA", \
- "ProjectManagement", "Presentation", "Spreadsheet", "WordProcessor", \
- "2DGraphics", "VectorGraphics", "RasterGraphics", "3DGraphics", \
- "Scanning", "OCR", "Photography", "Viewer", "DesktopSettings", \
- "HardwareSettings", "PackageManager", "Dialup", "InstantMessaging", \
- "IRCClient", "FileTransfer", "HamRadio", "News", "P2P", "RemoteAccess", \
- "Telephony", "WebBrowser", "WebDevelopment", "Midi", "Mixer", "Sequencer", \
- "Tuner", "TV", "AudioVideoEditing", "Player", "Recorder", "DiscBurning", \
- "ActionGame", "AdventureGame", "ArcadeGame", "BoardGame", "BlocksGame", \
- "CardGame", "KidsGame", "LogicGame", "RolePlaying", "Simulation", \
- "SportsGame", "StrategyGame", "Art", "Construction", "Music", "Languages", \
- "Science", "Astronomy", "Biology", "Chemistry", "Geology", "Math", \
- "MedicalSoftware", "Physics", "Amusement", "Archiving", "Electronics", \
- "Emulator", "Engineering", "FileManager", "TerminalEmulator", \
- "Filesystem", "Monitor", "Security", "Accessibility", "Calculator", \
- "Clock", "TextEditor", "Core", "KDE", "GNOME", "GTK", "Qt", "Motif", \
- "Java", "ConsoleOnly"
-
- #define RESERVED_CATEGORIES \
- "Screensaver", "TrayIcon", "Applet", "Shell"
-
- /* Category list from Desktop Menu Specification version 1.0 */
- static const char *categories_keys[] = {
- MAIN_CATEGORIES, ADDITIONAL_CATEGORIES, RESERVED_CATEGORIES, NULL
- };
- char **vals;
- int i;
-
- validate_strings (value, key, locale, filename, df);
-
- vals = g_strsplit (value, ";", G_MAXINT);
+/* + A command line consists of an executable program optionally followed by
+ * one or more arguments. The executable program can either be specified with
+ * its full path or with the name of the executable only. If no full path is
+ * provided the executable is looked up in the $PATH used by the desktop
+ * environment. The name or path of the executable program may not contain
+ * the equal sign ("=").
+ * FIXME
+ * + Arguments are separated by a space.
+ * FIXME
+ * + Arguments may be quoted in whole.
+ * FIXME
+ * + If an argument contains a reserved character the argument must be quoted.
+ * Checked.
+ * + The rules for quoting of arguments is also applicable to the executable
+ * name or path of the executable program as provided.
+ * FIXME
+ * + Quoting must be done by enclosing the argument between double quotes and
+ * escaping the double quote character, backtick character ("`"), dollar sign
+ * ("$") and backslash character ("\") by preceding it with an additional
+ * backslash character. Implementations must undo quoting before expanding
+ * field codes and before passing the argument to the executable program.
+ * Reserved characters are space (" "), tab, newline, double quote, single
+ * quote ("'"), backslash character ("\"), greater-than sign (">"), less-than
+ * sign ("<"), tilde ("~"), vertical bar ("|"), ampersand ("&"), semicolon
+ * (";"), dollar sign ("$"), asterisk ("*"), question mark ("?"), hash mark
+ * ("#"), parenthesis ("(") and (")") and backtick character ("`").
+ * Checked.
+ * + Note that the general escape rule for values of type string states that
+ * the backslash character can be escaped as ("\\") as well and that this
+ * escape rule is applied before the quoting rule. As such, to unambiguously
+ * represent a literal backslash character in a quoted argument in a desktop
+ * entry file requires the use of four successive backslash characters
+ * ("\\\\"). Likewise, a literal dollar sign in a quoted argument in a
+ * desktop entry file is unambiguously represented with ("\\$").
+ * Checked.
+ * + Field codes consist of the percentage character ("%") followed by an alpha
+ * character. Literal percentage characters must be escaped as %%.
+ * Checked.
+ * + Command lines that contain a field code that is not listed in this
+ * specification are invalid and must not be processed, in particular
+ * implementations may not introduce support for field codes not listed in
+ * this specification. Extensions, if any, should be introduced by means of a
+ * new key.
+ * Checked.
+ * + A command line may contain at most one %f, %u, %F or %U field code.
+ * Checked.
+ * + The %F and %U field codes may only be used as an argument on their own.
+ * FIXME
+ */
+static gboolean
+handle_exec_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value)
+{
+ gboolean retval;
+ gboolean file_uri;
+ gboolean in_quote;
+ gboolean escaped;
+ gboolean flag;
+ const char *c;
- i = 0;
- while (vals[i])
- ++i;
+ handle_key_for_application (kf, locale_key, value);
- if (i == 0)
- {
- g_strfreev (vals);
- return;
- }
-
- /* Drop the empty string g_strsplit leaves in the vector since
- * our list of strings ends in ";"
- */
- --i;
- g_free (vals[i]);
- vals[i] = NULL;
+ retval = TRUE;
- i = 0;
- while (vals[i])
- {
- int j = 0;
-
- if (strncmp ("X-", vals[i], 2) == 0)
- {
- i++;
- continue;
+ file_uri = FALSE;
+ in_quote = FALSE;
+ escaped = FALSE;
+ flag = FALSE;
+
+#define PRINT_INVALID_IF_FLAG \
+ if (flag) { \
+ print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" " \
+ "contains an invalid field code \"%%%c\"\n", \
+ value, locale_key, kf->main_group, *c); \
+ retval = FALSE; \
+ flag = FALSE; \
+ break; \
+ }
+
+ c = value;
+ while (*c) {
+ switch (*c) {
+ /* quotes and escaped characters in quotes */
+ case '"':
+ PRINT_INVALID_IF_FLAG;
+ if (in_quote) {
+ if (!escaped)
+ in_quote = FALSE;
+ } else {
+ if (!escaped)
+ in_quote = TRUE;
+ else {
+ print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "contains an escaped double quote (\\\\\") "
+ "outside of a quote, but the double quote is "
+ "a reserved character\n",
+ value, locale_key, kf->main_group);
+ retval = FALSE;
+
+ escaped = FALSE;
+ }
+ }
+ break;
+ case '`':
+ case '$':
+ PRINT_INVALID_IF_FLAG;
+ if (in_quote) {
+ if (!escaped) {
+ print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "contains a non-escaped character '%c' in a "
+ "quote, but it should be escaped with two "
+ "backslashes (\"\\\\%c\")\n",
+ value, locale_key, kf->main_group, *c, *c);
+ retval = FALSE;
+ } else
+ escaped = FALSE;
+ } else {
+ print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "contains a reserved character '%c' outside of a "
+ "quote\n",
+ value, locale_key, kf->main_group, *c);
+ retval = FALSE;
+ }
+ break;
+ case '\\':
+ PRINT_INVALID_IF_FLAG;
+ c++;
+ if (*c == '\\' && in_quote)
+ escaped = !escaped;
+ break;
+
+ /* reserved characters */
+ case ' ':
+ //FIXME
+ break;
+ case '\t':
+ case '\n':
+ case '\'':
+ case '>':
+ case '<':
+ case '~':
+ case '|':
+ case '&':
+ case ';':
+ case '*':
+ case '?':
+ case '#':
+ case '(':
+ case ')':
+ PRINT_INVALID_IF_FLAG;
+ if (!in_quote) {
+ print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "contains a reserved character '%c' outside of a "
+ "quote\n",
+ value, locale_key, kf->main_group, *c);
+ retval = FALSE;
}
+ break;
+
+ /* flags */
+ case '%':
+ flag = !flag;
+ break;
+ case 'f':
+ case 'u':
+ if (flag) {
+ if (file_uri) {
+ print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "may contain at most one \"%f\", \"%u\", "
+ "\"%F\" or \"%U\" field code\n",
+ value, locale_key, kf->main_group);
+ retval = FALSE;
+ }
- while (categories_keys[j])
- {
- if (g_ascii_strcasecmp (vals[i], categories_keys[j]) == 0)
- {
- if (strcmp (vals[i], categories_keys[j]) != 0)
- {
- print_warning (filename, "%s values are case sensitive (should be \"%s\" instead of \"%s\")\n",
- key, categories_keys[j], vals[i]);
- }
-
- break;
- }
- ++j;
- }
-
- if (categories_keys[j] == NULL)
- {
- char *valid_categories;
-
- if ((g_ascii_strcasecmp (vals[i], "Application") == 0) ||
- (g_ascii_strcasecmp (vals[i], "Applications") == 0))
- {
- valid_categories = g_strjoin ("\", \"", MAIN_CATEGORIES, NULL);
- print_warning (filename, "The '%s' category is not defined by the desktop entry specification. Please use one of \"%s\" instead\n",
- vals[i], valid_categories);
- } else {
- valid_categories = g_strjoinv ("\", \"", (gchar **) categories_keys);
- print_warning (filename, "%s values must be one of \"%s\" (found \"%s\")\n",
- key, valid_categories, vals[i]);
+ file_uri = TRUE;
+ flag = FALSE;
+ }
+ break;
+ case 'F':
+ case 'U':
+ if (flag) {
+ if (file_uri) {
+ print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "may contain at most one \"%f\", \"%u\", "
+ "\"%F\" or \"%U\" field code\n",
+ value, locale_key, kf->main_group);
+ retval = FALSE;
}
- g_free (valid_categories);
- }
- ++i;
+
+ file_uri = TRUE;
+ flag = FALSE;
+ }
+ break;
+ case 'i':
+ case 'c':
+ case 'k':
+ if (flag)
+ flag = FALSE;
+ break;
+ case 'd':
+ case 'D':
+ case 'n':
+ case 'N':
+ case 'v':
+ case 'm':
+ if (flag) {
+ if (!kf->no_deprecated_warnings)
+ print_warning (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "contains a deprecated field code \"%%%c\"\n",
+ value, locale_key, kf->main_group, *c);
+ flag = FALSE;
+ }
+ break;
+
+ default:
+ PRINT_INVALID_IF_FLAG;
+ break;
}
- g_strfreev (vals);
+ c++;
+ }
+
+ if (in_quote) {
+ print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" contains a "
+ "quote which is not closed\n",
+ value, locale_key, kf->main_group);
+ retval = FALSE;
+ }
+
+ if (flag) {
+ print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" contains a "
+ "non-complete field code\n",
+ value, locale_key, kf->main_group);
+ retval = FALSE;
+ }
+
+ return retval;
}
-static void
-validate_only_show_in (const char *value, const char *key, const char *locale, const char *filename, GnomeDesktopFile *df)
+/* + If entry is of type Application, the working directory to run the program
+ * in. (probably implies an absolute path)
+ * Checked.
+ */
+static gboolean
+handle_path_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value)
{
- const char *onlyshowin_keys[] = {
- "KDE", "GNOME", "ROX", "XFCE", "Old", NULL
- };
- char **vals;
- int i;
+ handle_key_for_application (kf, locale_key, value);
- validate_strings (value, key, locale, filename, df);
+ if (!g_path_is_absolute (value))
+ print_warning (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "does not look like an absolute path\n",
+ value, locale_key, kf->main_group);
- vals = g_strsplit (value, ";", G_MAXINT);
+ return TRUE;
+}
- i = 0;
- while (vals[i])
- ++i;
+/* + The MIME type(s) supported by this application. Check they are valid
+ * MIME types.
+ * Checked.
+ * FIXME: need to verify what is the exact definition of a MIME type.
+ * Look at is_valid_mime_type()
+ */
+static gboolean
+handle_mime_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value)
+{
+ gboolean retval;
+ char **types;
+ char *slash;
+ GHashTable *hashtable;
+ int i;
+
+ handle_key_for_application (kf, locale_key, value);
- if (i == 0)
- {
- g_strfreev (vals);
- return;
+ retval = TRUE;
+
+ hashtable = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+ types = g_strsplit (value, ";", 0);
+
+ for (i = 0; types[i]; i++) {
+ /* since the value ends with a semicolon, we'll have an empty string
+ * at the end */
+ if (*types[i] == '\0' && types[i + 1] == NULL)
+ break;
+
+ if (g_hash_table_lookup (hashtable, types[i])) {
+ print_warning (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "contains \"%s\" more than once\n",
+ value, locale_key, kf->main_group, types[i]);
+ continue;
}
-
- /* Drop the empty string g_strsplit leaves in the vector since
- * our list of strings ends in ";"
- */
- --i;
- g_free (vals[i]);
- vals[i] = NULL;
- i = 0;
- while (vals[i])
- {
- int j = 0;
-
- while (onlyshowin_keys[j])
- {
- if (g_ascii_strcasecmp (vals[i], onlyshowin_keys[j]) == 0)
- {
- if (strcmp (vals[i], onlyshowin_keys[j]) != 0)
- {
- print_fatal (filename, "%s values are case sensitive (should be \"%s\" instead of \"%s\")\n",
- key, onlyshowin_keys[j], vals[i]);
- }
- break;
- }
- ++j;
- }
-
- if (onlyshowin_keys[j] == NULL)
- {
- char *valid_onlyshowins;
-
- valid_onlyshowins = g_strjoinv ("\", \"", (gchar **) onlyshowin_keys);
- print_fatal (filename, "%s values must be one of \"%s\" (found \"%s\")\n",
- key, valid_onlyshowins, vals[i]);
- g_free (valid_onlyshowins);
- }
- ++i;
+ g_hash_table_insert (hashtable, types[i], types[i]);
+
+ slash = strchr (types[i], '/');
+ if (!slash || strchr (slash + 1, '/')) {
+ print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "contains value \"%s\" which does not look like "
+ "a MIME type\n",
+ value, locale_key, kf->main_group, types[i]);
+ retval = FALSE;
}
+ }
+
+ g_strfreev (types);
+ g_hash_table_destroy (hashtable);
- g_strfreev (vals);
+ return retval;
}
-static void
-validate_localestring (const char *value, const char *key, const char *locale, const char *filename, GnomeDesktopFile *df)
+/* + FIXME: is there restrictions on how a category should be named?
+ * + Categories in which the entry should be shown in a menu (for possible
+ * values see the Desktop Menu Specification).
+ * Checked.
+ * + The table below describes Reserved Categories. Reserved Categories have a
+ * specific desktop specific meaning that has not been standardized (yet).
+ * Desktop entry files that use a reserved category MUST also include an
+ * appropriate OnlyShowIn= entry to restrict themselves to those environments
+ * that properly support the reserved category as used.
+ * Checked.
+ * + Accept "Application" as a deprecated category.
+ * Checked.
+ * FIXME: it's not really deprecated, so the error message is wrong
+ * + All categories extending the format should start with "X-".
+ * Checked.
+ */
+static gboolean
+handle_categories_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value)
{
- char *k;
- const char *encoding;
- char *res;
- GError *error;
+ gboolean retval;
+ char **categories;
+ GHashTable *hashtable;
+ int i;
+ unsigned int j;
- if (locale)
- k = g_strdup_printf ("%s[%s]", key, locale);
- else
- k = g_strdup_printf ("%s", key);
+ handle_key_for_application (kf, locale_key, value);
+
+ retval = TRUE;
+
+ hashtable = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+ categories = g_strsplit (value, ";", 0);
+ for (i = 0; categories[i]; i++) {
+ /* since the value ends with a semicolon, we'll have an empty string
+ * at the end */
+ if (*categories[i] == '\0' && categories[i + 1] == NULL)
+ break;
- if (gnome_desktop_file_get_encoding (df) == GNOME_DESKTOP_FILE_ENCODING_UTF8)
- {
- if (!g_utf8_validate (value, -1, NULL))
- print_fatal (filename, "value for key \"%s\" contains invalid UTF-8 characters, even though the encoding is UTF-8\n", k);
+ if (g_hash_table_lookup (hashtable, categories[i])) {
+ print_warning (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "contains \"%s\" more than once\n",
+ value, locale_key, kf->main_group, categories[i]);
+ continue;
}
- else if (gnome_desktop_file_get_encoding (df) == GNOME_DESKTOP_FILE_ENCODING_LEGACY)
- {
- if (locale)
- {
- encoding = desktop_file_get_encoding_for_locale (locale);
-
- if (encoding)
- {
- error = NULL;
- res = g_convert (value, -1,
- "UTF-8",
- encoding,
- NULL,
- NULL,
- &error);
- if (!res && error && error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
- print_fatal (filename, "value for key \"%s\" contains characters that are invalid in the \"%s\" encoding\n", k, encoding);
- else if (!res && error && error->code == G_CONVERT_ERROR_NO_CONVERSION)
- print_warning (filename, "encoding \"%s\" for key \"%s\" is not supported by iconv\n", encoding, k);
-
- g_free (res);
- }
- else
- print_fatal (filename, "no encoding specified for locale \"%s\"\n", locale);
- }
- else
- {
- guchar *p = (guchar *)value;
- gboolean ok = TRUE;
- /* non-translated strings in legacy-mixed has to be ascii. */
- while (*p)
- {
- if (*p > 127)
- {
- ok = FALSE;
- break;
- }
-
- p++;
- }
- if (!ok)
- print_fatal (filename, "untranslated localestring key \"%s\" has non-ASCII characters in its value\n", key);
- }
+
+ g_hash_table_insert (hashtable, categories[i], categories[i]);
+
+ if (!strncmp (categories[i], "X-", 2))
+ continue;
+
+#define IF_CHECK_REGISTERED_CATEGORIES(table) \
+ for (j = 0; j < G_N_ELEMENTS (table); j++) { \
+ if (!strcmp (categories[i], table[j])) \
+ break; \
+ } \
+ if (j != G_N_ELEMENTS (table))
+
+ IF_CHECK_REGISTERED_CATEGORIES (main_categories_registered)
+ continue;
+ IF_CHECK_REGISTERED_CATEGORIES (additional_categories_registered)
+ continue;
+ IF_CHECK_REGISTERED_CATEGORIES (reserved_categories_registered) {
+ if (!g_key_file_has_key (kf->keyfile, kf->main_group,
+ "OnlyShowIn", NULL)) {
+ print_fatal (kf, "value \"%s\" in key \"%s\" in group \"%s\" "
+ "is a reserved category, so a \"OnlyShowIn\" key "
+ "must be included\n",
+ categories[i], locale_key, kf->main_group);
+ retval = FALSE;
+ }
+ continue;
+ }
+ IF_CHECK_REGISTERED_CATEGORIES (deprecated_categories_registered) {
+ if (!kf->no_deprecated_warnings)
+ print_warning (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "contains a deprecated value \"%s\"\n",
+ value, locale_key, kf->main_group,
+ categories[i]);
+ continue;
}
- g_free (k);
+ print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "contains an unregistered value \"%s\"\n",
+ value, locale_key, kf->main_group, categories[i]);
+ retval = FALSE;
+ }
+
+ g_strfreev (categories);
+ g_hash_table_destroy (hashtable);
+
+ return retval;
}
-static void
-validate_regexps (const char *value, const char *key, const char *locale, const char *filename, GnomeDesktopFile *df)
-{
- const char *p;
- gboolean ok = TRUE;
- char *k;
-
- p = value;
- while (*p)
- {
- if (!(g_ascii_isprint (*p) || *p == '\n' || *p == '\t'))
- {
- ok = FALSE;
- break;
- }
-
- p++;
+/* FIXME: we don't know the format for this, so we'll just assume that it's
+ * always valid...
+ * This could be wrong because we could use the characters that are
+ * valid for a group name. And also, since it's strings, it should be only
+ * characters accepted for string values.
+ */
+static gboolean
+handle_actions_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value)
+{
+ char **actions;
+ char *action;
+ int i;
+
+ actions = g_strsplit (value, ";", 0);
+
+ for (i = 0; actions[i]; i++) {
+ /* since the value ends with a semicolon, we'll have an empty string
+ * at the end */
+ if (*actions[i] == '\0') {
+ if (actions[i + 1] != NULL)
+ print_warning (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "contains an empty action\n",
+ value, locale_key, kf->main_group);
+ continue;
}
- if (!ok)
- {
- if (locale)
- k = g_strdup_printf ("%s[%s]", key, locale);
- else
- k = g_strdup_printf ("%s", key);
- print_fatal (filename, "invalid characters in value of key \"%s\", keys of type regexps may contain ASCII characters except control characters\n", k);
- g_free (k);
+ if (g_hash_table_lookup (kf->action_values, actions[i])) {
+ print_warning (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "contains action \"%s\" more than once\n",
+ value, locale_key, kf->main_group, actions[i]);
+ continue;
}
+
+ action = g_strdup (actions[i]);
+ g_hash_table_insert (kf->action_values, action, action);
+ }
+
+ g_strfreev (actions);
+
+ return TRUE;
}
-static void
-validate_boolean (const char *value, const char *key, const char *locale, const char *filename, GnomeDesktopFile *df)
+/* + The device to mount. (probably implies an absolute path)
+ * Checked.
+ */
+static gboolean
+handle_dev_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value)
{
- if (strcmp (value, "true") != 0 &&
- strcmp (value, "false") != 0)
- print_fatal (filename, "invalid characters in value of key \"%s\", boolean values must be \"false\" or \"true\" (found \"%s\")\n", key, value);
-
+ handle_key_for_fsdevice (kf, locale_key, value);
+
+ if (!g_path_is_absolute (value))
+ print_warning (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "does not look like an absolute path\n",
+ value, locale_key, kf->main_group);
+
+ return TRUE;
}
-static void
-validate_boolean_or_01 (const char *value, const char *key, const char *locale, const char *filename, GnomeDesktopFile *df)
+/* + The mount point of the device in question. (probably implies an absolute
+ * path)
+ * Checked.
+ */
+static gboolean
+handle_mountpoint_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value)
{
- if (strcmp (value, "true") != 0 &&
- strcmp (value, "false") != 0 &&
- strcmp (value, "0") != 0 &&
- strcmp (value, "1") != 0)
- print_fatal (filename, "invalid characters in value of key \"%s\", boolean values must be \"false\" or \"true\" (found \"%s\")\n", key, value);
+ handle_key_for_fsdevice (kf, locale_key, value);
- if (strcmp (value, "0") == 0 ||
- strcmp (value, "1") == 0)
- print_warning (filename, "boolean key \"%s\" has value \"%s\", boolean values should be \"false\" or \"true\", although \"0\" and \"1\" are allowed in this field for backwards compatibility\n", key, value);
+ if (!g_path_is_absolute (value))
+ print_warning (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "does not look like an absolute path\n",
+ value, locale_key, kf->main_group);
+
+ return TRUE;
}
-static void
-validate_numeric (const char *value, const char *key, const char *locale, const char *filename, GnomeDesktopFile *df)
+/* + Possible values are UTF-8 and Legacy-Mixed.
+ * Checked.
+ */
+static gboolean
+handle_encoding_key (kf_validator *kf,
+ const char *locale_key,
+ const char *value)
{
- float d;
- int res;
-
- res = sscanf( value, "%f", &d);
- if (res == 0)
- print_fatal (filename, "numeric key \"%s\" has value \"%s\", which doesn't look like a number\n", key, value);
+ if (!strcmp (value, "UTF-8") || !strcmp (value, "Legacy-Mixed"))
+ return TRUE;
+
+ print_fatal (kf, "value \"%s\" for key \"%s\" in group \"%s\" "
+ "is not a registered encoding value (\"UTF-8\", and "
+ "\"Legacy-Mixed\")\n",
+ value, locale_key, kf->main_group);
+
+ return FALSE;
}
-struct {
- char *keyname;
- void (*validate_type) (const char *value, const char *key, const char *locale, const char *filename, GnomeDesktopFile *df);
- gboolean deprecated;
-} key_table[] = {
- { "Encoding", validate_string },
- { "Version", validate_numeric },
- { "Name", validate_localestring },
- { "GenericName", validate_localestring },
- { "Type", validate_string },
- { "FilePattern", validate_regexps },
- { "TryExec", validate_string },
- { "NoDisplay", validate_boolean },
- { "Comment", validate_localestring },
- { "Exec", validate_string },
- { "Actions", validate_strings },
- { "Icon", validate_string },
- { "MiniIcon", validate_string, TRUE }, /* 0.9.4: deprecated */
- { "Hidden", validate_boolean },
- { "Path", validate_string },
- { "Terminal", validate_boolean_or_01 },
- { "TerminalOptions", validate_string, TRUE }, /* 0.9.4: deprecated */
- { "SwallowTitle", validate_localestring },
- { "SwallowExec", validate_string },
- { "MimeType", validate_regexps },
- { "Patterns", validate_regexps, TRUE }, /* 0.9.4: deprecated */
- { "DefaultApp", validate_string, TRUE }, /* 0.9.4: deprecated */
- { "Dev", validate_string },
- { "FSType", validate_string },
- { "MountPoint", validate_string },
- { "ReadOnly", validate_boolean_or_01 },
- { "UnmountIcon", validate_string },
- { "SortOrder", validate_strings /* FIXME: Also comma-separated */},
- { "URL", validate_string },
- { "Categories", validate_categories },
- { "OnlyShowIn", validate_only_show_in },
- { "NotShowIn", validate_only_show_in },
- { "StartupNotify", validate_boolean },
- { "StartupWMClass", validate_string },
- { "BinaryPattern", validate_string, TRUE }, /* 0.9.3: deprecated */
- { "DocPath", validate_string }, /* 0.9.4: within KDE only */
- { "Extensions", validate_string, TRUE }, /* 0.9.3: deprecated */
- { "InitialPreference", validate_string }, /* 0.9.4: within KDE only */
- { "Keywords", validate_localestring }, /* 0.9.4: within KDE only */
- { "MapNotify", validate_string, TRUE }, /* 0.9.3: deprecated */
- { "Protocols", validate_string, TRUE }, /* 0.9.3: deprecated */
- { "ServiceTypes", validate_string }, /* 0.9.4: within KDE only */
-};
+static gboolean
+handle_key_for_application (kf_validator *kf,
+ const char *locale_key,
+ const char *value)
+{
+ kf->application_keys = g_list_append (kf->application_keys,
+ g_strdup (locale_key));
+ return TRUE;
+}
-static void
-enum_keys (GnomeDesktopFile *df,
- const char *key, /* If NULL, value is comment line */
- const char *locale,
- const char *value, /* This is raw unescaped data */
- gpointer user_data)
-{
- struct KeyData *data = user_data;
- struct KeyHashData *hash_data;
- const char *p;
- int i;
-
- if (key == NULL)
- {
- if (!g_utf8_validate (value, -1, NULL))
- print_warning (data->filename, "file contains non-UTF-8 comments\n");
+static gboolean
+handle_key_for_link (kf_validator *kf,
+ const char *locale_key,
+ const char *value)
+{
+ kf->link_keys = g_list_append (kf->link_keys,
+ g_strdup (locale_key));
+ return TRUE;
+}
- return;
- }
+static gboolean
+handle_key_for_fsdevice (kf_validator *kf,
+ const char *locale_key,
+ const char *value)
+{
+ kf->fsdevice_keys = g_list_append (kf->fsdevice_keys,
+ g_strdup (locale_key));
+ return TRUE;
+}
- hash_data = g_hash_table_lookup (data->hash, key);
- if (hash_data == NULL)
- {
- hash_data = g_new0 (struct KeyHashData, 1);
- g_hash_table_insert (data->hash, (char *)key, hash_data);
- }
+static gboolean
+handle_key_for_mimetype (kf_validator *kf,
+ const char *locale_key,
+ const char *value)
+{
+ kf->mimetype_keys = g_list_append (kf->mimetype_keys,
+ g_strdup (locale_key));
+ return TRUE;
+}
- if (locale == NULL) {
- if (hash_data->has_non_translated)
- print_fatal (data->filename, "file contains multiple assignments of key \"%s\"\n", key);
-
- hash_data->has_non_translated = TRUE;
- } else {
- hash_data->has_translated = TRUE;
+/* + Key names must contain only the characters A-Za-z0-9-.
+ * Checked.
+ * + LOCALE must be of the form lang_COUNTRY.ENCODING@MODIFIER, where _COUNTRY,
+ * .ENCODING, and @MODIFIER may be omitted.
+ * Checked.
+ */
+static gboolean
+key_extract_locale (const char *key,
+ char **real_key,
+ char **locale)
+{
+ const char *start_locale;
+ char c;
+ int len;
+ int i;
+
+ if (real_key)
+ *real_key = NULL;
+ if (locale)
+ *locale = NULL;
+
+ start_locale = g_strrstr (key, "[");
+
+ if (start_locale)
+ len = start_locale - key;
+ else
+ len = strlen (key);
+
+ for (i = 0; i < len; i++) {
+ c = key[i];
+ if (!g_ascii_isalnum (c) && c != '-')
+ return FALSE;
+ }
+
+ if (!start_locale) {
+ if (real_key)
+ *real_key = g_strdup (key);
+ if (locale)
+ *locale = NULL;
+
+ return TRUE;
+ }
+
+ len = strlen (start_locale);
+ if (len <= 2 || start_locale[len - 1] != ']')
+ return FALSE;
+
+ /* ignore first [ and last ] */
+ for (i = 1; i < len - 2; i++) {
+ c = start_locale[i];
+ if (!g_ascii_isalnum (c) && c != '-' && c != '_' && c != '.' && c != '@')
+ return FALSE;
}
-#ifdef VERIFY_CANONICAL_ENCODING_NAME
+ if (real_key)
+ *real_key = g_strndup (key, strlen (key) - len);
if (locale)
- {
- const char *encoding;
- const char *canonical;
-
- encoding = strchr(locale, '.');
-
- if (encoding)
- {
- encoding++;
-
- canonical = get_canonical_encoding (encoding);
- if (strcmp (encoding, canonical) != 0)
- print_warning (data->filename, "non-canonical encoding \"%s\" specified, the canonical name of the encoding is \"%s\"\n", encoding, canonical);
- }
- }
-#endif
+ *locale = g_strndup (start_locale + 1, len - 2);
- for (i = 0; i < (int) G_N_ELEMENTS (key_table); i++)
- {
- if (strcmp (key_table[i].keyname, key) == 0)
- break;
- }
+ return TRUE;
+}
- if (i < (int) G_N_ELEMENTS (key_table))
- {
- if (key_table[i].validate_type)
- (*key_table[i].validate_type) (value, key, locale, data->filename, df);
- if (key_table[i].deprecated)
- print_warning (data->filename, "file contains key \"%s\", usage of this key is not recommended, since it has been deprecated\n", key);
- if (strcmp (key, "ServiceTypes") == 0 ||
- strcmp (key, "DocPath") == 0 ||
- strcmp (key, "Keywords") == 0 ||
- strcmp (key, "InitialPreference") == 0)
- print_warning (data->filename, "file contains key \"%s\", this key is currently reserved for use within KDE, and should in the future KDE releases be prefixed by \"X-\"\n", key);
+/* + All keys extending the format should start with "X-".
+ * Checked.
+ */
+static gboolean
+validate_desktop_key (kf_validator *kf,
+ const char *locale_key,
+ const char *key,
+ const char *locale)
+{
+ unsigned int i;
+ unsigned int j;
+ char *value;
+
+ if (!strncmp (key, "X-", 2))
+ return TRUE;
+
+ for (i = 0; i < G_N_ELEMENTS (registered_desktop_keys); i++) {
+ if (strcmp (key, registered_desktop_keys[i].name))
+ continue;
+
+ if (registered_desktop_keys[i].type != DESKTOP_LOCALESTRING_TYPE &&
+ locale != NULL) {
+ print_fatal (kf, "file contains key \"%s\" in group \"%s\", "
+ "but \"%s\" is not defined as a locale string\n",
+ locale_key, kf->main_group, key);
+ return FALSE;
}
- else
- {
- if (strncmp (key, "X-", 2) != 0)
- print_warning (data->filename, "non-standard key \"%s\" lacks the \"X-\" prefix\n", key);
+
+ for (j = 0; j < G_N_ELEMENTS (validate_for_type); j++) {
+ if (validate_for_type[j].type == registered_desktop_keys[i].type)
+ break;
}
- /* Validation of specific keys */
+ g_assert (j != G_N_ELEMENTS (validate_for_type));
+
+ if (!kf->no_deprecated_warnings && registered_desktop_keys[i].deprecated)
+ print_warning (kf, "key \"%s\" in group \"%s\" is deprecated\n",
+ locale_key, kf->main_group);
+
+ if (registered_desktop_keys[i].kde_reserved && kf->kde_reserved_warnings)
+ print_warning (kf, "key \"%s\" in group \"%s\" is a reserved key for "
+ "KDE\n",
+ locale_key, kf->main_group);
+
+ value = g_key_file_get_value (kf->keyfile, kf->main_group,
+ locale_key, NULL);
+
+ /* this is not supposed to happen since we got the key from the list of
+ * existing keys */
+ g_assert (value != NULL);
- if (strcmp (key, "Icon") == 0)
- {
-#if 0
- /* With new icon theme spec we allow this */
- if (strchr (value, '.') == NULL)
- print_warning (data->filename, "icon \"%s\" specified does not seem to contain a filename extension\n", value);
-#endif
+ if (!validate_for_type[j].validate (kf, key, locale, value)) {
+ g_free (value);
+ return FALSE;
}
-
- if (strcmp (key, "Exec") == 0)
- {
- if (strstr (value, "NO_XALF") != NULL)
- print_fatal (data->filename, "the Exec string includes the nonstandard, broken NO_XALF prefix\n");
-
- p = value;
- while (*p)
- {
- if (*p == '%')
- {
- p++;
- if (*p != 'f' && *p != 'F' &&
- *p != 'u' && *p != 'U' &&
- *p != 'd' && *p != 'D' &&
- *p != 'n' && *p != 'N' &&
- *p != 'i' && *p != 'm' &&
- *p != 'c' && *p != 'k' &&
- *p != 'v' && *p != '%')
- print_fatal (data->filename, "the Exec string includes the non-standard parameter \"%%%c\"\n", *p);
- if (*p == 0)
- break;
- }
- p++;
- }
+
+ if (registered_desktop_keys[i].handle_and_validate != NULL) {
+ if (!registered_desktop_keys[i].handle_and_validate (kf, locale_key,
+ value)) {
+ g_free (value);
+ return FALSE;
+ }
}
-
-}
+ g_free (value);
+ break;
+ }
-static void
-enum_hash_keys (gpointer key,
- gpointer value,
- gpointer user_data)
-{
- struct KeyData *data = user_data;
- struct KeyHashData *hash_data = value;
+ if (i == G_N_ELEMENTS (registered_desktop_keys)) {
+ print_fatal (kf, "file contains key \"%s\" in group \"%s\", but "
+ "keys extending the format should start with "
+ "\"X-\"\n", key, kf->main_group);
+ return FALSE;
+ }
- if (hash_data->has_translated &&
- !hash_data->has_non_translated)
- print_fatal (data->filename, "key \"%s\" is translated, but no untranslated version exists\n", (char *)key);
-
+ return TRUE;
}
-static void
-generic_keys (GnomeDesktopFile *df, const char *filename)
+/* + Multiple keys in the same group may not have the same name.
+ * Checked.
+ */
+static gboolean
+validate_keys_for_group (kf_validator *kf,
+ const char *group)
{
- struct KeyData data = {0 };
-
- data.hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
- data.filename = filename;
-
- gnome_desktop_file_foreach_key (df, NULL, TRUE,
- enum_keys, &data);
+ gboolean desktop_group;
+ gboolean retval;
+ int i;
+ char *key;
+ char *locale;
+ char **keys;
+ GHashTable *hashtable;
- g_hash_table_foreach (data.hash,
- enum_hash_keys,
- &data);
+ retval = TRUE;
- g_hash_table_destroy (data.hash);
-}
+ desktop_group = (!strcmp (group, GROUP_DESKTOP_ENTRY) ||
+ !strcmp (group, GROUP_KDE_DESKTOP_ENTRY));
-struct SectionData {
- GHashTable *hash;
- const char *filename;
- const char *main_section;
- gboolean has_kde_desktop_entry;
-};
+ keys = g_key_file_get_keys (kf->keyfile, group, NULL, NULL);
-static void
-enum_sections (GnomeDesktopFile *df,
- const char *name,
- gpointer data)
-{
- struct SectionData *section = data;
-
- /* Initial sections (for any comments at top of
- * file, etc) have name == NULL.
- */
- if (!name)
- return;
-
- if (strcmp (name, "Desktop Entry") == 0 ||
- strcmp (name, "KDE Desktop Entry") == 0)
- {
- if (!section->main_section)
- {
- section->main_section = name;
- }
- else
- {
- print_fatal (section->filename, "file already contains section %s, should not contain another section %s\n", section->main_section, name);
- }
-
- if (strcmp (name, "KDE Desktop Entry") == 0)
- section->has_kde_desktop_entry = TRUE;
- }
- else if (strncmp (name, "Desktop Action ", 15) != 0 &&
- strncmp (name, "X-", 2) != 0)
- {
- print_fatal (section->filename, "file contains section %s, extensions to the spec should use section names starting with \"X-\".\n", name);
+ g_assert (keys != NULL);
+
+ hashtable = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+
+ for (i = 0; keys[i] != NULL; i++) {
+ if (!key_extract_locale (keys[i], &key, &locale)) {
+ print_fatal (kf, "file contains key \"%s\" in group \"%s\", but "
+ "key names must contain only the characters "
+ "A-Za-z0-9- (they may have a \"[LOCALE]\" postfix)\n",
+ keys[i], group);
+ retval = FALSE;
+
+ key = g_strdup (keys[i]);
}
- if (g_hash_table_lookup (section->hash, name))
- print_fatal (section->filename, "file contains multiple sections named %s\n", name);
- else
- g_hash_table_insert (section->hash, (char *)name, (char *)name);
-}
+ g_assert (key != NULL);
-static const char *
-required_section (GnomeDesktopFile *df, const char *filename)
-{
- struct SectionData section;
-
- section.hash = g_hash_table_new (g_str_hash, g_str_equal);
- section.filename = filename;
- section.main_section = NULL;
- section.has_kde_desktop_entry = FALSE;
-
- gnome_desktop_file_foreach_section (df, enum_sections, &section);
-
- if (!section.main_section)
- {
- print_fatal (filename, "file doesn't contain a \"Desktop Entry\" section\n");
+ if (g_hash_table_lookup (hashtable, keys[i])) {
+ print_fatal (kf, "file contains multiple keys named \"%s\" in "
+ "group \"%s\"\n", keys[i], group);
+ retval = FALSE;
}
- else if (section.has_kde_desktop_entry)
- {
- print_warning (filename, "file contains a \"KDE Desktop Entry\" section, this has been deprecated in favor of \"Desktop Entry\"\n");
+
+ if (desktop_group) {
+ if (!validate_desktop_key (kf, keys[i], key, locale))
+ retval = FALSE;
}
- g_hash_table_destroy (section.hash);
-
- return section.main_section;
+ g_hash_table_insert (hashtable, keys[i], keys[i]);
+
+ g_free (key);
+ key = NULL;
+ g_free (locale);
+ locale = NULL;
+ }
+
+ g_hash_table_destroy (hashtable);
+ g_strfreev (keys);
+
+ return retval;
}
static gboolean
-required_keys (GnomeDesktopFile *df, const char *section, const char *filename)
+validate_group_name (kf_validator *kf,
+ const char *group)
{
- const char *val;
-
- if (gnome_desktop_file_get_raw (df, section,
- "Encoding",
- NULL, &val))
- {
- if (strcmp (val, "UTF-8") != 0 &&
- strcmp (val, "Legacy-Mixed") != 0)
- print_fatal (filename, "unknown Encoding type \"%s\", should be one of \"UTF-8\", \"Legacy-Mixed\"\n", val);
- }
- else
- {
- print_fatal (filename, "required key \"Encoding\" not found\n");
+ int i;
+ char c;
+
+ for (i = 0; group[i] != '\0'; i++) {
+ c = group[i];
+ if (!g_ascii_isprint (c) || c == '[' || c == ']') {
+ print_fatal (kf, "file contains group \"%s\", but group names "
+ "may contain all ASCII characters except for [ "
+ "and ] and control characters\n", group);
+ return FALSE;
}
+ }
- if (!gnome_desktop_file_get_raw (df, section,
- "Name",
- NULL, &val))
- {
- print_fatal (filename, "required key \"Name\" not found\n");
- }
+ if (!strncmp (group, "X-", 2))
+ return TRUE;
- if (gnome_desktop_file_get_raw (df, section,
- "Type",
- NULL, &val))
- {
- if (strcmp (val, "Application") != 0 &&
- strcmp (val, "Link") != 0 &&
- strcmp (val, "FSDevice") != 0 &&
- strcmp (val, "Directory") != 0)
- {
- if (strcmp (val, "MimeType") == 0)
- {
- print_warning (filename, "file specifies \"Type=MimeType\", usage of the \"MimeType\" value for \"Type\" is not recommended, since it has been deprecated\n");
- }
- else if (strcmp (val, "Service") == 0 ||
- strcmp (val, "ServiceType") == 0)
- {
- print_warning (filename, "file specifies \"Type=%s\", this value for \"Type\" is currently reserved for use within KDE, and should in future KDE releases be prefixed by \"X-\"\n", val);
- }
- else
- {
- print_fatal (filename, "invalid Type \"%s\"\n", val);
- }
- }
- }
- else
- {
- print_fatal (filename, "required key \"Type\" not found\n");
+ if (!strcmp (group, GROUP_DESKTOP_ENTRY)) {
+ if (kf->main_group && !strcmp (kf->main_group, GROUP_KDE_DESKTOP_ENTRY))
+ print_warning (kf, "file contains groups \"%s\" and \"%s\", which play "
+ "the same role\n",
+ GROUP_KDE_DESKTOP_ENTRY, GROUP_DESKTOP_ENTRY);
+
+ kf->main_group = GROUP_DESKTOP_ENTRY;
+
+ return TRUE;
+ }
+
+ if (!strcmp (group, GROUP_KDE_DESKTOP_ENTRY)) {
+ if (kf->kde_reserved_warnings || !kf->no_deprecated_warnings)
+ print_warning (kf, "file contains group \"%s\", which is deprecated "
+ "in favor of \"%s\"\n", group, GROUP_DESKTOP_ENTRY);
+
+ if (kf->main_group && !strcmp (kf->main_group, GROUP_DESKTOP_ENTRY))
+ print_warning (kf, "file contains groups \"%s\" and \"%s\", which play "
+ "the same role\n",
+ GROUP_DESKTOP_ENTRY, GROUP_KDE_DESKTOP_ENTRY);
+
+ kf->main_group = GROUP_KDE_DESKTOP_ENTRY;
+
+ return TRUE;
+ }
+
+ if (!strncmp (group, GROUP_DESKTOP_ACTION, strlen (GROUP_DESKTOP_ACTION))) {
+ if (group[strlen (GROUP_DESKTOP_ACTION) - 1] == '\0') {
+ print_fatal (kf, "file contains group \"%s\", which is an action "
+ "group with no action name\n", group);
+ return FALSE;
+ } else {
+ char *action;
+
+ action = g_strdup (group + strlen (GROUP_DESKTOP_ACTION));
+ g_hash_table_insert (kf->action_groups, action, action);
+
+ return TRUE;
}
- return TRUE;
-}
+ }
-struct ActionsData {
- GHashTable *hash;
- const char *filename;
-};
+ print_fatal (kf, "file contains group \"%s\", but groups extending "
+ "the format should start with \"X-\"\n", group);
+ return FALSE;
+}
-static void
-enum_actions (GnomeDesktopFile *df,
- const char *section,
- gpointer data)
+/* + Only comments are accepted before the first group.
+ * FIXME: verify that GKeyFile handles this.
+ * + Using [KDE Desktop Entry] instead of [Desktop Entry] as header is
+ * deprecated.
+ * Checked.
+ * + The first group should be "Desktop Entry".
+ * Checked.
+ * + Group names may contain all ASCII characters except for [ and ] and
+ * control characters.
+ * Checked.
+ * + Multiple groups may not have the same name.
+ * FIXME: GKeyFile can't let us verify this.
+ * + All groups extending the format should start with "X-".
+ * Checked.
+ * + Accept "Desktop Action foobar" group if the value for the Action key
+ * contains "foobar". (This is not in spec 1.0, but it was there before and
+ * it wasn't deprecated)
+ * Checked.
+ */
+static gboolean
+validate_groups_and_keys (kf_validator *kf)
{
- struct ActionsData *actions_data = data;
- const char *val;
- const char *action;
+ gboolean retval;
+ int i;
+ char *group;
+ char **groups;
- /* Initial sections (for any comments at top of
- * file, etc) have name == NULL.
- */
- if (!section)
- return;
+ retval = TRUE;
+
+ group = g_key_file_get_start_group (kf->keyfile);
+ if (!group ||
+ (strcmp (group, GROUP_DESKTOP_ENTRY) &&
+ strcmp (group, GROUP_KDE_DESKTOP_ENTRY)))
+ print_fatal (kf, "first group is not \"" GROUP_DESKTOP_ENTRY "\"\n");
+ g_free (group);
+
+ groups = g_key_file_get_groups (kf->keyfile, NULL);
+ i = 0;
+ for (i = 0; groups[i] != NULL; i++) {
+ group = groups[i];
+
+ if (!validate_group_name (kf, group))
+ retval = FALSE;
- if (strncmp (section, "Desktop Action ", 15) != 0)
- return;
+ if (!validate_keys_for_group (kf, group))
+ retval = FALSE;
+ }
- action = section + 15;
+ g_strfreev (groups);
+ return retval;
+}
- /* Already verified this */
- g_assert (!g_hash_table_lookup (actions_data->hash, action));
+static gboolean
+validate_required_keys (kf_validator *kf)
+{
+ gboolean retval;
+ unsigned int i;
- g_hash_table_insert (actions_data->hash, (char *) action, (char *) action);
+ retval = TRUE;
- if (!gnome_desktop_file_get_raw (df, section,
- "Exec",
- NULL, &val))
- {
- print_fatal (actions_data->filename, "file contains \"Desktop Action %s\" section which lacks an Exec key\n", section);
+ for (i = 0; i < G_N_ELEMENTS (registered_desktop_keys); i++) {
+ if (registered_desktop_keys[i].required) {
+ if (!g_key_file_has_key (kf->keyfile, kf->main_group,
+ registered_desktop_keys[i].name, NULL)) {
+ print_fatal (kf, "required key \"%s\" in group \"%s\" is not "
+ "present\n",
+ registered_desktop_keys[i].name, kf->main_group);
+ retval = FALSE;
+ }
}
+ }
+
+ return retval;
}
-static void
-error_orphaned_action (gpointer key,
- gpointer value,
- gpointer user_data)
+#define PRINT_ERROR_FOREACH_KEY(lower, real) \
+static void \
+print_error_foreach_##lower##_key (const char *name, \
+ kf_validator *kf) \
+{ \
+ print_fatal (kf, "key \"%s\" is present in group \"%s\", but the type is " \
+ "\"%s\" while this key is only valid for type \"%s\"\n", \
+ name, kf->main_group, kf->type_string, real); \
+}
+
+PRINT_ERROR_FOREACH_KEY (application, "Application")
+PRINT_ERROR_FOREACH_KEY (link, "Link")
+PRINT_ERROR_FOREACH_KEY (fsdevice, "FSDevice")
+PRINT_ERROR_FOREACH_KEY (mimetype, "MimeType")
+
+static gboolean
+validate_type_keys (kf_validator *kf)
{
- const char *action = key;
- const char *filename = user_data;
+ gboolean retval;
- print_fatal (filename, "file contains \"Desktop Action %s\" but Actions key does not contain '%s'\n", action, action);
+ retval = TRUE;
+
+ switch (kf->type) {
+ case INVALID_TYPE:
+ break;
+ case APPLICATION_TYPE:
+ g_list_foreach (kf->link_keys,
+ (GFunc) print_error_foreach_link_key, kf);
+ g_list_foreach (kf->fsdevice_keys,
+ (GFunc) print_error_foreach_fsdevice_key, kf);
+ g_list_foreach (kf->mimetype_keys,
+ (GFunc) print_error_foreach_mimetype_key, kf);
+ retval = (g_list_length (kf->link_keys) +
+ g_list_length (kf->fsdevice_keys) +
+ g_list_length (kf->mimetype_keys) == 0);
+ break;
+ case LINK_TYPE:
+ g_list_foreach (kf->application_keys,
+ (GFunc) print_error_foreach_application_key, kf);
+ g_list_foreach (kf->fsdevice_keys,
+ (GFunc) print_error_foreach_fsdevice_key, kf);
+ g_list_foreach (kf->mimetype_keys,
+ (GFunc) print_error_foreach_mimetype_key, kf);
+ retval = (g_list_length (kf->application_keys) +
+ g_list_length (kf->fsdevice_keys) +
+ g_list_length (kf->mimetype_keys) == 0);
+ break;
+ case DIRECTORY_TYPE:
+ case SERVICE_TYPE:
+ case SERVICE_TYPE_TYPE:
+ g_list_foreach (kf->application_keys,
+ (GFunc) print_error_foreach_application_key, kf);
+ g_list_foreach (kf->link_keys,
+ (GFunc) print_error_foreach_link_key, kf);
+ g_list_foreach (kf->fsdevice_keys,
+ (GFunc) print_error_foreach_fsdevice_key, kf);
+ g_list_foreach (kf->mimetype_keys,
+ (GFunc) print_error_foreach_mimetype_key, kf);
+ retval = (g_list_length (kf->application_keys) +
+ g_list_length (kf->link_keys) +
+ g_list_length (kf->fsdevice_keys) +
+ g_list_length (kf->mimetype_keys) == 0);
+ break;
+ case FSDEVICE_TYPE:
+ g_list_foreach (kf->application_keys,
+ (GFunc) print_error_foreach_application_key, kf);
+ g_list_foreach (kf->link_keys,
+ (GFunc) print_error_foreach_link_key, kf);
+ g_list_foreach (kf->mimetype_keys,
+ (GFunc) print_error_foreach_mimetype_key, kf);
+ retval = (g_list_length (kf->application_keys) +
+ g_list_length (kf->link_keys) +
+ g_list_length (kf->mimetype_keys) == 0);
+ break;
+ case MIMETYPE_TYPE:
+ g_list_foreach (kf->application_keys,
+ (GFunc) print_error_foreach_application_key, kf);
+ g_list_foreach (kf->link_keys,
+ (GFunc) print_error_foreach_link_key, kf);
+ g_list_foreach (kf->fsdevice_keys,
+ (GFunc) print_error_foreach_fsdevice_key, kf);
+ retval = (g_list_length (kf->application_keys) +
+ g_list_length (kf->link_keys) +
+ g_list_length (kf->fsdevice_keys) == 0);
+ break;
+ case LAST_TYPE:
+ g_assert_not_reached ();
+ }
+
+ return retval;
}
static gboolean
-required_actions (GnomeDesktopFile *df, const char *filename)
+lookup_group_foreach_action (char *key,
+ char *value,
+ kf_validator *kf)
{
- struct ActionsData actions_data;
- gboolean retval = FALSE;
+ if (g_hash_table_lookup (kf->action_groups, key)) {
+ g_hash_table_remove (kf->action_groups, key);
+ return TRUE;
+ }
- actions_data.hash = g_hash_table_new (g_str_hash, g_str_equal);
- actions_data.filename = filename;
-
- gnome_desktop_file_foreach_section (df, enum_actions, &actions_data);
+ return FALSE;
+}
- if (g_hash_table_size (actions_data.hash) > 0)
- {
- const char *val;
- char **actions;
- int i;
+static void
+print_error_foreach_action (char *key,
+ char *value,
+ kf_validator *kf)
+{
+ print_fatal (kf, "action \"%s\" is defined, but there is no matching "
+ "\"%s%s\" group\n", key, GROUP_DESKTOP_ACTION, key);
+}
- if (!gnome_desktop_file_get_raw (df, NULL, "Actions", NULL, &val))
- {
- print_fatal (filename, "file has \"Desktop Action\" sections but no Action key\n");
- goto out;
- }
+static void
+print_error_foreach_group (char *key,
+ char *value,
+ kf_validator *kf)
+{
+ print_fatal (kf, "action group \"%s%s\" exists, but there is no matching "
+ "action \"%s\"\n", GROUP_DESKTOP_ACTION, key, key);
+}
- actions = g_strsplit (val, ";", G_MAXINT);
- for (i = 0; actions [i]; i++)
- {
- if (*actions [i] == '\0')
- continue;
+static gboolean
+validate_actions (kf_validator *kf)
+{
+ g_hash_table_foreach_remove (kf->action_values,
+ (GHRFunc) lookup_group_foreach_action, kf);
- if (!g_hash_table_lookup (actions_data.hash, actions [i]))
- {
- print_fatal (filename, "Action key contains '%s' but file has \"Desktop Action %s\" section\n",
- actions [i], actions [i]);
- goto out;
- }
+ g_hash_table_foreach (kf->action_values,
+ (GHFunc) print_error_foreach_action, kf);
- g_hash_table_remove (actions_data.hash, actions [i]);
- }
+ g_hash_table_foreach (kf->action_groups,
+ (GHFunc) print_error_foreach_group, kf);
- g_strfreev (actions);
+ return (g_hash_table_size (kf->action_values) +
+ g_hash_table_size (kf->action_groups) == 0);
+}
- if (g_hash_table_size (actions_data.hash) > 0)
- {
- g_hash_table_foreach (actions_data.hash,
- error_orphaned_action,
- (char *) filename);
- goto out;
- }
+/* + These desktop entry files should have the extension .desktop.
+ * Checked.
+ * + Desktop entries which describe how a directory is to be
+ * formatted/displayed should be simply called .directory.
+ * Checked.
+ * + Using .kdelnk instead of .desktop as the file extension is deprecated.
+ * Checked.
+ * FIXME: we're not doing what the spec says wrt Directory.
+ */
+static gboolean
+validate_filename (kf_validator *kf)
+{
+ if (kf->type == DIRECTORY_TYPE) {
+ if (g_str_has_suffix (kf->filename, ".directory"))
+ return TRUE;
+ else {
+ print_fatal (kf, "file is of type \"Directory\", but filename does not "
+ "have a .directory extension\n");
+ return FALSE;
}
+ }
- retval = TRUE;
+ if (g_str_has_suffix (kf->filename, ".desktop"))
+ return TRUE;
- out:
- g_hash_table_destroy (actions_data.hash);
+ if (g_str_has_suffix (kf->filename, ".kdelnk")) {
+ if (kf->kde_reserved_warnings || !kf->no_deprecated_warnings)
+ print_warning (kf, "filename has a .kdelnk extension, which is "
+ "deprecated in favor of .desktop\n");
+ return TRUE;
+ }
- return retval;
+ print_fatal (kf, "filename does not have a .desktop extension\n");
+ return FALSE;
}
gboolean
-desktop_file_validate (GnomeDesktopFile *df, const char *filename)
+desktop_file_validate (const char *filename,
+ gboolean warn_kde,
+ gboolean no_warn_deprecated)
{
- const char *name;
- const char *comment;
- const char *main_section;
+ GError *error;
+ kf_validator kf;
+
+ /* just a consistency check */
+ g_assert (G_N_ELEMENTS (registered_types) == LAST_TYPE - 1);
+
+ kf.filename = filename;
+ kf.keyfile = g_key_file_new ();
+ kf.kde_reserved_warnings = warn_kde;
+ kf.no_deprecated_warnings = no_warn_deprecated;
+
+ error = NULL;
+ if (!g_key_file_load_from_file (kf.keyfile, filename,
+ G_KEY_FILE_KEEP_COMMENTS|
+ G_KEY_FILE_KEEP_TRANSLATIONS,
+ &error)) {
+ print_fatal (&kf, "parse error: %s\n", error->message);
+ g_error_free (error);
+ g_key_file_free (kf.keyfile);
+
+ return FALSE;
+ }
- /* FIXME global variable cruft */
- fatal_error_occurred = FALSE;
-
- if ((main_section = required_section (df, filename)) == NULL)
- return !fatal_error_occurred;
- if (!required_keys (df, main_section, filename))
- return !fatal_error_occurred;
- if (!required_actions (df, filename))
- return !fatal_error_occurred;
-
- generic_keys (df, filename);
-
- if (gnome_desktop_file_get_raw (df, NULL, "Name", NULL, &name) &&
- gnome_desktop_file_get_raw (df, NULL, "Comment", NULL, &comment))
- {
- if (strcmp (name, comment) == 0)
- print_warning (filename, "the fields \"Name\" and \"Comment\" have the same value\n");
- }
+ kf.main_group = NULL;
+ kf.type = INVALID_TYPE;
+ kf.type_string = NULL;
+ kf.show_in = FALSE;
+ kf.application_keys = NULL;
+ kf.link_keys = NULL;
+ kf.fsdevice_keys = NULL;
+ kf.mimetype_keys = NULL;
+ kf.action_values = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, g_free);
+ kf.action_groups = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, g_free);
+ kf.fatal_error = FALSE;
+
+ validate_groups_and_keys (&kf);
+ //FIXME: this does not work well if there are both a Desktop Entry and a KDE
+ //Desktop Entry groups since only the last one will be validated for this.
+ if (kf.main_group) {
+ validate_required_keys (&kf);
+ validate_type_keys (&kf);
+ }
+ validate_actions (&kf);
+ validate_filename (&kf);
+
+ g_list_foreach (kf.application_keys, (GFunc) g_free, NULL);
+ g_list_free (kf.application_keys);
+ g_list_foreach (kf.link_keys, (GFunc) g_free, NULL);
+ g_list_free (kf.link_keys);
+ g_list_foreach (kf.fsdevice_keys, (GFunc) g_free, NULL);
+ g_list_free (kf.fsdevice_keys);
+ g_list_foreach (kf.mimetype_keys, (GFunc) g_free, NULL);
+ g_list_free (kf.mimetype_keys);
+
+ g_hash_table_destroy (kf.action_values);
+ g_hash_table_destroy (kf.action_groups);
- return !fatal_error_occurred;
+ g_key_file_free (kf.keyfile);
+
+ return (!kf.fatal_error);
}
/* return FALSE if we were unable to fix the file */
gboolean
-desktop_file_fixup (GnomeDesktopFile *df,
- const char *filename)
+desktop_file_fixup (GKeyFile *kf,
+ const char *filename)
{
- const char *val;
- gboolean fix_encoding;
- const char *string_list_keys[] = { "Actions", "SortOrder", "Categories" };
- int i;
-
- if (gnome_desktop_file_has_section (df, "KDE Desktop Entry"))
- {
- g_printerr (_("%s: changing deprecated [KDE Desktop Entry] to plain [Desktop Entry]\n"),
- filename);
- gnome_desktop_file_rename_section (df,
- "KDE Desktop Entry",
- "Desktop Entry");
- }
+ char *value;
+ unsigned int i;
- fix_encoding = FALSE;
+ if (g_key_file_has_group (kf, GROUP_KDE_DESKTOP_ENTRY)) {
+ g_printerr ("%s: renaming deprecated \"%s\" group to \"%s\"\n",
+ filename, GROUP_KDE_DESKTOP_ENTRY, GROUP_DESKTOP_ENTRY);
+ dfu_key_file_rename_group (kf,
+ GROUP_KDE_DESKTOP_ENTRY, GROUP_DESKTOP_ENTRY);
+ }
- if (gnome_desktop_file_get_raw (df, NULL,
- "Encoding",
- NULL, &val))
- {
- if (strcmp (val, "UTF-8") != 0 &&
- strcmp (val, "Legacy-Mixed") != 0)
- {
- g_printerr (_("%s: bogus encoding \"%s\" "),
- filename, val);
- fix_encoding = TRUE;
- }
- }
- else
- {
- g_printerr (_("%s: missing encoding "),
- filename);
- fix_encoding = TRUE;
- }
-
- if (fix_encoding)
- {
- /* If Encoding was missing or bogus, the desktop file parser guessed */
- switch (gnome_desktop_file_get_encoding (df))
- {
- case GNOME_DESKTOP_FILE_ENCODING_LEGACY:
- g_printerr (_(" (guessed Legacy-Mixed)\n"));
- gnome_desktop_file_set_raw (df, NULL, "Encoding", NULL, "Legacy-Mixed");
- break;
- case GNOME_DESKTOP_FILE_ENCODING_UTF8:
- g_printerr (_(" (guessed UTF-8)\n"));
- gnome_desktop_file_set_raw (df, NULL, "Encoding", NULL, "UTF-8");
- break;
- case GNOME_DESKTOP_FILE_ENCODING_UNKNOWN:
- g_printerr (_("\n%s: not enough data to guess encoding!\n"),
- filename);
- return FALSE;
- break;
- }
+ /* Fix lists to have a ';' at the end if they don't */
+ for (i = 0; i < G_N_ELEMENTS (registered_desktop_keys); i++) {
+ if (registered_desktop_keys[i].type != DESKTOP_STRING_LIST_TYPE &&
+ registered_desktop_keys[i].type != DESKTOP_REGEXP_LIST_TYPE)
+ continue;
+
+ value = g_key_file_get_value (kf, GROUP_DESKTOP_ENTRY,
+ registered_desktop_keys[i].name, NULL);
+ if (value) {
+ int len;
+
+ len = strlen (value);
+
+ if (len > 0 && (value[len - 1] != ';' ||
+ (len > 1 && value[len - 2] == '\\' &&
+ (len < 3 || value[len - 3] != '\\')))) {
+ char *str;
+
+ g_printerr ("%s: key \"%s\" is a list and does not have a "
+ "semicolon as trailing character, fixing\n",
+ filename, registered_desktop_keys[i].name);
+
+ str = g_strconcat (value, ";", NULL);
+ g_key_file_set_value (kf, GROUP_DESKTOP_ENTRY,
+ registered_desktop_keys[i].name, str);
+ g_free (str);
+ }
}
+ }
- /* Fix string lists to have a ';' at the end if they don't */
- i = 0;
- while (i < (int) G_N_ELEMENTS (string_list_keys))
- {
- if (gnome_desktop_file_get_raw (df, NULL,
- string_list_keys[i],
- NULL, &val))
- {
- int len;
-
- len = strlen (val);
-
- if (len > 0 && val[len-1] != ';')
- {
- char *str;
-
- g_printerr ("%s: key \"%s\" string list not semicolon-terminated, fixing\n",
- filename, string_list_keys[i]);
-
- str = g_strconcat (val, ";", NULL);
- gnome_desktop_file_set_raw (df, NULL,
- string_list_keys[i],
- NULL, str);
- g_free (str);
- }
- }
-
- ++i;
- }
-
return TRUE;
}
diff --git a/src/validate.h b/src/validate.h
index 7cc4524..0057943 100644
--- a/src/validate.h
+++ b/src/validate.h
@@ -1,10 +1,39 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include "desktop_file.h"
-
-gboolean desktop_file_validate (GnomeDesktopFile *df,
- const char *filename);
-gboolean desktop_file_fixup (GnomeDesktopFile *df,
- const char *filename);
+/* validate.h: validate a desktop entry file
+ *
+ * Copyright (C) 2007 Vincent Untz <vuntz@gnome.org>
+ *
+ * A really small portion of this code comes from the old validate.h.
+ * Authors of the old validate.h are:
+ * Mark McLoughlin
+ * Havoc Pennington
+ * Ray Strode
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <glib.h>
+
+#define CURRENT_SPEC_VERSION "1.0"
+
+#define GROUP_KDE_DESKTOP_ENTRY "KDE Desktop Entry"
+#define GROUP_DESKTOP_ACTION "Desktop Action "
+
+gboolean desktop_file_validate (const char *filename,
+ gboolean warn_kde,
+ gboolean no_warn_deprecated);
+gboolean desktop_file_fixup (GKeyFile *keyfile,
+ const char *filename);
diff --git a/src/validator.c b/src/validator.c
index 042b10d..ec24c98 100644
--- a/src/validator.c
+++ b/src/validator.c
@@ -1,38 +1,76 @@
+/* validator.c: validate a desktop entry file
+ *
+ * Copyright (C) 2007 Vincent Untz <vuntz@gnome.org>
+ *
+ * A really small portion of this code comes from the old validator.c.
+ * Authors of the old validator.c are:
+ * Mark McLoughlin
+ * Havoc Pennington
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
#include "validate.h"
+static gboolean warn_kde = TRUE;
+static gboolean no_warn_deprecated = FALSE;
+static char **filename = NULL;
+
+static GOptionEntry option_entries[] = {
+ { "warn-kde", 0, 0, G_OPTION_ARG_NONE, &warn_kde, "Warn about usage of KDE extensions to the specification", NULL },
+ { "no-warn-deprecated", 0, 0, G_OPTION_ARG_NONE, &no_warn_deprecated, "Do not warn about usage of deprecated items", NULL },
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filename, NULL, "<desktop-file>" },
+ { NULL }
+};
+
int
main (int argc, char *argv[])
{
- char *contents;
- GnomeDesktopFile *df;
- GError *error;
- char *filename;
+ GOptionContext *context;
+ GError *error;
- if (argc == 2)
- filename = argv[1];
- else
- {
- g_printerr ("Usage: %s <desktop-file>\n", argv[0]);
- exit (1);
- }
-
- if (!g_file_get_contents (filename, &contents,
- NULL, NULL))
- {
- g_printerr ("%s: error reading desktop file\n", filename);
- return 1;
- }
+ context = g_option_context_new (NULL);
+ g_option_context_set_summary (context, "Validate desktop entry files "
+ "according to the desktop entry "
+ "specification "CURRENT_SPEC_VERSION
+ ".\n"
+ "For information about this "
+ "specification, see:\n\t"
+ "http://freedesktop.org/wiki/Standards/desktop-entry-spec");
+ g_option_context_add_main_entries (context, option_entries, NULL);
error = NULL;
- df = gnome_desktop_file_new_from_string (contents, &error);
-
- if (!df)
- {
- g_printerr ("%s: parse error: %s\n", filename, error->message);
- return 1;
- }
-
- if (desktop_file_validate (df, filename))
+ if (!g_option_context_parse (context, &argc, &argv, &error)) {
+ g_printerr ("Error while parsing arguments: %s\n", error->message);
+ g_error_free (error);
+ return 1;
+ }
+
+ /* only accept one desktop file argument */
+ if (filename == NULL || filename[0] == NULL || filename[1] != NULL) {
+ g_printerr ("See \"%s --help\" for correct usage.\n", g_get_prgname ());
+ return 1;
+ }
+
+ if (!g_file_test (filename[0], G_FILE_TEST_IS_REGULAR)) {
+ g_printerr ("%s: file does not exist\n", filename[0]);
+ return 1;
+ }
+
+ if (desktop_file_validate (filename[0], warn_kde, no_warn_deprecated))
return 0;
else
return 1;