+ n_vars = dict_get_var_cnt (dict);
+ if (count == 3 * n_vars)
+ includes_width = true;
+ else if (count == 2 * n_vars)
+ includes_width = false;
+ else
+ {
+ sys_warn (r, _("Extension 11 has bad count %zu (for %zu variables)."),
+ count, n_vars);
+ skip_bytes (r, size * count);
+ return;
+ }
+
+ for (i = 0; i < n_vars; ++i)
+ {
+ struct variable *v = dict_get_var (dict, i);
+ int measure = read_int (r);
+ int width = includes_width ? read_int (r) : 0;
+ int align = read_int (r);
+
+ /* SPSS 14 sometimes seems to set string variables' measure
+ to zero. */
+ if (0 == measure && var_is_alpha (v))
+ measure = 1;
+
+ if (measure < 1 || measure > 3 || align < 0 || align > 2)
+ {
+ if (!warned)
+ sys_warn (r, _("Invalid variable display parameters "
+ "for variable %zu (%s). "
+ "Default parameters substituted."),
+ i, var_get_name (v));
+ warned = true;
+ continue;
+ }
+
+ var_set_measure (v, (measure == 1 ? MEASURE_NOMINAL
+ : measure == 2 ? MEASURE_ORDINAL
+ : MEASURE_SCALE));
+ var_set_alignment (v, (align == 0 ? ALIGN_LEFT
+ : align == 1 ? ALIGN_RIGHT
+ : ALIGN_CENTRE));
+
+ /* Older versions (SPSS 9.0) sometimes set the display
+ width to zero. This causes confusion in the GUI, so
+ only set the width if it is nonzero. */
+ if (width > 0)
+ var_set_display_width (v, width);
+ }
+}
+
+/* Reads record type 7, subtype 13, which gives the long name
+ that corresponds to each short name. Modifies variable names
+ in DICT accordingly. */
+static void
+read_long_var_name_map (struct sfm_reader *r, size_t size, size_t count,
+ struct dictionary *dict)
+{
+ struct text_record *text;
+ struct variable *var;
+ char *long_name;
+
+ text = open_text_record (r, size * count);
+ while (read_variable_to_value_pair (r, dict, text, &var, &long_name))
+ {
+ char **short_names;
+ size_t short_name_cnt;
+ size_t i;
+
+ /* Validate long name. */
+ if (!var_is_valid_name (long_name, false))
+ {
+ sys_warn (r, _("Long variable mapping from %s to invalid "
+ "variable name `%s'."),
+ var_get_name (var), long_name);
+ continue;
+ }
+
+ /* Identify any duplicates. */
+ if (strcasecmp (var_get_short_name (var, 0), long_name)
+ && dict_lookup_var (dict, long_name) != NULL)
+ {
+ sys_warn (r, _("Duplicate long variable name `%s' "
+ "within system file."), long_name);
+ continue;
+ }
+
+ /* Renaming a variable may clear its short names, but we
+ want to retain them, so we save them and re-set them
+ afterward. */
+ short_name_cnt = var_get_short_name_cnt (var);
+ short_names = xnmalloc (short_name_cnt, sizeof *short_names);
+ for (i = 0; i < short_name_cnt; i++)
+ {
+ const char *s = var_get_short_name (var, i);
+ short_names[i] = s != NULL ? xstrdup (s) : NULL;
+ }
+
+ /* Set long name. */
+ dict_rename_var (dict, var, long_name);
+
+ /* Restore short names. */
+ for (i = 0; i < short_name_cnt; i++)
+ {
+ var_set_short_name (var, i, short_names[i]);
+ free (short_names[i]);
+ }
+ free (short_names);
+ }
+ close_text_record (r, text);
+ r->has_long_var_names = true;
+}
+
+/* Reads record type 7, subtype 14, which gives the real length
+ of each very long string. Rearranges DICT accordingly. */
+static void
+read_long_string_map (struct sfm_reader *r, size_t size, size_t count,
+ struct dictionary *dict)
+{
+ struct text_record *text;
+ struct variable *var;
+ char *length_s;
+
+ text = open_text_record (r, size * count);
+ while (read_variable_to_value_pair (r, dict, text, &var, &length_s))
+ {
+ size_t idx = var_get_dict_index (var);
+ long int length;
+ int segment_cnt;
+ int i;
+
+ /* Get length. */
+ length = strtol (length_s, NULL, 10);
+ if (length < 1 || length > MAX_STRING)
+ {
+ sys_warn (r, _("%s listed as string of invalid length %s "
+ "in very length string record."),
+ var_get_name (var), length_s);
+ continue;
+ }
+
+ /* Check segments. */
+ segment_cnt = sfm_width_to_segments (length);
+ if (segment_cnt == 1)
+ {
+ sys_warn (r, _("%s listed in very long string record with width %s, "
+ "which requires only one segment."),
+ var_get_name (var), length_s);
+ continue;
+ }
+ if (idx + segment_cnt > dict_get_var_cnt (dict))
+ sys_error (r, _("Very long string %s overflows dictionary."),
+ var_get_name (var));
+
+ /* Get the short names from the segments and check their
+ lengths. */
+ for (i = 0; i < segment_cnt; i++)
+ {
+ struct variable *seg = dict_get_var (dict, idx + i);
+ int alloc_width = sfm_segment_alloc_width (length, i);
+ int width = var_get_width (seg);
+
+ if (i > 0)
+ var_set_short_name (var, i, var_get_short_name (seg, 0));
+ if (ROUND_UP (width, 8) != ROUND_UP (alloc_width, 8))
+ sys_error (r, _("Very long string with width %ld has segment %d "
+ "of width %d (expected %d)"),
+ length, i, width, alloc_width);
+ }
+ dict_delete_consecutive_vars (dict, idx + 1, segment_cnt - 1);
+ var_set_width (var, length);
+ }
+ close_text_record (r, text);
+ dict_compact_values (dict);