Add support for value labels on long string variables.
authorBen Pfaff <blp@gnu.org>
Mon, 25 May 2009 02:35:40 +0000 (19:35 -0700)
committerBen Pfaff <blp@gnu.org>
Sun, 7 Jun 2009 04:11:12 +0000 (21:11 -0700)
14 files changed:
NEWS
doc/dev/concepts.texi
doc/dev/system-file-format.texi
doc/variables.texi
perl-module/PSPP.xs
perl-module/t/Pspp.t
src/data/por-file-reader.c
src/data/sys-file-reader.c
src/data/sys-file-writer.c
src/data/variable.c
src/language/dictionary/apply-dictionary.c
src/language/dictionary/value-labels.c
src/ui/gui/psppire-var-sheet.c
tests/dissect-sysfile.c

diff --git a/NEWS b/NEWS
index a83f83102ef3c29c5d04f1e9651bbcbf9fca9a66..a02810cd287c7ee3768702c58dd4fa5bcbe09d22 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,5 @@
 PSPP NEWS -- history of user-visible changes.
-Time-stamp: <2009-02-05 20:31:51 blp>
+Time-stamp: <2009-05-24 16:40:48 blp>
 Copyright (C) 1996-9, 2000, 2008, 2009 Free Software Foundation, Inc.
 See the end for copying conditions.
 
@@ -9,6 +9,8 @@ Changes from 0.7.1 to 0.7.2:
 
  * Updated Perl module interface.
 
+ * Value labels for long string variables are now supported.
+
 Changes from 0.7.0 to 0.7.1:
 
  *  Added a perl module to facilitate reading and writing of pspp system 
index 9360576759367dcb47cfa3bd6fab9100964a0a7d..ac45416fc088371a4392a637d88b0ee2687f66d6 100644 (file)
@@ -1005,12 +1005,7 @@ All of the values in a set of value labels have the same width, which
 for a set of value labels owned by a variable (the common case) is the
 same as its variable.
 
-Numeric and short string sets of value labels may contain any number
-of entries.  Long string sets of value labels may not contain any
-value labels at all, due to a corresponding restriction in SPSS.  In
-PSPP we could easily eliminate this restriction, but doing so would
-also require us to extend the system file format in an incompatible
-way, which we consider a bad tradeoff.
+Sets of value labels may contain any number of entries.
 
 It is rarely necessary to interact directly with a @struct{val_labs}
 object.  Instead, the most common operation, looking up the label for
@@ -1091,31 +1086,24 @@ value in it may be resized to that width, as determined by
 Changes the width of @var{val_labs}'s values to @var{new_width}, which
 must be a valid new width as determined by
 @func{val_labs_can_set_width}.
-
-If @var{new_width} is a long string width, this function deletes all
-value labels from @var{val_labs}.
 @end deftypefun
 
 @node Value Labels Adding and Removing Labels
 @subsection Adding and Removing Labels
 
 These functions add and remove value labels from a @struct{val_labs}
-object.  These functions apply only to numeric and short string sets
-of value labels.  They have no effect on long string sets of value
-labels, since these sets are always empty.
+object.
 
 @deftypefun bool val_labs_add (struct val_labs *@var{val_labs}, union value @var{value}, const char *@var{label})
 Adds @var{label} to in @var{var_labs} as a label for @var{value},
 which must have the same width as the set of value labels.  Returns
-true if successful, false if @var{value} already has a label or if
-@var{val_labs} has long string width.
+true if successful, false if @var{value} already has a label.
 @end deftypefun
 
 @deftypefun void val_labs_replace (struct val_labs *@var{val_labs}, union value @var{value}, const char *@var{label})
 Adds @var{label} to in @var{var_labs} as a label for @var{value},
 which must have the same width as the set of value labels.  If
 @var{value} already has a label in @var{var_labs}, it is replaced.
-Has no effect if @var{var_labs} has long string width.
 @end deftypefun
 
 @deftypefun bool val_labs_remove (struct val_labs *@var{val_labs}, union value @var{value})
@@ -1433,11 +1421,10 @@ the variable (making a second copy):
 @deftypefun bool var_add_value_label (struct variable *@var{var}, const union value *@var{value}, const char *@var{label})
 Attempts to add a copy of @var{label} as a label for @var{value} for
 the given @var{var}.  @var{value} must have the same width as
-@var{var}.  If @var{value} already has a label, then the old
-label is retained.  Returns true if a label is added, false if there
-was an existing label for @var{value} or if @var{var} is a long string
-variable.  Either way, the caller retains ownership of @var{value} and
-@var{label}.
+@var{var}.  If @var{value} already has a label, then the old label is
+retained.  Returns true if a label is added, false if there was an
+existing label for @var{value}.  Either way, the caller retains
+ownership of @var{value} and @var{label}.
 @end deftypefun
 
 @deftypefun void var_replace_value_label (struct variable *@var{var}, const union value *@var{value}, const char *@var{label})
@@ -1446,8 +1433,6 @@ the given @var{var}.  @var{value} must have the same width as
 @var{var}.  If @var{value} already has a label, then
 @var{label} replaces the old label.  Either way, the caller retains
 ownership of @var{value} and @var{label}.
-
-If @var{var} is a long string variable, this function has no effect.
 @end deftypefun
 
 @node Variable Print and Write Formats
index 164807b80115e4796bb394c0336522b0d84344ba..0358961bab9aa39426cc2233048ae478b56e86db 100644 (file)
@@ -97,6 +97,7 @@ Each type of record is described separately below.
 * Long Variable Names Record::
 * Very Long String Record::
 * Character Encoding Record::
+* Long String Value Labels Record::
 * Data File and Variable Attributes Records::
 * Miscellaneous Informational Records::
 * Dictionary Termination Record::
@@ -398,6 +399,11 @@ Format types are defined as follows:
 @node Value Labels Records
 @section Value Labels Records
 
+The value label records documented in this section are used for
+numeric and short string variables only.  Long string variables may
+have value labels, but their value labels are recorded using a
+different record type (@pxref{Long String Value Labels Record}).
+
 The value label record has the following format:
 
 @example
@@ -458,7 +464,7 @@ A list of dictionary indexes of variables to which to apply the value
 labels (@pxref{Dictionary Index}).  There are @code{var_count}
 elements.
 
-String variables wider than 8 bytes may not have value labels.
+String variables wider than 8 bytes may not be specified in this list.
 @end table
 
 @node Document Record
@@ -837,6 +843,78 @@ See @url{http://www.iana.org/assignments/character-sets}.
 This record is not present in files generated by older software.
 See also @ref{character-code}.
 
+@node Long String Value Labels Record
+@section Long String Value Labels Record
+
+This record, if present, specifies value labels for long string
+variables.
+
+@example
+/* @r{Header.} */
+int32               rec_type;
+int32               subtype;
+int32               size;
+int32               count;
+
+/* @r{Repeated up to exactly @code{count} bytes.} */
+int32               var_name_len;
+char                var_name[];
+int32               var_width;
+int32               n_labels;
+long_string_label   labels[];
+@end example
+
+@table @code
+@item int32 rec_type;
+Record type.  Always set to 7.
+
+@item int32 subtype;
+Record subtype.  Always set to 21.
+
+@item int32 size;
+Always set to 1.
+
+@item int32 count;
+The number of bytes following the header until the next header.
+
+@item int32 var_name_len;
+@itemx char var_name[];
+The number of bytes in the name of the variable that has long string
+value labels, plus the variable name itself, which consists of exactly
+@code{var_name_len} bytes.  The variable name is not padded to any
+particular boundary, nor is it null-terminated.
+
+@item int32 var_width;
+The width of the variable, in bytes, which will be between 9 and
+32767.
+
+@item int32 n_labels;
+@itemx long_string_label labels[];
+The long string labels themselves.  The @code{labels} array contains
+exactly @code{n_labels} elements, each of which has the following
+substructure:
+
+@example
+int32               value_len;
+char                value[];
+int32               label_len;
+char                label[];
+@end example
+
+@table @code
+@item int32 value_len;
+@itemx char value[];
+The string value being labeled.  @code{value_len} is the number of
+bytes in @code{value}; it is equal to @code{var_width}.  The
+@code{value} array is not padded or null-terminated.
+
+@item int32 label_len;
+@itemx char label[];
+The label for the string value.  @code{label_len}, which must be
+between 0 and 120, is the number of bytes in @code{label}.  The
+@code{label} array is not padded or null-terminated.
+@end table
+@end table
 
 @node Data File and Variable Attributes Records
 @section Data File and Variable Attributes Records
index 08c4bdeac0cd72a34be7f06dfcbba4f9e499e224..8396b71475b22288e703c692247734844a1126ed 100644 (file)
@@ -342,8 +342,7 @@ stand for a long value.
 
 To set up value labels for a set of variables, specify the
 variable names after a slash (@samp{/}), followed by a list of values
-and their associated labels, separated by spaces.  Long string
-variables may not be specified.
+and their associated labels, separated by spaces.
 
 Before @cmd{VALUE LABELS} is executed, any existing value labels
 are cleared from the variables specified.  Use @cmd{ADD VALUE LABELS}
index 13386588b62c9738a6346a758717da61937cdb82..8179d29a3721aa27b0b0e7417085a515eb65c591 100644 (file)
@@ -404,26 +404,27 @@ INIT:
  sv_setpv (errstr, "");
 CODE:
  union value the_value;
+ int width = var_get_width (var);
+ int ok;
 
+ value_init (&the_value, width);
  if ( var_is_numeric (var))
  {
   if ( ! looks_like_number (key))
     {
       sv_setpv (errstr, "Cannot add label with string key to a numeric variable");
+      value_destroy (&the_value, width);
       XSRETURN_IV (0);
     }
   the_value.f = SvNV (key);
  }
  else
  {
-   if ( var_is_long_string (var) )
-     {
-      sv_setpv (errstr, "Cannot add label to a long string variable");
-      XSRETURN_IV (0);
-     }
-  strncpy (the_value.short_string, SvPV_nolen(key), MAX_SHORT_STRING);
+  value_copy_str_rpad (&the_value, width, SvPV_nolen(key), ' ');
  }
- if (! var_add_value_label (var, &the_value, label) )
+ ok = var_add_value_label (var, &the_value, label);
+ value_destroy (&the_value, width);
+ if (!ok)
  {
    sv_setpv (errstr, "Something went wrong");
    XSRETURN_IV (0);
index cdea86464e0b086db46b3ff3a29c5f1c951c08aa..92180aba9cde097c4409b2d71f6d78c66974504c 100644 (file)
@@ -8,7 +8,7 @@
 
 # change 'tests => 1' to 'tests => last_test_to_print';
 
-use Test::More tests => 37;
+use Test::More tests => 36;
 use Text::Diff;
 use File::Temp qw/ tempfile tempdir /;
 BEGIN { use_ok('PSPP') };
@@ -243,9 +243,7 @@ RESULT
 
       $longstr->set_label ("My Long String");
       my $re = $longstr->add_value_label ("xxx", "xfoo");
-      ok (($re == 0), "Long strings cant have labels");
-
-      ok ($PSPP::errstr eq "Cannot add label to a long string variable", "Error msg");
+      ok ($re, "Value label for long string");
 
       $int->set_missing_values (9, 99);
 
@@ -259,36 +257,38 @@ GET FILE='$tempfile'.
 DISPLAY DICTIONARY.
 SYNTAX
 1.1 DISPLAY.  
-+----------+-----------------------------------------+--------+
-|Variable  |Description                              |Position|
-#==========#=========================================#========#
-|integer   |My Integer                               |       1|
-|          |Format: F8.0                             |        |
-|          |Measure: Scale                           |        |
-|          |Display Alignment: Right                 |        |
-|          |Display Width: 8                         |        |
-|          |Missing Values: 9; 99                    |        |
-|          +-----+-----------------------------------+        |
-|          |    0|Zero                               |        |
-|          |    1|Unity                              |        |
-|          |    2|Duality                            |        |
-+----------+-----+-----------------------------------+--------+
-|string    |My String                                |       2|
-|          |Format: A8                               |        |
-|          |Measure: Nominal                         |        |
-|          |Display Alignment: Left                  |        |
-|          |Display Width: 8                         |        |
-|          |Missing Values: "this    "; "that    "   |        |
-|          +-----+-----------------------------------+        |
-|          |   xx|foo                                |        |
-|          |   yy|bar                                |        |
-+----------+-----+-----------------------------------+--------+
-|longstring|My Long String                           |       3|
-|          |Format: A9                               |        |
-|          |Measure: Nominal                         |        |
-|          |Display Alignment: Left                  |        |
-|          |Display Width: 9                         |        |
-+----------+-----------------------------------------+--------+
++----------+---------------------------------------------+--------+
+|Variable  |Description                                  |Position|
+#==========#=============================================#========#
+|integer   |My Integer                                   |       1|
+|          |Format: F8.0                                 |        |
+|          |Measure: Scale                               |        |
+|          |Display Alignment: Right                     |        |
+|          |Display Width: 8                             |        |
+|          |Missing Values: 9; 99                        |        |
+|          +---------+-----------------------------------+        |
+|          |        0|Zero                               |        |
+|          |        1|Unity                              |        |
+|          |        2|Duality                            |        |
++----------+---------+-----------------------------------+--------+
+|string    |My String                                    |       2|
+|          |Format: A8                                   |        |
+|          |Measure: Nominal                             |        |
+|          |Display Alignment: Left                      |        |
+|          |Display Width: 8                             |        |
+|          |Missing Values: "this    "; "that    "       |        |
+|          +---------+-----------------------------------+        |
+|          | xx      |foo                                |        |
+|          | yy      |bar                                |        |
++----------+---------+-----------------------------------+--------+
+|longstring|My Long String                               |       3|
+|          |Format: A9                                   |        |
+|          |Measure: Nominal                             |        |
+|          |Display Alignment: Left                      |        |
+|          |Display Width: 9                             |        |
+|          +---------+-----------------------------------+        |
+|          |xxx      |xfoo                               |        |
++----------+---------+-----------------------------------+--------+
 
 RESULT
 
index 00ca4196fd502314b558693e54fea300b8663275..c8e492897f3c117bfbc86e6c6b38a5f68249928d 100644 (file)
@@ -785,12 +785,7 @@ read_value_label (struct pfm_reader *r, struct dictionary *dict)
 
       /* Assign the value label to each variable. */
       for (j = 0; j < nv; j++)
-       {
-         struct variable *var = v[j];
-
-         if (!var_is_long_string (var))
-            var_replace_value_label (var, &val, label);
-       }
+        var_replace_value_label (v[j], &val, label);
 
       value_destroy (&val, var_get_width (v[0]));
     }
index 983ae5024e7eb0254fbbc3069cb95c7264c4dd54..cff462ac143df5184aeeec231d1ad388ef278cde 100644 (file)
@@ -182,6 +182,9 @@ static void read_data_file_attributes (struct sfm_reader *,
 static void read_variable_attributes (struct sfm_reader *,
                                       size_t size, size_t count,
                                       struct dictionary *);
+static void read_long_string_value_labels (struct sfm_reader *,
+                                          size_t size, size_t count,
+                                          struct dictionary *);
 
 /* Opens the system file designated by file handle FH for
    reading.  Reads the system file's dictionary into *DICT.
@@ -790,9 +793,8 @@ read_extension_record (struct sfm_reader *r, struct dictionary *dict,
     case 21:
       /* New in SPSS 16.  Encodes value labels for long string
          variables. */
-      sys_warn (r, _("Ignoring value labels for long string variables, "
-                     "which PSPP does not yet support."));
-      break;
+      read_long_string_value_labels (r, size, count, dict);
+      return;
 
     default:
       sys_warn (r, _("Unrecognized record type 7, subtype %d.  Please send a copy of this file, and the syntax which created it to %s"),
@@ -1202,8 +1204,9 @@ read_value_labels (struct sfm_reader *r,
     {
       var[i] = lookup_var_by_value_idx (r, var_by_value_idx, read_int (r));
       if (var_is_long_string (var[i]))
-        sys_error (r, _("Value labels are not allowed on long string "
-                        "variables (%s)."), var_get_name (var[i]));
+        sys_error (r, _("Value labels may not be added to long string "
+                        "variables (e.g. %s) using records types 3 and 4."),
+                   var_get_name (var[i]));
       max_width = MAX (max_width, var_get_width (var[i]));
     }
 
@@ -1328,6 +1331,113 @@ read_data_file_attributes (struct sfm_reader *r,
   close_text_record (r, text);
 }
 
+static void
+skip_long_string_value_labels (struct sfm_reader *r, size_t n_labels)
+{
+  size_t i;
+
+  for (i = 0; i < n_labels; i++)
+    {
+      size_t value_length, label_length;
+
+      value_length = read_int (r);
+      skip_bytes (r, value_length);
+      label_length = read_int (r);
+      skip_bytes (r, label_length);
+    }
+}
+
+static void
+read_long_string_value_labels (struct sfm_reader *r,
+                              size_t size, size_t count,
+                              struct dictionary *d)
+{
+  const off_t start = ftello (r->file);
+  while (ftello (r->file) - start < size * count)
+    {
+      char var_name[VAR_NAME_LEN + 1];
+      size_t n_labels, i;
+      struct variable *v;
+      union value value;
+      int var_name_len;
+      int width;
+
+      /* Read header. */
+      var_name_len = read_int (r);
+      if (var_name_len > VAR_NAME_LEN)
+        sys_error (r, _("Variable name length in long string value label "
+                        "record (%d) exceeds %d-byte limit."),
+                   var_name_len, VAR_NAME_LEN);
+      read_string (r, var_name, var_name_len + 1);
+      width = read_int (r);
+      n_labels = read_int (r);
+
+      v = dict_lookup_var (d, var_name);
+      if (v == NULL)
+        {
+          sys_warn (r, _("Ignoring long string value record for "
+                         "unknown variable %s."), var_name);
+          skip_long_string_value_labels (r, n_labels);
+          continue;
+        }
+      if (var_is_numeric (v))
+        {
+          sys_warn (r, _("Ignoring long string value record for "
+                         "numeric variable %s."), var_name);
+          skip_long_string_value_labels (r, n_labels);
+          continue;
+        }
+      if (width != var_get_width (v))
+        {
+          sys_warn (r, _("Ignoring long string value record for variable %s "
+                         "because the record's width (%d) does not match the "
+                         "variable's width (%d)"),
+                    var_name, width, var_get_width (v));
+          skip_long_string_value_labels (r, n_labels);
+          continue;
+        }
+
+      /* Read values. */
+      value_init_pool (r->pool, &value, width);
+      for (i = 0; i < n_labels; i++)
+       {
+          size_t value_length, label_length;
+          char label[256];
+          bool skip = false;
+
+          /* Read value. */
+          value_length = read_int (r);
+          if (value_length == width)
+            read_string (r, value_str_rw (&value, width), width + 1);
+          else
+            {
+              sys_warn (r, _("Ignoring long string value %zu for variable %s, "
+                             "with width %d, that has bad value width %zu."),
+                        i, var_get_name (v), width, value_length);
+              skip_bytes (r, value_length);
+              skip = true;
+            }
+
+          /* Read label. */
+          label_length = read_int (r);
+          read_string (r, label, MIN (sizeof label, label_length + 1));
+          if (label_length >= sizeof label)
+            {
+              /* Skip and silently ignore label text after the
+                 first 255 bytes.  The maximum documented length
+                 of a label is 120 bytes so this is more than
+                 generous. */
+              skip_bytes (r, sizeof label - (label_length + 1));
+            }
+
+          if (!skip && !var_add_value_label (v, &value, label))
+            sys_warn (r, _("Duplicate value label for \"%.*s\" on %s."),
+                      width, value_str (&value, width), var_get_name (v));
+        }
+    }
+}
+
+
 /* Reads record type 7, subtype 18, which lists custom
    attributes on individual variables.  */
 static void
index 4907ad7389ba32a2ecf900e762b06a5651b1c321..c1ff9a83a212fac17bf2767cadbc92ef1feb1581 100644 (file)
@@ -109,6 +109,8 @@ static void write_encoding_record (struct sfm_writer *w,
 static void write_vls_length_table (struct sfm_writer *w,
                              const struct dictionary *dict);
 
+static void write_long_string_value_labels (struct sfm_writer *,
+                                            const struct dictionary *);
 
 static void write_variable_display_parameters (struct sfm_writer *w,
                                                const struct dictionary *dict);
@@ -245,6 +247,8 @@ sfm_open_writer (struct file_handle *fh, struct dictionary *d,
 
   write_vls_length_table (w, d);
 
+  write_long_string_value_labels (w, d);
+
   if (attrset_count (dict_get_attributes (d)))
     write_data_file_attributes (w, d);
   write_variable_attributes (w, d);
@@ -492,7 +496,10 @@ write_variable (struct sfm_writer *w, const struct variable *v)
 }
 
 /* Writes the value labels for variable V having system file
-   variable index IDX to system file W. */
+   variable index IDX to system file W.
+
+   Value labels for long string variables are written separately,
+   by write_long_string_value_labels. */
 static void
 write_value_labels (struct sfm_writer *w, struct variable *v, int idx)
 {
@@ -503,7 +510,7 @@ write_value_labels (struct sfm_writer *w, struct variable *v, int idx)
 
   val_labs = var_get_value_labels (v);
   n_labels = val_labs_count (val_labs);
-  if (n_labels == 0)
+  if (n_labels == 0 || var_get_width (v) > 8)
     return;
 
   /* Value label record. */
@@ -670,6 +677,71 @@ write_vls_length_table (struct sfm_writer *w,
 }
 
 
+static void
+write_long_string_value_labels (struct sfm_writer *w,
+                                const struct dictionary *dict)
+{
+  size_t n_vars = dict_get_var_cnt (dict);
+  size_t size, i;
+  off_t start UNUSED;
+
+  /* Figure out the size in advance. */
+  size = 0;
+  for (i = 0; i < n_vars; i++)
+    {
+      struct variable *var = dict_get_var (dict, i);
+      const struct val_labs *val_labs = var_get_value_labels (var);
+      int width = var_get_width (var);
+      const struct val_lab *val_lab;
+
+      if (val_labs_count (val_labs) == 0 || width < 9)
+        continue;
+
+      size += 12 + strlen (var_get_name (var));
+      for (val_lab = val_labs_first (val_labs); val_lab != NULL;
+           val_lab = val_labs_next (val_labs, val_lab))
+        size += 8 + width + strlen (val_lab_get_label (val_lab));
+    }
+  if (size == 0)
+    return;
+
+  write_int (w, 7);             /* Record type. */
+  write_int (w, 21);            /* Record subtype */
+  write_int (w, 1);             /* Data item (byte) size. */
+  write_int (w, size);          /* Number of data items. */
+
+  start = ftello (w->file);
+  for (i = 0; i < n_vars; i++)
+    {
+      struct variable *var = dict_get_var (dict, i);
+      const struct val_labs *val_labs = var_get_value_labels (var);
+      const char *var_name = var_get_name (var);
+      int width = var_get_width (var);
+      const struct val_lab *val_lab;
+
+      if (val_labs_count (val_labs) == 0 || width < 9)
+        continue;
+
+      write_int (w, strlen (var_name));
+      write_bytes (w, var_name, strlen (var_name));
+      write_int (w, width);
+      write_int (w, val_labs_count (val_labs));
+      for (val_lab = val_labs_first (val_labs); val_lab != NULL;
+           val_lab = val_labs_next (val_labs, val_lab))
+        {
+          const char *label = val_lab_get_label (val_lab);
+          size_t label_length = strlen (label);
+
+          write_int (w, width);
+          write_bytes (w, value_str (val_lab_get_value (val_lab), width),
+                       width);
+          write_int (w, label_length);
+          write_bytes (w, label, label_length);
+        }
+    }
+  assert (ftello (w->file) == start + size);
+}
+
 static void
 write_encoding_record (struct sfm_writer *w,
                       const struct dictionary *d)
index 730317b7d26599215b4a18e24a24769d10b74304..e4961b23843177078371176d346620d6e62f8d75 100644 (file)
@@ -550,7 +550,6 @@ var_set_value_labels (struct variable *v, const struct val_labs *vls)
 static void
 alloc_value_labels (struct variable *v)
 {
-  assert (!var_is_long_string (v));
   if (v->val_labs == NULL)
     v->val_labs = val_labs_create (v->width);
 }
index a1b3310176e1687385dd2dbf3bc124e711e08da1..0e8eb6c82e703b9d759ce142a2a1c12d8ca0c9ae 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009 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
@@ -89,16 +89,9 @@ cmd_apply_dictionary (struct lexer *lexer, struct dataset *ds)
 
       if (var_has_value_labels (s))
         {
-          if (!var_is_long_string (t))
-            {
-              const struct val_labs *value_labels = var_get_value_labels (s);
-              if (val_labs_can_set_width (value_labels, var_get_width (t)))
-                var_set_value_labels (s, value_labels);
-            }
-          else
-            msg (SW, _("Cannot add value labels from source file to "
-                       "long string variable %s."),
-                 var_get_name (s));
+          const struct val_labs *value_labels = var_get_value_labels (s);
+          if (val_labs_can_set_width (value_labels, var_get_width (t)))
+            var_set_value_labels (s, value_labels);
         }
 
       if (var_has_missing_values (s))
index 6f9652f58bea708e0db93a85c79b765b16de69f6..5e3ea11f24654d0af73ff4cc1113123134bca00b 100644 (file)
@@ -24,6 +24,7 @@
 #include <data/variable.h>
 #include <language/command.h>
 #include <language/lexer/lexer.h>
+#include <language/lexer/value-parser.h>
 #include <language/lexer/variable-parser.h>
 #include <libpspp/hash.h>
 #include <libpspp/message.h>
@@ -38,7 +39,6 @@
 
 static int do_value_labels (struct lexer *,
                            const struct dictionary *dict, bool);
-static int verify_val_labs (struct variable **vars, size_t var_cnt);
 static void erase_labels (struct variable **vars, size_t var_cnt);
 static int get_label (struct lexer *, struct variable **vars, size_t var_cnt);
 \f
@@ -70,14 +70,12 @@ do_value_labels (struct lexer *lexer, const struct dictionary *dict, bool erase)
   while (lex_token (lexer) != '.')
     {
       parse_err = !parse_variables (lexer, dict, &vars, &var_cnt,
-                                   PV_SAME_TYPE) ;
+                                   PV_SAME_WIDTH);
       if (var_cnt < 1)
        {
          free(vars);
          return CMD_FAILURE;
        }
-      if (!verify_val_labs (vars, var_cnt))
-        goto lossage;
       if (erase)
         erase_labels (vars, var_cnt);
       while (lex_token (lexer) != '/' && lex_token (lexer) != '.')
@@ -105,27 +103,6 @@ do_value_labels (struct lexer *lexer, const struct dictionary *dict, bool erase)
   return CMD_FAILURE;
 }
 
-/* Verifies that none of the VAR_CNT variables in VARS are long
-   string variables. */
-static int
-verify_val_labs (struct variable **vars, size_t var_cnt)
-{
-  size_t i;
-
-  for (i = 0; i < var_cnt; i++)
-    {
-      const struct variable *vp = vars[i];
-
-      if (var_is_long_string (vp))
-       {
-         msg (SE, _("It is not possible to assign value labels to long "
-                    "string variables such as %s."), var_get_name (vp));
-         return 0;
-       }
-    }
-  return 1;
-}
-
 /* Erases all the labels for the VAR_CNT variables in VARS. */
 static void
 erase_labels (struct variable **vars, size_t var_cnt)
@@ -145,40 +122,22 @@ get_label (struct lexer *lexer, struct variable **vars, size_t var_cnt)
   /* Parse all the labels and add them to the variables. */
   do
     {
+      int width = var_get_width (vars[0]);
       union value value;
       struct string label;
-      int width;
       size_t i;
 
       /* Set value. */
-      if (var_is_alpha (vars[0]))
-       {
-         if (lex_token (lexer) != T_STRING)
-           {
-              lex_error (lexer, _("expecting string"));
-             return 0;
-           }
-          width = MAX_SHORT_STRING;
-          value_init (&value, width);
-         buf_copy_str_rpad (value_str_rw (&value, width), width,
-                             ds_cstr (lex_tokstr (lexer)), ' ');
-       }
-      else
-       {
-         if (!lex_is_number (lexer))
-           {
-             lex_error (lexer, _("expecting number"));
-             return 0;
-           }
-          width = 0;
-          value_init (&value, width);
-         value.f = lex_tokval (lexer);
-       }
-      lex_get (lexer);
+      value_init (&value, width);
+      if (!parse_value (lexer, &value, width))
+        {
+          value_destroy (&value, width);
+          return 0;
+        }
       lex_match (lexer, ',');
 
       /* Set label. */
-      if (!lex_force_string (lexer))
+      if (lex_token (lexer) != T_ID && !lex_force_string (lexer))
         {
           value_destroy (&value, width);
           return 0;
index 94b88050a135d2d90fcb6a313fa7181cc2dbd60f..7925268dc946c36a0b9d4fa8c75109d8c7d625cb 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009 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
@@ -363,11 +363,6 @@ var_sheet_change_active_cell (PsppireVarSheet *vs,
        customEntry =
          PSPPIRE_CUSTOM_ENTRY (psppire_sheet_get_entry (sheet));
 
-       if ( var_is_long_string (var))
-         g_object_set (customEntry,
-                       "editable", FALSE,
-                       NULL);
-
        val_labs_dialog_set_target_variable (vs->val_labs_dialog, var);
 
        g_signal_connect_swapped (customEntry,
index 62161f9c540200b1bcc954d5cef829342e8d671f..897d48ffb59bf26d832c1d069f4173ed1c0ee9b5 100644 (file)
@@ -36,6 +36,8 @@
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
+#define VAR_NAME_LEN 64
+
 struct sfm_reader
   {
     const char *file_name;
@@ -68,7 +70,8 @@ static void read_variable_attributes (struct sfm_reader *r,
                                       size_t size, size_t count);
 static void read_character_encoding (struct sfm_reader *r,
                                       size_t size, size_t count);
-
+static void read_long_string_value_labels (struct sfm_reader *r,
+                                           size_t size, size_t count);
 
 static struct text_record *open_text_record (
   struct sfm_reader *, size_t size);
@@ -517,6 +520,10 @@ read_extension_record (struct sfm_reader *r)
       read_character_encoding (r, size, count);
       return;
 
+    case 21:
+      read_long_string_value_labels (r, size, count);
+      return;
+
     default:
       sys_warn (r, _("Unrecognized record type 7, subtype %d."), subtype);
       break;
@@ -729,6 +736,65 @@ read_character_encoding (struct sfm_reader *r, size_t size, size_t count)
   printf ("%08lx: Character Encoding: %s\n", posn, encoding);
 }
 
+static void
+read_long_string_value_labels (struct sfm_reader *r, size_t size, size_t count)
+{
+  const long start = ftell (r->file);
+
+  printf ("%08lx: long string value labels\n", start);
+  while (ftell (r->file) - start < size * count)
+    {
+      long posn = ftell (r->file);
+      char var_name[VAR_NAME_LEN + 1];
+      int var_name_len;
+      int n_values;
+      int width;
+      int i;
+
+      /* Read variable name. */
+      var_name_len = read_int (r);
+      if (var_name_len > VAR_NAME_LEN)
+        sys_error (r, _("Variable name length in long string value label "
+                        "record (%d) exceeds %d-byte limit."),
+                   var_name_len, VAR_NAME_LEN);
+      read_string (r, var_name, var_name_len + 1);
+
+      /* Read width, number of values. */
+      width = read_int (r);
+      n_values = read_int (r);
+
+      printf ("\t%08lx: %s, width %d, %d values\n",
+              posn, var_name, width, n_values);
+
+      /* Read values. */
+      for (i = 0; i < n_values; i++)
+       {
+          char *value;
+          int value_length;
+
+          char *label;
+         int label_length;
+
+          posn = ftell (r->file);
+
+          /* Read value. */
+          value_length = read_int (r);
+          value = xmalloc (value_length + 1);
+          read_string (r, value, value_length + 1);
+
+          /* Read label. */
+          label_length = read_int (r);
+          label = xmalloc (label_length + 1);
+          read_string (r, label, label_length + 1);
+
+          printf ("\t\t%08lx: \"%s\" (%d bytes) => \"%s\" (%d bytes)\n",
+                  posn, value, value_length, label, label_length);
+
+          free (value);
+          free (label);
+       }
+    }
+}
 
 static void
 read_variable_attributes (struct sfm_reader *r, size_t size, size_t count)