psppire-delimited-text: Clear entire 'cache_starts' buffer.
[pspp] / src / ui / gui / psppire-delimited-text.c
index fec2f707723ff3971074f5cd699fa81ceac7974c..dd3715f6b97bb68a36bfefb855d980788daf9ed2 100644 (file)
@@ -35,46 +35,80 @@ enum
     PROP_FIRST_LINE
   };
 
+struct enclosure
+{
+  gunichar opening;
+  gunichar closing;
+};
+
+static const struct enclosure enclosures[3] =
+  {
+    {'(',   ')'},
+    {'"',   '"'},
+    {'\'',  '\''}
+  };
+
 static void
 count_delims (PsppireDelimitedText *tf)
 {
-  if (tf->child)
+  if (tf->child == NULL)
+    return;
+
+  tf->max_delimiters = 0;
+  GtkTreeIter iter;
+  gboolean valid;
+  for (valid = gtk_tree_model_get_iter_first (tf->child, &iter);
+       valid;
+       valid = gtk_tree_model_iter_next (tf->child, &iter))
     {
-      tf->max_delimiters = 0;
-      GtkTreeIter iter;
-      gboolean valid;
-      for (valid = gtk_tree_model_get_iter_first (tf->child, &iter);
-          valid;
-          valid = gtk_tree_model_iter_next (tf->child, &iter))
-       {
-         // FIXME: Box these lines to avoid constant allocation/deallocation
-         gchar *foo = 0;
-         gtk_tree_model_get (tf->child, &iter, 1, &foo, -1);
+      gint enc = -1;
+      // FIXME: Box these lines to avoid constant allocation/deallocation
+      gchar *line = NULL;
+      gtk_tree_model_get (tf->child, &iter, 1, &line, -1);
+      {
+       char *p;
+       gint count = 0;
+       for (p = line; ; p = g_utf8_find_next_char (p, NULL))
          {
-           char *line = foo;
-           gint count = 0;
-           while (*line)
+           const gunichar c = g_utf8_get_char (p);
+           if (c == 0)
+             break;
+           if (enc == -1)
+             {
+               gint i;
+               for (i = 0; i < 3; ++i)
+                 {
+                   if (c == enclosures[i].opening)
+                     {
+                       enc = i;
+                       break;
+                     }
+                 }
+             }
+           else if (c == enclosures[enc].closing)
+             {
+               enc = -1;
+             }
+           if (enc == -1)
              {
                GSList *del;
                for (del = tf->delimiters; del; del = g_slist_next (del))
                  {
-                   if (*line == GPOINTER_TO_INT (del->data))
+                   if (c == GPOINTER_TO_INT (del->data))
                      count++;
                  }
-               line++;
              }
-           tf->max_delimiters = MAX (tf->max_delimiters, count);
          }
-         g_free (foo);
-       }
+       tf->max_delimiters = MAX (tf->max_delimiters, count);
+      }
+      g_free (line);
     }
-  //  g_print ("Max Number of delimiters per row: %d\n", tf->max_delimiters);
 }
 
 static void
 cache_invalidate (PsppireDelimitedText *tf)
 {
-  memset (tf->cache_starts, 0, 512);
+  memset (tf->cache_starts, 0, sizeof tf->cache_starts);
   if (tf->const_cache.string)
     {
       ss_dealloc (&tf->const_cache);
@@ -98,6 +132,7 @@ psppire_delimited_text_set_property (GObject         *object,
       break;
     case PROP_CHILD:
       tf->child = g_value_get_object (value);
+      g_return_if_fail (PSPPIRE_IS_TEXT_FILE (tf->child));
       break;
     case PROP_DELIMITERS:
       g_slist_free (tf->delimiters);
@@ -143,6 +178,13 @@ static void psppire_delimited_text_dispose        (GObject           *object);
 
 static GObjectClass *parent_class = NULL;
 
+static gint
+n_lines (PsppireDelimitedText *file)
+{
+  PsppireTextFile *child = PSPPIRE_TEXT_FILE (file->child);
+
+  return child->maximum_lines;
+}
 
 static gboolean
 __tree_get_iter (GtkTreeModel *tree_model,
@@ -153,7 +195,6 @@ __tree_get_iter (GtkTreeModel *tree_model,
   if (path == NULL)
     return FALSE;
 
-  //  g_print ("%s:%d %s %s\n", __FILE__, __LINE__, __FUNCTION__, gtk_tree_path_to_string (path));
 
   gint *indices = gtk_tree_path_get_indices (path);
 
@@ -162,12 +203,11 @@ __tree_get_iter (GtkTreeModel *tree_model,
 
   gint n = *indices;
 
-  gint children = gtk_tree_model_iter_n_children (file->child, NULL);
+  gint children = n_lines (file);
 
   if (n >= children - file->first_line)
     return FALSE;
 
-  //  g_print ("%s:%d %s  %d Children: %d\n", __FILE__, __LINE__, __FUNCTION__, n, children);
 
   iter->user_data = GINT_TO_POINTER (n);
   iter->stamp = file->stamp;
@@ -185,9 +225,8 @@ __tree_iter_next (GtkTreeModel *tree_model,
 
   gint n = GPOINTER_TO_INT (iter->user_data);
 
-  //  g_print ("%s:%d %s %d\n", __FILE__, __LINE__, __FUNCTION__, n);
 
-  gint children = gtk_tree_model_iter_n_children (file->child, NULL);
+  gint children = n_lines (file);
 
   if (n + 1 >= children - file->first_line)
     return FALSE;
@@ -202,7 +241,6 @@ static GType
 __tree_get_column_type (GtkTreeModel *tree_model,
                        gint          index)
 {
-  //  g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
   if (index == 0)
     return G_TYPE_INT;
 
@@ -213,7 +251,6 @@ static gboolean
 __iter_has_child (GtkTreeModel *tree_model,
                  GtkTreeIter  *iter)
 {
-  g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
   return 0;
 }
 
@@ -223,7 +260,6 @@ __iter_parent     (GtkTreeModel *tree_model,
                   GtkTreeIter  *iter,
                   GtkTreeIter  *child)
 {
-  g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
   return 0;
 }
 
@@ -231,13 +267,12 @@ static GtkTreePath *
 __tree_get_path (GtkTreeModel *tree_model,
                 GtkTreeIter  *iter)
 {
-  //  g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
   PsppireDelimitedText *file  = PSPPIRE_DELIMITED_TEXT (tree_model);
   g_return_val_if_fail (file->stamp == iter->stamp, FALSE);
 
   gint n = GPOINTER_TO_INT (iter->user_data);
 
-  gint children = gtk_tree_model_iter_n_children (file->child, NULL);
+  gint children = n_lines (file);
 
   if (n >= children - file->first_line)
     return NULL;
@@ -251,7 +286,6 @@ __iter_children (GtkTreeModel *tree_model,
                               GtkTreeIter *iter,
                               GtkTreeIter *parent)
 {
-  g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
   return 0;
 }
 
@@ -260,15 +294,17 @@ static gint
 __tree_model_iter_n_children (GtkTreeModel *tree_model,
                              GtkTreeIter *iter)
 {
-  g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
+  PsppireDelimitedText *file  = PSPPIRE_DELIMITED_TEXT (tree_model);
   g_assert (iter == NULL);
-  return 0;
+
+  gint children = n_lines (file);
+
+  return children - file->first_line;
 }
 
 static GtkTreeModelFlags
 __tree_model_get_flags (GtkTreeModel *model)
 {
-  //  g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
   g_return_val_if_fail (PSPPIRE_IS_DELIMITED_TEXT (model), (GtkTreeModelFlags) 0);
 
   return GTK_TREE_MODEL_LIST_ONLY;
@@ -277,7 +313,6 @@ __tree_model_get_flags (GtkTreeModel *model)
 static gint
 __tree_model_get_n_columns (GtkTreeModel *tree_model)
 {
-  //  g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
   PsppireDelimitedText *tf  = PSPPIRE_DELIMITED_TEXT (tree_model);
 
   /* + 1 for the trailing field and +1 for the leading line number column */
@@ -291,7 +326,6 @@ __iter_nth_child (GtkTreeModel *tree_model,
                  GtkTreeIter *parent,
                  gint n)
 {
-  //  g_print ("%s:%d %s %d\n", __FILE__, __LINE__, __FUNCTION__, n);
   PsppireDelimitedText *file  = PSPPIRE_DELIMITED_TEXT (tree_model);
 
   g_assert (parent == NULL);
@@ -314,6 +348,18 @@ __iter_nth_child (GtkTreeModel *tree_model,
 }
 
 
+static void
+nullify_char (struct substring cs)
+{
+  int char_len = ss_first_mblen (cs);
+  while (char_len > 0)
+    {
+      cs.string[char_len - 1] = '\0';
+      char_len--;
+    }
+}
+
+
 /* Split row N into it's delimited fields (if it is not already cached)
    and set this row as the current cache. */
 static void
@@ -324,7 +370,7 @@ split_row_into_fields (PsppireDelimitedText *file, gint n)
       return;
     }
 
-  memset (file->cache_starts, 0, 512);
+  memset (file->cache_starts, 0, sizeof file->cache_starts);
   /* Cache miss */
   if (file->const_cache.string)
     {
@@ -335,25 +381,47 @@ split_row_into_fields (PsppireDelimitedText *file, gint n)
   struct substring cs = file->const_cache;
   int field = 0;
   file->cache_starts[0] = cs.string;
+  gint enc = -1;
   for (;
        UINT32_MAX != ss_first_mb (cs);
        ss_get_mb (&cs))
     {
-      ucs4_t xx = ss_first_mb (cs);
-      GSList *del;
-      for (del = file->delimiters; del; del = g_slist_next (del))
+      ucs4_t character = ss_first_mb (cs);
+      gboolean char_is_quote = FALSE;
+      if (enc == -1)
        {
-         if (xx == GPOINTER_TO_INT (del->data))
+         gint i;
+         for (i = 0; i < 3; ++i)
            {
-             field++;
-             int char_len = ss_first_mblen (cs);
-             file->cache_starts[field] = cs.string + char_len;
-             while (char_len > 0)
+             if (character == enclosures[i].opening)
                {
-                 cs.string[char_len - 1] = '\0';
-                 char_len--;
+                 enc = i;
+                 char_is_quote = TRUE;
+                 file->cache_starts[field] += ss_first_mblen (cs);
+                 break;
+               }
+           }
+       }
+      else if (character == enclosures[enc].closing)
+       {
+         char_is_quote = TRUE;
+         nullify_char (cs);
+         enc = -1;
+       }
+
+      if (enc == -1 && char_is_quote == FALSE)
+       {
+         GSList *del;
+         for (del = file->delimiters; del; del = g_slist_next (del))
+           {
+             if (character == GPOINTER_TO_INT (del->data))
+               {
+                 field++;
+                 int char_len = ss_first_mblen (cs);
+                 file->cache_starts[field] = cs.string + char_len;
+                 nullify_char (cs);
+                 break;
                }
-             break;
            }
        }
     }
@@ -378,14 +446,12 @@ __get_value (GtkTreeModel *tree_model,
             gint column,
             GValue *value)
 {
-  //  g_print ("%s:%d %s Col: %d\n", __FILE__, __LINE__, __FUNCTION__, column);
   PsppireDelimitedText *file  = PSPPIRE_DELIMITED_TEXT (tree_model);
 
   g_return_if_fail (iter->stamp == file->stamp);
 
   gint n = GPOINTER_TO_INT (iter->user_data) + file->first_line;
 
-  //  g_print ("%s:%d Row: %d\n", __FILE__, __LINE__, n);
 
   if (column == 0)
     {
@@ -517,7 +583,7 @@ psppire_delimited_text_init (PsppireDelimitedText *text_file)
   text_file->const_cache.string = NULL;
   text_file->const_cache.length = 0;
   text_file->cache_row = -1;
-  memset (text_file->cache_starts, 0, 512);
+  memset (text_file->cache_starts, 0, sizeof text_file->cache_starts);
 
   text_file->max_delimiters = 0;
 
@@ -526,15 +592,13 @@ psppire_delimited_text_init (PsppireDelimitedText *text_file)
 }
 
 
-GtkTreeModel *
+PsppireDelimitedText *
 psppire_delimited_text_new (GtkTreeModel *child)
 {
-  PsppireDelimitedText *retval =
+  return
     g_object_new (PSPPIRE_TYPE_DELIMITED_TEXT,
                  "child", child,
                  NULL);
-
-  return retval;
 }
 
 static void