str: Make str_format_26adic() able to use lowercase.
[pspp] / src / data / dictionary.c
index 79d36374fc42767660234911ed533c5732270b92..4eaaefae18ed9ae4889275bccd00b0e568c26e8d 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
 
    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
@@ -87,16 +87,8 @@ struct dictionary
 static void dict_unset_split_var (struct dictionary *, struct variable *);
 static void dict_unset_mrset_var (struct dictionary *, struct variable *);
 
-void
-dict_set_encoding (struct dictionary *d, const char *enc)
-{
-  if (enc)
-    {
-      free (d->encoding);
-      d->encoding = xstrdup (enc);
-    }
-}
-
+/* Returns the encoding for data in dictionary D.  The return value is a
+   nonnull string that contains an IANA character set name. */
 const char *
 dict_get_encoding (const struct dictionary *d)
 {
@@ -171,14 +163,16 @@ dict_copy_callbacks (struct dictionary *dest,
   dest->cb_data = src->cb_data;
 }
 
-/* Creates and returns a new dictionary. */
+/* Creates and returns a new dictionary with the specified ENCODING. */
 struct dictionary *
-dict_create (void)
+dict_create (const char *encoding)
 {
   struct dictionary *d = xzalloc (sizeof *d);
 
+  d->encoding = xstrdup (encoding);
   hmap_init (&d->name_map);
   attrset_init (&d->attributes);
+
   return d;
 }
 
@@ -189,14 +183,16 @@ dict_create (void)
    dictionary.  If the new dictionary won't be used to access
    cases produced with the old dictionary, then the new
    dictionary's case indexes should be compacted with
-   dict_compact_values to save space. */
+   dict_compact_values to save space.
+
+   Callbacks are not cloned. */
 struct dictionary *
 dict_clone (const struct dictionary *s)
 {
   struct dictionary *d;
   size_t i;
 
-  d = dict_create ();
+  d = dict_create (s->encoding);
 
   for (i = 0; i < s->var_cnt; i++)
     {
@@ -235,9 +231,6 @@ dict_clone (const struct dictionary *s)
   for (i = 0; i < s->vector_cnt; i++)
     d->vector[i] = vector_clone (s->vector[i], s, d);
 
-  if ( s->encoding)
-    d->encoding = xstrdup (s->encoding);
-
   dict_set_attributes (d, dict_get_attributes (s));
 
   for (i = 0; i < s->n_mrsets; i++)
@@ -286,17 +279,6 @@ dict_clear (struct dictionary *d)
   attrset_clear (&d->attributes);
 }
 
-/* Destroys the aux data for every variable in D, by calling
-   var_clear_aux() for each variable. */
-void
-dict_clear_aux (struct dictionary *d)
-{
-  int i;
-
-  for (i = 0; i < d->var_cnt; i++)
-    var_clear_aux (d->var[i].var);
-}
-
 /* Clears a dictionary and destroys it. */
 void
 dict_destroy (struct dictionary *d)
@@ -308,6 +290,7 @@ dict_destroy (struct dictionary *d)
       d->callbacks  = NULL ;
 
       dict_clear (d);
+      string_array_destroy (&d->documents);
       hmap_destroy (&d->name_map);
       attrset_destroy (&d->attributes);
       dict_clear_mrsets (d);
@@ -380,10 +363,13 @@ dict_get_vars_mutable (const struct dictionary *d, struct variable ***vars,
 }
 
 static struct variable *
-add_var (struct dictionary *d, struct variable *v)
+add_var_with_case_index (struct dictionary *d, struct variable *v,
+                         int case_index)
 {
   struct vardict_info *vardict;
 
+  assert (case_index >= d->next_value_idx);
+
   /* Update dictionary. */
   if (d->var_cnt >= d->var_cap)
     {
@@ -403,20 +389,26 @@ add_var (struct dictionary *d, struct variable *v)
   vardict->dict = d;
   vardict->var = v;
   hmap_insert (&d->name_map, &vardict->name_node,
-               hash_case_string (var_get_name (v), 0));
-  vardict->case_index = d->next_value_idx;
+               utf8_hash_case_string (var_get_name (v), 0));
+  vardict->case_index = case_index;
   var_set_vardict (v, vardict);
 
   if ( d->changed ) d->changed (d, d->changed_data);
   if ( d->callbacks &&  d->callbacks->var_added )
     d->callbacks->var_added (d, var_get_dict_index (v), d->cb_data);
 
-  d->next_value_idx++;
   invalidate_proto (d);
+  d->next_value_idx = case_index + 1;
 
   return v;
 }
 
+static struct variable *
+add_var (struct dictionary *d, struct variable *v)
+{
+  return add_var_with_case_index (d, v, d->next_value_idx);
+}
+
 /* Creates and returns a new variable in D with the given NAME
    and WIDTH.  Returns a null pointer if the given NAME would
    duplicate that of an existing variable in the dictionary. */
@@ -485,6 +477,15 @@ dict_clone_var_as_assert (struct dictionary *d, const struct variable *old_var,
   return add_var (d, new_var);
 }
 
+struct variable *
+dict_clone_var_in_place_assert (struct dictionary *d,
+                                const struct variable *old_var)
+{
+  assert (dict_lookup_var (d, var_get_name (old_var)) == NULL);
+  return add_var_with_case_index (d, var_clone (old_var),
+                                  var_get_case_index (old_var));
+}
+
 /* Returns the variable named NAME in D, or a null pointer if no
    variable has that name. */
 struct variable *
@@ -493,10 +494,10 @@ dict_lookup_var (const struct dictionary *d, const char *name)
   struct vardict_info *vardict;
 
   HMAP_FOR_EACH_WITH_HASH (vardict, struct vardict_info, name_node,
-                           hash_case_string (name, 0), &d->name_map)
+                           utf8_hash_case_string (name, 0), &d->name_map)
     {
       struct variable *var = vardict->var;
-      if (!strcasecmp (var_get_name (var), name))
+      if (!utf8_strcasecmp (var_get_name (var), name))
         return var;
     }
 
@@ -546,6 +547,7 @@ static void
 reindex_var (struct dictionary *d, struct vardict_info *vardict)
 {
   struct variable *var = vardict->var;
+  struct variable *old = var_clone (var);
 
   var_set_vardict (var, vardict);
   hmap_insert_fast (&d->name_map, &vardict->name_node,
@@ -553,7 +555,8 @@ reindex_var (struct dictionary *d, struct vardict_info *vardict)
 
   if ( d->changed ) d->changed (d, d->changed_data);
   if ( d->callbacks &&  d->callbacks->var_changed )
-    d->callbacks->var_changed (d, var_get_dict_index (var), d->cb_data);
+    d->callbacks->var_changed (d, var_get_dict_index (var), VAR_TRAIT_POSITION, old, d->cb_data);
+  var_destroy (old);
 }
 
 /* Sets the case_index in V's vardict to CASE_INDEX. */
@@ -588,7 +591,7 @@ reindex_vars (struct dictionary *d, size_t from, size_t to)
 /* Deletes variable V from dictionary D and frees V.
 
    This is a very bad idea if there might be any pointers to V
-   from outside D.  In general, no variable in the active file's
+   from outside D.  In general, no variable in the active dataset's
    dictionary should be deleted when any transformations are
    active on the dictionary's dataset, because those
    transformations might reference the deleted variable.  The
@@ -603,13 +606,9 @@ dict_delete_var (struct dictionary *d, struct variable *v)
 {
   int dict_index = var_get_dict_index (v);
   const int case_index = var_get_case_index (v);
-  const int width = var_get_width (v);
 
   assert (dict_contains_var (d, v));
 
-  /* Delete aux data. */
-  var_clear_aux (v);
-
   dict_unset_split_var (d, v);
   dict_unset_mrset_var (d, v);
 
@@ -631,13 +630,14 @@ dict_delete_var (struct dictionary *d, struct variable *v)
 
   /* Free memory. */
   var_clear_vardict (v);
-  var_destroy (v);
 
   if ( d->changed ) d->changed (d, d->changed_data);
 
   invalidate_proto (d);
   if (d->callbacks &&  d->callbacks->var_deleted )
-    d->callbacks->var_deleted (d, dict_index, case_index, width, d->cb_data);
+    d->callbacks->var_deleted (d, v, dict_index, case_index, d->cb_data);
+
+  var_destroy (v);
 }
 
 /* Deletes the COUNT variables listed in VARS from D.  This is
@@ -748,7 +748,7 @@ rename_var (struct variable *v, const char *new_name)
   struct vardict_info *vardict = var_get_vardict (v);
   var_clear_vardict (v);
   var_set_name (v, new_name);
-  vardict->name_node.hash = hash_case_string (new_name, 0);
+  vardict->name_node.hash = utf8_hash_case_string (new_name, 0);
   var_set_vardict (v, vardict);
 }
 
@@ -759,7 +759,8 @@ void
 dict_rename_var (struct dictionary *d, struct variable *v,
                  const char *new_name)
 {
-  assert (!strcasecmp (var_get_name (v), new_name)
+  struct variable *old = var_clone (v);
+  assert (!utf8_strcasecmp (var_get_name (v), new_name)
           || dict_lookup_var (d, new_name) == NULL);
 
   unindex_var (d, var_get_vardict (v));
@@ -771,7 +772,9 @@ dict_rename_var (struct dictionary *d, struct variable *v,
 
   if ( d->changed ) d->changed (d, d->changed_data);
   if ( d->callbacks &&  d->callbacks->var_changed )
-    d->callbacks->var_changed (d, var_get_dict_index (v), d->cb_data);
+    d->callbacks->var_changed (d, var_get_dict_index (v), VAR_TRAIT_NAME, old, d->cb_data);
+
+  var_destroy (old);
 }
 
 /* Renames COUNT variables specified in VARS to the names given
@@ -904,7 +907,7 @@ make_hinted_name (const struct dictionary *dict, const char *hint)
           char *name;
 
           suffix[0] = '_';
-          if (!str_format_26adic (i + 1, &suffix[1], sizeof suffix - 1))
+          if (!str_format_26adic (i + 1, true, &suffix[1], sizeof suffix - 1))
             NOT_REACHED ();
 
           name = utf8_encoding_concat (root, suffix, dict->encoding, 64);
@@ -1253,15 +1256,18 @@ dict_get_label (const struct dictionary *d)
   return d->label;
 }
 
-/* Sets D's file label to LABEL, truncating it to a maximum of 60
-   characters.
+/* Sets D's file label to LABEL, truncating it to at most 60 bytes in D's
+   encoding.
 
    Removes D's label if LABEL is null or the empty string. */
 void
 dict_set_label (struct dictionary *d, const char *label)
 {
   free (d->label);
-  d->label = label != NULL && label[0] != '\0' ? xstrndup (label, 60) : NULL;
+  if (label == NULL || label[0] == '\0')
+    d->label = NULL;
+  else
+    d->label = utf8_encoding_trunc (label, d->encoding, 60);
 }
 
 /* Returns the documents for D, as an UTF-8 encoded string_array.  The
@@ -1414,7 +1420,7 @@ dict_lookup_vector (const struct dictionary *d, const char *name)
 {
   size_t i;
   for (i = 0; i < d->vector_cnt; i++)
-    if (!strcasecmp (vector_get_name (d->vector[i]), name))
+    if (!utf8_strcasecmp (vector_get_name (d->vector[i]), name))
       return d->vector[i];
   return NULL;
 }
@@ -1459,7 +1465,7 @@ dict_lookup_mrset_idx (const struct dictionary *dict, const char *name)
   size_t i;
 
   for (i = 0; i < dict->n_mrsets; i++)
-    if (!strcasecmp (name, dict->mrsets[i]->name))
+    if (!utf8_strcasecmp (name, dict->mrsets[i]->name))
       return i;
 
   return SIZE_MAX;
@@ -1590,10 +1596,12 @@ dict_has_attributes (const struct dictionary *d)
   return attrset_count (&d->attributes) > 0;
 }
 
-/* Called from variable.c to notify the dictionary that some property of
-   the variable has changed */
+/* Called from variable.c to notify the dictionary that some property (indicated
+   by WHAT) of the variable has changed.  OLDVAR is a copy of V as it existed
+   prior to the change.  OLDVAR is destroyed by this function.
+*/
 void
-dict_var_changed (const struct variable *v)
+dict_var_changed (const struct variable *v, unsigned int what, struct variable *oldvar)
 {
   if ( var_has_vardict (v))
     {
@@ -1605,49 +1613,12 @@ dict_var_changed (const struct variable *v)
 
       if (d->changed ) d->changed (d, d->changed_data);
       if ( d->callbacks && d->callbacks->var_changed )
-       d->callbacks->var_changed (d, var_get_dict_index (v), d->cb_data);
-    }
-}
-
-
-/* Called from variable.c to notify the dictionary that the variable's width
-   has changed */
-void
-dict_var_resized (const struct variable *v, int old_width)
-{
-  if ( var_has_vardict (v))
-    {
-      const struct vardict_info *vardict = var_get_vardict (v);
-      struct dictionary *d;
-
-      d = vardict->dict;
-
-      if (d->changed) d->changed (d, d->changed_data);
-
-      invalidate_proto (d);
-      if ( d->callbacks && d->callbacks->var_resized )
-       d->callbacks->var_resized (d, var_get_dict_index (v), old_width,
-                                   d->cb_data);
+       d->callbacks->var_changed (d, var_get_dict_index (v), what, oldvar, d->cb_data);
     }
+  var_destroy (oldvar);
 }
 
-/* Called from variable.c to notify the dictionary that the variable's display width
-   has changed */
-void
-dict_var_display_width_changed (const struct variable *v)
-{
-  if ( var_has_vardict (v))
-    {
-      const struct vardict_info *vardict = var_get_vardict (v);
-      struct dictionary *d;
-
-      d = vardict->dict;
 
-      if (d->changed) d->changed (d, d->changed_data);
-      if ( d->callbacks && d->callbacks->var_display_width_changed )
-       d->callbacks->var_display_width_changed (d, var_get_dict_index (v), d->cb_data);
-    }
-}
 \f
 /* Dictionary used to contain "internal variables". */
 static struct dictionary *internal_dict;
@@ -1658,7 +1629,7 @@ struct variable *
 dict_create_internal_var (int case_idx, int width)
 {
   if (internal_dict == NULL)
-    internal_dict = dict_create ();
+    internal_dict = dict_create ("UTF-8");
 
   for (;;)
     {