Bug #18497.
variable names after a slash (@samp{/}), followed by a list of values
and their associated labels, separated by spaces.
+Value labels in output are normally broken into lines automatically.
+Put @samp{\n} in a label string to force a line break at that point.
+The label may still be broken into lines at additional points.
+
Before @cmd{VALUE LABELS} is executed, any existing value labels
are cleared from the variables specified. Use @cmd{ADD VALUE LABELS}
(@pxref{ADD VALUE LABELS}) to add value labels without clearing those
{
const struct val_lab *vl = labels[i];
write_value (w, val_lab_get_value (vl), var_get_width (v));
- write_string (w, val_lab_get_label (vl));
+ write_string (w, val_lab_get_escaped_label (vl));
}
free (labels);
}
for (i = 0; i < n_labels; i++)
{
const struct val_lab *vl = labels[i];
- char *label = recode_string (var_get_encoding (v), UTF8, val_lab_get_label (vl), -1);
+ char *label = recode_string (var_get_encoding (v), UTF8,
+ val_lab_get_escaped_label (vl), -1);
uint8_t len = MIN (strlen (label), 255);
write_value (w, val_lab_get_value (vl), var_get_width (v));
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));
+ size += 8 + width + strlen (val_lab_get_escaped_label (val_lab));
}
if (size == 0)
return;
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);
+ const char *label = val_lab_get_escaped_label (val_lab);
size_t label_length = strlen (label);
write_int (w, width);
copy = val_labs_create (vls->width);
HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
- val_labs_add (copy, &label->value, label->label);
+ val_labs_add (copy, &label->value, label->escaped_label);
return copy;
}
hmap_delete (&vls->labels, &label->node);
value_destroy (&label->value, vls->width);
intern_unref (label->label);
+ intern_unref (label->escaped_label);
free (label);
}
}
return vls == NULL ? 0 : hmap_count (&vls->labels);
}
\f
+static void
+set_label (struct val_lab *lab, const char *escaped_label)
+{
+ lab->escaped_label = intern_new (escaped_label);
+ if (strstr (escaped_label, "\\n") == NULL)
+ lab->label = intern_ref (lab->escaped_label);
+ else
+ {
+ struct string s;
+ const char *p;
+
+ ds_init_empty (&s);
+ ds_extend (&s, intern_strlen (lab->escaped_label));
+ for (p = escaped_label; *p != '\0'; p++)
+ {
+ char c = *p;
+ if (c == '\\' && p[1] == 'n')
+ {
+ c = '\n';
+ p++;
+ }
+ ds_put_byte (&s, c);
+ }
+ lab->label = intern_new (ds_cstr (&s));
+ ds_destroy (&s);
+ }
+}
+
static void
do_add_val_lab (struct val_labs *vls, const union value *value,
- const char *label)
+ const char *escaped_label)
{
struct val_lab *lab = xmalloc (sizeof *lab);
value_clone (&lab->value, value, vls->width);
- lab->label = intern_new (label);
+ set_label (lab, escaped_label);
hmap_insert (&vls->labels, &lab->node, value_hash (value, vls->width, 0));
}
-/* If VLS does not already contain a value label for VALUE, adds
- LABEL for it and returns true. Otherwise, returns false. */
+/* If VLS does not already contain a value label for VALUE, adds the UTF-8
+ encoded LABEL for it and returns true. Otherwise, returns false.
+
+ In LABEL, the two-byte sequence "\\n" is interpreted as a new-line. */
bool
val_labs_add (struct val_labs *vls, const union value *value,
const char *label)
}
/* Sets LABEL as the value label for VALUE in VLS, replacing any
- existing label for VALUE. */
+ existing label for VALUE.
+
+ In LABEL, the two-byte sequence "\\n" is interpreted as a new-line. */
void
val_labs_replace (struct val_labs *vls, const union value *value,
const char *label)
if (vl != NULL)
{
intern_unref (vl->label);
- vl->label = intern_new (label);
+ intern_unref (vl->escaped_label);
+ set_label (vl, label);
}
else
do_add_val_lab (vls, value, label);
hmap_delete (&vls->labels, &label->node);
value_destroy (&label->value, vls->width);
intern_unref (label->label);
+ intern_unref (label->escaped_label);
free (label);
}
-/* Searches VLS for a value label for VALUE. If successful,
- returns the string used as the label; otherwise, returns a
- null pointer. Returns a null pointer if VLS is null. */
+/* Searches VLS for a value label for VALUE. If successful, returns the string
+ used as the label, as a UTF-8 encoded string in a format suitable for
+ output. Otherwise, returns a null pointer. Returns a null pointer if VLS
+ is null. */
const char *
val_labs_find (const struct val_labs *vls, const union value *value)
{
struct hmap_node node; /* Node in hash map. */
union value value; /* The value being labeled. */
const char *label; /* An interned string. */
+ const char *escaped_label; /* An interned string. */
};
/* Returns the value in VL. The caller must not modify or free
return &vl->value;
}
-/* Returns the label in VL. The caller must not modify or free the returned
- value. */
+/* Returns the label in VL as a UTF-8 encoded interned string, in a format
+ appropriate for use in output. The caller must not modify or free the
+ returned value. */
static inline const char *
val_lab_get_label (const struct val_lab *vl)
{
return vl->label;
}
+
+/* Returns the label in VL as a UTF-8 encoded interned string. Any new-line
+ characters in the label's usual output form are represented in the returned
+ string as the two-byte sequence "\\n". This form is used on the VALUE
+ LABELS command, in system and portable files, and passed to val_labs_add()
+ and val_labs_replace().
+
+ The caller must not modify or free the returned value. */
+static inline const char *
+val_lab_get_escaped_label (const struct val_lab *vl)
+{
+ return vl->escaped_label;
+}
\f
/* A set of value labels. */
struct val_labs
/* Looking up value labels. */
const char *val_labs_find (const struct val_labs *, const union value *);
struct val_lab *val_labs_lookup (const struct val_labs *,
- const union value *);
+ const union value *);
/* Basic properties. */
size_t val_labs_count (const struct val_labs *);
v->val_labs = val_labs_create (v->width);
}
-/* Attempts to add a value label with the given VALUE and LABEL
- to V. Returns true if successful, false otherwise (probably
- due to an existing label). */
+/* Attempts to add a value label with the given VALUE and UTF-8 encoded LABEL
+ to V. Returns true if successful, false otherwise (probably due to an
+ existing label).
+
+ In LABEL, the two-byte sequence "\\n" is interpreted as a new-line. */
bool
var_add_value_label (struct variable *v,
const union value *value, const char *label)
return val_labs_add (v->val_labs, value, label);
}
-/* Adds or replaces a value label with the given VALUE and LABEL
+/* Adds or replaces a value label with the given VALUE and UTF-8 encoded LABEL
to V.
-*/
+
+ In LABEL, the two-byte sequence "\\n" is interpreted as a new-line. */
void
var_replace_value_label (struct variable *v,
const union value *value, const char *label)
var_set_value_labels (v, NULL);
}
-/* Returns the label associated with VALUE for variable V,
- or a null pointer if none. */
+/* Returns the label associated with VALUE for variable V, as a UTF-8 string in
+ a format suitable for output, or a null pointer if none. */
const char *
var_lookup_value_label (const struct variable *v, const union value *value)
{
const struct val_lab *vl = labels[i];
tab_value (t, 1, r, TAB_NONE, &vl->value, v, NULL);
- tab_text (t, 2, r, TAB_LEFT, val_lab_get_label (vl));
+ tab_text (t, 2, r, TAB_LEFT, val_lab_get_escaped_label (vl));
r++;
}
free (labels);
{
gchar *const vstr = value_to_text (vl->value, dict, *write_spec);
- return g_strdup_printf (_("{%s,`%s'}_"), vstr, val_lab_get_label (vl));
+ return g_strdup_printf (_("{%s,`%s'}_"), vstr,
+ val_lab_get_escaped_label (vl));
}
}
}
ds_put_cstr (s, "\n ");
syntax_gen_value (s, &vl->value, width, format);
ds_put_byte (s, ' ');
- syntax_gen_string (s, ss_cstr (val_lab_get_label (vl)));
+ syntax_gen_string (s, ss_cstr (val_lab_get_escaped_label (vl)));
}
free (labels);
ds_put_cstr (s, ".\n");
/* PSPPIRE - a graphical user interface for PSPP.
- Copyright (C) 2005, 2009 Free Software Foundation
+ Copyright (C) 2005, 2009, 2011 Free Software Foundation
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 (valuep != NULL)
*valuep = value;
if (label != NULL)
- *label = val_labs_find (dialog->labs, &value);
+ {
+ struct val_lab *vl = val_labs_lookup (dialog->labs, &value);
+ if (vl != NULL)
+ *label = val_lab_get_escaped_label (vl);
+ }
}
value_to_text (vl->value, dialog->dict,
*var_get_write_format (dialog->pv));
- gchar *const text = g_strdup_printf (_("%s = `%s'"),
- vstr, val_lab_get_label (vl));
+ gchar *const text = g_strdup_printf (_("%s = `%s'"), vstr,
+ val_lab_get_escaped_label (vl));
gtk_list_store_append (list_store, &iter);
gtk_list_store_set (list_store, &iter,
/* PSPPIRE - a graphical user interface for PSPP.
- Copyright (C) 2007, 2009, 2010 Free Software Foundation
+ Copyright (C) 2007, 2009, 2010, 2011 Free Software Foundation
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
gchar *const vstr =
value_to_text (vl->value, dict, *var_get_print_format (var));
- g_string_append_printf (gstring, _("%s %s\n"), vstr, val_lab_get_label (vl));
+ g_string_append_printf (gstring, _("%s %s\n"),
+ vstr, val_lab_get_escaped_label (vl));
g_free (vstr);
}
])
AT_CLEANUP
+AT_SETUP([VALUE LABELS with new-line])
+AT_DATA([value-labels.sps], [dnl
+DATA LIST LIST NOTABLE /x.
+VALUE LABELS x 1 'one' 2 'first line\nsecond line' 3 'three'.
+BEGIN DATA.
+1
+2
+3
+END DATA.
+DISPLAY DICTIONARY.
+FREQUENCIES x/STAT=NONE.
+])
+AT_CHECK([pspp -O format=csv value-labels.sps], [0], [dnl
+Variable,Description,,Position
+x,Format: F8.2,,1
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+,1.00,one,
+,2.00,first line\nsecond line,
+,3.00,three,
+
+Table: x
+Value Label,Value,Frequency,Percent,Valid Percent,Cum Percent
+one,1.00,1,33.33,33.33,33.33
+"first line
+second line",2.00,1,33.33,33.33,66.67
+three,3.00,1,33.33,33.33,100.00
+Total,,3,100.0,100.0,
+])
+AT_CLEANUP
+
+AT_SETUP([VALUE LABELS with new-line in system file])
+AT_DATA([save.sps], [dnl
+DATA LIST LIST NOTABLE /x.
+VALUE LABELS x 1 'one' 2 'first line\nsecond line' 3 'three'.
+BEGIN DATA.
+1
+2
+3
+END DATA.
+SAVE OUTFILE='value-labels.sav'.
+])
+AT_CHECK([pspp -O format=csv save.sps])
+AT_DATA([get.sps], [dnl
+GET FILE='value-labels.sav'.
+DISPLAY DICTIONARY.
+FREQUENCIES x/STAT=NONE.
+])
+AT_CHECK([pspp -O format=csv get.sps], [0], [dnl
+Variable,Description,,Position
+x,Format: F8.2,,1
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+,1.00,one,
+,2.00,first line\nsecond line,
+,3.00,three,
+
+Table: x
+Value Label,Value,Frequency,Percent,Valid Percent,Cum Percent
+one,1.00,1,33.33,33.33,33.33
+"first line
+second line",2.00,1,33.33,33.33,66.67
+three,3.00,1,33.33,33.33,100.00
+Total,,3,100.0,100.0,
+])
+AT_CLEANUP
+
dnl Tests for a bug which caused VALUE LABELS to
dnl crash when given invalid syntax.
AT_SETUP([VALUE LABELS invalid syntax bug])