Merge commit 'origin/data-encoding'
authorJohn Darrington <john@darrington.wattle.id.au>
Sat, 18 Jul 2009 11:20:38 +0000 (13:20 +0200)
committerJohn Darrington <john@darrington.wattle.id.au>
Sat, 18 Jul 2009 11:20:38 +0000 (13:20 +0200)
Conflicts:

src/language/dictionary/split-file.c

1  2 
src/data/sys-file-reader.c
src/data/value.h
src/language/data-io/data-parser.c
src/language/data-io/print.c
src/language/dictionary/split-file.c
src/language/stats/crosstabs.q
src/math/interaction.c
src/output/table.c
src/output/table.h

index cbbbdd153095ec710b216848c99c0486a4db6f74,0f583a12a5005fe360abd8662847039742b9afbb..9978d43a4e58d993649935a9d2580ce1cf4faa2b
@@@ -25,6 -25,7 +25,7 @@@
  #include <setjmp.h>
  #include <stdlib.h>
  
+ #include <libpspp/i18n.h>
  #include <libpspp/assertion.h>
  #include <libpspp/message.h>
  #include <libpspp/compiler.h>
@@@ -186,6 -187,62 +187,62 @@@ static void read_long_string_value_labe
                                           size_t size, size_t count,
                                           struct dictionary *);
  
+ /* Convert all the strings in DICT from the dict encoding to UTF8 */
+ static void
+ recode_strings (struct dictionary *dict)
+ {
+   int i;
+   const char *enc = dict_get_encoding (dict);
+   if ( NULL == enc)
+     enc = get_default_encoding ();
+   for (i = 0 ; i < dict_get_var_cnt (dict); ++i)
+     {
+       /* Convert the long variable name */
+       struct variable *var = dict_get_var (dict, i);
+       const char *native_name = var_get_name (var);
+       char *utf8_name = recode_string (UTF8, enc, native_name, -1);
+       if ( 0 != strcmp (utf8_name, native_name))
+       {
+         if ( NULL == dict_lookup_var (dict, utf8_name))
+           dict_rename_var (dict, var, utf8_name);
+         else
+           msg (MW,
+            _("Recoded variable name duplicates an existing `%s' within system file."), utf8_name);
+     }
+       free (utf8_name);
+       /* Convert the variable label */
+       if (var_has_label (var))
+       {
+         char *utf8_label = recode_string (UTF8, enc, var_get_label (var), -1);
+         var_set_label (var, utf8_label);
+         free (utf8_label);
+       }
+       if (var_has_value_labels (var))
+       {
+         const struct val_lab *vl = NULL;
+         const struct val_labs *vlabs = var_get_value_labels (var);
+         for (vl = val_labs_first (vlabs); vl != NULL; vl = val_labs_next (vlabs, vl))
+           {
+             const union value *val = val_lab_get_value (vl);
+             const char *label = val_lab_get_label (vl);
+             char *new_label = NULL;
+             new_label = recode_string (UTF8, enc, label, -1);
+             var_replace_value_label (var, val, new_label);
+             free (new_label);
+           }
+       }
+     }
+ }
  /* Opens the system file designated by file handle FH for
     reading.  Reads the system file's dictionary into *DICT.
     If INFO is non-null, then it receives additional info about the
@@@ -303,6 -360,8 +360,8 @@@ sfm_open_reader (struct file_handle *fh
        r->has_long_var_names = true;
      }
  
+   recode_strings (*dict);
    /* Read record 999 data, which is just filler. */
    read_int (r);
  
@@@ -518,7 -577,7 +577,7 @@@ read_variable_record (struct sfm_reade
  
    /* Create variable. */
    if (width < 0 || width > 255)
 -    sys_error (r, _("Bad variable width %d."), width);
 +    sys_error (r, _("Bad width %d for variable %s."), width, name);
    var = dict_create_var (dict, name, width);
    if (var == NULL)
      sys_error (r,
            value_set_missing (&value, mv_width);
            for (i = 0; i < missing_value_code; i++)
              {
-               char *s = value_str_rw (&value, mv_width);
+               uint8_t *s = value_str_rw (&value, mv_width);
                read_bytes (r, s, 8);
                mv_add_str (&mv, s);
              }
@@@ -922,16 -981,11 +981,16 @@@ read_machine_float_info (struct sfm_rea
                 size, count);
  
    if (sysmis != SYSMIS)
 -    sys_warn (r, _("File specifies unexpected value %g as SYSMIS."), sysmis);
 +    sys_warn (r, _("File specifies unexpected value %g as %s."),
 +              sysmis, "SYSMIS");
 +
    if (highest != HIGHEST)
 -    sys_warn (r, _("File specifies unexpected value %g as HIGHEST."), highest);
 +    sys_warn (r, _("File specifies unexpected value %g as %s."),
 +              highest, "HIGHEST");
 +
    if (lowest != LOWEST)
 -    sys_warn (r, _("File specifies unexpected value %g as LOWEST."), lowest);
 +    sys_warn (r, _("File specifies unexpected value %g as %s."),
 +              lowest, "LOWEST");
  }
  
  /* Read record type 7, subtype 11, which specifies how variables
@@@ -1138,7 -1192,7 +1197,7 @@@ read_value_labels (struct sfm_reader *r
  
    struct label
      {
-       char raw_value[8];        /* Value as uninterpreted bytes. */
+       uint8_t raw_value[8];        /* Value as uninterpreted bytes. */
        union value value;        /* Value. */
        char *label;              /* Null-terminated label string. */
      };
  
        value_init_pool (subpool, &label->value, max_width);
        if (var_is_alpha (var[0]))
-         buf_copy_rpad (value_str_rw (&label->value, max_width), max_width,
+         u8_buf_copy_rpad (value_str_rw (&label->value, max_width), max_width,
                         label->raw_value, sizeof label->raw_value, ' ');
        else
          label->value.f = float_get_double (r->float_format, label->raw_value);
@@@ -1416,7 -1470,7 +1475,7 @@@ read_long_string_value_labels (struct s
            /* Read value. */
            value_length = read_int (r);
            if (value_length == width)
-             read_string (r, value_str_rw (&value, width), width + 1);
+             read_bytes (r, value_str_rw (&value, width), width);
            else
              {
                sys_warn (r, _("Ignoring long string value %zu for variable %s, "
@@@ -1473,11 -1527,11 +1532,11 @@@ static void partial_record (struct sfm_
  static void read_error (struct casereader *, const struct sfm_reader *);
  
  static bool read_case_number (struct sfm_reader *, double *);
- static bool read_case_string (struct sfm_reader *, char *, size_t);
+ static bool read_case_string (struct sfm_reader *, uint8_t *, size_t);
  static int read_opcode (struct sfm_reader *);
  static bool read_compressed_number (struct sfm_reader *, double *);
- static bool read_compressed_string (struct sfm_reader *, char *);
- static bool read_whole_strings (struct sfm_reader *, char *, size_t);
+ static bool read_compressed_string (struct sfm_reader *, uint8_t *);
+ static bool read_whole_strings (struct sfm_reader *, uint8_t *, size_t);
  static bool skip_whole_strings (struct sfm_reader *, size_t);
  
  /* Reads and returns one case from READER's file.  Returns a null
@@@ -1512,7 -1566,7 +1571,7 @@@ sys_file_casereader_read (struct casere
          }
        else
          {
-           char *s = value_str_rw (v, sv->var_width);
+           uint8_t *s = value_str_rw (v, sv->var_width);
            if (!read_case_string (r, s + sv->offset, sv->segment_width))
              goto eof;
            if (!skip_whole_strings (r, ROUND_DOWN (sv->padding, 8)))
@@@ -1574,7 -1628,7 +1633,7 @@@ read_case_number (struct sfm_reader *r
     Returns true if successful, false if end of file is
     reached immediately. */
  static bool
- read_case_string (struct sfm_reader *r, char *s, size_t length)
+ read_case_string (struct sfm_reader *r, uint8_t *s, size_t length)
  {
    size_t whole = ROUND_DOWN (length, 8);
    size_t partial = length % 8;
  
    if (partial)
      {
-       char bounce[8];
+       uint8_t bounce[8];
        if (!read_whole_strings (r, bounce, sizeof bounce))
          {
            if (whole)
@@@ -1658,7 -1712,7 +1717,7 @@@ read_compressed_number (struct sfm_read
     Returns true if successful, false if end of file is
     reached immediately. */
  static bool
- read_compressed_string (struct sfm_reader *r, char *dst)
+ read_compressed_string (struct sfm_reader *r, uint8_t *dst)
  {
    switch (read_opcode (r))
      {
     Returns true if successful, false if end of file is
     reached immediately. */
  static bool
- read_whole_strings (struct sfm_reader *r, char *s, size_t length)
+ read_whole_strings (struct sfm_reader *r, uint8_t *s, size_t length)
  {
    assert (length % 8 == 0);
    if (!r->compressed)
  static bool
  skip_whole_strings (struct sfm_reader *r, size_t length)
  {
-   char buffer[1024];
+   uint8_t buffer[1024];
    assert (length < sizeof buffer);
    return read_whole_strings (r, buffer, length);
  }
diff --combined src/data/value.h
index 6955eea0bd358e144fc1bd4ff8ed9e615aa7b68f,84f08d87ce5da9241c07192fda84b45665eab365..f9782e2d867d47028f134c4b406dbfd53313d68c
@@@ -20,6 -20,7 +20,7 @@@
  #include <assert.h>
  #include <stdbool.h>
  #include <stdlib.h>
+ #include <stdint.h>
  #include <string.h>
  #include "xalloc.h"
  \f
@@@ -45,8 -46,8 +46,8 @@@
  union value
    {
      double f;
-     char short_string[MAX_SHORT_STRING];
-     char *long_string;
+     uint8_t short_string[MAX_SHORT_STRING];
+     uint8_t *long_string;
    };
  
  static inline void value_init (union value *, int width);
@@@ -55,17 -56,20 +56,17 @@@ static inline bool value_try_init (unio
  static inline void value_destroy (union value *, int width);
  
  static inline double value_num (const union value *);
- static inline const char *value_str (const union value *, int width);
- static inline char *value_str_rw (union value *, int width);
+ static inline const uint8_t *value_str (const union value *, int width);
+ static inline uint8_t *value_str_rw (union value *, int width);
  
 -int compare_values (const void *, const void *, const void *var);
 -unsigned hash_value (const void *, const void *var);
 -
  static inline void value_copy (union value *, const union value *, int width);
  void value_copy_rpad (union value *, int dst_width,
                        const union value *, int src_width,
                        char pad);
- void value_copy_str_rpad (union value *, int dst_width, const char *,
+ void value_copy_str_rpad (union value *, int dst_width, const uint8_t *,
                            char pad);
  void value_copy_buf_rpad (union value *dst, int dst_width,
-                           const char *src, size_t src_len, char pad);
+                           const uint8_t *src, size_t src_len, char pad);
  void value_set_missing (union value *, int width);
  int value_compare_3way (const union value *, const union value *, int width);
  bool value_equal (const union value *, const union value *, int width);
@@@ -147,7 -151,7 +148,7 @@@ value_num (const union value *v
     It is important that WIDTH be the actual value that was passed
     to value_init.  Passing, e.g., a smaller value because only
     that number of bytes will be accessed will not always work. */
- static inline const char *
+ static inline const uint8_t *
  value_str (const union value *v, int width)
  {
    assert (width > 0);
     It is important that WIDTH be the actual value that was passed
     to value_init.  Passing, e.g., a smaller value because only
     that number of bytes will be accessed will not always work. */
- static inline char *
+ static inline uint8_t *
  value_str_rw (union value *v, int width)
  {
    assert (width > 0);
index e7e92bf6d37395f239f2906912e7f835524898d7,8f189b1b212c6bccf497e28b68900612e6989149..6ec3d0d50bc94e258d44306d25d1157483f73c70
@@@ -41,6 -41,7 +41,7 @@@
  /* Data parser for textual data like that read by DATA LIST. */
  struct data_parser
    {
+     const struct dictionary *dict; /*Dictionary of destination */
      enum data_parser_type type; /* Type of data to parse. */
      int skip_records;           /* Records to skip before first real data. */
      casenumber max_cases;       /* Max number of cases to read. */
@@@ -79,7 -80,7 +80,7 @@@ static void set_any_sep (struct data_pa
  
  /* Creates and returns a new data parser. */
  struct data_parser *
- data_parser_create (void)
+ data_parser_create (const struct dictionary *dict)
  {
    struct data_parser *parser = xmalloc (sizeof *parser);
  
@@@ -91,6 -92,7 +92,7 @@@
    parser->fields = NULL;
    parser->field_cnt = 0;
    parser->field_allocated = 0;
+   parser->dict = dict;
  
    parser->span = true;
    parser->empty_line_has_field = false;
@@@ -505,7 -507,7 +507,7 @@@ static boo
  parse_fixed (const struct data_parser *parser, struct dfm_reader *reader,
               struct ccase *c)
  {
-   enum legacy_encoding encoding = dfm_reader_get_legacy_encoding (reader);
+   const char *encoding = dfm_reader_get_legacy_encoding (reader);
    struct field *f;
    int row;
  
                              f->format.w),
                   encoding, f->format.type, f->format.d,
                   f->first_column, f->first_column + f->format.w,
+                parser->dict,
                   case_data_rw_idx (c, f->case_idx),
                   fmt_var_width (&f->format));
  
@@@ -547,7 -550,7 +550,7 @@@ static boo
  parse_delimited_span (const struct data_parser *parser,
                        struct dfm_reader *reader, struct ccase *c)
  {
-   enum legacy_encoding encoding = dfm_reader_get_legacy_encoding (reader);
+   const char *encoding = dfm_reader_get_legacy_encoding (reader);
    struct string tmp = DS_EMPTY_INITIALIZER;
    struct field *f;
  
  
        data_in (s, encoding, f->format.type, 0,
                 first_column, last_column,
+              parser->dict,
                 case_data_rw_idx (c, f->case_idx),
                 fmt_var_width (&f->format));
      }
@@@ -588,7 -592,7 +592,7 @@@ static boo
  parse_delimited_no_span (const struct data_parser *parser,
                           struct dfm_reader *reader, struct ccase *c)
  {
-   enum legacy_encoding encoding = dfm_reader_get_legacy_encoding (reader);
+   const char *encoding = dfm_reader_get_legacy_encoding (reader);
    struct string tmp = DS_EMPTY_INITIALIZER;
    struct substring s;
    struct field *f;
  
        data_in (s, encoding, f->format.type, 0,
                 first_column, last_column,
+              parser->dict,
                 case_data_rw_idx (c, f->case_idx),
                 fmt_var_width (&f->format));
      }
@@@ -656,9 -661,9 +661,9 @@@ dump_fixed_table (const struct data_par
        int row = i + 1;
  
        tab_text (t, 0, row, TAB_LEFT, f->name);
 -      tab_text (t, 1, row, TAT_PRINTF, "%d", f->record);
 -      tab_text (t, 2, row, TAT_PRINTF, "%3d-%3d",
 -                f->first_column, f->first_column + f->format.w - 1);
 +      tab_text_format (t, 1, row, 0, "%d", f->record);
 +      tab_text_format (t, 2, row, 0, "%3d-%3d",
 +                       f->first_column, f->first_column + f->format.w - 1);
        tab_text (t, 3, row, TAB_LEFT | TAB_FIX,
                  fmt_to_string (&f->format, fmt_string));
      }
index 34042aa83022640997faf985d6e6b499bdca946a,c981bb1dcc2300f1053f741bc5dd9b93a7c8569e..e345477baf706f50add706bbac7e5f04b0d4057d
@@@ -32,6 -32,7 +32,7 @@@
  #include <language/lexer/lexer.h>
  #include <language/lexer/variable-parser.h>
  #include <libpspp/assertion.h>
+ #include <libpspp/i18n.h>
  #include <libpspp/compiler.h>
  #include <libpspp/ll.h>
  #include <libpspp/message.h>
@@@ -83,7 -84,7 +84,7 @@@ struct print_trn
      struct pool *pool;          /* Stores related data. */
      bool eject;                 /* Eject page before printing? */
      bool include_prefix;        /* Prefix lines with space? */
-     enum legacy_encoding encoding; /* Encoding to use for output. */
+     const char *encoding;       /* Encoding to use for output. */
      struct dfm_writer *writer;        /* Output file, NULL=listing file. */
      struct ll_list specs;       /* List of struct prt_out_specs. */
      size_t record_cnt;          /* Number of records to write. */
@@@ -413,9 -414,8 +414,9 @@@ dump_table (struct print_trns *trns, co
        switch (spec->type)
          {
          case PRT_LITERAL:
 -          tab_text (t, 0, row, TAB_LEFT | TAB_FIX | TAT_PRINTF, "\"%.*s\"",
 -                    (int) ds_length (&spec->string), ds_data (&spec->string));
 +          tab_text_format (t, 0, row, TAB_LEFT | TAB_FIX, "\"%.*s\"",
 +                           (int) ds_length (&spec->string),
 +                           ds_data (&spec->string));
            width = ds_length (&spec->string);
            break;
          case PRT_VAR:
          default:
            NOT_REACHED ();
        }
 -      tab_text (t, 1, row, TAT_PRINTF, "%d", spec->record);
 -      tab_text (t, 2, row, TAT_PRINTF, "%3d-%3d",
 -                spec->first_column, spec->first_column + width - 1);
 +      tab_text_format (t, 1, row, 0, "%d", spec->record);
 +      tab_text_format (t, 2, row, 0, "%3d-%3d",
 +                       spec->first_column, spec->first_column + width - 1);
        row++;
      }
  
@@@ -480,12 -480,13 +481,13 @@@ print_trns_proc (void *trns_, struct cc
        else
          {
            ds_put_substring (&trns->line, ds_ss (&spec->string));
-           if (trns->encoding != LEGACY_NATIVE)
+           if (0 != strcmp (trns->encoding, LEGACY_NATIVE))
              {
                size_t length = ds_length (&spec->string);
                char *data = ss_data (ds_tail (&trns->line, length));
-               legacy_recode (LEGACY_NATIVE, data,
-                              trns->encoding, data, length);
+             char *s = recode_string (trns->encoding, LEGACY_NATIVE, data, length);
+             memcpy (data, s, length);
+             free (s);
              }
          }
      }
index d2b79c63dd6ce2a6c65cac7153ad468e52b6877f,0024787fdd203e022b9e6b2911e3333c90ab6f76..5d2b42d758eed2d1e305d05046d59cf0aa290877
@@@ -88,17 -88,17 +88,17 @@@ output_split_file_values (const struct 
    for (i = 0; i < split_cnt; i++)
      {
        const struct variable *v = split[i];
-       char temp_buf[80];
+       char *s;
        const char *val_lab;
        const struct fmt_spec *print = var_get_print_format (v);
  
 -      tab_text (t, 0, i + 1, TAB_LEFT | TAT_PRINTF, "%s", var_get_name (v));
 +      tab_text_format (t, 0, i + 1, TAB_LEFT, "%s", var_get_name (v));
  
-       data_out (case_data (c, v), print, temp_buf);
-       temp_buf[print->w] = 0;
-       tab_text_format (t, 1, i + 1, 0, "%.*s", print->w, temp_buf);
+       s = data_out (case_data (c, v), dict_get_encoding (dict), print);
++      tab_text_format (t, 1, i + 1, 0, "%.*s", print->w, s);
  
 -      tab_text (t, 1, i + 1, TAT_PRINTF, "%.*s", print->w, s);
+       free (s);
+       
        val_lab = var_lookup_value_label (v, case_data (c, v));
        if (val_lab)
        tab_text (t, 2, i + 1, TAB_LEFT, val_lab);
index 8347bb5f09f81637bbceba245463c0397388bb78,e3f54c817630178cdca465f50d40f8baf9693b64..99fa41d9c056a039ef9d92fdc9090758539c9d15
@@@ -177,6 -177,7 +177,7 @@@ get_var_range (const struct variable *v
  
  struct crosstabs_proc
    {
+     const struct dictionary *dict;
      enum { INTEGER, GENERAL } mode;
      enum mv_class exclude;
      bool pivot;
@@@ -204,6 -205,7 +205,7 @@@ static voi
  init_proc (struct crosstabs_proc *proc, struct dataset *ds)
  {
    const struct variable *wv = dict_get_weight (dataset_dict (ds));
+   proc->dict = dataset_dict (ds);
    proc->bad_warn = true;
    proc->variables = NULL;
    proc->n_variables = 0;
@@@ -881,8 -883,8 +883,8 @@@ make_summary_table (struct crosstabs_pr
          {
            tab_double (summary, i * 2 + 1, 0, TAB_RIGHT, n[i],
                        &proc->weight_format);
 -          tab_text (summary, i * 2 + 2, 0, TAB_RIGHT | TAT_PRINTF, "%.1f%%",
 -                    n[i] / n[2] * 100.);
 +          tab_text_format (summary, i * 2 + 2, 0, TAB_RIGHT, "%.1f%%",
 +                           n[i] / n[2] * 100.);
          }
  
        tab_next_row (summary);
@@@ -1184,14 -1186,16 +1186,16 @@@ create_crosstab_table (struct crosstabs
      {
        const struct variable *var = pt->const_vars[i];
        size_t ofs;
+       char *s = NULL;
  
        ds_put_format (&title, ", %s=", var_get_name (var));
  
        /* Insert the formatted value of the variable, then trim
           leading spaces in what was just inserted. */
        ofs = ds_length (&title);
-       data_out (&pt->const_values[i], var_get_print_format (var),
-                 ds_put_uninit (&title, var_get_width (var)));
+       s = data_out (&pt->const_values[i], dict_get_encoding (proc->dict), var_get_print_format (var));
+       ds_put_cstr (&title, s);
+       free (s);
        ds_remove (&title, ofs, ss_cspan (ds_substr (&title, ofs, SIZE_MAX),
                                          ss_cstr (" ")));
      }
@@@ -1233,9 -1237,9 +1237,9 @@@ create_chisq_table (struct pivot_table 
    tab_text (chisq, 3, 0, TAB_RIGHT | TAT_TITLE,
              _("Asymp. Sig. (2-sided)"));
    tab_text (chisq, 4, 0, TAB_RIGHT | TAT_TITLE,
 -            _("Exact. Sig. (2-sided)"));
 +            _("Exact Sig. (2-sided)"));
    tab_text (chisq, 5, 0, TAB_RIGHT | TAT_TITLE,
 -            _("Exact. Sig. (1-sided)"));
 +            _("Exact Sig. (1-sided)"));
    tab_offset (chisq, 0, 1);
  
    return chisq;
@@@ -1276,8 -1280,8 +1280,8 @@@ create_risk_table (struct pivot_table *
    tab_title (risk, _("Risk estimate."));
  
    tab_offset (risk, pt->n_vars - 2, 0);
 -  tab_joint_text (risk, 2, 0, 3, 0, TAB_CENTER | TAT_TITLE | TAT_PRINTF,
 -                  _("95%% Confidence Interval"));
 +  tab_joint_text_format (risk, 2, 0, 3, 0, TAB_CENTER | TAT_TITLE,
 +                         _("95%% Confidence Interval"));
    tab_text (risk, 0, 1, TAB_LEFT | TAT_TITLE, _("Statistic"));
    tab_text (risk, 1, 1, TAB_RIGHT | TAT_TITLE, _("Value"));
    tab_text (risk, 2, 1, TAB_RIGHT | TAT_TITLE, _("Lower"));
@@@ -1519,9 -1523,8 +1523,8 @@@ table_value_missing (struct crosstabs_p
        return;
      }
  
-   s.string = tab_alloc (table, print->w);
-   data_out (v, print, s.string);
-   s.length = print->w;
+   s = ss_cstr (data_out_pool (v, dict_get_encoding (proc->dict), print,
+                            table->container));
    if (proc->exclude == MV_NEVER && var_is_num_missing (var, v->f, MV_USER))
      s.string[s.length++] = 'M';
    while (s.length && *s.string == ' ')
@@@ -1554,16 -1557,15 +1557,15 @@@ display_dimensions (struct crosstabs_pr
     additionally suffixed with a letter `M'. */
  static void
  format_cell_entry (struct tab_table *table, int c, int r, double value,
-                    char suffix, bool mark_missing)
+                    char suffix, bool mark_missing, const struct dictionary *dict)
  {
    const struct fmt_spec f = {FMT_F, 10, 1};
    union value v;
    struct substring s;
  
-   s.length = 10;
-   s.string = tab_alloc (table, 16);
    v.f = value;
-   data_out (&v, &f, s.string);
+   s = ss_cstr (data_out_pool (&v, dict_get_encoding (dict), &f, table->container));
    while (*s.string == ' ')
      {
        s.length--;
@@@ -1649,7 -1651,7 +1651,7 @@@ display_crosstabulation (struct crossta
                  default:
                    NOT_REACHED ();
                  }
-               format_cell_entry (table, c, i, v, suffix, mark_missing);
+               format_cell_entry (table, c, i, v, suffix, mark_missing, proc->dict);
              }
  
            mp++;
                NOT_REACHED ();
              }
  
-           format_cell_entry (table, pt->n_cols, 0, v, suffix, mark_missing);
+           format_cell_entry (table, pt->n_cols, 0, v, suffix, mark_missing, proc->dict);
            tab_next_row (table);
          }
      }
                NOT_REACHED ();
              }
  
-           format_cell_entry (table, c, i, v, suffix, mark_missing);
+           format_cell_entry (table, c, i, v, suffix, mark_missing, proc->dict);
          }
        last_row = i;
      }
@@@ -2060,8 -2062,8 +2062,8 @@@ display_directional (struct crosstabs_p
                  else
                    string = var_get_name (pt->vars[1]);
  
 -                tab_text (direct, j, 0, TAB_LEFT | TAT_PRINTF,
 -                          gettext (stats_names[j][k]), string);
 +                tab_text_format (direct, j, 0, TAB_LEFT,
 +                                   gettext (stats_names[j][k]), string);
                }
            }
        }
diff --combined src/math/interaction.c
index 444edbfa308723a5638bb726a84fdc8e59ff729e,cd151097d6242408c93a31dc877e4f6faffc8345..7911c8e74d3166bb48e3f0ab4dde771fbd16652f
@@@ -149,7 -149,7 +149,7 @@@ interaction_value_create (const struct 
    if (var != NULL)
      {
        int val_width = 1;
-       char *val;
+       uint8_t *val;
  
        result = xmalloc (sizeof (*result));
        result->intr = var;
@@@ -246,10 -246,12 +246,10 @@@ interaction_case_data (const struct cca
  {
    size_t i;
    size_t n_vars;
 -  const struct variable *intr;
    const struct variable *member;
    const union value **vals = NULL;
  
    n_vars = interaction_get_n_vars (iv);
 -  intr = interaction_get_variable (iv);
    vals = xnmalloc (n_vars, sizeof (*vals));
  
    for (i = 0; i < n_vars; i++)
diff --combined src/output/table.c
index 5686e2bf18dd128d4d3d11bdc2572a5e48da5053,04181e44f709b9ca1d2fcb9cab3abcce207b9595..72edf17f08b7f6e9e0dec91ad8b56f5b9e87b88b
@@@ -29,6 -29,7 +29,7 @@@
  #include <data/data-out.h>
  #include <data/format.h>
  #include <data/value.h>
+ #include <data/dictionary.h>
  #include <libpspp/assertion.h>
  #include <libpspp/compiler.h>
  #include <libpspp/misc.h>
@@@ -372,6 -373,18 +373,6 @@@ tab_box (struct tab_table *t, int f_h, 
      }
  }
  
 -/* Formats text TEXT and arguments ARGS as indicated in OPT in
 -   TABLE's pool and returns the resultant string. */
 -static struct substring
 -text_format (struct tab_table *table, int opt, const char *text, va_list args)
 -{
 -  assert (table != NULL && text != NULL);
 -
 -  return ss_cstr (opt & TAT_PRINTF
 -                  ? pool_vasprintf (table->container, text, args)
 -                  : pool_strdup (table->container, text));
 -}
 -
  /* Set the title of table T to TITLE, which is formatted as if
     passed to printf(). */
  void
@@@ -506,7 -519,8 +507,8 @@@ tab_natural_dimensions (struct tab_tabl
     from V, displayed with format spec F. */
  void
  tab_value (struct tab_table *table, int c, int r, unsigned char opt,
-          const union value *v, const struct fmt_spec *f)
+          const union value *v, const struct dictionary *dict, 
+          const struct fmt_spec *f)
  {
    char *contents;
  
      }
  #endif
  
-   contents = pool_alloc (table->container, f->w);
-   table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
-   table->ct[c + r * table->cf] = opt;
+   contents = data_out_pool (v, dict_get_encoding (dict), f, table->container);
  
-   data_out (v, f, contents);
+   table->cc[c + r * table->cf] = ss_cstr (contents);
+   table->ct[c + r * table->cf] = opt;
  }
  
  /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
@@@ -538,8 -551,7 +539,7 @@@ voi
  tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
           double val, int w, int d)
  {
-   char *contents;
-   char buf[40], *cp;
+   char *s, *cp;
  
    struct fmt_spec f;
    union value double_value;
  #endif
  
    double_value.f = val;
-   data_out (&double_value, &f, buf);
+   s = data_out_pool (&double_value, LEGACY_NATIVE, &f, table->container);
  
-   cp = buf;
-   while (isspace ((unsigned char) *cp) && cp < &buf[w])
+   cp = s;
+   while (isspace ((unsigned char) *cp) && cp < &s[w])
      cp++;
-   f.w = w - (cp - buf);
+   f.w = w - (cp - s);
  
-   contents = pool_alloc (table->container, f.w);
-   table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
+   table->cc[c + r * table->cf] = ss_buffer (cp, f.w);
    table->ct[c + r * table->cf] = opt;
-   memcpy (contents, cp, f.w);
  }
  
  /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
@@@ -589,11 -599,8 +587,8 @@@ voi
  tab_double (struct tab_table *table, int c, int r, unsigned char opt,
           double val, const struct fmt_spec *fmt)
  {
-   int w;
-   char *contents;
-   char buf[40], *cp;
-   union value double_value;
+   struct substring ss;
+   union value double_value ;
  
    assert (table != NULL);
  
  #endif
  
    double_value.f = val;
-   data_out (&double_value, fmt, buf);
+   ss = ss_cstr (data_out_pool (&double_value, LEGACY_NATIVE, fmt, table->container));
  
-   cp = buf;
-   while (isspace ((unsigned char) *cp) && cp < &buf[fmt->w])
-     cp++;
-   w = fmt->w - (cp - buf);
+   ss_ltrim (&ss, ss_cstr (" "));
  
-   contents = pool_alloc (table->container, w);
-   table->cc[c + r * table->cf] = ss_buffer (contents, w);
+   table->cc[c + r * table->cf] = ss;
    table->ct[c + r * table->cf] = opt;
-   memcpy (contents, cp, w);
  }
  
  
 -/* Sets cell (C,R) in TABLE, with options OPT, to have text value
 -   TEXT. */
 -void
 -tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
 +static void
 +do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text)
  {
 -  va_list args;
 -
 -  assert (table != NULL && text != NULL);
 -
 -  assert (c >= 0 );
 -  assert (r >= 0 );
 +  assert (c >= 0);
 +  assert (r >= 0);
    assert (c < table->nc);
    assert (r < table->nr);
  
 -
  #if DEBUGGING
    if (c + table->col_ofs < 0 || r + table->row_ofs < 0
        || c + table->col_ofs >= table->nc
      }
  #endif
  
 -  va_start (args, text);
 -  table->cc[c + r * table->cf] = text_format (table, opt, text, args);
 +  table->cc[c + r * table->cf] = ss_cstr (text);
    table->ct[c + r * table->cf] = opt;
 -  va_end (args);
  }
  
 -/* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
 -   options OPT to have text value TEXT. */
 +/* Sets cell (C,R) in TABLE, with options OPT, to have text value
 +   TEXT. */
  void
 -tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
 -              unsigned opt, const char *text, ...)
 +tab_text (struct tab_table *table, int c, int r, unsigned opt,
 +          const char *text)
  {
 -  struct tab_joined_cell *j;
 +  do_tab_text (table, c, r, opt, pool_strdup (table->container, text));
 +}
  
 -  assert (table != NULL && text != NULL);
 +/* Sets cell (C,R) in TABLE, with options OPT, to have text value
 +   FORMAT, which is formatted as if passed to printf. */
 +void
 +tab_text_format (struct tab_table *table, int c, int r, unsigned opt,
 +                 const char *format, ...)
 +{
 +  va_list args;
 +
 +  va_start (args, format);
 +  do_tab_text (table, c, r, opt,
 +               pool_vasprintf (table->container, format, args));
 +  va_end (args);
 +}
 +
 +static void
 +do_tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
 +                   unsigned opt, char *text)
 +{
 +  struct tab_joined_cell *j;
  
    assert (x1 + table->col_ofs >= 0);
    assert (y1 + table->row_ofs >= 0);
    j->y1 = y1 + table->row_ofs;
    j->x2 = ++x2 + table->col_ofs;
    j->y2 = ++y2 + table->row_ofs;
 -
 -  {
 -    va_list args;
 -
 -    va_start (args, text);
 -    j->contents = text_format (table, opt, text, args);
 -    va_end (args);
 -  }
 +  j->contents = ss_cstr (text);
  
    opt |= TAB_JOIN;
  
    }
  }
  
 +/* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
 +   options OPT to have text value TEXT. */
 +void
 +tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
 +                unsigned opt, const char *text)
 +{
 +  do_tab_joint_text (table, x1, y1, x2, y2, opt,
 +                     pool_strdup (table->container, text));
 +}
 +
 +/* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them
 +   with options OPT to have text value FORMAT, which is formatted
 +   as if passed to printf. */
 +void
 +tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2, int y2,
 +                       unsigned opt, const char *format, ...)
 +{
 +  va_list args;
 +
 +  va_start (args, format);
 +  do_tab_joint_text (table, x1, y1, x2, y2, opt,
 +                     pool_vasprintf (table->container, format, args));
 +  va_end (args);
 +}
 +
  /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
  void
  tab_raw (struct tab_table *table, int c, int r, unsigned opt,
@@@ -820,41 -794,30 +810,41 @@@ wrap_dim (struct tab_table *t, struct o
    t->h[0] = tab_natural_height (t, d, 0);
  }
  
 -/* Outputs text BUF as a table with a single cell having cell options
 +static void
 +do_tab_output_text (struct tab_table *t, int options, char *text)
 +{
 +  do_tab_text (t, 0, 0, options, text);
 +  tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
 +  tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL);
 +  tab_submit (t);
 +}
 +
 +/* Outputs TEXT as a table with a single cell having cell options
     OPTIONS, which is a combination of the TAB_* and TAT_*
 -   constants. */
 +   constants.  */
  void
 -tab_output_text (int options, const char *buf, ...)
 +tab_output_text (int options, const char *text)
  {
 -  struct tab_table *t = tab_create (1, 1, 0);
 -  char *tmp_buf = NULL;
 -
 -  if (options & TAT_PRINTF)
 -    {
 -      va_list args;
 +  struct tab_table *table = tab_create (1, 1, 0);
 +  do_tab_output_text (table, options, pool_strdup (table->container, text));
 +}
  
 -      va_start (args, buf);
 -      buf = tmp_buf = xvasprintf (buf, args);
 -      va_end (args);
 -    }
 +/* Outputs FORMAT as a table with a single cell having cell
 +   options OPTIONS, which is a combination of the TAB_* and TAT_*
 +   constants.  FORMAT is formatted as if it was passed through
 +   printf. */
 +void
 +tab_output_text_format (int options, const char *format, ...)
 +{
 +  struct tab_table *table;
 +  va_list args;
  
 -  tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
 -  tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
 -  tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL);
 -  tab_submit (t);
 +  table = tab_create (1, 1, 0);
  
 -  free (tmp_buf);
 +  va_start (args, format);
 +  do_tab_output_text (table, options,
 +                      pool_vasprintf (table->container, format, args));
 +  va_end (args);
  }
  
  /* Set table flags to FLAGS. */
diff --combined src/output/table.h
index 21d3b7bfd28f16cd9c295a7ff8fa7f5134cef864,e138361343ba3846fe6de8ad1b63de59d3f85bc0..1748c24c855e71ec8c0212dac76d00662db76f67
@@@ -147,15 -147,18 +147,17 @@@ void tab_box (struct tab_table *, int f
  enum
    {
      TAT_NONE = 0,             /* No options. */
 -    TAT_PRINTF = 0x0100,      /* Format the text string with sprintf. */
      TAT_TITLE = 0x0200 | TAB_EMPH, /* Title attributes. */
      TAT_NOWRAP = 0x0800         /* No text wrap (tab_output_text() only). */
    };
  
  /* Cells. */
  struct fmt_spec;
+ struct dictionary;
  union value;
  void tab_value (struct tab_table *, int c, int r, unsigned char opt,
-               const union value *, const struct fmt_spec *);
+               const union value *, const struct dictionary *dict,
+               const struct fmt_spec *);
  
  void tab_fixed (struct tab_table *, int c, int r, unsigned char opt,
                double v, int w, int d);
  void tab_double (struct tab_table *, int c, int r, unsigned char opt,
                double v, const struct fmt_spec *);
  
 -void tab_text (struct tab_table *, int c, int r, unsigned opt,
 -             const char *, ...)
 +void tab_text (struct tab_table *, int c, int r, unsigned opt, const char *);
 +void tab_text_format (struct tab_table *, int c, int r, unsigned opt,
 +                      const char *, ...)
       PRINTF_FORMAT (5, 6);
 +
  void tab_joint_text (struct tab_table *, int x1, int y1, int x2, int y2,
 -                   unsigned opt, const char *, ...)
 +                   unsigned opt, const char *);
 +void tab_joint_text_format (struct tab_table *, int x1, int y1, int x2, int y2,
 +                            unsigned opt, const char *, ...)
       PRINTF_FORMAT (7, 8);
  
  /* Cell low-level access. */
@@@ -188,8 -187,7 +190,8 @@@ void tab_next_row (struct tab_table *)
  #define tab_col(TABLE) ((TABLE)->col_ofs)
  
  /* Simple output. */
 -void tab_output_text (int options, const char *string, ...)
 +void tab_output_text (int options, const char *string);
 +void tab_output_text_format (int options, const char *, ...)
       PRINTF_FORMAT (2, 3);
  
  /* Embedding the command name in the output. */