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.
* 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
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
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})
@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})
@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
* 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::
@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
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
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
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}
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);
# 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') };
$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);
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
/* 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]));
}
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.
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"),
{
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]));
}
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
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);
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);
}
/* 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)
{
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. */
}
+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)
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);
}
/* 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
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))
#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>
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
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) != '.')
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)
/* 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;
/* 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
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,
#include "gettext.h"
#define _(msgid) gettext (msgid)
+#define VAR_NAME_LEN 64
+
struct sfm_reader
{
const char *file_name;
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);
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;
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)