sys-file-reader: Avoid assert-fail for duplicate attribute names.
[pspp] / src / data / attributes.c
index d99e945b014def2836ec1eb57ea33631fc5086d4..d7b45ff59571dd65567349d937f35ee4e038fa1d 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009, 2011, 2012, 2016 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
 
 #include <config.h>
 
-#include <data/attributes.h>
+#include "data/attributes.h"
+
 #include <assert.h>
 #include <string.h>
-#include <libpspp/array.h>
-#include <libpspp/hash-functions.h>
-#include "xalloc.h"
+
+#include "libpspp/array.h"
+#include "libpspp/compiler.h"
+#include "libpspp/hash-functions.h"
+#include "libpspp/i18n.h"
+
+#include "gl/xalloc.h"
 
 /* A custom attribute of the sort maintained by the DATAFILE
    ATTRIBUTE and VARIABLE ATTRIBUTE commands.
@@ -216,14 +221,28 @@ attrset_count (const struct attrset *set)
    case-insensitively, or a null pointer if SET does not contain
    an attribute with that name. */
 struct attribute *
-attrset_lookup (struct attrset *set, const char *name)
+attrset_lookup (const struct attrset *set, const char *name)
 {
-  struct attribute *attr;
+  const struct attribute *attr;
   HMAP_FOR_EACH_WITH_HASH (attr, struct attribute, node,
-                           hash_case_string (name, 0), &set->map)
-    if (!strcasecmp (attribute_get_name (attr), name))
+                           utf8_hash_case_string (name, 0), &set->map)
+    if (!utf8_strcasecmp (attribute_get_name (attr), name))
       break;
-  return attr;
+  return CONST_CAST (struct attribute *, attr);
+}
+
+/* Adds ATTR to SET.  Succeeds and returns true if SET does not already contain
+   an attribute with the same name (matched case insensitively); otherwise
+   fails and returns false.  On success only, ownership of ATTR is transferred
+   to SET. */
+bool
+attrset_try_add (struct attrset *set, struct attribute *attr)
+{
+  const char *name = attribute_get_name (attr);
+  if (attrset_lookup (set, name))
+    return false;
+  hmap_insert (&set->map, &attr->node, utf8_hash_case_string (name, 0));
+  return true;
 }
 
 /* Adds ATTR to SET, which must not already contain an attribute
@@ -232,9 +251,8 @@ attrset_lookup (struct attrset *set, const char *name)
 void
 attrset_add (struct attrset *set, struct attribute *attr)
 {
-  const char *name = attribute_get_name (attr);
-  assert (attrset_lookup (set, name) == NULL);
-  hmap_insert (&set->map, &attr->node, hash_case_string (name, 0));
+  bool ok UNUSED = attrset_try_add (set, attr);
+  assert (ok);
 }
 
 /* Deletes any attribute from SET that matches NAME
@@ -296,3 +314,38 @@ attrset_next (const struct attrset *set, struct attrset_iterator *iterator)
   iterator->node = hmap_next (&set->map, iterator->node);
   return iterator_data (iterator);
 }
+
+static int
+compare_attribute_by_name (const void *a_, const void *b_)
+{
+  const struct attribute *const *a = a_;
+  const struct attribute *const *b = b_;
+
+  return strcmp ((*a)->name, (*b)->name);
+}
+
+/* Allocates and returns an array of pointers to attributes
+   that is sorted by attribute name.  The array has
+   'attrset_count (SET)' elements.  The caller is responsible for
+   freeing the array. */
+struct attribute **
+attrset_sorted (const struct attrset *set)
+{
+  if (set != NULL && attrset_count (set) > 0)
+    {
+      struct attribute **attrs;
+      struct attribute *attr;
+      size_t i;
+
+      attrs = xmalloc (attrset_count (set) * sizeof *attrs);
+      i = 0;
+      HMAP_FOR_EACH (attr, struct attribute, node, &set->map)
+        attrs[i++] = attr;
+      assert (i == attrset_count (set));
+      qsort (attrs, attrset_count (set), sizeof *attrs,
+             compare_attribute_by_name);
+      return attrs;
+    }
+  else
+    return NULL;
+}