Actually implement the new procedure code and adapt all of its clients
[pspp] / src / data / dictionary.c
index 0dadb959d371d5d0624f4dca21ff5d7c54d0a4ed..34ffc6af541eaccaba3695dc46f1244c07a0254c 100644 (file)
@@ -24,7 +24,6 @@
 #include <ctype.h>
 
 #include "case.h"
-#include "cat-routines.h"
 #include "category.h"
 #include "settings.h"
 #include "value-labels.h"
@@ -52,13 +51,13 @@ struct dictionary
     size_t var_cnt, var_cap;    /* Number of variables, capacity. */
     struct hsh_table *name_tab;        /* Variable index by name. */
     int next_value_idx;         /* Index of next `union value' to allocate. */
-    struct variable **split;    /* SPLIT FILE vars. */
+    const struct variable **split;    /* SPLIT FILE vars. */
     size_t split_cnt;           /* SPLIT FILE count. */
     struct variable *weight;    /* WEIGHT variable. */
     struct variable *filter;    /* FILTER variable. */
     size_t case_limit;          /* Current case limit (N command). */
     char *label;               /* File label. */
-    char *documents;           /* Documents, as a string. */
+    struct string documents;    /* Documents, as a string. */
     struct vector **vector;     /* Vectors of variables. */
     size_t vector_cnt;          /* Number of vectors. */
     const struct dict_callbacks *callbacks; /* Callbacks on dictionary
@@ -178,8 +177,7 @@ dict_clear (struct dictionary *d)
   d->case_limit = 0;
   free (d->label);
   d->label = NULL;
-  free (d->documents);
-  d->documents = NULL;
+  ds_destroy (&d->documents);
   dict_clear_vectors (d);
 }
 
@@ -233,13 +231,20 @@ dict_get_var (const struct dictionary *d, size_t idx)
   return d->var[idx];
 }
 
+inline void
+dict_get_vars (const struct dictionary *d, const struct variable ***vars,
+               size_t *cnt, unsigned exclude_classes)
+{
+  dict_get_vars_mutable (d, (struct variable ***) vars, cnt, exclude_classes);
+}
+
 /* Sets *VARS to an array of pointers to variables in D and *CNT
    to the number of variables in *D.  All variables are returned
    if EXCLUDE_CLASSES is 0, or it may contain one or more of (1u
    << DC_ORDINARY), (1u << DC_SYSTEM), or (1u << DC_SCRATCH) to
    exclude the corresponding type of variable. */
 void
-dict_get_vars (const struct dictionary *d, struct variable ***vars,
+dict_get_vars_mutable (const struct dictionary *d, struct variable ***vars,
                size_t *cnt, unsigned exclude_classes)
 {
   size_t count;
@@ -438,11 +443,12 @@ 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 should be deleted when
-   any transformations are active on the dictionary's dataset, because
-   those transformations might reference the deleted variable.
-   The safest time to delete a variable is just after a procedure
-   has been executed, as done by MODIFY VARS.
+   from outside D.  In general, no variable in the active file's
+   dictionary should be deleted when any transformations are
+   active on the dictionary's dataset, because those
+   transformations might reference the deleted variable.  The
+   safest time to delete a variable is just after a procedure has
+   been executed, as done by MODIFY VARS.
 
    Pointers to V within D are not a problem, because
    dict_delete_var() knows to remove V from split variables,
@@ -712,7 +718,7 @@ dict_get_case_weight (const struct dictionary *d, const struct ccase *c,
       double w = case_num (c, d->weight);
       if (w < 0.0 || var_is_num_missing (d->weight, w, MV_ANY))
         w = 0.0;
-      if ( w == 0.0 && *warn_on_invalid ) {
+      if ( w == 0.0 && warn_on_invalid != NULL && *warn_on_invalid ) {
          *warn_on_invalid = false;
          msg (SW, _("At least one case in the data file had a weight value "
                     "that was user-missing, system-missing, zero, or "
@@ -1009,7 +1015,7 @@ dict_compactor_destroy (struct dict_compactor *compactor)
    dict_get_split_cnt() to determine how many SPLIT FILE vars
    there are.  Returns a null pointer if and only if there are no
    SPLIT FILE vars. */
-struct variable *const *
+const struct variable *const *
 dict_get_split_vars (const struct dictionary *d)
 {
   assert (d != NULL);
@@ -1051,7 +1057,7 @@ dict_set_split_vars (struct dictionary *d,
   assert (cnt == 0 || split != NULL);
 
   d->split_cnt = cnt;
-  d->split = xnrealloc (d->split, cnt, sizeof *d->split);
+  d->split = cnt > 0 ? xnrealloc (d->split, cnt, sizeof *d->split) : NULL;
   memcpy (d->split, split, cnt * sizeof *d->split);
 
   if ( d->callbacks &&  d->callbacks->split_changed )
@@ -1089,27 +1095,73 @@ dict_set_label (struct dictionary *d, const char *label)
 }
 
 /* Returns the documents for D, or a null pointer if D has no
-   documents (see cmd_document()).. */
+   documents.  If the return value is nonnull, then the string
+   will be an exact multiple of DOC_LINE_LENGTH bytes in length,
+   with each segment corresponding to one line. */
 const char *
 dict_get_documents (const struct dictionary *d)
 {
-  assert (d != NULL);
-
-  return d->documents;
+  return ds_is_empty (&d->documents) ? NULL : ds_cstr (&d->documents);
 }
 
 /* Sets the documents for D to DOCUMENTS, or removes D's
-   documents if DOCUMENT is a null pointer. */
+   documents if DOCUMENT is a null pointer.  If DOCUMENTS is
+   nonnull, then it should be an exact multiple of
+   DOC_LINE_LENGTH bytes in length, with each segment
+   corresponding to one line. */
 void
 dict_set_documents (struct dictionary *d, const char *documents)
 {
-  assert (d != NULL);
+  size_t remainder;
+  
+  ds_assign_cstr (&d->documents, documents != NULL ? documents : "");
 
-  free (d->documents);
-  if (documents == NULL)
-    d->documents = NULL;
-  else
-    d->documents = xstrdup (documents);
+  /* In case the caller didn't get it quite right, pad out the
+     final line with spaces. */
+  remainder = ds_length (&d->documents) % DOC_LINE_LENGTH;
+  if (remainder != 0)
+    ds_put_char_multiple (&d->documents, ' ', DOC_LINE_LENGTH - remainder);
+}
+
+/* Drops the documents from dictionary D. */
+void
+dict_clear_documents (struct dictionary *d) 
+{
+  ds_clear (&d->documents);
+}
+
+/* Appends LINE to the documents in D.  LINE will be truncated or
+   padded on the right with spaces to make it exactly
+   DOC_LINE_LENGTH bytes long. */
+void
+dict_add_document_line (struct dictionary *d, const char *line)
+{
+  if (strlen (line) > DOC_LINE_LENGTH) 
+    {
+      /* Note to translators: "bytes" is correct, not characters */
+      msg (SW, _("Truncating document line to %d bytes."), DOC_LINE_LENGTH); 
+    }
+  buf_copy_str_rpad (ds_put_uninit (&d->documents, DOC_LINE_LENGTH),
+                     DOC_LINE_LENGTH, line);
+}
+
+/* Returns the number of document lines in dictionary D. */
+size_t
+dict_get_document_line_cnt (const struct dictionary *d) 
+{
+  return ds_length (&d->documents) / DOC_LINE_LENGTH;
+}
+
+/* Copies document line number IDX from dictionary D into
+   LINE, trimming off any trailing white space. */
+void
+dict_get_document_line (const struct dictionary *d,
+                        size_t idx, struct string *line) 
+{
+  assert (idx < dict_get_document_line_cnt (d));
+  ds_assign_substring (line, ds_substr (&d->documents, idx * DOC_LINE_LENGTH,
+                                        DOC_LINE_LENGTH));
+  ds_rtrim (line, ss_cstr (CC_SPACES));
 }
 
 /* Creates in D a vector named NAME that contains the CNT
@@ -1137,6 +1189,18 @@ dict_create_vector (struct dictionary *d,
     return false;
 }
 
+/* Creates in D a vector named NAME that contains the CNT
+   variables in VAR.  A vector named NAME must not already exist
+   in D. */
+void
+dict_create_vector_assert (struct dictionary *d,
+                           const char *name,
+                           struct variable **var, size_t cnt)
+{
+  assert (dict_lookup_vector (d, name) == NULL);
+  dict_create_vector (d, name, var, cnt);
+}
+
 /* Returns the vector in D with index IDX, which must be less
    than dict_get_vector_cnt (D). */
 const struct vector *