Add support for reading SPSS/PC+ system files.
[pspp] / src / language / dictionary / sys-file-info.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <ctype.h>
20 #include <errno.h>
21 #include <float.h>
22 #include <stdlib.h>
23
24 #include "data/any-reader.h"
25 #include "data/attributes.h"
26 #include "data/casereader.h"
27 #include "data/dataset.h"
28 #include "data/dictionary.h"
29 #include "data/file-handle-def.h"
30 #include "data/format.h"
31 #include "data/missing-values.h"
32 #include "data/value-labels.h"
33 #include "data/variable.h"
34 #include "data/vector.h"
35 #include "language/command.h"
36 #include "language/data-io/file-handle.h"
37 #include "language/lexer/lexer.h"
38 #include "language/lexer/variable-parser.h"
39 #include "libpspp/array.h"
40 #include "libpspp/hash-functions.h"
41 #include "libpspp/i18n.h"
42 #include "libpspp/message.h"
43 #include "libpspp/misc.h"
44 #include "libpspp/pool.h"
45 #include "libpspp/string-array.h"
46 #include "output/tab.h"
47 #include "output/text-item.h"
48 #include "output/table-item.h"
49
50 #include "gl/localcharset.h"
51 #include "gl/intprops.h"
52 #include "gl/minmax.h"
53 #include "gl/xalloc.h"
54
55 #include "gettext.h"
56 #define _(msgid) gettext (msgid)
57
58 /* Information to include in displaying a dictionary. */
59 enum 
60   {
61     DF_DICT_INDEX       = 1 << 0,
62     DF_FORMATS          = 1 << 1,
63     DF_VALUE_LABELS     = 1 << 2,
64     DF_VARIABLE_LABELS  = 1 << 3,
65     DF_MISSING_VALUES   = 1 << 4,
66     DF_AT_ATTRIBUTES    = 1 << 5, /* Attributes whose names begin with @. */
67     DF_ATTRIBUTES       = 1 << 6, /* All other attributes. */
68     DF_MEASURE          = 1 << 7,
69     DF_ROLE             = 1 << 8,
70     DF_ALIGNMENT        = 1 << 9,
71     DF_WIDTH            = 1 << 10,
72     DF_ALL              = (1 << 11) - 1
73   };
74
75 static unsigned int dict_display_mask (const struct dictionary *);
76
77 static struct table *describe_variable (const struct variable *v, int flags);
78
79 static void report_encodings (const struct file_handle *, struct pool *,
80                               char **titles, bool *ids,
81                               char **strings, size_t n_strings);
82
83 /* SYSFILE INFO utility. */
84 int
85 cmd_sysfile_info (struct lexer *lexer, struct dataset *ds UNUSED)
86 {
87   struct any_reader *any_reader;
88   struct file_handle *h;
89   struct dictionary *d;
90   struct tab_table *t;
91   struct casereader *reader;
92   struct any_read_info info;
93   char *encoding;
94   struct table *table;
95   int r, i;
96
97   h = NULL;
98   encoding = NULL;
99   for (;;)
100     {
101       lex_match (lexer, T_SLASH);
102
103       if (lex_match_id (lexer, "FILE") || lex_is_string (lexer))
104         {
105           lex_match (lexer, T_EQUALS);
106
107           fh_unref (h);
108           h = fh_parse (lexer, FH_REF_FILE, NULL);
109           if (h == NULL)
110             goto error;
111         }
112       else if (lex_match_id (lexer, "ENCODING"))
113         {
114           lex_match (lexer, T_EQUALS);
115
116           if (!lex_force_string (lexer))
117             goto error;
118
119           free (encoding);
120           encoding = ss_xstrdup (lex_tokss (lexer));
121
122           lex_get (lexer);
123         }
124       else
125         break;
126     }
127
128   if (h == NULL)
129     {
130       lex_sbc_missing ("FILE");
131       goto error;
132     }
133
134   any_reader = any_reader_open (h);
135   if (!any_reader)
136     return CMD_FAILURE;
137
138   if (encoding && !strcasecmp (encoding, "detect"))
139     {
140       char **titles, **strings;
141       struct pool *pool;
142       size_t n_strings;
143       bool *ids;
144
145       pool = pool_create ();
146       n_strings = any_reader_get_strings (any_reader, pool,
147                                           &titles, &ids, &strings);
148       any_reader_close (any_reader);
149
150       report_encodings (h, pool, titles, ids, strings, n_strings);
151       fh_unref (h);
152       pool_destroy (pool);
153
154       return CMD_SUCCESS;
155     }
156
157   reader = any_reader_decode (any_reader, encoding, &d, &info);
158   if (!reader)
159     goto error;
160   casereader_destroy (reader);
161
162   t = tab_create (2, 11 + (info.product_ext != NULL));
163   r = 0;
164   tab_vline (t, TAL_GAP, 1, 0, 8);
165
166   tab_text (t, 0, r, TAB_LEFT, _("File:"));
167   tab_text (t, 1, r++, TAB_LEFT, fh_get_file_name (h));
168
169   tab_text (t, 0, r, TAB_LEFT, _("Label:"));
170   {
171     const char *label = dict_get_label (d);
172     if (label == NULL)
173       label = _("No label.");
174     tab_text (t, 1, r++, TAB_LEFT, label);
175   }
176
177   tab_text (t, 0, r, TAB_LEFT, _("Created:"));
178   tab_text_format (t, 1, r++, TAB_LEFT, "%s %s by %s",
179                    info.creation_date, info.creation_time, info.product);
180
181   if (info.product_ext)
182     {
183       tab_text (t, 0, r, TAB_LEFT, _("Product:"));
184       tab_text (t, 1, r++, TAB_LEFT, info.product_ext);
185     }
186
187   tab_text (t, 0, r, TAB_LEFT, _("Integer Format:"));
188   tab_text (t, 1, r++, TAB_LEFT,
189             info.integer_format == INTEGER_MSB_FIRST ? _("Big Endian")
190             : info.integer_format == INTEGER_LSB_FIRST ? _("Little Endian")
191             : _("Unknown"));
192
193   tab_text (t, 0, r, TAB_LEFT, _("Real Format:"));
194   tab_text (t, 1, r++, TAB_LEFT,
195             info.float_format == FLOAT_IEEE_DOUBLE_LE ? _("IEEE 754 LE.")
196             : info.float_format == FLOAT_IEEE_DOUBLE_BE ? _("IEEE 754 BE.")
197             : info.float_format == FLOAT_VAX_D ? _("VAX D.")
198             : info.float_format == FLOAT_VAX_G ? _("VAX G.")
199             : info.float_format == FLOAT_Z_LONG ? _("IBM 390 Hex Long.")
200             : _("Unknown"));
201
202   tab_text (t, 0, r, TAB_LEFT, _("Variables:"));
203   tab_text_format (t, 1, r++, TAB_LEFT, "%zu", dict_get_var_cnt (d));
204
205   tab_text (t, 0, r, TAB_LEFT, _("Cases:"));
206   if (info.case_cnt == -1)
207     tab_text (t, 1, r, TAB_LEFT, _("Unknown"));
208   else
209     tab_text_format (t, 1, r, TAB_LEFT, "%ld", (long int) info.case_cnt);
210   r++;
211
212   tab_text (t, 0, r, TAB_LEFT, _("Type:"));
213   tab_text (t, 1, r++, TAB_LEFT, gettext (info.klass->name));
214
215   tab_text (t, 0, r, TAB_LEFT, _("Weight:"));
216   {
217     struct variable *weight_var = dict_get_weight (d);
218     tab_text (t, 1, r++, TAB_LEFT,
219               (weight_var != NULL
220                ? var_get_name (weight_var) : _("Not weighted.")));
221   }
222
223   tab_text (t, 0, r, TAB_LEFT, _("Compression:"));
224   tab_text_format (t, 1, r++, TAB_LEFT,
225                    info.compression == ANY_COMP_NONE ? _("None")
226                    : info.compression == ANY_COMP_SIMPLE ? "SAV"
227                    : "ZSAV");
228
229   tab_text (t, 0, r, TAB_LEFT, _("Encoding:"));
230   tab_text (t, 1, r++, TAB_LEFT, dict_get_encoding (d));
231
232   tab_submit (t);
233
234   t = tab_create (3, 1);
235   tab_headers (t, 0, 0, 1, 0);
236   tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
237   tab_text (t, 1, 0, TAB_LEFT | TAT_TITLE, _("Description"));
238   tab_text (t, 2, 0, TAB_LEFT | TAT_TITLE, _("Position"));
239   tab_hline (t, TAL_2, 0, 2, 1);
240
241   table = &t->table;
242   for (i = 0; i < dict_get_var_cnt (d); i++)
243     table = table_vpaste (table,
244                           describe_variable (dict_get_var (d, i),
245                                              DF_ALL & ~DF_AT_ATTRIBUTES));
246
247   table_item_submit (table_item_create (table, NULL /* XXX */, NULL));
248
249   dict_destroy (d);
250
251   fh_unref (h);
252   any_read_info_destroy (&info);
253   return CMD_SUCCESS;
254
255 error:
256   fh_unref (h);
257   free (encoding);
258   return CMD_FAILURE;
259 }
260 \f
261 /* DISPLAY utility. */
262
263 static void display_macros (void);
264 static void display_documents (const struct dictionary *dict);
265 static void display_variables (const struct variable **, size_t, int);
266 static void display_vectors (const struct dictionary *dict, int sorted);
267 static void display_data_file_attributes (struct attrset *, int flags);
268
269 int
270 cmd_display (struct lexer *lexer, struct dataset *ds)
271 {
272   /* Whether to sort the list of variables alphabetically. */
273   int sorted;
274
275   /* Variables to display. */
276   size_t n;
277   const struct variable **vl;
278
279   if (lex_match_id (lexer, "MACROS"))
280     display_macros ();
281   else if (lex_match_id (lexer, "DOCUMENTS"))
282     display_documents (dataset_dict (ds));
283   else if (lex_match_id (lexer, "FILE"))
284     {
285       if (!lex_force_match_id (lexer, "LABEL"))
286         return CMD_FAILURE;
287       if (dict_get_label (dataset_dict (ds)) == NULL)
288         tab_output_text (TAB_LEFT,
289                          _("The active dataset does not have a file label."));
290       else
291         tab_output_text_format (TAB_LEFT, _("File label: %s"),
292                                 dict_get_label (dataset_dict (ds)));
293     }
294   else
295     {
296       int flags;
297
298       sorted = lex_match_id (lexer, "SORTED");
299
300       if (lex_match_id (lexer, "VECTORS"))
301         {
302           display_vectors (dataset_dict(ds), sorted);
303           return CMD_SUCCESS;
304         }
305       else if (lex_match_id (lexer, "SCRATCH")) 
306         {
307           dict_get_vars (dataset_dict (ds), &vl, &n, DC_ORDINARY);
308           flags = 0;
309         }
310       else 
311         {
312           struct subcommand 
313             {
314               const char *name;
315               int flags;
316             };
317           static const struct subcommand subcommands[] = 
318             {
319               {"@ATTRIBUTES", DF_ATTRIBUTES | DF_AT_ATTRIBUTES},
320               {"ATTRIBUTES", DF_ATTRIBUTES},
321               {"DICTIONARY", DF_ALL & ~DF_AT_ATTRIBUTES},
322               {"INDEX", DF_DICT_INDEX},
323               {"LABELS", DF_DICT_INDEX | DF_VARIABLE_LABELS},
324               {"NAMES", 0},
325               {"VARIABLES",
326                DF_DICT_INDEX | DF_FORMATS | DF_MISSING_VALUES
327                | DF_MEASURE | DF_ROLE | DF_ALIGNMENT | DF_WIDTH},
328               {NULL, 0},
329             };
330           const struct subcommand *sbc;
331           struct dictionary *dict = dataset_dict (ds);
332
333           flags = 0;
334           for (sbc = subcommands; sbc->name != NULL; sbc++)
335             if (lex_match_id (lexer, sbc->name))
336               {
337                 flags = sbc->flags & dict_display_mask (dict);
338                 break;
339               }
340
341           lex_match (lexer, T_SLASH);
342           lex_match_id (lexer, "VARIABLES");
343           lex_match (lexer, T_EQUALS);
344
345           if (lex_token (lexer) != T_ENDCMD)
346             {
347               if (!parse_variables_const (lexer, dict, &vl, &n, PV_NONE))
348                 {
349                   free (vl);
350                   return CMD_FAILURE;
351                 }
352             }
353           else
354             dict_get_vars (dict, &vl, &n, 0);
355         }
356
357       if (n > 0) 
358         {
359           sort (vl, n, sizeof *vl,
360                 (sorted
361                  ? compare_var_ptrs_by_name
362                  : compare_var_ptrs_by_dict_index), NULL);
363           display_variables (vl, n, flags);
364         }
365       else
366         msg (SW, _("No variables to display."));
367       free (vl);
368
369       if (flags & (DF_ATTRIBUTES | DF_AT_ATTRIBUTES))
370         display_data_file_attributes (dict_get_attributes (dataset_dict (ds)),
371                                       flags);
372     }
373
374   return CMD_SUCCESS;
375 }
376
377 static void
378 display_macros (void)
379 {
380   tab_output_text (TAB_LEFT, _("Macros not supported."));
381 }
382
383 static void
384 display_documents (const struct dictionary *dict)
385 {
386   const struct string_array *documents = dict_get_documents (dict);
387
388   if (string_array_is_empty (documents))
389     tab_output_text (TAB_LEFT, _("The active dataset dictionary does not "
390                                  "contain any documents."));
391   else
392     {
393       size_t i;
394
395       tab_output_text (TAB_LEFT | TAT_TITLE,
396                        _("Documents in the active dataset:"));
397       for (i = 0; i < dict_get_document_line_cnt (dict); i++)
398         tab_output_text (TAB_LEFT | TAB_FIX, dict_get_document_line (dict, i));
399     }
400 }
401
402 static int
403 count_columns (int flags)
404 {
405   int nc = 1;
406   if (flags & ~DF_DICT_INDEX)
407     nc++;
408   if (flags & DF_DICT_INDEX)
409     nc++;
410
411   return nc;
412 }
413
414 static int
415 position_column (int flags)
416 {
417   int pc = 1;
418   if (flags & ~DF_DICT_INDEX)
419     pc++;
420   return pc;
421 }
422
423 static void
424 display_variables (const struct variable **vl, size_t n, int flags)
425 {
426   struct tab_table *t;
427   struct table *table;
428   size_t i;
429   int nc;
430
431   nc = count_columns (flags);
432   t = tab_create (nc, 1);
433   tab_headers (t, 0, 0, 1, 0);
434   tab_hline (t, TAL_2, 0, nc - 1, 1);
435   tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
436   if (flags & ~DF_DICT_INDEX) 
437     tab_text (t, 1, 0, TAB_LEFT | TAT_TITLE,
438               (flags & ~(DF_DICT_INDEX | DF_VARIABLE_LABELS)
439                ? _("Description") : _("Label")));
440   if (flags & DF_DICT_INDEX)
441     tab_text (t, position_column (flags), 0, TAB_LEFT | TAT_TITLE,
442               _("Position"));
443
444   table = &t->table;
445   for (i = 0; i < n; i++)
446     table = table_vpaste (table, describe_variable (vl[i], flags));
447
448   table_item_submit (table_item_create (table, NULL /* XXX */, NULL));
449 }
450 \f
451 static bool
452 is_at_name (const char *name) 
453 {
454   return name[0] == '@' || (name[0] == '$' && name[1] == '@');
455 }
456
457 static size_t
458 count_attributes (const struct attrset *set, int flags) 
459 {
460   struct attrset_iterator i;
461   struct attribute *attr;
462   size_t n_attrs;
463   
464   n_attrs = 0;
465   for (attr = attrset_first (set, &i); attr != NULL;
466        attr = attrset_next (set, &i)) 
467     if (flags & DF_AT_ATTRIBUTES || !is_at_name (attribute_get_name (attr)))
468       n_attrs += attribute_get_n_values (attr);
469   return n_attrs;
470 }
471
472 static struct table *
473 describe_attributes (const struct attrset *set, int flags)
474 {
475   struct attribute **attrs;
476   struct tab_table *t;
477   size_t n_attrs;
478   size_t i;
479   int r = 1;
480
481   t = tab_create (2, 1 + count_attributes (set, flags));
482   tab_headers (t, 0, 0, 1, 0);
483   tab_box (t, TAL_1, TAL_1, -1, TAL_1, 0, 0, tab_nc (t) - 1, tab_nr (t) - 1);
484   tab_hline (t, TAL_1, 0, 1, 1);
485   tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Attribute"));
486   tab_text (t, 1, 0, TAB_LEFT | TAT_TITLE, _("Value"));
487
488   n_attrs = attrset_count (set);
489   attrs = attrset_sorted (set);
490   for (i = 0; i < n_attrs; i++)
491     {
492       const struct attribute *attr = attrs[i];
493       const char *name = attribute_get_name (attr);
494       size_t n_values;
495       size_t j;
496
497       if (!(flags & DF_AT_ATTRIBUTES) && is_at_name (name))
498         continue;
499
500       n_values = attribute_get_n_values (attr);
501       for (j = 0; j < n_values; j++)
502         {
503           if (n_values > 1)
504             tab_text_format (t, 0, r, TAB_LEFT, "%s[%zu]", name, j + 1);
505           else
506             tab_text (t, 0, r, TAB_LEFT, name);
507           tab_text (t, 1, r, TAB_LEFT, attribute_get_value (attr, j));
508           r++;
509         }
510     }
511   free (attrs);
512
513   return &t->table;
514 }
515
516 static void
517 display_data_file_attributes (struct attrset *set, int flags) 
518 {
519   if (count_attributes (set, flags))
520     table_item_submit (table_item_create (describe_attributes (set, flags),
521                                           _("Custom data file attributes."),
522                                           NULL));
523 }
524
525 static struct table *
526 describe_value_labels (const struct variable *var)
527 {
528   const struct val_labs *val_labs = var_get_value_labels (var);
529   size_t n_labels = val_labs_count (val_labs);
530   const struct val_lab **labels;
531   struct tab_table *t;
532   size_t i;
533
534   t = tab_create (2, n_labels + 1);
535   tab_box (t, TAL_1, TAL_1, -1, TAL_1, 0, 0, tab_nc (t) - 1, tab_nr (t) - 1);
536
537   tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Value"));
538   tab_text (t, 1, 0, TAB_LEFT | TAT_TITLE, _("Label"));
539
540   tab_hline (t, TAL_1, 0, 1, 1);
541   tab_vline (t, TAL_1, 1, 0, n_labels);
542
543   labels = val_labs_sorted (val_labs);
544   for (i = 0; i < n_labels; i++)
545     {
546       const struct val_lab *vl = labels[i];
547
548       tab_value (t, 0, i + 1, TAB_NONE, &vl->value, var, NULL);
549       tab_text (t, 1, i + 1, TAB_LEFT, val_lab_get_escaped_label (vl));
550     }
551   free (labels);
552
553   return &t->table;
554 }
555
556 static struct table *
557 describe_variable_details (const struct variable *v, int flags)
558 {
559   struct table *table;
560   struct string s;
561
562   ds_init_empty (&s);
563
564   /* Variable label. */
565   if (flags & DF_VARIABLE_LABELS && var_has_label (v))
566     {
567       if (flags & ~(DF_DICT_INDEX | DF_VARIABLE_LABELS))
568         ds_put_format (&s, _("Label: %s\n"), var_get_label (v));
569       else
570         ds_put_format (&s, "%s\n", var_get_label (v));
571     }
572
573   /* Print/write format, or print and write formats. */
574   if (flags & DF_FORMATS) 
575     {
576       const struct fmt_spec *print = var_get_print_format (v);
577       const struct fmt_spec *write = var_get_write_format (v);
578       char str[FMT_STRING_LEN_MAX + 1];
579
580       if (fmt_equal (print, write))
581         ds_put_format (&s, _("Format: %s\n"), fmt_to_string (print, str));
582       else
583         {
584           ds_put_format (&s, _("Print Format: %s\n"),
585                          fmt_to_string (print, str));
586           ds_put_format (&s, _("Write Format: %s\n"),
587                          fmt_to_string (write, str));
588         }
589     }
590
591   /* Measurement level, role, display width, alignment. */
592   if (flags & DF_MEASURE)
593     ds_put_format (&s, _("Measure: %s\n"),
594                    measure_to_string (var_get_measure (v)));
595
596   if (flags & DF_ROLE)
597     ds_put_format (&s, _("Role: %s\n"), var_role_to_string (var_get_role (v)));
598
599
600   if (flags & DF_ALIGNMENT)
601     ds_put_format (&s, _("Display Alignment: %s\n"),
602                    alignment_to_string (var_get_alignment (v)));
603
604   if (flags & DF_WIDTH)
605     ds_put_format (&s, _("Display Width: %d\n"), var_get_display_width (v));
606
607   /* Missing values if any. */
608   if (flags & DF_MISSING_VALUES && var_has_missing_values (v))
609     {
610       const struct missing_values *mv = var_get_missing_values (v);
611       int cnt = 0;
612       int i;
613
614       ds_put_cstr (&s, _("Missing Values: "));
615
616       if (mv_has_range (mv))
617         {
618           double x, y;
619           mv_get_range (mv, &x, &y);
620           if (x == LOWEST)
621             ds_put_format (&s, "LOWEST THRU %.*g", DBL_DIG + 1, y);
622           else if (y == HIGHEST)
623             ds_put_format (&s, "%.*g THRU HIGHEST", DBL_DIG + 1, x);
624           else
625             ds_put_format (&s, "%.*g THRU %.*g",
626                            DBL_DIG + 1, x,
627                            DBL_DIG + 1, y);
628           cnt++;
629         }
630       for (i = 0; i < mv_n_values (mv); i++)
631         {
632           const union value *value = mv_get_value (mv, i);
633           if (cnt++ > 0)
634             ds_put_cstr (&s, "; ");
635           if (var_is_numeric (v))
636             ds_put_format (&s, "%.*g", DBL_DIG + 1, value->f);
637           else
638             {
639               int width = var_get_width (v);
640               int mv_width = MIN (width, MV_MAX_STRING);
641
642               ds_put_byte (&s, '"');
643               memcpy (ds_put_uninit (&s, mv_width),
644                       value_str (value, width), mv_width);
645               ds_put_byte (&s, '"');
646             }
647         }
648       ds_put_byte (&s, '\n');
649     }
650
651   ds_chomp_byte (&s, '\n');
652
653   table = (ds_is_empty (&s)
654            ? NULL
655            : table_from_string (TAB_LEFT, ds_cstr (&s)));
656   ds_destroy (&s);
657
658   /* Value labels. */
659   if (flags & DF_VALUE_LABELS && var_has_value_labels (v))
660     table = table_vpaste (table, table_create_nested (describe_value_labels (v)));
661
662   if (flags & (DF_ATTRIBUTES | DF_AT_ATTRIBUTES))
663     {
664       struct attrset *attrs = var_get_attributes (v);
665
666       if (count_attributes (attrs, flags))
667         table = table_vpaste (
668           table, table_create_nested (describe_attributes (attrs, flags)));
669     }
670
671   return table ? table : table_from_string (TAB_LEFT, "");
672 }
673
674 /* Puts a description of variable V into table T starting at row
675    R.  The variable will be described in the format given by
676    FLAGS.  Returns the next row available for use in the
677    table. */
678 static struct table *
679 describe_variable (const struct variable *v, int flags)
680 {
681   struct table *table;
682
683   table = flags & ~DF_DICT_INDEX ? describe_variable_details (v, flags) : NULL;
684   table = table_hpaste (table_from_string (0, var_get_name (v)),
685                         table ? table_stomp (table) : NULL);
686   if (flags & DF_DICT_INDEX)
687     {
688       char s[INT_BUFSIZE_BOUND (size_t)];
689
690       sprintf (s, "%zu", var_get_dict_index (v) + 1);
691       table = table_hpaste (table, table_from_string (0, s));
692     }
693
694   return table;
695 }
696
697 /* Display a list of vectors.  If SORTED is nonzero then they are
698    sorted alphabetically. */
699 static void
700 display_vectors (const struct dictionary *dict, int sorted)
701 {
702   const struct vector **vl;
703   int i;
704   struct tab_table *t;
705   size_t nvec;
706   size_t nrow;
707   size_t row;
708
709   nvec = dict_get_vector_cnt (dict);
710   if (nvec == 0)
711     {
712       msg (SW, _("No vectors defined."));
713       return;
714     }
715
716   vl = xnmalloc (nvec, sizeof *vl);
717   nrow = 0;
718   for (i = 0; i < nvec; i++)
719     {
720       vl[i] = dict_get_vector (dict, i);
721       nrow += vector_get_var_cnt (vl[i]);
722     }
723   if (sorted)
724     qsort (vl, nvec, sizeof *vl, compare_vector_ptrs_by_name);
725
726   t = tab_create (4, nrow + 1);
727   tab_headers (t, 0, 0, 1, 0);
728   tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, 3, nrow);
729   tab_box (t, -1, -1, -1, TAL_1, 0, 0, 3, nrow);
730   tab_hline (t, TAL_2, 0, 3, 1);
731   tab_text (t, 0, 0, TAT_TITLE | TAB_LEFT, _("Vector"));
732   tab_text (t, 1, 0, TAT_TITLE | TAB_LEFT, _("Position"));
733   tab_text (t, 2, 0, TAT_TITLE | TAB_LEFT, _("Variable"));
734   tab_text (t, 3, 0, TAT_TITLE | TAB_LEFT, _("Print Format"));
735
736   row = 1;
737   for (i = 0; i < nvec; i++)
738     {
739       const struct vector *vec = vl[i];
740       size_t j;
741
742       tab_joint_text (t, 0, row, 0, row + vector_get_var_cnt (vec) - 1,
743                       TAB_LEFT, vector_get_name (vl[i]));
744
745       for (j = 0; j < vector_get_var_cnt (vec); j++)
746         {
747           struct variable *var = vector_get_var (vec, j);
748           char fmt_string[FMT_STRING_LEN_MAX + 1];
749           fmt_to_string (var_get_print_format (var), fmt_string);
750
751           tab_text_format (t, 1, row, TAB_RIGHT, "%zu", j + 1);
752           tab_text (t, 2, row, TAB_LEFT, var_get_name (var));
753           tab_text (t, 3, row, TAB_LEFT, fmt_string);
754           row++;
755         }
756       tab_hline (t, TAL_1, 0, 3, row);
757     }
758
759   tab_submit (t);
760
761   free (vl);
762 }
763 \f
764 /* Encoding analysis. */
765
766 static const char *encoding_names[] = {
767   /* These encodings are from http://encoding.spec.whatwg.org/, as retrieved
768      February 2014.  Encodings not supported by glibc and encodings relevant
769      only to HTML have been removed. */
770   "utf-8",
771   "windows-1252",
772   "iso-8859-2",
773   "iso-8859-3",
774   "iso-8859-4",
775   "iso-8859-5",
776   "iso-8859-6",
777   "iso-8859-7",
778   "iso-8859-8",
779   "iso-8859-10",
780   "iso-8859-13",
781   "iso-8859-14",
782   "iso-8859-16",
783   "macintosh",
784   "windows-874",
785   "windows-1250",
786   "windows-1251",
787   "windows-1253",
788   "windows-1254",
789   "windows-1255",
790   "windows-1256",
791   "windows-1257",
792   "windows-1258",
793   "koi8-r",
794   "koi8-u",
795   "ibm866",
796   "gb18030",
797   "big5",
798   "euc-jp",
799   "iso-2022-jp",
800   "shift_jis",
801   "euc-kr",
802
803   /* Added by user request. */
804   "ibm850",
805   "din_66003",
806 };
807 #define N_ENCODING_NAMES (sizeof encoding_names / sizeof *encoding_names)
808
809 struct encoding
810   {
811     uint64_t encodings;
812     char **utf8_strings;
813     unsigned int hash;
814   };
815
816 static char **
817 recode_strings (struct pool *pool,
818                 char **strings, bool *ids, size_t n,
819                 const char *encoding)
820 {
821   char **utf8_strings;
822   size_t i;
823
824   utf8_strings = pool_alloc (pool, n * sizeof *utf8_strings);
825   for (i = 0; i < n; i++)
826     {
827       struct substring utf8;
828       int error;
829
830       error = recode_pedantically ("UTF-8", encoding, ss_cstr (strings[i]),
831                                    pool, &utf8);
832       if (!error)
833         {
834           ss_rtrim (&utf8, ss_cstr (" "));
835           utf8.string[utf8.length] = '\0';
836
837           if (ids[i] && !id_is_plausible (utf8.string, false))
838             error = EINVAL;
839         }
840
841       if (error)
842         return NULL;
843
844       utf8_strings[i] = utf8.string;
845     }
846
847   return utf8_strings;
848 }
849
850 static struct encoding *
851 find_duplicate_encoding (struct encoding *encodings, size_t n_encodings,
852                          char **utf8_strings, size_t n_strings,
853                          unsigned int hash)
854 {
855   struct encoding *e;
856
857   for (e = encodings; e < &encodings[n_encodings]; e++)
858     {
859       int i;
860
861       if (e->hash != hash)
862         goto next_encoding;
863
864       for (i = 0; i < n_strings; i++)
865         if (strcmp (utf8_strings[i], e->utf8_strings[i]))
866           goto next_encoding;
867
868       return e;
869     next_encoding:;
870     }
871
872   return NULL;
873 }
874
875 static bool
876 all_equal (const struct encoding *encodings, size_t n_encodings,
877            size_t string_idx)
878 {
879   const char *s0;
880   size_t i;
881
882   s0 = encodings[0].utf8_strings[string_idx];
883   for (i = 1; i < n_encodings; i++)
884     if (strcmp (s0, encodings[i].utf8_strings[string_idx]))
885       return false;
886
887   return true;
888 }
889
890 static int
891 equal_prefix (const struct encoding *encodings, size_t n_encodings,
892               size_t string_idx)
893 {
894   const char *s0;
895   size_t prefix;
896   size_t i;
897
898   s0 = encodings[0].utf8_strings[string_idx];
899   prefix = strlen (s0);
900   for (i = 1; i < n_encodings; i++)
901     {
902       const char *si = encodings[i].utf8_strings[string_idx];
903       size_t j;
904
905       for (j = 0; j < prefix; j++)
906         if (s0[j] != si[j])
907           {
908             prefix = j;
909             if (!prefix)
910               return 0;
911             break;
912           }
913     }
914
915   while (prefix > 0 && s0[prefix - 1] != ' ')
916     prefix--;
917   return prefix;
918 }
919
920 static int
921 equal_suffix (const struct encoding *encodings, size_t n_encodings,
922               size_t string_idx)
923 {
924   const char *s0;
925   size_t s0_len;
926   size_t suffix;
927   size_t i;
928
929   s0 = encodings[0].utf8_strings[string_idx];
930   s0_len = strlen (s0);
931   suffix = s0_len;
932   for (i = 1; i < n_encodings; i++)
933     {
934       const char *si = encodings[i].utf8_strings[string_idx];
935       size_t si_len = strlen (si);
936       size_t j;
937
938       if (si_len < suffix)
939         suffix = si_len;
940       for (j = 0; j < suffix; j++)
941         if (s0[s0_len - j - 1] != si[si_len - j - 1])
942           {
943             suffix = j;
944             if (!suffix)
945               return 0;
946             break;
947           }
948     }
949
950   while (suffix > 0 && s0[s0_len - suffix] != ' ')
951     suffix--;
952   return suffix;
953 }
954
955 static void
956 report_encodings (const struct file_handle *h, struct pool *pool,
957                   char **titles, bool *ids, char **strings, size_t n_strings)
958 {
959   struct encoding encodings[N_ENCODING_NAMES];
960   size_t n_encodings, n_unique_strings;
961   size_t i, j;
962   struct tab_table *t;
963   size_t row;
964
965   n_encodings = 0;
966   for (i = 0; i < N_ENCODING_NAMES; i++)
967     {
968       char **utf8_strings;
969       struct encoding *e;
970       unsigned int hash;
971
972       utf8_strings = recode_strings (pool, strings, ids, n_strings,
973                                      encoding_names[i]);
974       if (!utf8_strings)
975         continue;
976
977       /* Hash utf8_strings. */
978       hash = 0;
979       for (j = 0; j < n_strings; j++)
980         hash = hash_string (utf8_strings[j], hash);
981
982       /* If there's a duplicate encoding, just mark it. */
983       e = find_duplicate_encoding (encodings, n_encodings,
984                                    utf8_strings, n_strings, hash);
985       if (e)
986         {
987           e->encodings |= UINT64_C (1) << i;
988           continue;
989         }
990
991       e = &encodings[n_encodings++];
992       e->encodings = UINT64_C (1) << i;
993       e->utf8_strings = utf8_strings;
994       e->hash = hash;
995     }
996   if (!n_encodings)
997     {
998       msg (SW, _("No valid encodings found."));
999       return;
1000     }
1001
1002   t = tab_create (2, n_encodings + 1);
1003   tab_title (t, _("Usable encodings for %s."), fh_get_name (h));
1004   tab_caption (t, _("Encodings that can successfully read %s (by specifying "
1005                     "the encoding name on the GET command's ENCODING "
1006                     "subcommand).  Encodings that yield identical text are "
1007                     "listed together."), fh_get_name (h));
1008   tab_headers (t, 1, 0, 1, 0);
1009   tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, 1, n_encodings);
1010   tab_hline (t, TAL_1, 0, 1, 1);
1011   tab_text (t, 0, 0, TAB_RIGHT, "#");
1012   tab_text (t, 1, 0, TAB_LEFT, _("Encodings"));
1013   for (i = 0; i < n_encodings; i++)
1014     {
1015       struct string s;
1016
1017       ds_init_empty (&s);
1018       for (j = 0; j < 64; j++)
1019         if (encodings[i].encodings & (UINT64_C (1) << j))
1020           ds_put_format (&s, "%s, ", encoding_names[j]);
1021       ds_chomp (&s, ss_cstr (", "));
1022
1023       tab_text_format (t, 0, i + 1, TAB_RIGHT, "%zu", i + 1);
1024       tab_text (t, 1, i + 1, TAB_LEFT, ds_cstr (&s));
1025       ds_destroy (&s);
1026     }
1027   tab_submit (t);
1028
1029   n_unique_strings = 0;
1030   for (i = 0; i < n_strings; i++)
1031     if (!all_equal (encodings, n_encodings, i))
1032       n_unique_strings++;
1033   if (!n_unique_strings)
1034     return;
1035
1036   t = tab_create (3, (n_encodings * n_unique_strings) + 1);
1037   tab_title (t, _("%s encoded text strings."), fh_get_name (h));
1038   tab_caption (t, _("Text strings in the file dictionary that the previously "
1039                     "listed encodings interpret differently, along with the "
1040                     "interpretations."));
1041   tab_headers (t, 1, 0, 1, 0);
1042   tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, 2, n_encodings * n_unique_strings);
1043   tab_hline (t, TAL_1, 0, 2, 1);
1044
1045   tab_text (t, 0, 0, TAB_LEFT, _("Purpose"));
1046   tab_text (t, 1, 0, TAB_RIGHT, "#");
1047   tab_text (t, 2, 0, TAB_LEFT, _("Text"));
1048
1049   row = 1;
1050   for (i = 0; i < n_strings; i++)
1051     if (!all_equal (encodings, n_encodings, i))
1052       {
1053         int prefix = equal_prefix (encodings, n_encodings, i);
1054         int suffix = equal_suffix (encodings, n_encodings, i);
1055
1056         tab_joint_text (t, 0, row, 0, row + n_encodings - 1,
1057                         TAB_LEFT, titles[i]);
1058         tab_hline (t, TAL_1, 0, 2, row);
1059         for (j = 0; j < n_encodings; j++)
1060           {
1061             const char *s = encodings[j].utf8_strings[i] + prefix;
1062
1063             tab_text_format (t, 1, row, TAB_RIGHT, "%zu", j + 1);
1064             if (prefix || suffix)
1065               {
1066                 size_t len = strlen (s) - suffix;
1067                 struct string entry;
1068
1069                 ds_init_empty (&entry);
1070                 if (prefix)
1071                   ds_put_cstr (&entry, "...");
1072                 ds_put_substring (&entry, ss_buffer (s, len));
1073                 if (suffix)
1074                   ds_put_cstr (&entry, "...");
1075                 tab_text (t, 2, row, TAB_LEFT, ds_cstr (&entry));
1076               }
1077             else
1078               tab_text (t, 2, row, TAB_LEFT, s);
1079             row++;
1080           }
1081       }
1082   tab_submit (t);
1083 }
1084
1085 static unsigned int
1086 dict_display_mask (const struct dictionary *d)
1087 {
1088   size_t n_vars = dict_get_var_cnt (d);
1089   unsigned int mask;
1090   size_t i;
1091
1092   mask = DF_ALL & ~(DF_MEASURE | DF_ROLE | DF_ALIGNMENT | DF_WIDTH);
1093   for (i = 0; i < n_vars; i++)
1094     {
1095       const struct variable *v = dict_get_var (d, i);
1096       enum val_type val = var_get_type (v);
1097       int width = var_get_width (v);
1098
1099       if (var_get_measure (v) != var_default_measure (val))
1100         mask |= DF_MEASURE;
1101
1102       if (var_get_role (v) != ROLE_INPUT)
1103         mask |= DF_ROLE;
1104
1105       if (var_get_alignment (v) != var_default_alignment (val))
1106         mask |= DF_ALIGNMENT;
1107
1108       if (var_get_display_width (v) != var_default_display_width (width))
1109         mask |= DF_WIDTH;
1110     }
1111
1112   return mask;
1113 }