cleanup
[platform/upstream/glib.git] / gio / glib-compile-schemas.c
index c2725b4..e42949b 100644 (file)
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  *
  * Author: Ryan Lortie <desrt@desrt.ca>
  */
 
 /* Prologue {{{1 */
-#define _GNU_SOURCE
 #include "config.h"
 
 #include <gstdio.h>
-#include <locale.h>
+#include <gi18n.h>
+
 #include <string.h>
-#include <unistd.h>
 #include <stdio.h>
-
-#include <gi18n.h>
+#include <locale.h>
 
 #include "gvdb/gvdb-builder.h"
 #include "strinfo.c"
 
+#ifdef G_OS_WIN32
+#include "glib/glib-private.h"
+#endif
+
+static void
+strip_string (GString *string)
+{
+  gint i;
+
+  for (i = 0; g_ascii_isspace (string->str[i]); i++);
+  g_string_erase (string, 0, i);
+
+  if (string->len > 0)
+    {
+      /* len > 0, so there must be at least one non-whitespace character */
+      for (i = string->len - 1; g_ascii_isspace (string->str[i]); i--);
+      g_string_truncate (string, i + 1);
+    }
+}
+
 /* Handling of <enum> {{{1 */
 typedef struct
 {
@@ -51,7 +67,7 @@ enum_state_free (gpointer data)
   g_slice_free (EnumState, state);
 }
 
-EnumState *
+static EnumState *
 enum_state_new (gboolean is_flags)
 {
   EnumState *state;
@@ -107,6 +123,12 @@ enum_state_add_value (EnumState    *state,
       return;
     }
 
+  /* Silently drop the null case if it is mentioned.
+   * It is properly denoted with an empty array.
+   */
+  if (state->is_flags && value == 0)
+    return;
+
   if (state->is_flags && (value & (value - 1)))
     {
       g_set_error (error, G_MARKUP_ERROR,
@@ -121,7 +143,6 @@ enum_state_add_value (EnumState    *state,
    * If we loosen the one-bit-set restriction we need an overlap check.
    */
 
-
   strinfo_builder_append_item (state->strinfo, nick, value);
 }
 
@@ -323,6 +344,23 @@ key_state_set_range (KeyState     *state,
                      const gchar  *max_str,
                      GError      **error)
 {
+  const struct {
+    const gchar  type;
+    const gchar *min;
+    const gchar *max;
+  } table[] = {
+    { 'y',                    "0",                  "255" },
+    { 'n',               "-32768",                "32767" },
+    { 'q',                    "0",                "65535" },
+    { 'i',          "-2147483648",           "2147483647" },
+    { 'u',                    "0",           "4294967295" },
+    { 'x', "-9223372036854775808",  "9223372036854775807" },
+    { 't',                    "0", "18446744073709551615" },
+    { 'd',                 "-inf",                  "inf" },
+  };
+  gboolean type_ok = FALSE;
+  gint i;
+
   if (state->minimum)
     {
       g_set_error_literal (error, G_MARKUP_ERROR,
@@ -331,7 +369,16 @@ key_state_set_range (KeyState     *state,
       return;
     }
 
-  if (strchr ("ynqiuxtd", *(char *) state->type) == NULL)
+  for (i = 0; i < G_N_ELEMENTS (table); i++)
+    if (*(char *) state->type == table[i].type)
+      {
+        min_str = min_str ? min_str : table[i].min;
+        max_str = max_str ? max_str : table[i].max;
+        type_ok = TRUE;
+        break;
+      }
+
+  if (!type_ok)
     {
       gchar *type = g_variant_type_dup_string (state->type);
       g_set_error (error, G_MARKUP_ERROR,
@@ -430,7 +477,7 @@ key_state_start_choices (KeyState  *state,
     {
       g_set_error_literal (error, G_MARKUP_ERROR,
                            G_MARKUP_ERROR_INVALID_CONTENT,
-                           "<choices> can not be specified for keys "
+                           "<choices> cannot be specified for keys "
                            "tagged as having an enumerated type");
       return;
     }
@@ -497,8 +544,7 @@ key_state_start_aliases (KeyState  *state,
     g_set_error_literal (error, G_MARKUP_ERROR,
                          G_MARKUP_ERROR_INVALID_CONTENT,
                          "<aliases> already specified for this key");
-
-  if (!state->is_flags && !state->is_enum && !state->has_choices)
+  else if (!state->is_flags && !state->is_enum && !state->has_choices)
     g_set_error_literal (error, G_MARKUP_ERROR,
                          G_MARKUP_ERROR_INVALID_CONTENT,
                          "<aliases> can only be specified for keys with "
@@ -597,6 +643,23 @@ key_state_serialise (KeyState *state)
           /* translation */
           if (state->l10n)
             {
+              /* We are going to store the untranslated default for
+               * runtime translation according to the current locale.
+               * We need to strip leading and trailing whitespace from
+               * the string so that it's exactly the same as the one
+               * that ended up in the .po file for translation.
+               *
+               * We want to do this so that
+               *
+               *   <default l10n='messages'>
+               *     ['a', 'b', 'c']
+               *   </default>
+               *
+               * ends up in the .po file like "['a', 'b', 'c']",
+               * omitting the extra whitespace at the start and end.
+               */
+              strip_string (state->unparsed_default_value);
+
               if (state->l10n_context)
                 {
                   gint len;
@@ -624,12 +687,18 @@ key_state_serialise (KeyState *state)
           if (state->strinfo->len)
             {
               GVariant *array;
+              guint32 *words;
               gpointer data;
               gsize size;
+              gint i;
 
               data = state->strinfo->str;
               size = state->strinfo->len;
 
+              words = data;
+              for (i = 0; i < size / sizeof (guint32); i++)
+                words[i] = GUINT32_TO_LE (words[i]);
+
               array = g_variant_new_from_data (G_VARIANT_TYPE ("au"),
                                                data, size, TRUE,
                                                g_free, data);
@@ -723,7 +792,7 @@ is_valid_keyname (const gchar  *key,
         {
           g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
                        _("invalid name '%s': invalid character '%c'; "
-                         "only lowercase letters, numbers and dash ('-') "
+                         "only lowercase letters, numbers and hyphen ('-') "
                          "are permitted."), key, key[i]);
           return FALSE;
         }
@@ -731,7 +800,7 @@ is_valid_keyname (const gchar  *key,
       if (key[i] == '-' && key[i + 1] == '-')
         {
           g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
-                       _("invalid name '%s': two successive dashes ('--') "
+                       _("invalid name '%s': two successive hyphens ('--') "
                          "are not permitted."), key);
           return FALSE;
         }
@@ -741,14 +810,14 @@ is_valid_keyname (const gchar  *key,
     {
       g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
                    _("invalid name '%s': the last character may not be a "
-                     "dash ('-')."), key);
+                     "hyphen ('-')."), key);
       return FALSE;
     }
 
-  if (i > 32)
+  if (i > 1024)
     {
       g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
-                   _("invalid name '%s': maximum length is 32"), key);
+                   _("invalid name '%s': maximum length is 1024"), key);
       return FALSE;
     }
 
@@ -843,7 +912,7 @@ schema_state_add_key (SchemaState  *state,
     {
       g_set_error_literal (error, G_MARKUP_ERROR,
                            G_MARKUP_ERROR_INVALID_CONTENT,
-                           _("can not add keys to a 'list-of' schema"));
+                           _("cannot add keys to a 'list-of' schema"));
       return NULL;
     }
 
@@ -1051,8 +1120,8 @@ parse_state_start_schema (ParseState  *state,
         {
           g_set_error (error, G_MARKUP_ERROR,
                        G_MARKUP_ERROR_INVALID_CONTENT,
-                       _("<schema id='%s'> extends not yet "
-                         "existing schema '%s'"), id, extends_name);
+                       _("<schema id='%s'> extends not yet existing "
+                         "schema '%s'"), id, extends_name);
           return;
         }
     }
@@ -1067,8 +1136,8 @@ parse_state_start_schema (ParseState  *state,
         {
           g_set_error (error, G_MARKUP_ERROR,
                        G_MARKUP_ERROR_INVALID_CONTENT,
-                       _("<schema id='%s'> is list of not yet "
-                         "existing schema '%s'"), id, list_of);
+                       _("<schema id='%s'> is list of not yet existing "
+                         "schema '%s'"), id, list_of);
           return;
         }
 
@@ -1133,6 +1202,12 @@ parse_state_start_schema (ParseState  *state,
       return;
     }
 
+  if (path && (g_str_has_prefix (path, "/apps/") ||
+               g_str_has_prefix (path, "/desktop/") ||
+               g_str_has_prefix (path, "/system/")))
+    g_printerr ("warning: Schema '%s' has path '%s'.  Paths starting with "
+                "'/apps/', '/desktop/' or '/system/' are deprecated.\n", id, path);
+
   state->schema_state = schema_state_new (path, gettext_domain,
                                           extends, extends_name, list_of);
 
@@ -1218,7 +1293,9 @@ start_element (GMarkupParseContext  *context,
                        OPTIONAL | STRING, "gettext-domain", &gettext_domain,
                        OPTIONAL | STRING, "extends", &extends,
                        OPTIONAL | STRING, "list-of", &list_of))
-            parse_state_start_schema (state, id, path, gettext_domain,
+            parse_state_start_schema (state, id, path,
+                                      gettext_domain ? gettext_domain
+                                                     : state->schemalist_domain,
                                       extends, list_of, error);
           return;
         }
@@ -1308,7 +1385,8 @@ start_element (GMarkupParseContext  *context,
       else if (strcmp (element_name, "range") == 0)
         {
           const gchar *min, *max;
-          if (COLLECT (STRING, "min", &min, STRING, "max", &max))
+          if (COLLECT (STRING | OPTIONAL, "min", &min,
+                       STRING | OPTIONAL, "max", &max))
             key_state_set_range (state->key_state, min, max, error);
           return;
         }
@@ -1376,7 +1454,7 @@ start_element (GMarkupParseContext  *context,
                  element_name, container);
   else
     g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
-                 _("Element <%s> not allowed at toplevel"), element_name);
+                 _("Element <%s> not allowed at the top level"), element_name);
 }
 /* 2}}} */
 /* End element {{{2 */
@@ -1403,9 +1481,6 @@ static void
 schema_state_end (SchemaState **state_ptr,
                   GError      **error)
 {
-  SchemaState *state;
-
-  state = *state_ptr;
   *state_ptr = NULL;
 }
 
@@ -1460,21 +1535,38 @@ text (GMarkupParseContext  *context,
       GError              **error)
 {
   ParseState *state = user_data;
-  gsize i;
-
-  for (i = 0; i < text_len; i++)
-    if (!g_ascii_isspace (text[i]))
-      {
-        if (state->string)
-          g_string_append_len (state->string, text, text_len);
 
-        else
-          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
-                       _("text may not appear inside <%s>"),
-                       g_markup_parse_context_get_element (context));
+  if (state->string)
+    {
+      /* we are expecting a string, so store the text data.
+       *
+       * we store the data verbatim here and deal with whitespace
+       * later on.  there are two reasons for that:
+       *
+       *  1) whitespace is handled differently depending on the tag
+       *     type.
+       *
+       *  2) we could do leading whitespace removal by refusing to
+       *     insert it into state->string if it's at the start, but for
+       *     trailing whitespace, we have no idea if there is another
+       *     text() call coming or not.
+       */
+      g_string_append_len (state->string, text, text_len);
+    }
+  else
+    {
+      /* string is not expected: accept (and ignore) pure whitespace */
+      gsize i;
 
-        break;
-      }
+      for (i = 0; i < text_len; i++)
+        if (!g_ascii_isspace (text[i]))
+          {
+            g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                         _("text may not appear inside <%s>"),
+                         g_markup_parse_context_get_element (context));
+            break;
+          }
+    }
 }
 
 /* Write to GVDB {{{1 */
@@ -1493,6 +1585,13 @@ gvdb_pair_init (GvdbPair *pair)
 
 typedef struct
 {
+  GHashTable *schema_table;
+  GvdbPair root_pair;
+} WriteToFileData;
+
+typedef struct
+{
+  GHashTable *schema_table;
   GvdbPair pair;
   gboolean l10n;
 } OutputSchemaData;
@@ -1517,6 +1616,11 @@ output_key (gpointer key,
 
   if (state->l10n)
     data->l10n = TRUE;
+
+  if (state->child_schema &&
+      !g_hash_table_lookup (data->schema_table, state->child_schema))
+    g_printerr ("warning: undefined reference to <schema id='%s'/>\n",
+                state->child_schema);
 }
 
 static void
@@ -1524,6 +1628,7 @@ output_schema (gpointer key,
                gpointer value,
                gpointer user_data)
 {
+  WriteToFileData *wtf_data = user_data;
   OutputSchemaData data;
   GvdbPair *root_pair;
   SchemaState *state;
@@ -1532,8 +1637,9 @@ output_schema (gpointer key,
 
   id = key;
   state = value;
-  root_pair = user_data;
+  root_pair = &wtf_data->root_pair;
 
+  data.schema_table = wtf_data->schema_table;
   gvdb_pair_init (&data.pair);
   data.l10n = FALSE;
 
@@ -1552,7 +1658,7 @@ output_schema (gpointer key,
 
   if (state->list_of)
     gvdb_hash_table_insert_string (data.pair.table, ".list-of",
-                                   state->extends_name);
+                                   state->list_of);
 
   if (data.l10n)
     gvdb_hash_table_insert_string (data.pair.table,
@@ -1565,17 +1671,19 @@ write_to_file (GHashTable   *schema_table,
                const gchar  *filename,
                GError      **error)
 {
+  WriteToFileData data;
   gboolean success;
-  GvdbPair pair;
 
-  gvdb_pair_init (&pair);
+  data.schema_table = schema_table;
 
-  g_hash_table_foreach (schema_table, output_schema, &pair);
+  gvdb_pair_init (&data.root_pair);
 
-  success = gvdb_table_write_contents (pair.table, filename,
+  g_hash_table_foreach (schema_table, output_schema, &data);
+
+  success = gvdb_table_write_contents (data.root_pair.table, filename,
                                        G_BYTE_ORDER != G_LITTLE_ENDIAN,
                                        error);
-  g_hash_table_unref (pair.table);
+  g_hash_table_unref (data.root_pair.table);
 
   return success;
 }
@@ -1613,7 +1721,9 @@ parse_gschema_files (gchar    **files,
         }
 
       context = g_markup_parse_context_new (&parser,
-                                            G_MARKUP_PREFIX_ERROR_POSITION,
+                                            G_MARKUP_TREAT_CDATA_AS_TEXT |
+                                            G_MARKUP_PREFIX_ERROR_POSITION |
+                                            G_MARKUP_IGNORE_QUALIFIED,
                                             &state, NULL);
 
 
@@ -1634,6 +1744,7 @@ parse_gschema_files (gchar    **files,
 
           /* let them know */
           fprintf (stderr, "%s: %s.  ", filename, error->message);
+          g_clear_error (&error);
 
           if (strict)
             {
@@ -1745,8 +1856,8 @@ set_overrides (GHashTable  *schema_table,
 
               if (state == NULL)
                 {
-                  fprintf (stderr, _("No such key `%s' in schema `%s' as "
-                                     "specified in override file `%s'"),
+                  fprintf (stderr, _("No such key '%s' in schema '%s' as "
+                                     "specified in override file '%s'"),
                            key, group, filename);
 
                   if (!strict)
@@ -1771,9 +1882,9 @@ set_overrides (GHashTable  *schema_table,
 
               if (value == NULL)
                 {
-                  fprintf (stderr, _("error parsing key `%s' in schema `%s' "
-                                     "as specified in override file `%s': "
-                                     "%s.  "),
+                  fprintf (stderr, _("error parsing key '%s' in schema '%s' "
+                                     "as specified in override file '%s': "
+                                     "%s."),
                            key, group, filename, error->message);
 
                   g_clear_error (&error);
@@ -1799,8 +1910,8 @@ set_overrides (GHashTable  *schema_table,
                       g_variant_compare (value, state->maximum) > 0)
                     {
                       fprintf (stderr,
-                               _("override for key `%s' in schema `%s' in "
-                                 "override file `%s' is out of the range "
+                               _("override for key '%s' in schema '%s' in "
+                                 "override file '%s' is outside the range "
                                  "given in the schema"),
                                key, group, filename);
 
@@ -1827,8 +1938,8 @@ set_overrides (GHashTable  *schema_table,
                   if (!is_valid_choices (value, state->strinfo))
                     {
                       fprintf (stderr,
-                               _("override for key `%s' in schema `%s' in "
-                                 "override file `%s' is not in the list "
+                               _("override for key '%s' in schema '%s' in "
+                                 "override file '%s' is not in the list "
                                  "of valid choices"),
                                key, group, filename);
 
@@ -1874,7 +1985,6 @@ main (int argc, char **argv)
   gchar *srcdir;
   gchar *targetdir = NULL;
   gchar *target;
-  gboolean uninstall = FALSE;
   gboolean dry_run = FALSE;
   gboolean strict = FALSE;
   gchar **schema_files = NULL;
@@ -1884,7 +1994,6 @@ main (int argc, char **argv)
     { "targetdir", 0, 0, G_OPTION_ARG_FILENAME, &targetdir, N_("where to store the gschemas.compiled file"), N_("DIRECTORY") },
     { "strict", 0, 0, G_OPTION_ARG_NONE, &strict, N_("Abort on any errors in schemas"), NULL },
     { "dry-run", 0, 0, G_OPTION_ARG_NONE, &dry_run, N_("Do not write the gschema.compiled file"), NULL },
-    { "uninstall", 0, 0, G_OPTION_ARG_NONE, &uninstall, N_("This option will be removed soon.") },
     { "allow-any-name", 0, 0, G_OPTION_ARG_NONE, &allow_any_name, N_("Do not enforce key name restrictions") },
 
     /* These options are only for use in the gschema-compile tests */
@@ -1892,7 +2001,24 @@ main (int argc, char **argv)
     { NULL }
   };
 
+#ifdef G_OS_WIN32
+  gchar *tmp;
+#endif
+
   setlocale (LC_ALL, "");
+  textdomain (GETTEXT_PACKAGE);
+
+#ifdef G_OS_WIN32
+  tmp = _glib_get_locale_dir ();
+  bindtextdomain (GETTEXT_PACKAGE, tmp);
+  g_free (tmp);
+#else
+  bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
+#endif
+
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
 
   context = g_option_context_new (N_("DIRECTORY"));
   g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
@@ -1952,13 +2078,13 @@ main (int argc, char **argv)
 
       if (files->len == 0)
         {
-          fprintf (stderr, _("No schema files found: "));
+          fprintf (stdout, _("No schema files found: "));
 
           if (g_unlink (target))
-            fprintf (stderr, _("doing nothing.\n"));
+            fprintf (stdout, _("doing nothing.\n"));
 
           else
-            fprintf (stderr, _("removed existing output file.\n"));
+            fprintf (stdout, _("removed existing output file.\n"));
 
           return 0;
         }