1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
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.
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.
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/>. */
23 #include "data/any-reader.h"
24 #include "data/attributes.h"
25 #include "data/casereader.h"
26 #include "data/dataset.h"
27 #include "data/dictionary.h"
28 #include "data/file-handle-def.h"
29 #include "data/format.h"
30 #include "data/missing-values.h"
31 #include "data/value-labels.h"
32 #include "data/variable.h"
33 #include "data/vector.h"
34 #include "language/command.h"
35 #include "language/commands/file-handle.h"
36 #include "language/lexer/lexer.h"
37 #include "language/lexer/variable-parser.h"
38 #include "libpspp/array.h"
39 #include "libpspp/hash-functions.h"
40 #include "libpspp/i18n.h"
41 #include "libpspp/message.h"
42 #include "libpspp/misc.h"
43 #include "libpspp/pool.h"
44 #include "libpspp/string-array.h"
45 #include "output/pivot-table.h"
46 #include "output/output-item.h"
48 #include "gl/count-one-bits.h"
49 #include "gl/localcharset.h"
50 #include "gl/intprops.h"
51 #include "gl/minmax.h"
52 #include "gl/xalloc.h"
55 #define _(msgid) gettext (msgid)
56 #define N_(msgid) (msgid)
58 /* Information to include in displaying a dictionary. */
65 DF_MEASUREMENT_LEVEL = 1 << 3,
68 DF_ALIGNMENT = 1 << 6,
69 DF_PRINT_FORMAT = 1 << 7,
70 DF_WRITE_FORMAT = 1 << 8,
71 DF_MISSING_VALUES = 1 << 9,
72 #define DF_ALL_VARIABLE ((1 << 10) - 1)
74 /* Value labels table. */
75 DF_VALUE_LABELS = 1 << 10,
77 /* Attribute table. */
78 DF_AT_ATTRIBUTES = 1 << 11, /* Attributes whose names begin with @. */
79 DF_ATTRIBUTES = 1 << 12, /* All other attributes. */
82 static void display_variables (const struct variable **, size_t, int flags);
83 static void display_value_labels (const struct variable **, size_t);
84 static void display_attributes (const struct attrset *,
85 const struct variable **, size_t, int flags);
87 static void report_encodings (const struct file_handle *, struct pool *,
88 char **titles, bool *ids,
89 char **strings, size_t n_strings);
91 static char *get_documents_as_string (const struct dictionary *);
94 add_row (struct pivot_table *table, const char *attribute,
95 struct pivot_value *value)
97 int row = pivot_category_create_leaf (table->dimensions[0]->root,
98 pivot_value_new_text (attribute));
100 pivot_table_put1 (table, row, value);
103 /* SYSFILE INFO utility. */
105 cmd_sysfile_info (struct lexer *lexer, struct dataset *ds)
107 struct any_reader *any_reader;
108 struct file_handle *h;
109 struct dictionary *d;
110 struct casereader *reader;
111 struct any_read_info info;
118 lex_match (lexer, T_SLASH);
120 if (lex_match_id (lexer, "FILE") || lex_is_string (lexer))
122 lex_match (lexer, T_EQUALS);
125 h = fh_parse (lexer, FH_REF_FILE, NULL);
129 else if (lex_match_id (lexer, "ENCODING"))
131 lex_match (lexer, T_EQUALS);
133 if (!lex_force_string (lexer))
137 encoding = ss_xstrdup (lex_tokss (lexer));
147 lex_sbc_missing (lexer, "FILE");
151 any_reader = any_reader_open (h);
158 if (encoding && !strcasecmp (encoding, "detect"))
160 char **titles, **strings;
165 pool = pool_create ();
166 n_strings = any_reader_get_strings (any_reader, pool,
167 &titles, &ids, &strings);
168 any_reader_close (any_reader);
170 report_encodings (h, pool, titles, ids, strings, n_strings);
178 reader = any_reader_decode (any_reader, encoding, &d, &info);
181 casereader_destroy (reader);
183 struct pivot_table *table = pivot_table_create (N_("File Information"));
184 pivot_dimension_create (table, PIVOT_AXIS_ROW, N_("Attribute"));
186 add_row (table, N_("File"),
187 pivot_value_new_user_text (fh_get_file_name (h), -1));
189 const char *label = dict_get_label (d);
190 add_row (table, N_("Label"),
191 label ? pivot_value_new_user_text (label, -1) : NULL);
193 add_row (table, N_("Created"),
194 pivot_value_new_user_text_nocopy (
195 xasprintf ("%s %s by %s", info.creation_date,
196 info.creation_time, info.product)));
198 if (info.product_ext)
199 add_row (table, N_("Product"),
200 pivot_value_new_user_text (info.product_ext, -1));
202 add_row (table, N_("Integer Format"),
203 pivot_value_new_text (
204 info.integer_format == INTEGER_MSB_FIRST ? N_("Big Endian")
205 : info.integer_format == INTEGER_LSB_FIRST ? N_("Little Endian")
208 add_row (table, N_("Real Format"),
209 pivot_value_new_text (
210 info.float_format == FLOAT_IEEE_DOUBLE_LE ? N_("IEEE 754 LE.")
211 : info.float_format == FLOAT_IEEE_DOUBLE_BE ? N_("IEEE 754 BE.")
212 : info.float_format == FLOAT_VAX_D ? N_("VAX D.")
213 : info.float_format == FLOAT_VAX_G ? N_("VAX G.")
214 : info.float_format == FLOAT_Z_LONG ? N_("IBM 390 Hex Long.")
217 add_row (table, N_("Variables"),
218 pivot_value_new_integer (dict_get_n_vars (d)));
220 add_row (table, N_("Cases"),
222 ? pivot_value_new_text (N_("Unknown"))
223 : pivot_value_new_integer (info.n_cases)));
225 add_row (table, N_("Type"),
226 pivot_value_new_text (info.klass->name));
228 struct variable *weight_var = dict_get_weight (d);
229 add_row (table, N_("Weight"),
231 ? pivot_value_new_variable (weight_var)
232 : pivot_value_new_text (N_("Not weighted"))));
234 add_row (table, N_("Compression"),
235 (info.compression == ANY_COMP_NONE
236 ? pivot_value_new_text (N_("None"))
237 : pivot_value_new_user_text (
238 info.compression == ANY_COMP_SIMPLE ? "SAV" : "ZSAV", -1)));
240 add_row (table, N_("Encoding"),
241 pivot_value_new_user_text (dict_get_encoding (d), -1));
243 if (dict_get_document_n_lines (d) > 0)
244 add_row (table, N_("Documents"),
245 pivot_value_new_user_text_nocopy (get_documents_as_string (d)));
247 pivot_table_submit (table);
249 size_t n_vars = dict_get_n_vars (d);
250 const struct variable **vars = xnmalloc (n_vars, sizeof *vars);
251 for (size_t i = 0; i < dict_get_n_vars (d); i++)
252 vars[i] = dict_get_var (d, i);
253 display_variables (vars, n_vars, DF_ALL_VARIABLE);
254 display_value_labels (vars, n_vars);
255 display_attributes (dict_get_attributes (dataset_dict (ds)),
256 vars, n_vars, DF_ATTRIBUTES);
263 any_read_info_destroy (&info);
272 /* DISPLAY utility. */
274 static void display_macros (void);
275 static void display_documents (const struct dictionary *dict);
276 static void display_vectors (const struct dictionary *dict, int sorted);
279 cmd_display (struct lexer *lexer, struct dataset *ds)
281 /* Whether to sort the list of variables alphabetically. */
284 /* Variables to display. */
286 const struct variable **vl;
288 if (lex_match_id (lexer, "MACROS"))
290 else if (lex_match_id (lexer, "DOCUMENTS"))
291 display_documents (dataset_dict (ds));
292 else if (lex_match_id (lexer, "FILE"))
294 if (!lex_force_match_id (lexer, "LABEL"))
297 const char *label = dict_get_label (dataset_dict (ds));
299 struct pivot_table *table = pivot_table_create (N_("File Label"));
300 pivot_dimension_create (table, PIVOT_AXIS_ROW, N_("Label"),
302 pivot_table_put1 (table, 0,
303 (label ? pivot_value_new_user_text (label, -1)
304 : pivot_value_new_text (N_("(none)"))));
305 pivot_table_submit (table);
311 sorted = lex_match_id (lexer, "SORTED");
313 if (lex_match_id (lexer, "VECTORS"))
315 display_vectors (dataset_dict(ds), sorted);
318 else if (lex_match_id (lexer, "SCRATCH"))
320 dict_get_vars (dataset_dict (ds), &vl, &n, DC_ORDINARY);
330 static const struct subcommand subcommands[] =
332 {"@ATTRIBUTES", DF_ATTRIBUTES | DF_AT_ATTRIBUTES},
333 {"ATTRIBUTES", DF_ATTRIBUTES},
334 {"DICTIONARY", (DF_NAME | DF_POSITION | DF_LABEL
335 | DF_MEASUREMENT_LEVEL | DF_ROLE | DF_WIDTH
336 | DF_ALIGNMENT | DF_PRINT_FORMAT
337 | DF_WRITE_FORMAT | DF_MISSING_VALUES
339 {"INDEX", DF_NAME | DF_POSITION},
340 {"LABELS", DF_NAME | DF_POSITION | DF_LABEL},
342 {"VARIABLES", (DF_NAME | DF_POSITION | DF_PRINT_FORMAT
343 | DF_WRITE_FORMAT | DF_MISSING_VALUES)},
346 const struct subcommand *sbc;
347 struct dictionary *dict = dataset_dict (ds);
350 for (sbc = subcommands; sbc->name != NULL; sbc++)
351 if (lex_match_id (lexer, sbc->name))
357 lex_match (lexer, T_SLASH);
358 lex_match_id (lexer, "VARIABLES");
359 lex_match (lexer, T_EQUALS);
361 if (lex_token (lexer) != T_ENDCMD)
363 if (!parse_variables_const (lexer, dict, &vl, &n, PV_NONE))
370 dict_get_vars (dict, &vl, &n, 0);
375 sort (vl, n, sizeof *vl, (sorted
376 ? compare_var_ptrs_by_name
377 : compare_var_ptrs_by_dict_index), NULL);
379 int variable_flags = flags & DF_ALL_VARIABLE;
381 display_variables (vl, n, variable_flags);
383 if (flags & DF_VALUE_LABELS)
384 display_value_labels (vl, n);
386 int attribute_flags = flags & (DF_ATTRIBUTES | DF_AT_ATTRIBUTES);
388 display_attributes (dict_get_attributes (dataset_dict (ds)),
389 vl, n, attribute_flags);
392 msg (SN, _("No variables to display."));
401 display_macros (void)
403 msg (SW, _("Macros not supported."));
407 get_documents_as_string (const struct dictionary *dict)
409 const struct string_array *documents = dict_get_documents (dict);
410 struct string s = DS_EMPTY_INITIALIZER;
411 for (size_t i = 0; i < documents->n; i++)
414 ds_put_byte (&s, '\n');
415 ds_put_cstr (&s, documents->strings[i]);
417 return ds_steal_cstr (&s);
421 display_documents (const struct dictionary *dict)
423 struct pivot_table *table = pivot_table_create (N_("Documents"));
424 struct pivot_dimension *d = pivot_dimension_create (
425 table, PIVOT_AXIS_COLUMN, N_("Documents"), N_("Document"));
426 d->hide_all_labels = true;
428 if (!dict_get_documents (dict)->n)
429 pivot_table_put1 (table, 0, pivot_value_new_text (N_("(none)")));
432 char *docs = get_documents_as_string (dict);
433 pivot_table_put1 (table, 0, pivot_value_new_user_text_nocopy (docs));
436 pivot_table_submit (table);
440 display_variables (const struct variable **vl, size_t n, int flags)
442 struct pivot_table *table = pivot_table_create (N_("Variables"));
444 struct pivot_dimension *attributes = pivot_dimension_create (
445 table, PIVOT_AXIS_COLUMN, N_("Attributes"));
452 static const struct heading headings[] = {
453 { DF_POSITION, N_("Position") },
454 { DF_LABEL, N_("Label") },
455 { DF_MEASUREMENT_LEVEL, N_("Measurement Level") },
456 { DF_ROLE, N_("Role") },
457 { DF_WIDTH, N_("Width") },
458 { DF_ALIGNMENT, N_("Alignment") },
459 { DF_PRINT_FORMAT, N_("Print Format") },
460 { DF_WRITE_FORMAT, N_("Write Format") },
461 { DF_MISSING_VALUES, N_("Missing Values") },
463 for (size_t i = 0; i < sizeof headings / sizeof *headings; i++)
464 if (flags & headings[i].flag)
465 pivot_category_create_leaf (attributes->root,
466 pivot_value_new_text (headings[i].title));
468 struct pivot_dimension *names = pivot_dimension_create (
469 table, PIVOT_AXIS_ROW, N_("Name"));
470 names->root->show_label = true;
472 for (size_t i = 0; i < n; i++)
474 const struct variable *v = vl[i];
476 struct pivot_value *name = pivot_value_new_variable (v);
477 name->variable.show = SETTINGS_VALUE_SHOW_VALUE;
478 int row = pivot_category_create_leaf (names->root, name);
481 if (flags & DF_POSITION)
482 pivot_table_put2 (table, x++, row, pivot_value_new_integer (
483 var_get_dict_index (v) + 1));
485 if (flags & DF_LABEL)
487 const char *label = var_get_label (v);
489 pivot_table_put2 (table, x, row,
490 pivot_value_new_user_text (label, -1));
494 if (flags & DF_MEASUREMENT_LEVEL)
497 pivot_value_new_text (measure_to_string (var_get_measure (v))));
502 pivot_value_new_text (var_role_to_string (var_get_role (v))));
504 if (flags & DF_WIDTH)
507 pivot_value_new_integer (var_get_display_width (v)));
509 if (flags & DF_ALIGNMENT)
512 pivot_value_new_text (alignment_to_string (
513 var_get_alignment (v))));
515 if (flags & DF_PRINT_FORMAT)
517 const struct fmt_spec *print = var_get_print_format (v);
518 char s[FMT_STRING_LEN_MAX + 1];
522 pivot_value_new_user_text (fmt_to_string (print, s), -1));
525 if (flags & DF_WRITE_FORMAT)
527 const struct fmt_spec *write = var_get_write_format (v);
528 char s[FMT_STRING_LEN_MAX + 1];
532 pivot_value_new_user_text (fmt_to_string (write, s), -1));
535 if (flags & DF_MISSING_VALUES)
537 char *s = mv_to_string (var_get_missing_values (v),
538 var_get_encoding (v));
542 pivot_value_new_user_text_nocopy (s));
548 pivot_table_submit (table);
552 any_value_labels (const struct variable **vars, size_t n_vars)
554 for (size_t i = 0; i < n_vars; i++)
555 if (val_labs_count (var_get_value_labels (vars[i])))
561 display_value_labels (const struct variable **vars, size_t n_vars)
563 if (!any_value_labels (vars, n_vars))
566 struct pivot_table *table = pivot_table_create (N_("Value Labels"));
568 pivot_dimension_create (table, PIVOT_AXIS_COLUMN,
569 N_("Label"), N_("Label"));
571 struct pivot_dimension *values = pivot_dimension_create (
572 table, PIVOT_AXIS_ROW, N_("Variable Value"));
573 values->root->show_label = true;
575 struct pivot_footnote *missing_footnote = pivot_table_create_footnote (
576 table, pivot_value_new_text (N_("User-missing value")));
578 for (size_t i = 0; i < n_vars; i++)
580 const struct val_labs *val_labs = var_get_value_labels (vars[i]);
581 size_t n_labels = val_labs_count (val_labs);
585 struct pivot_category *group = pivot_category_create_group__ (
586 values->root, pivot_value_new_variable (vars[i]));
588 const struct val_lab **labels = val_labs_sorted (val_labs);
589 for (size_t j = 0; j < n_labels; j++)
591 const struct val_lab *vl = labels[j];
593 struct pivot_value *value = pivot_value_new_var_value (
594 vars[i], &vl->value);
595 if (value->type == PIVOT_VALUE_NUMERIC)
596 value->numeric.show = SETTINGS_VALUE_SHOW_VALUE;
598 value->string.show = SETTINGS_VALUE_SHOW_VALUE;
599 if (var_is_value_missing (vars[i], &vl->value) == MV_USER)
600 pivot_value_add_footnote (value, missing_footnote);
601 int row = pivot_category_create_leaf (group, value);
603 struct pivot_value *label = pivot_value_new_var_value (
604 vars[i], &vl->value);
605 char *escaped_label = xstrdup (val_lab_get_escaped_label (vl));
606 if (label->type == PIVOT_VALUE_NUMERIC)
608 free (label->numeric.value_label);
609 label->numeric.value_label = escaped_label;
610 label->numeric.show = SETTINGS_VALUE_SHOW_LABEL;
614 free (label->string.value_label);
615 label->string.value_label = escaped_label;
616 label->string.show = SETTINGS_VALUE_SHOW_LABEL;
618 pivot_table_put2 (table, 0, row, label);
622 pivot_table_submit (table);
626 is_at_name (const char *name)
628 return name[0] == '@' || (name[0] == '$' && name[1] == '@');
632 count_attributes (const struct attrset *set, int flags)
634 struct attrset_iterator i;
635 struct attribute *attr;
639 for (attr = attrset_first (set, &i); attr != NULL;
640 attr = attrset_next (set, &i))
641 if (flags & DF_AT_ATTRIBUTES || !is_at_name (attribute_get_name (attr)))
642 n_attrs += attribute_get_n_values (attr);
647 display_attrset (struct pivot_table *table, struct pivot_value *set_name,
648 const struct attrset *set, int flags)
650 size_t n_total = count_attributes (set, flags);
653 pivot_value_destroy (set_name);
657 struct pivot_category *group = pivot_category_create_group__ (
658 table->dimensions[1]->root, set_name);
660 size_t n_attrs = attrset_count (set);
661 struct attribute **attrs = attrset_sorted (set);
662 for (size_t i = 0; i < n_attrs; i++)
664 const struct attribute *attr = attrs[i];
665 const char *name = attribute_get_name (attr);
667 if (!(flags & DF_AT_ATTRIBUTES) && is_at_name (name))
670 size_t n_values = attribute_get_n_values (attr);
671 for (size_t j = 0; j < n_values; j++)
673 int row = pivot_category_create_leaf (
676 ? pivot_value_new_user_text_nocopy (xasprintf (
677 "%s[%zu]", name, j + 1))
678 : pivot_value_new_user_text (name, -1)));
679 pivot_table_put2 (table, 0, row,
680 pivot_value_new_user_text (
681 attribute_get_value (attr, j), -1));
688 display_attributes (const struct attrset *dict_attrset,
689 const struct variable **vars, size_t n_vars, int flags)
691 struct pivot_table *table = pivot_table_create (
692 N_("Variable and Dataset Attributes"));
694 pivot_dimension_create (table, PIVOT_AXIS_COLUMN,
695 N_("Value"), N_("Value"));
697 struct pivot_dimension *variables = pivot_dimension_create (
698 table, PIVOT_AXIS_ROW, N_("Variable and Name"));
699 variables->root->show_label = true;
701 display_attrset (table, pivot_value_new_text (N_("(dataset)")),
702 dict_attrset, flags);
703 for (size_t i = 0; i < n_vars; i++)
704 display_attrset (table, pivot_value_new_variable (vars[i]),
705 var_get_attributes (vars[i]), flags);
707 if (pivot_table_is_empty (table))
708 pivot_table_unref (table);
710 pivot_table_submit (table);
713 /* Display a list of vectors. If SORTED is nonzero then they are
714 sorted alphabetically. */
716 display_vectors (const struct dictionary *dict, int sorted)
718 size_t n_vectors = dict_get_n_vectors (dict);
721 msg (SN, _("No vectors defined."));
725 const struct vector **vectors = xnmalloc (n_vectors, sizeof *vectors);
726 for (size_t i = 0; i < n_vectors; i++)
727 vectors[i] = dict_get_vector (dict, i);
729 qsort (vectors, n_vectors, sizeof *vectors, compare_vector_ptrs_by_name);
731 struct pivot_table *table = pivot_table_create (N_("Vectors"));
732 pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Attributes"),
733 N_("Variable"), N_("Print Format"));
734 struct pivot_dimension *vector_dim = pivot_dimension_create (
735 table, PIVOT_AXIS_ROW, N_("Vector and Position"));
736 vector_dim->root->show_label = true;
738 for (size_t i = 0; i < n_vectors; i++)
740 const struct vector *vec = vectors[i];
742 struct pivot_category *group = pivot_category_create_group__ (
743 vector_dim->root, pivot_value_new_user_text (
744 vector_get_name (vectors[i]), -1));
746 for (size_t j = 0; j < vector_get_n_vars (vec); j++)
748 struct variable *var = vector_get_var (vec, j);
750 int row = pivot_category_create_leaf (
751 group, pivot_value_new_integer (j + 1));
753 pivot_table_put2 (table, 0, row, pivot_value_new_variable (var));
754 char fmt_string[FMT_STRING_LEN_MAX + 1];
755 fmt_to_string (var_get_print_format (var), fmt_string);
756 pivot_table_put2 (table, 1, row,
757 pivot_value_new_user_text (fmt_string, -1));
761 pivot_table_submit (table);
766 /* Encoding analysis. */
768 static const char *encoding_names[] = {
769 /* These encodings are from http://encoding.spec.whatwg.org/, as retrieved
770 February 2014. Encodings not supported by glibc and encodings relevant
771 only to HTML have been removed. */
805 /* Added by user request. */
809 #define N_ENCODING_NAMES (sizeof encoding_names / sizeof *encoding_names)
819 recode_strings (struct pool *pool,
820 char **strings, bool *ids, size_t n,
821 const char *encoding)
826 utf8_strings = pool_alloc (pool, n * sizeof *utf8_strings);
827 for (i = 0; i < n; i++)
829 struct substring utf8;
832 error = recode_pedantically ("UTF-8", encoding, ss_cstr (strings[i]),
836 ss_rtrim (&utf8, ss_cstr (" "));
837 utf8.string[utf8.length] = '\0';
839 if (ids[i] && !id_is_plausible (utf8.string))
846 utf8_strings[i] = utf8.string;
852 static struct encoding *
853 find_duplicate_encoding (struct encoding *encodings, size_t n_encodings,
854 char **utf8_strings, size_t n_strings,
859 for (e = encodings; e < &encodings[n_encodings]; e++)
866 for (i = 0; i < n_strings; i++)
867 if (strcmp (utf8_strings[i], e->utf8_strings[i]))
878 all_equal (const struct encoding *encodings, size_t n_encodings,
884 s0 = encodings[0].utf8_strings[string_idx];
885 for (i = 1; i < n_encodings; i++)
886 if (strcmp (s0, encodings[i].utf8_strings[string_idx]))
893 equal_prefix (const struct encoding *encodings, size_t n_encodings,
900 s0 = encodings[0].utf8_strings[string_idx];
901 prefix = strlen (s0);
902 for (i = 1; i < n_encodings; i++)
904 const char *si = encodings[i].utf8_strings[string_idx];
907 for (j = 0; j < prefix; j++)
917 while (prefix > 0 && s0[prefix - 1] != ' ')
923 equal_suffix (const struct encoding *encodings, size_t n_encodings,
931 s0 = encodings[0].utf8_strings[string_idx];
932 s0_len = strlen (s0);
934 for (i = 1; i < n_encodings; i++)
936 const char *si = encodings[i].utf8_strings[string_idx];
937 size_t si_len = strlen (si);
942 for (j = 0; j < suffix; j++)
943 if (s0[s0_len - j - 1] != si[si_len - j - 1])
952 while (suffix > 0 && s0[s0_len - suffix] != ' ')
958 report_encodings (const struct file_handle *h, struct pool *pool,
959 char **titles, bool *ids, char **strings, size_t n_strings)
961 struct encoding encodings[N_ENCODING_NAMES];
962 size_t n_encodings, n_unique_strings;
965 for (size_t i = 0; i < N_ENCODING_NAMES; i++)
971 utf8_strings = recode_strings (pool, strings, ids, n_strings,
976 /* Hash utf8_strings. */
978 for (size_t j = 0; j < n_strings; j++)
979 hash = hash_string (utf8_strings[j], hash);
981 /* If there's a duplicate encoding, just mark it. */
982 e = find_duplicate_encoding (encodings, n_encodings,
983 utf8_strings, n_strings, hash);
986 e->encodings |= UINT64_C (1) << i;
990 e = &encodings[n_encodings++];
991 e->encodings = UINT64_C (1) << i;
992 e->utf8_strings = utf8_strings;
997 msg (SW, _("No valid encodings found."));
1001 /* Table of valid encodings. */
1002 struct pivot_table *table = pivot_table_create__ (
1003 pivot_value_new_text_format (N_("Usable encodings for %s."),
1004 fh_get_name (h)), "Usable Encodings");
1005 pivot_table_set_caption (
1006 table, pivot_value_new_text_format (
1007 N_("Encodings that can successfully read %s (by specifying the encoding "
1008 "name on the GET command's ENCODING subcommand). Encodings that "
1009 "yield identical text are listed together."),
1012 pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Encodings"),
1014 struct pivot_dimension *number = pivot_dimension_create__ (
1015 table, PIVOT_AXIS_ROW, pivot_value_new_user_text ("#", -1));
1016 number->root->show_label = true;
1018 for (size_t i = 0; i < n_encodings; i++)
1020 struct string s = DS_EMPTY_INITIALIZER;
1021 for (size_t j = 0; j < 64; j++)
1022 if (encodings[i].encodings & (UINT64_C (1) << j))
1023 ds_put_format (&s, "%s, ", encoding_names[j]);
1024 ds_chomp (&s, ss_cstr (", "));
1026 int row = pivot_category_create_leaf (number->root,
1027 pivot_value_new_integer (i + 1));
1029 table, 0, row, pivot_value_new_user_text_nocopy (ds_steal_cstr (&s)));
1031 pivot_table_submit (table);
1033 n_unique_strings = 0;
1034 for (size_t i = 0; i < n_strings; i++)
1035 if (!all_equal (encodings, n_encodings, i))
1037 if (!n_unique_strings)
1040 /* Table of alternative interpretations. */
1041 table = pivot_table_create__ (
1042 pivot_value_new_text_format (N_("%s Encoded Text Strings"),
1044 "Alternate Encoded Text Strings");
1045 pivot_table_set_caption (
1046 table, pivot_value_new_text (
1047 N_("Text strings in the file dictionary that the previously listed "
1048 "encodings interpret differently, along with the interpretations.")));
1050 pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Text"), N_("Text"));
1052 number = pivot_dimension_create__ (table, PIVOT_AXIS_ROW,
1053 pivot_value_new_user_text ("#", -1));
1054 number->root->show_label = true;
1055 for (size_t i = 0; i < n_encodings; i++)
1056 pivot_category_create_leaf (number->root,
1057 pivot_value_new_integer (i + 1));
1059 struct pivot_dimension *purpose = pivot_dimension_create (
1060 table, PIVOT_AXIS_ROW, N_("Purpose"));
1061 purpose->root->show_label = true;
1063 for (size_t i = 0; i < n_strings; i++)
1064 if (!all_equal (encodings, n_encodings, i))
1066 int prefix = equal_prefix (encodings, n_encodings, i);
1067 int suffix = equal_suffix (encodings, n_encodings, i);
1069 int purpose_idx = pivot_category_create_leaf (
1070 purpose->root, pivot_value_new_user_text (titles[i], -1));
1072 for (size_t j = 0; j < n_encodings; j++)
1074 const char *s = encodings[j].utf8_strings[i] + prefix;
1076 if (prefix || suffix)
1078 size_t len = strlen (s) - suffix;
1079 struct string entry;
1081 ds_init_empty (&entry);
1083 ds_put_cstr (&entry, "...");
1084 ds_put_substring (&entry, ss_buffer (s, len));
1086 ds_put_cstr (&entry, "...");
1088 pivot_table_put3 (table, 0, j, purpose_idx,
1089 pivot_value_new_user_text_nocopy (
1090 ds_steal_cstr (&entry)));
1093 pivot_table_put3 (table, 0, j, purpose_idx,
1094 pivot_value_new_user_text (s, -1));
1098 pivot_table_submit (table);