From 6b562f8a8263930b8d1ed1862efec76f2511ed08 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sun, 24 May 2009 19:35:40 -0700 Subject: [PATCH] Add support for value labels on long string variables. --- NEWS | 4 +- doc/dev/concepts.texi | 29 ++--- doc/dev/system-file-format.texi | 80 +++++++++++++- doc/variables.texi | 3 +- perl-module/PSPP.xs | 15 +-- perl-module/t/Pspp.t | 68 ++++++------ src/data/por-file-reader.c | 7 +- src/data/sys-file-reader.c | 120 ++++++++++++++++++++- src/data/sys-file-writer.c | 76 ++++++++++++- src/data/variable.c | 1 - src/language/dictionary/apply-dictionary.c | 15 +-- src/language/dictionary/value-labels.c | 61 ++--------- src/ui/gui/psppire-var-sheet.c | 7 +- tests/dissect-sysfile.c | 68 +++++++++++- 14 files changed, 404 insertions(+), 150 deletions(-) diff --git a/NEWS b/NEWS index a83f8310..a02810cd 100644 --- 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 diff --git a/doc/dev/concepts.texi b/doc/dev/concepts.texi index 93605767..ac45416f 100644 --- a/doc/dev/concepts.texi +++ b/doc/dev/concepts.texi @@ -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 diff --git a/doc/dev/system-file-format.texi b/doc/dev/system-file-format.texi index 164807b8..0358961b 100644 --- a/doc/dev/system-file-format.texi +++ b/doc/dev/system-file-format.texi @@ -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 diff --git a/doc/variables.texi b/doc/variables.texi index 08c4bdea..8396b714 100644 --- a/doc/variables.texi +++ b/doc/variables.texi @@ -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} diff --git a/perl-module/PSPP.xs b/perl-module/PSPP.xs index 13386588..8179d29a 100644 --- a/perl-module/PSPP.xs +++ b/perl-module/PSPP.xs @@ -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); diff --git a/perl-module/t/Pspp.t b/perl-module/t/Pspp.t index cdea8646..92180aba 100644 --- a/perl-module/t/Pspp.t +++ b/perl-module/t/Pspp.t @@ -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 diff --git a/src/data/por-file-reader.c b/src/data/por-file-reader.c index 00ca4196..c8e49289 100644 --- a/src/data/por-file-reader.c +++ b/src/data/por-file-reader.c @@ -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])); } diff --git a/src/data/sys-file-reader.c b/src/data/sys-file-reader.c index 983ae502..cff462ac 100644 --- a/src/data/sys-file-reader.c +++ b/src/data/sys-file-reader.c @@ -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 diff --git a/src/data/sys-file-writer.c b/src/data/sys-file-writer.c index 4907ad73..c1ff9a83 100644 --- a/src/data/sys-file-writer.c +++ b/src/data/sys-file-writer.c @@ -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) diff --git a/src/data/variable.c b/src/data/variable.c index 730317b7..e4961b23 100644 --- a/src/data/variable.c +++ b/src/data/variable.c @@ -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); } diff --git a/src/language/dictionary/apply-dictionary.c b/src/language/dictionary/apply-dictionary.c index a1b33101..0e8eb6c8 100644 --- a/src/language/dictionary/apply-dictionary.c +++ b/src/language/dictionary/apply-dictionary.c @@ -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)) diff --git a/src/language/dictionary/value-labels.c b/src/language/dictionary/value-labels.c index 6f9652f5..5e3ea11f 100644 --- a/src/language/dictionary/value-labels.c +++ b/src/language/dictionary/value-labels.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -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); @@ -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; diff --git a/src/ui/gui/psppire-var-sheet.c b/src/ui/gui/psppire-var-sheet.c index 94b88050..7925268d 100644 --- a/src/ui/gui/psppire-var-sheet.c +++ b/src/ui/gui/psppire-var-sheet.c @@ -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, diff --git a/tests/dissect-sysfile.c b/tests/dissect-sysfile.c index 62161f9c..897d48ff 100644 --- a/tests/dissect-sysfile.c +++ b/tests/dissect-sysfile.c @@ -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) -- 2.30.2