Change license from GPLv2+ to GPLv3+.
[pspp] / src / data / dictionary.c
index ca1a286c4d135ad20345fabe11484242bac3d9af..d580474edced2e47bf6e4a01424b0a19f19f442c 100644 (file)
@@ -1,20 +1,18 @@
-/* PSPP - computes sample statistics.
+/* PSPP - a program for statistical analysis.
    Copyright (C) 1997-9, 2000, 2006, 2007 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 the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
+   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
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
 
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   General Public License for more details.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301, USA. */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
 
@@ -24,7 +22,6 @@
 #include <ctype.h>
 
 #include "case.h"
-#include "cat-routines.h"
 #include "category.h"
 #include "settings.h"
 #include "value-labels.h"
@@ -52,13 +49,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 +175,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 +229,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 +441,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 +716,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 +1013,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 +1055,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 +1093,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;
 
-  free (d->documents);
-  if (documents == NULL)
-    d->documents = NULL;
-  else
-    d->documents = xstrdup (documents);
+  ds_assign_cstr (&d->documents, documents != NULL ? 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