sys-file-reader: Add support for long string missing values.
[pspp] / utilities / pspp-dump-sav.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 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 <getopt.h>
22 #include <inttypes.h>
23 #include <limits.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26
27 #include "data/val-type.h"
28 #include "libpspp/cast.h"
29 #include "libpspp/compiler.h"
30 #include "libpspp/float-format.h"
31 #include "libpspp/integer-format.h"
32 #include "libpspp/misc.h"
33
34 #include "gl/error.h"
35 #include "gl/minmax.h"
36 #include "gl/progname.h"
37 #include "gl/version-etc.h"
38 #include "gl/xalloc.h"
39
40 #define ID_MAX_LEN 64
41
42 struct sfm_reader
43   {
44     const char *file_name;
45     FILE *file;
46
47     int n_variable_records, n_variables;
48
49     int *var_widths;
50     size_t n_var_widths, allocated_var_widths;
51
52     enum integer_format integer_format;
53     enum float_format float_format;
54
55     bool compressed;
56     double bias;
57   };
58
59 static void read_header (struct sfm_reader *);
60 static void read_variable_record (struct sfm_reader *);
61 static void read_value_label_record (struct sfm_reader *);
62 static void read_document_record (struct sfm_reader *);
63 static void read_extension_record (struct sfm_reader *);
64 static void read_machine_integer_info (struct sfm_reader *,
65                                        size_t size, size_t count);
66 static void read_machine_float_info (struct sfm_reader *,
67                                      size_t size, size_t count);
68 static void read_extra_product_info (struct sfm_reader *,
69                                      size_t size, size_t count);
70 static void read_mrsets (struct sfm_reader *, size_t size, size_t count);
71 static void read_display_parameters (struct sfm_reader *,
72                                      size_t size, size_t count);
73 static void read_long_var_name_map (struct sfm_reader *r,
74                                     size_t size, size_t count);
75 static void read_long_string_map (struct sfm_reader *r,
76                                   size_t size, size_t count);
77 static void read_datafile_attributes (struct sfm_reader *r,
78                                       size_t size, size_t count);
79 static void read_variable_attributes (struct sfm_reader *r,
80                                       size_t size, size_t count);
81 static void read_ncases64 (struct sfm_reader *, size_t size, size_t count);
82 static void read_character_encoding (struct sfm_reader *r,
83                                        size_t size, size_t count);
84 static void read_long_string_value_labels (struct sfm_reader *r,
85                                            size_t size, size_t count);
86 static void read_long_string_missing_values (struct sfm_reader *r,
87                                              size_t size, size_t count);
88 static void read_unknown_extension (struct sfm_reader *,
89                                     size_t size, size_t count);
90 static void read_compressed_data (struct sfm_reader *, int max_cases);
91
92 static struct text_record *open_text_record (
93   struct sfm_reader *, size_t size);
94 static void close_text_record (struct text_record *);
95 static bool read_variable_to_value_pair (struct text_record *,
96                                          char **key, char **value);
97 static char *text_tokenize (struct text_record *, int delimiter);
98 static bool text_match (struct text_record *text, int c);
99 static const char *text_parse_counted_string (struct text_record *);
100 static size_t text_pos (const struct text_record *);
101 static const char *text_get_all (const struct text_record *);
102
103 static void usage (void);
104 static void sys_warn (struct sfm_reader *, const char *, ...)
105      PRINTF_FORMAT (2, 3);
106 static void sys_error (struct sfm_reader *, const char *, ...)
107      PRINTF_FORMAT (2, 3)
108      NO_RETURN;
109
110 static void read_bytes (struct sfm_reader *, void *, size_t);
111 static bool try_read_bytes (struct sfm_reader *, void *, size_t);
112 static int read_int (struct sfm_reader *);
113 static int64_t read_int64 (struct sfm_reader *);
114 static double read_float (struct sfm_reader *);
115 static void read_string (struct sfm_reader *, char *, size_t);
116 static void skip_bytes (struct sfm_reader *, size_t);
117 static void trim_spaces (char *);
118
119 static void print_string (const char *s, size_t len);
120
121 int
122 main (int argc, char *argv[])
123 {
124   int max_cases = 0;
125   struct sfm_reader r;
126   int i;
127
128   set_program_name (argv[0]);
129
130   for (;;)
131     {
132       static const struct option long_options[] =
133         {
134           { "data",    optional_argument, NULL, 'd' },
135           { "help",    no_argument,       NULL, 'h' },
136           { "version", no_argument,       NULL, 'v' },
137           { NULL,      0,                 NULL, 0 },
138         };
139
140       int c;
141
142       c = getopt_long (argc, argv, "d::hv", long_options, NULL);
143       if (c == -1)
144         break;
145
146       switch (c)
147         {
148         case 'd':
149           max_cases = optarg ? atoi (optarg) : INT_MAX;
150           break;
151
152         case 'v':
153           version_etc (stdout, "pspp-dump-sav", PACKAGE_NAME, PACKAGE_VERSION,
154                        "Ben Pfaff", "John Darrington", NULL_SENTINEL);
155           exit (EXIT_SUCCESS);
156
157         case 'h':
158           usage ();
159           exit (EXIT_SUCCESS);
160
161         default:
162           exit (EXIT_FAILURE);
163         }
164     }
165
166   if (optind == argc)
167     error (1, 0, "at least one non-option argument is required; "
168            "use --help for help");
169
170   for (i = optind; i < argc; i++)
171     {
172       int rec_type;
173
174       r.file_name = argv[i];
175       r.file = fopen (r.file_name, "rb");
176       if (r.file == NULL)
177         error (EXIT_FAILURE, errno, "error opening `%s'", r.file_name);
178       r.n_variable_records = 0;
179       r.n_variables = 0;
180       r.n_var_widths = 0;
181       r.allocated_var_widths = 0;
182       r.var_widths = 0;
183       r.compressed = false;
184
185       if (argc - optind > 1)
186         printf ("Reading \"%s\":\n", r.file_name);
187       
188       read_header (&r);
189       while ((rec_type = read_int (&r)) != 999)
190         {
191           switch (rec_type)
192             {
193             case 2:
194               read_variable_record (&r);
195               break;
196
197             case 3:
198               read_value_label_record (&r);
199               break;
200
201             case 4:
202               sys_error (&r, "Misplaced type 4 record.");
203
204             case 6:
205               read_document_record (&r);
206               break;
207
208             case 7:
209               read_extension_record (&r);
210               break;
211
212             default:
213               sys_error (&r, "Unrecognized record type %d.", rec_type);
214             }
215         }
216       printf ("%08llx: end-of-dictionary record "
217               "(first byte of data at %08llx)\n",
218               (long long int) ftello (r.file),
219               (long long int) ftello (r.file) + 4);
220
221       if (r.compressed && max_cases > 0)
222         read_compressed_data (&r, max_cases);
223
224       fclose (r.file);
225     }
226   
227   return 0;
228 }
229
230 static void
231 read_header (struct sfm_reader *r)
232 {
233   char rec_type[5];
234   char eye_catcher[61];
235   uint8_t raw_layout_code[4];
236   int32_t layout_code;
237   int32_t compressed;
238   int32_t weight_index;
239   int32_t ncases;
240   uint8_t raw_bias[8];
241   char creation_date[10];
242   char creation_time[9];
243   char file_label[65];
244
245   read_string (r, rec_type, sizeof rec_type);
246   read_string (r, eye_catcher, sizeof eye_catcher);
247
248   if (strcmp ("$FL2", rec_type) != 0)
249     sys_error (r, "This is not an SPSS system file.");
250
251   /* Identify integer format. */
252   read_bytes (r, raw_layout_code, sizeof raw_layout_code);
253   if ((!integer_identify (2, raw_layout_code, sizeof raw_layout_code,
254                           &r->integer_format)
255        && !integer_identify (3, raw_layout_code, sizeof raw_layout_code,
256                              &r->integer_format))
257       || (r->integer_format != INTEGER_MSB_FIRST
258           && r->integer_format != INTEGER_LSB_FIRST))
259     sys_error (r, "This is not an SPSS system file.");
260   layout_code = integer_get (r->integer_format,
261                              raw_layout_code, sizeof raw_layout_code);
262
263   read_int (r);                 /* Nominal case size (not actually useful). */
264   compressed = read_int (r);
265   weight_index = read_int (r);
266   ncases = read_int (r);
267
268   r->compressed = compressed != 0;
269
270   /* Identify floating-point format and obtain compression bias. */
271   read_bytes (r, raw_bias, sizeof raw_bias);
272   if (float_identify (100.0, raw_bias, sizeof raw_bias, &r->float_format) == 0)
273     {
274       sys_warn (r, "Compression bias is not the usual value of 100, or system "
275                 "file uses unrecognized floating-point format.");
276       if (r->integer_format == INTEGER_MSB_FIRST)
277         r->float_format = FLOAT_IEEE_DOUBLE_BE;
278       else
279         r->float_format = FLOAT_IEEE_DOUBLE_LE;
280     }
281   r->bias = float_get_double (r->float_format, raw_bias);
282
283   read_string (r, creation_date, sizeof creation_date);
284   read_string (r, creation_time, sizeof creation_time);
285   read_string (r, file_label, sizeof file_label);
286   trim_spaces (file_label);
287   skip_bytes (r, 3);
288
289   printf ("File header record:\n");
290   printf ("\t%17s: %s\n", "Product name", eye_catcher);
291   printf ("\t%17s: %"PRId32"\n", "Layout code", layout_code);
292   printf ("\t%17s: %"PRId32"\n", "Compressed", compressed);
293   printf ("\t%17s: %"PRId32"\n", "Weight index", weight_index);
294   printf ("\t%17s: %"PRId32"\n", "Number of cases", ncases);
295   printf ("\t%17s: %g\n", "Compression bias", r->bias);
296   printf ("\t%17s: %s\n", "Creation date", creation_date);
297   printf ("\t%17s: %s\n", "Creation time", creation_time);
298   printf ("\t%17s: \"%s\"\n", "File label", file_label);
299 }
300
301 static const char *
302 format_name (int format)
303 {
304   switch ((format >> 16) & 0xff)
305     {
306     case 1: return "A";
307     case 2: return "AHEX";
308     case 3: return "COMMA";
309     case 4: return "DOLLAR";
310     case 5: return "F";
311     case 6: return "IB";
312     case 7: return "PIBHEX";
313     case 8: return "P";
314     case 9: return "PIB";
315     case 10: return "PK";
316     case 11: return "RB";
317     case 12: return "RBHEX";
318     case 15: return "Z";
319     case 16: return "N";
320     case 17: return "E";
321     case 20: return "DATE";
322     case 21: return "TIME";
323     case 22: return "DATETIME";
324     case 23: return "ADATE";
325     case 24: return "JDATE";
326     case 25: return "DTIME";
327     case 26: return "WKDAY";
328     case 27: return "MONTH";
329     case 28: return "MOYR";
330     case 29: return "QYR";
331     case 30: return "WKYR";
332     case 31: return "PCT";
333     case 32: return "DOT";
334     case 33: return "CCA";
335     case 34: return "CCB";
336     case 35: return "CCC";
337     case 36: return "CCD";
338     case 37: return "CCE";
339     case 38: return "EDATE";
340     case 39: return "SDATE";
341     default: return "invalid";
342     }
343 }
344
345 /* Reads a variable (type 2) record from R and adds the
346    corresponding variable to DICT.
347    Also skips past additional variable records for long string
348    variables. */
349 static void
350 read_variable_record (struct sfm_reader *r)
351 {
352   int width;
353   int has_variable_label;
354   int missing_value_code;
355   int print_format;
356   int write_format;
357   char name[9];
358
359   printf ("%08llx: variable record #%d\n",
360           (long long int) ftello (r->file), r->n_variable_records++);
361
362   width = read_int (r);
363   has_variable_label = read_int (r);
364   missing_value_code = read_int (r);
365   print_format = read_int (r);
366   write_format = read_int (r);
367   read_string (r, name, sizeof name);
368   name[strcspn (name, " ")] = '\0';
369
370   if (width >= 0)
371     r->n_variables++;
372
373   if (r->n_var_widths >= r->allocated_var_widths)
374     r->var_widths = x2nrealloc (r->var_widths, &r->allocated_var_widths,
375                                 sizeof *r->var_widths);
376   r->var_widths[r->n_var_widths++] = width;
377
378   printf ("\tWidth: %d (%s)\n",
379           width,
380           width > 0 ? "string"
381           : width == 0 ? "numeric"
382           : "long string continuation record");
383   printf ("\tVariable label: %d\n", has_variable_label);
384   printf ("\tMissing values code: %d (%s)\n", missing_value_code,
385           (missing_value_code == 0 ? "no missing values"
386            : missing_value_code == 1 ? "one missing value"
387            : missing_value_code == 2 ? "two missing values"
388            : missing_value_code == 3 ? "three missing values"
389            : missing_value_code == -2 ? "one missing value range"
390            : missing_value_code == -3 ? "one missing value, one range"
391            : "bad value"));
392   printf ("\tPrint format: %06x (%s%d.%d)\n",
393           print_format, format_name (print_format),
394           (print_format >> 8) & 0xff, print_format & 0xff);
395   printf ("\tWrite format: %06x (%s%d.%d)\n",
396           write_format, format_name (write_format),
397           (write_format >> 8) & 0xff, write_format & 0xff);
398   printf ("\tName: %s\n", name);
399
400   /* Get variable label, if any. */
401   if (has_variable_label != 0 && has_variable_label != 1)
402     sys_error (r, "Variable label indicator field is not 0 or 1.");
403   if (has_variable_label == 1)
404     {
405       long long int offset = ftello (r->file);
406       size_t len, read_len;
407       char label[255 + 1];
408
409       len = read_int (r);
410
411       /* Read up to 255 bytes of label. */
412       read_len = MIN (sizeof label - 1, len);
413       read_string (r, label, read_len + 1);
414       printf("\t%08llx Variable label: \"%s\"\n", offset, label);
415
416       /* Skip unread label bytes. */
417       skip_bytes (r, len - read_len);
418
419       /* Skip label padding up to multiple of 4 bytes. */
420       skip_bytes (r, ROUND_UP (len, 4) - len);
421     }
422
423   /* Set missing values. */
424   if (missing_value_code != 0)
425     {
426       int i;
427
428       printf ("\t%08llx Missing values:", (long long int) ftello (r->file));
429       if (!width)
430         {
431           if (missing_value_code < -3 || missing_value_code > 3
432               || missing_value_code == -1)
433             sys_error (r, "Numeric missing value indicator field is not "
434                        "-3, -2, 0, 1, 2, or 3.");
435           if (missing_value_code < 0)
436             {
437               double low = read_float (r);
438               double high = read_float (r);
439               printf (" %g...%g", low, high);
440               missing_value_code = -missing_value_code - 2;
441             }
442           for (i = 0; i < missing_value_code; i++)
443             printf (" %g", read_float (r));
444         }
445       else if (width > 0)
446         {
447           if (missing_value_code < 1 || missing_value_code > 3)
448             sys_error (r, "String missing value indicator field is not "
449                        "0, 1, 2, or 3.");
450           for (i = 0; i < missing_value_code; i++)
451             {
452               char string[9];
453               read_string (r, string, sizeof string);
454               printf (" \"%s\"", string);
455             }
456         }
457       putchar ('\n');
458     }
459 }
460
461 static void
462 print_untyped_value (struct sfm_reader *r, char raw_value[8])
463 {
464   int n_printable;
465   double value;
466
467   value = float_get_double (r->float_format, raw_value);
468   for (n_printable = 0; n_printable < 8; n_printable++)
469     if (!isprint (raw_value[n_printable]))
470       break;
471
472   printf ("%g/\"%.*s\"", value, n_printable, raw_value);
473 }
474
475 /* Reads value labels from sysfile R and inserts them into the
476    associated dictionary. */
477 static void
478 read_value_label_record (struct sfm_reader *r)
479 {
480   int label_cnt, var_cnt;
481   int i;
482
483   printf ("%08llx: value labels record\n", (long long int) ftello (r->file));
484
485   /* Read number of labels. */
486   label_cnt = read_int (r);
487   for (i = 0; i < label_cnt; i++)
488     {
489       char raw_value[8];
490       unsigned char label_len;
491       size_t padded_len;
492       char label[256];
493
494       read_bytes (r, raw_value, sizeof raw_value);
495
496       /* Read label length. */
497       read_bytes (r, &label_len, sizeof label_len);
498       padded_len = ROUND_UP (label_len + 1, 8);
499
500       /* Read label, padding. */
501       read_bytes (r, label, padded_len - 1);
502       label[label_len] = 0;
503
504       printf ("\t");
505       print_untyped_value (r, raw_value);
506       printf (": \"%s\"\n", label);
507     }
508
509   /* Now, read the type 4 record that has the list of variables
510      to which the value labels are to be applied. */
511
512   /* Read record type of type 4 record. */
513   if (read_int (r) != 4)
514     sys_error (r, "Variable index record (type 4) does not immediately "
515                "follow value label record (type 3) as it should.");
516
517   /* Read number of variables associated with value label from type 4
518      record. */
519   printf ("\t%08llx: apply to variables", (long long int) ftello (r->file));
520   var_cnt = read_int (r);
521   for (i = 0; i < var_cnt; i++)
522     printf (" #%d", read_int (r));
523   putchar ('\n');
524 }
525
526 static void
527 read_document_record (struct sfm_reader *r)
528 {
529   int n_lines;
530   int i;
531
532   printf ("%08llx: document record\n", (long long int) ftello (r->file));
533   n_lines = read_int (r);
534   printf ("\t%d lines of documents\n", n_lines);
535
536   for (i = 0; i < n_lines; i++)
537     {
538       char line[81];
539       printf ("\t%08llx: ", (long long int) ftello (r->file));
540       read_string (r, line, sizeof line);
541       trim_spaces (line);
542       printf ("line %d: \"%s\"\n", i, line);
543     }
544 }
545
546 static void
547 read_extension_record (struct sfm_reader *r)
548 {
549   long long int offset = ftello (r->file);
550   int subtype = read_int (r);
551   size_t size = read_int (r);
552   size_t count = read_int (r);
553   size_t bytes = size * count;
554
555   printf ("%08llx: Record 7, subtype %d, size=%zu, count=%zu\n",
556           offset, subtype, size, count);
557
558   switch (subtype)
559     {
560     case 3:
561       read_machine_integer_info (r, size, count);
562       return;
563
564     case 4:
565       read_machine_float_info (r, size, count);
566       return;
567
568     case 5:
569       /* Variable sets information.  We don't use these yet.
570          They only apply to GUIs; see VARSETS on the APPLY
571          DICTIONARY command in SPSS documentation. */
572       break;
573
574     case 6:
575       /* DATE variable information.  We don't use it yet, but we
576          should. */
577       break;
578
579     case 7:
580     case 19:
581       read_mrsets (r, size, count);
582       return;
583
584     case 10:
585       read_extra_product_info (r, size, count);
586       return;
587
588     case 11:
589       read_display_parameters (r, size, count);
590       return;
591
592     case 13:
593       read_long_var_name_map (r, size, count);
594       return;
595
596     case 14:
597       read_long_string_map (r, size, count);
598       return;
599
600     case 16:
601       read_ncases64 (r, size, count);
602       return;
603
604     case 17:
605       read_datafile_attributes (r, size, count);
606       return;
607
608     case 18:
609       read_variable_attributes (r, size, count);
610       return;
611
612     case 20:
613       read_character_encoding (r, size, count);
614       return;
615
616     case 21:
617       read_long_string_value_labels (r, size, count);
618       return;
619
620     case 22:
621       read_long_string_missing_values (r, size, count);
622       return;
623
624     default:
625       sys_warn (r, "Unrecognized record type 7, subtype %d.", subtype);
626       read_unknown_extension (r, size, count);
627       return;
628     }
629
630   skip_bytes (r, bytes);
631 }
632
633 static void
634 read_machine_integer_info (struct sfm_reader *r, size_t size, size_t count)
635 {
636   long long int offset = ftello (r->file);
637   int version_major = read_int (r);
638   int version_minor = read_int (r);
639   int version_revision = read_int (r);
640   int machine_code = read_int (r);
641   int float_representation = read_int (r);
642   int compression_code = read_int (r);
643   int integer_representation = read_int (r);
644   int character_code = read_int (r);
645
646   printf ("%08llx: machine integer info\n", offset);
647   if (size != 4 || count != 8)
648     sys_error (r, "Bad size (%zu) or count (%zu) field on record type 7, "
649                "subtype 3.", size, count);
650
651   printf ("\tVersion: %d.%d.%d\n",
652           version_major, version_minor, version_revision);
653   printf ("\tMachine code: %d\n", machine_code);
654   printf ("\tFloating point representation: %d (%s)\n",
655           float_representation,
656           float_representation == 1 ? "IEEE 754"
657           : float_representation == 2 ? "IBM 370"
658           : float_representation == 3 ? "DEC VAX"
659           : "unknown");
660   printf ("\tCompression code: %d\n", compression_code);
661   printf ("\tEndianness: %d (%s)\n", integer_representation,
662           integer_representation == 1 ? "big"
663           : integer_representation == 2 ? "little" : "unknown");
664   printf ("\tCharacter code: %d\n", character_code);
665 }
666
667 /* Read record type 7, subtype 4. */
668 static void
669 read_machine_float_info (struct sfm_reader *r, size_t size, size_t count)
670 {
671   long long int offset = ftello (r->file);
672   double sysmis = read_float (r);
673   double highest = read_float (r);
674   double lowest = read_float (r);
675
676   printf ("%08llx: machine float info\n", offset);
677   if (size != 8 || count != 3)
678     sys_error (r, "Bad size (%zu) or count (%zu) on extension 4.",
679                size, count);
680
681   printf ("\tsysmis: %g (%a)\n", sysmis, sysmis);
682   if (sysmis != SYSMIS)
683     sys_warn (r, "File specifies unexpected value %g (%a) as %s.",
684               sysmis, sysmis, "SYSMIS");
685
686   printf ("\thighest: %g (%a)\n", highest, highest);
687   if (highest != HIGHEST)
688     sys_warn (r, "File specifies unexpected value %g (%a) as %s.",
689               highest, highest, "HIGHEST");
690
691   printf ("\tlowest: %g (%a)\n", lowest, lowest);
692   if (lowest != LOWEST && lowest != SYSMIS)
693     sys_warn (r, "File specifies unexpected value %g (%a) as %s.",
694               lowest, lowest, "LOWEST");
695 }
696
697 static void
698 read_extra_product_info (struct sfm_reader *r,
699                          size_t size, size_t count)
700 {
701   struct text_record *text;
702   const char *s;
703
704   printf ("%08llx: extra product info\n", (long long int) ftello (r->file));
705   text = open_text_record (r, size * count);
706   s = text_get_all (text);
707   print_string (s, strlen (s));
708   close_text_record (text);
709 }
710
711 /* Read record type 7, subtype 7. */
712 static void
713 read_mrsets (struct sfm_reader *r, size_t size, size_t count)
714 {
715   struct text_record *text;
716
717   printf ("%08llx: multiple response sets\n",
718           (long long int) ftello (r->file));
719   text = open_text_record (r, size * count);
720   for (;;)
721     {
722       const char *name;
723       enum { MRSET_MC, MRSET_MD } type;
724       bool cat_label_from_counted_values = false;
725       bool label_from_var_label = false;
726       const char *counted;
727       const char *label;
728       const char *variables;
729
730       name = text_tokenize (text, '=');
731       if (name == NULL)
732         break;
733
734       if (text_match (text, 'C'))
735         {
736           type = MRSET_MC;
737           counted = NULL;
738           if (!text_match (text, ' '))
739             {
740               sys_warn (r, "missing space following 'C' at offset %zu "
741                         "in mrsets record", text_pos (text));
742               break;
743             }
744         }
745       else if (text_match (text, 'D'))
746         {
747           type = MRSET_MD;
748         }
749       else if (text_match (text, 'E'))
750         {
751           char *number;
752
753           type = MRSET_MD;
754           cat_label_from_counted_values = true;
755
756           if (!text_match (text, ' '))
757             {
758               sys_warn (r, "Missing space following `%c' at offset %zu "
759                         "in MRSETS record", 'E', text_pos (text));
760               break;
761             }
762
763           number = text_tokenize (text, ' ');
764           if (!strcmp (number, "11"))
765             label_from_var_label = true;
766           else if (strcmp (number, "1"))
767             sys_warn (r, "Unexpected label source value `%s' "
768                       "following `E' at offset %zu in MRSETS record",
769                       number, text_pos (text));
770
771         }
772       else
773         {
774           sys_warn (r, "missing `C', `D', or `E' at offset %zu "
775                     "in mrsets record", text_pos (text));
776           break;
777         }
778
779       if (type == MRSET_MD)
780         {
781           counted = text_parse_counted_string (text);
782           if (counted == NULL)
783             break;
784         }
785
786       label = text_parse_counted_string (text);
787       if (label == NULL)
788         break;
789
790       variables = text_tokenize (text, '\n');
791       if (variables == NULL)
792         {
793           sys_warn (r, "missing variable names following label "
794                     "at offset %zu in mrsets record", text_pos (text));
795           break;
796         }
797
798       printf ("\t\"%s\": multiple %s set",
799               name, type == MRSET_MC ? "category" : "dichotomy");
800       if (counted != NULL)
801         printf (", counted value \"%s\"", counted);
802       if (cat_label_from_counted_values)
803         printf (", category labels from counted values");
804       if (label[0] != '\0')
805         printf (", label \"%s\"", label);
806       if (label_from_var_label)
807         printf (", label from variable label");
808       printf(", variables \"%s\"\n", variables);
809     }
810   close_text_record (text);
811 }
812
813 /* Read record type 7, subtype 11. */
814 static void
815 read_display_parameters (struct sfm_reader *r, size_t size, size_t count)
816 {
817   size_t n_vars;
818   bool includes_width;
819   size_t i;
820
821   printf ("%08llx: variable display parameters\n",
822           (long long int) ftello (r->file));
823   if (size != 4)
824     {
825       sys_warn (r, "Bad size %zu on extension 11.", size);
826       skip_bytes (r, size * count);
827       return;
828     }
829
830   n_vars = r->n_variables;
831   if (count == 3 * n_vars)
832     includes_width = true;
833   else if (count == 2 * n_vars)
834     includes_width = false;
835   else
836     {
837       sys_warn (r, "Extension 11 has bad count %zu (for %zu variables.",
838                 count, n_vars);
839       skip_bytes (r, size * count);
840       return;
841     }
842
843   for (i = 0; i < n_vars; ++i)
844     {
845       int measure = read_int (r);
846       int width = includes_width ? read_int (r) : 0;
847       int align = read_int (r);
848
849       printf ("\tVar #%zu: measure=%d (%s)", i, measure,
850               (measure == 1 ? "nominal"
851                : measure == 2 ? "ordinal"
852                : measure == 3 ? "scale"
853                : "invalid"));
854       if (includes_width)
855         printf (", width=%d", width);
856       printf (", align=%d (%s)\n", align,
857               (align == 0 ? "left"
858                : align == 1 ? "right"
859                : align == 2 ? "centre"
860                : "invalid"));
861     }
862 }
863
864 /* Reads record type 7, subtype 13, which gives the long name
865    that corresponds to each short name.  */
866 static void
867 read_long_var_name_map (struct sfm_reader *r, size_t size, size_t count)
868 {
869   struct text_record *text;
870   char *var;
871   char *long_name;
872
873   printf ("%08llx: long variable names (short => long)\n",
874           (long long int) ftello (r->file));
875   text = open_text_record (r, size * count);
876   while (read_variable_to_value_pair (text, &var, &long_name))
877     printf ("\t%s => %s\n", var, long_name);
878   close_text_record (text);
879 }
880
881 /* Reads record type 7, subtype 14, which gives the real length
882    of each very long string.  Rearranges DICT accordingly. */
883 static void
884 read_long_string_map (struct sfm_reader *r, size_t size, size_t count)
885 {
886   struct text_record *text;
887   char *var;
888   char *length_s;
889
890   printf ("%08llx: very long strings (variable => length)\n",
891           (long long int) ftello (r->file));
892   text = open_text_record (r, size * count);
893   while (read_variable_to_value_pair (text, &var, &length_s))
894     printf ("\t%s => %d\n", var, atoi (length_s));
895   close_text_record (text);
896 }
897
898 static bool
899 read_attributes (struct sfm_reader *r, struct text_record *text,
900                  const char *variable)
901 {
902   const char *key;
903   int index;
904
905   for (;;) 
906     {
907       key = text_tokenize (text, '(');
908       if (key == NULL)
909         return true;
910   
911       for (index = 1; ; index++)
912         {
913           /* Parse the value. */
914           const char *value = text_tokenize (text, '\n');
915           if (value == NULL) 
916             {
917               sys_warn (r, "%s: Error parsing attribute value %s[%d]",
918                         variable, key, index);
919               return false;
920             }
921           if (strlen (value) < 2
922               || value[0] != '\'' || value[strlen (value) - 1] != '\'')
923             sys_warn (r, "%s: Attribute value %s[%d] is not quoted: %s",
924                       variable, key, index, value);
925           else
926             printf ("\t%s: %s[%d] = \"%.*s\"\n",
927                     variable, key, index, (int) strlen (value) - 2, value + 1);
928
929           /* Was this the last value for this attribute? */
930           if (text_match (text, ')'))
931             break;
932         }
933
934       if (text_match (text, '/'))
935         return true; 
936     }
937 }
938
939 /* Read extended number of cases record. */
940 static void
941 read_ncases64 (struct sfm_reader *r, size_t size, size_t count)
942 {
943   int64_t unknown, ncases64;
944
945   if (size != 8)
946     {
947       sys_warn (r, "Bad size %zu for extended number of cases.", size);
948       skip_bytes (r, size * count);
949       return;
950     }
951   if (count != 2)
952     {
953       sys_warn (r, "Bad count %zu for extended number of cases.", size);
954       skip_bytes (r, size * count);
955       return;
956     }
957   unknown = read_int64 (r);
958   ncases64 = read_int64 (r);
959   printf ("%08llx: extended number of cases: "
960           "unknown=%"PRId64", ncases64=%"PRId64"\n",
961           (long long int) ftello (r->file), unknown, ncases64);
962 }
963
964 static void
965 read_datafile_attributes (struct sfm_reader *r, size_t size, size_t count) 
966 {
967   struct text_record *text;
968   
969   printf ("%08llx: datafile attributes\n", (long long int) ftello (r->file));
970   text = open_text_record (r, size * count);
971   read_attributes (r, text, "datafile");
972   close_text_record (text);
973 }
974
975 static void
976 read_character_encoding (struct sfm_reader *r, size_t size, size_t count)
977 {
978   long long int posn =  ftello (r->file);
979   char *encoding = xcalloc (size, count + 1);
980   read_string (r, encoding, count + 1);
981
982   printf ("%08llx: Character Encoding: %s\n", posn, encoding);
983 }
984
985 static void
986 read_long_string_value_labels (struct sfm_reader *r, size_t size, size_t count)
987 {
988   long long int start = ftello (r->file);
989
990   printf ("%08llx: long string value labels\n", start);
991   while (ftello (r->file) - start < size * count)
992     {
993       long long posn = ftello (r->file);
994       char var_name[ID_MAX_LEN + 1];
995       int var_name_len;
996       int n_values;
997       int width;
998       int i;
999
1000       /* Read variable name. */
1001       var_name_len = read_int (r);
1002       if (var_name_len > ID_MAX_LEN)
1003         sys_error (r, "Variable name length in long string value label "
1004                    "record (%d) exceeds %d-byte limit.",
1005                    var_name_len, ID_MAX_LEN);
1006       read_string (r, var_name, var_name_len + 1);
1007
1008       /* Read width, number of values. */
1009       width = read_int (r);
1010       n_values = read_int (r);
1011
1012       printf ("\t%08llx: %s, width %d, %d values\n",
1013               posn, var_name, width, n_values);
1014
1015       /* Read values. */
1016       for (i = 0; i < n_values; i++)
1017         {
1018           char *value;
1019           int value_length;
1020
1021           char *label;
1022           int label_length;
1023
1024           posn = ftello (r->file);
1025
1026           /* Read value. */
1027           value_length = read_int (r);
1028           value = xmalloc (value_length + 1);
1029           read_string (r, value, value_length + 1);
1030
1031           /* Read label. */
1032           label_length = read_int (r);
1033           label = xmalloc (label_length + 1);
1034           read_string (r, label, label_length + 1);
1035
1036           printf ("\t\t%08llx: \"%s\" (%d bytes) => \"%s\" (%d bytes)\n",
1037                   posn, value, value_length, label, label_length);
1038
1039           free (value);
1040           free (label);
1041         }
1042     }
1043 }
1044
1045 static void
1046 read_long_string_missing_values (struct sfm_reader *r,
1047                                  size_t size, size_t count)
1048 {
1049   long long int start = ftello (r->file);
1050
1051   printf ("%08llx: long string missing values\n", start);
1052   while (ftello (r->file) - start < size * count)
1053     {
1054       long long posn = ftello (r->file);
1055       char var_name[ID_MAX_LEN + 1];
1056       uint8_t n_missing_values;
1057       int var_name_len;
1058       int i;
1059
1060       /* Read variable name. */
1061       var_name_len = read_int (r);
1062       if (var_name_len > ID_MAX_LEN)
1063         sys_error (r, "Variable name length in long string value label "
1064                    "record (%d) exceeds %d-byte limit.",
1065                    var_name_len, ID_MAX_LEN);
1066       read_string (r, var_name, var_name_len + 1);
1067
1068       /* Read number of values. */
1069       read_bytes (r, &n_missing_values, 1);
1070
1071       printf ("\t%08llx: %s, %d missing values:",
1072               posn, var_name, n_missing_values);
1073
1074       /* Read values. */
1075       for (i = 0; i < n_missing_values; i++)
1076         {
1077           char *value;
1078           int value_length;
1079
1080           posn = ftello (r->file);
1081
1082           /* Read value. */
1083           value_length = read_int (r);
1084           value = xmalloc (value_length + 1);
1085           read_string (r, value, value_length + 1);
1086
1087           printf (" \"%s\"", value);
1088
1089           free (value);
1090         }
1091       printf ("\n");
1092     }
1093 }
1094
1095 static void
1096 hex_dump (size_t offset, const void *buffer_, size_t buffer_size)
1097 {
1098   const uint8_t *buffer = buffer_;
1099
1100   while (buffer_size > 0)
1101     {
1102       size_t n = MIN (buffer_size, 16);
1103       size_t i;
1104
1105       printf ("%04zx", offset);
1106       for (i = 0; i < 16; i++)
1107         {
1108           if (i < n)
1109             printf ("%c%02x", i == 8 ? '-' : ' ', buffer[i]);
1110           else
1111             printf ("   ");
1112         }
1113
1114       printf (" |");
1115       for (i = 0; i < 16; i++)
1116         {
1117           unsigned char c = i < n ? buffer[i] : ' ';
1118           putchar (isprint (c) ? c : '.');
1119         }
1120       printf ("|\n");
1121
1122       offset += n;
1123       buffer += n;
1124       buffer_size -= n;
1125     }
1126 }
1127
1128 /* Reads and prints any type 7 record that we don't understand. */
1129 static void
1130 read_unknown_extension (struct sfm_reader *r, size_t size, size_t count)
1131 {
1132   unsigned char *buffer;
1133   size_t i;
1134
1135   if (size == 0 || count > 65536 / size)
1136     skip_bytes (r, size * count);
1137   else if (size != 1)
1138     {
1139       buffer = xmalloc (size);
1140       for (i = 0; i < count; i++)
1141         {
1142           read_bytes (r, buffer, size);
1143           hex_dump (i * size, buffer, size);
1144         }
1145       free (buffer);
1146     }
1147   else
1148     {
1149       buffer = xmalloc (count);
1150       read_bytes (r, buffer, count);
1151       print_string (CHAR_CAST (char *, buffer), count);
1152       free (buffer);
1153     }
1154 }
1155
1156 static void
1157 read_variable_attributes (struct sfm_reader *r, size_t size, size_t count) 
1158 {
1159   struct text_record *text;
1160   
1161   printf ("%08llx: variable attributes\n", (long long int) ftello (r->file));
1162   text = open_text_record (r, size * count);
1163   for (;;) 
1164     {
1165       const char *variable = text_tokenize (text, ':');
1166       if (variable == NULL || !read_attributes (r, text, variable))
1167         break; 
1168     }
1169   close_text_record (text);
1170 }
1171
1172 static void
1173 read_compressed_data (struct sfm_reader *r, int max_cases)
1174 {
1175   enum { N_OPCODES = 8 };
1176   uint8_t opcodes[N_OPCODES];
1177   long long int opcode_ofs;
1178   int opcode_idx;
1179   int case_num;
1180   int i;
1181
1182   read_int (r);
1183   printf ("\n%08llx: compressed data:\n", (long long int) ftello (r->file));
1184
1185   opcode_idx = N_OPCODES;
1186   opcode_ofs = 0;
1187   case_num = 0;
1188   for (case_num = 0; case_num < max_cases; case_num++)
1189     {
1190       printf ("%08llx: case %d's uncompressible data begins\n",
1191               (long long int) ftello (r->file), case_num);
1192       for (i = 0; i < r->n_var_widths; )
1193         {
1194           int width = r->var_widths[i];
1195           char raw_value[8];
1196           int opcode;
1197
1198           if (opcode_idx >= N_OPCODES)
1199             {
1200               opcode_ofs = ftello (r->file);
1201               if (i == 0)
1202                 {
1203                   if (!try_read_bytes (r, opcodes, 8))
1204                     return;
1205                 }
1206               else
1207                 read_bytes (r, opcodes, 8);
1208               opcode_idx = 0;
1209             }
1210           opcode = opcodes[opcode_idx];
1211           printf ("%08llx: variable %d: opcode %d: ",
1212                   opcode_ofs + opcode_idx, i, opcode);
1213
1214           switch (opcode)
1215             {
1216             default:
1217               printf ("%g", opcode - r->bias);
1218               if (width != 0)
1219                 printf (", but this is a string variable (width=%d)", width);
1220               printf ("\n");
1221               i++;
1222               break;
1223
1224             case 0:
1225               printf ("ignored padding\n");
1226               break;
1227
1228             case 252:
1229               printf ("end of data\n");
1230               return;
1231
1232             case 253:
1233               read_bytes (r, raw_value, 8);
1234               printf ("uncompressible data: ");
1235               print_untyped_value (r, raw_value);
1236               printf ("\n");
1237               i++;
1238               break;
1239
1240             case 254:
1241               printf ("spaces");
1242               if (width == 0)
1243                 printf (", but this is a numeric variable");
1244               printf ("\n");
1245               i++;
1246               break;
1247
1248             case 255:
1249               printf ("SYSMIS");
1250               if (width != 0)
1251                 printf (", but this is a string variable (width=%d)", width);
1252               printf ("\n");
1253               i++;
1254               break;
1255             }
1256
1257           opcode_idx++;
1258         }
1259     }
1260 }
1261 \f
1262 /* Helpers for reading records that consist of structured text
1263    strings. */
1264
1265 /* State. */
1266 struct text_record
1267   {
1268     struct sfm_reader *reader;  /* Reader. */
1269     char *buffer;               /* Record contents. */
1270     size_t size;                /* Size of buffer. */
1271     size_t pos;                 /* Current position in buffer. */
1272   };
1273
1274 /* Reads SIZE bytes into a text record for R,
1275    and returns the new text record. */
1276 static struct text_record *
1277 open_text_record (struct sfm_reader *r, size_t size)
1278 {
1279   struct text_record *text = xmalloc (sizeof *text);
1280   char *buffer = xmalloc (size + 1);
1281   read_bytes (r, buffer, size);
1282   buffer[size] = '\0';
1283   text->reader = r;
1284   text->buffer = buffer;
1285   text->size = size;
1286   text->pos = 0;
1287   return text;
1288 }
1289
1290 /* Closes TEXT and frees its storage.
1291    Not really needed, because the pool will free the text record anyway,
1292    but can be used to free it earlier. */
1293 static void
1294 close_text_record (struct text_record *text)
1295 {
1296   free (text->buffer);
1297   free (text);
1298 }
1299
1300 static char *
1301 text_tokenize (struct text_record *text, int delimiter)
1302 {
1303   size_t start = text->pos;
1304   while (text->pos < text->size
1305          && text->buffer[text->pos] != delimiter
1306          && text->buffer[text->pos] != '\0')
1307     text->pos++;
1308   if (start == text->pos)
1309     return NULL;
1310   text->buffer[text->pos++] = '\0';
1311   return &text->buffer[start];
1312 }
1313
1314 static bool
1315 text_match (struct text_record *text, int c) 
1316 {
1317   if (text->pos < text->size && text->buffer[text->pos] == c) 
1318     {
1319       text->pos++;
1320       return true;
1321     }
1322   else
1323     return false;
1324 }
1325
1326 /* Reads a integer value expressed in decimal, then a space, then a string that
1327    consists of exactly as many bytes as specified by the integer, then a space,
1328    from TEXT.  Returns the string, null-terminated, as a subset of TEXT's
1329    buffer (so the caller should not free the string). */
1330 static const char *
1331 text_parse_counted_string (struct text_record *text)
1332 {
1333   size_t start;
1334   size_t n;
1335   char *s;
1336
1337   start = text->pos;
1338   n = 0;
1339   while (isdigit ((unsigned char) text->buffer[text->pos]))
1340     n = (n * 10) + (text->buffer[text->pos++] - '0');
1341   if (start == text->pos)
1342     {
1343       sys_error (text->reader, "expecting digit at offset %zu in record",
1344                  text->pos);
1345       return NULL;
1346     }
1347
1348   if (!text_match (text, ' '))
1349     {
1350       sys_error (text->reader, "expecting space at offset %zu in record",
1351                  text->pos);
1352       return NULL;
1353     }
1354
1355   if (text->pos + n > text->size)
1356     {
1357       sys_error (text->reader, "%zu-byte string starting at offset %zu "
1358                  "exceeds record length %zu", n, text->pos, text->size);
1359       return NULL;
1360     }
1361
1362   s = &text->buffer[text->pos];
1363   if (s[n] != ' ')
1364     {
1365       sys_error (text->reader, "expecting space at offset %zu following "
1366                  "%zu-byte string", text->pos + n, n);
1367       return NULL;
1368     }
1369   s[n] = '\0';
1370   text->pos += n + 1;
1371   return s;
1372 }
1373
1374 /* Reads a variable=value pair from TEXT.
1375    Looks up the variable in DICT and stores it into *VAR.
1376    Stores a null-terminated value into *VALUE. */
1377 static bool
1378 read_variable_to_value_pair (struct text_record *text,
1379                              char **key, char **value)
1380 {
1381   *key = text_tokenize (text, '=');
1382   *value = text_tokenize (text, '\t');
1383   if (!*key || !*value)
1384     return false;
1385
1386   while (text->pos < text->size
1387          && (text->buffer[text->pos] == '\t'
1388              || text->buffer[text->pos] == '\0'))
1389     text->pos++;
1390   return true;
1391 }
1392
1393 /* Returns the current byte offset inside the TEXT's string. */
1394 static size_t
1395 text_pos (const struct text_record *text)
1396 {
1397   return text->pos;
1398 }
1399
1400 static const char *
1401 text_get_all (const struct text_record *text)
1402 {
1403   return text->buffer;
1404 }
1405 \f
1406 static void
1407 usage (void)
1408 {
1409   printf ("\
1410 %s, a utility for dissecting system files.\n\
1411 Usage: %s [OPTION]... SYSFILE...\n\
1412 where each SYSFILE is the name of a system file.\n\
1413 \n\
1414 Options:\n\
1415   --data[=MAXCASES]   print (up to MAXCASES cases of) compressed data\n\
1416   --help              display this help and exit\n\
1417   --version           output version information and exit\n",
1418           program_name, program_name);
1419 }
1420
1421 /* Displays a corruption message. */
1422 static void
1423 sys_msg (struct sfm_reader *r, const char *format, va_list args)
1424 {
1425   printf ("\"%s\" near offset 0x%llx: ",
1426           r->file_name, (long long int) ftello (r->file));
1427   vprintf (format, args);
1428   putchar ('\n');
1429 }
1430
1431 /* Displays a warning for the current file position. */
1432 static void
1433 sys_warn (struct sfm_reader *r, const char *format, ...)
1434 {
1435   va_list args;
1436
1437   va_start (args, format);
1438   sys_msg (r, format, args);
1439   va_end (args);
1440 }
1441
1442 /* Displays an error for the current file position,
1443    marks it as in an error state,
1444    and aborts reading it using longjmp. */
1445 static void
1446 sys_error (struct sfm_reader *r, const char *format, ...)
1447 {
1448   va_list args;
1449
1450   va_start (args, format);
1451   sys_msg (r, format, args);
1452   va_end (args);
1453
1454   exit (EXIT_FAILURE);
1455 }
1456 \f
1457 /* Reads BYTE_CNT bytes into BUF.
1458    Returns true if exactly BYTE_CNT bytes are successfully read.
1459    Aborts if an I/O error or a partial read occurs.
1460    If EOF_IS_OK, then an immediate end-of-file causes false to be
1461    returned; otherwise, immediate end-of-file causes an abort
1462    too. */
1463 static inline bool
1464 read_bytes_internal (struct sfm_reader *r, bool eof_is_ok,
1465                      void *buf, size_t byte_cnt)
1466 {
1467   size_t bytes_read = fread (buf, 1, byte_cnt, r->file);
1468   if (bytes_read == byte_cnt)
1469     return true;
1470   else if (ferror (r->file))
1471     sys_error (r, "System error: %s.", strerror (errno));
1472   else if (!eof_is_ok || bytes_read != 0)
1473     sys_error (r, "Unexpected end of file.");
1474   else
1475     return false;
1476 }
1477
1478 /* Reads BYTE_CNT into BUF.
1479    Aborts upon I/O error or if end-of-file is encountered. */
1480 static void
1481 read_bytes (struct sfm_reader *r, void *buf, size_t byte_cnt)
1482 {
1483   read_bytes_internal (r, false, buf, byte_cnt);
1484 }
1485
1486 /* Reads BYTE_CNT bytes into BUF.
1487    Returns true if exactly BYTE_CNT bytes are successfully read.
1488    Returns false if an immediate end-of-file is encountered.
1489    Aborts if an I/O error or a partial read occurs. */
1490 static bool
1491 try_read_bytes (struct sfm_reader *r, void *buf, size_t byte_cnt)
1492 {
1493   return read_bytes_internal (r, true, buf, byte_cnt);
1494 }
1495
1496 /* Reads a 32-bit signed integer from R and returns its value in
1497    host format. */
1498 static int
1499 read_int (struct sfm_reader *r)
1500 {
1501   uint8_t integer[4];
1502   read_bytes (r, integer, sizeof integer);
1503   return integer_get (r->integer_format, integer, sizeof integer);
1504 }
1505
1506 /* Reads a 64-bit signed integer from R and returns its value in
1507    host format. */
1508 static int64_t
1509 read_int64 (struct sfm_reader *r)
1510 {
1511   uint8_t integer[8];
1512   read_bytes (r, integer, sizeof integer);
1513   return integer_get (r->integer_format, integer, sizeof integer);
1514 }
1515
1516 /* Reads a 64-bit floating-point number from R and returns its
1517    value in host format. */
1518 static double
1519 read_float (struct sfm_reader *r)
1520 {
1521   uint8_t number[8];
1522   read_bytes (r, number, sizeof number);
1523   return float_get_double (r->float_format, number);
1524 }
1525
1526 /* Reads exactly SIZE - 1 bytes into BUFFER
1527    and stores a null byte into BUFFER[SIZE - 1]. */
1528 static void
1529 read_string (struct sfm_reader *r, char *buffer, size_t size)
1530 {
1531   assert (size > 0);
1532   read_bytes (r, buffer, size - 1);
1533   buffer[size - 1] = '\0';
1534 }
1535
1536 /* Skips BYTES bytes forward in R. */
1537 static void
1538 skip_bytes (struct sfm_reader *r, size_t bytes)
1539 {
1540   while (bytes > 0)
1541     {
1542       char buffer[1024];
1543       size_t chunk = MIN (sizeof buffer, bytes);
1544       read_bytes (r, buffer, chunk);
1545       bytes -= chunk;
1546     }
1547 }
1548
1549 static void
1550 trim_spaces (char *s)
1551 {
1552   char *end = strchr (s, '\0');
1553   while (end > s && end[-1] == ' ')
1554     end--;
1555   *end = '\0';
1556 }
1557
1558 static void
1559 print_string (const char *s, size_t len)
1560 {
1561   if (memchr (s, 0, len) == 0)
1562     {
1563       size_t i;
1564
1565       for (i = 0; i < len; i++)
1566         {
1567           unsigned char c = s[i];
1568
1569           if (c == '\\')
1570             printf ("\\\\");
1571           else if (c == '\n' || isprint (c))
1572             putchar (c);
1573           else
1574             printf ("\\%02x", c);
1575         }
1576       putchar ('\n');
1577     }
1578   else
1579     hex_dump (0, s, len);
1580 }