dump: Rename dump_value__() to just dump_value().
[pspp] / dump.c
1 #include <float.h>
2 #include <stdbool.h>
3 #include <stdint.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9
10 static uint8_t *data;
11 static size_t n;
12
13 static bool
14 all_ascii(const uint8_t *p, size_t n)
15 {
16   for (size_t i = 0; i < n; i++)
17     if (p[i] < 32 || p[i] > 126)
18       return false;
19   return true;
20 }
21
22 static size_t
23 try_find(const char *target, size_t target_len)
24 {
25   const uint8_t *pos = (const uint8_t *) memmem (data, n, target, target_len);
26   return pos ? pos - data : 0;
27 }
28
29 static size_t
30 find(const char *target, size_t target_len)
31 {
32   size_t pos = try_find(target, target_len);
33   if (!pos)
34     {
35       fprintf (stderr, "not found\n");
36       exit(1);
37     }
38   return pos;
39 }
40
41 size_t pos;
42
43 #define XSTR(x) #x
44 #define STR(x) XSTR(x)
45 #define WHERE __FILE__":" STR(__LINE__)
46
47 static unsigned int
48 get_u32(void)
49 {
50   uint32_t x;
51   memcpy(&x, &data[pos], 4);
52   pos += 4;
53   return x;
54 }
55
56 static double
57 get_double(void)
58 {
59   double x;
60   memcpy(&x, &data[pos], 8);
61   pos += 8;
62   return x;
63 }
64
65 static bool
66 match_u32(uint32_t x)
67 {
68   if (get_u32() == x)
69     return true;
70   pos -= 4;
71   return false;
72 }
73
74 static void
75 match_u32_assert(uint32_t x, const char *where)
76 {
77   unsigned int y = get_u32();
78   if (x != y)
79     {
80       fprintf(stderr, "%s: 0x%x: expected i%u, got i%u\n", where, pos - 4, x, y);
81       exit(1);
82     }
83 }
84 #define match_u32_assert(x) match_u32_assert(x, WHERE)
85
86 static bool
87 match_byte(uint8_t b)
88 {
89   if (pos < n && data[pos] == b)
90     {
91       pos++;
92       return true;
93     }
94   else
95     return false;
96 }
97
98 static void
99 match_byte_assert(uint8_t b, const char *where)
100 {
101   if (!match_byte(b))
102     {
103       fprintf(stderr, "%s: 0x%x: expected %02x, got %02x\n", where, pos, b, data[pos]);
104       exit(1);
105     }
106 }
107 #define match_byte_assert(b) match_byte_assert(b, WHERE)
108
109 static void
110 dump_raw(FILE *stream, int start, int end, const char *separator)
111 {
112   for (size_t i = start; i < end; )
113     {
114       if (i + 5 <= n
115           && data[i]
116           //&& !data[i + 1]
117           && !data[i + 2]
118           && !data[i + 3]
119           && i + 4 + data[i] + data[i + 1] * 256 <= end
120           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
121         {
122           fprintf(stream, "%s\"", separator);
123           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stream);
124           fputs("\" ", stream);
125
126           i += 4 + data[i] + data[i + 1] * 256;
127         }
128       else if (i + 12 <= end
129                && data[i + 1] == 40
130                && data[i + 2] == 5
131                && data[i + 3] == 0)
132         {
133           double d;
134
135           memcpy (&d, &data[i + 4], 8);
136           fprintf (stream, "F40.%d(%.*f)%s", data[i], data[i], d, separator);
137           i += 12;
138         }
139       else if (i + 12 <= end
140                && data[i + 1] == 40
141                && data[i + 2] == 31
142                && data[i + 3] == 0)
143         {
144           double d;
145
146           memcpy (&d, &data[i + 4], 8);
147           fprintf (stream, "PCT40.%d(%.*f)%s", data[i], data[i], d, separator);
148           i += 12;
149         }
150       else if (i + 4 <= end
151                && (data[i] && data[i] != 88 && data[i] != 0x41)
152                && !data[i + 1]
153                && !data[i + 2]
154                && !data[i + 3])
155         {
156           fprintf (stream, "i%d ", data[i]);
157           i += 4;
158         }
159       else
160         {
161           fprintf(stream, "%02x ", data[i]);
162           i++;
163         }
164     }
165
166
167 }
168
169 static char *
170 get_string(const char *where)
171 {
172   if (1
173       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
174       /*&& all_ascii(&data[pos + 4], data[pos])*/)
175     {
176       int len = data[pos] + data[pos + 1] * 256;
177       char *s = malloc(len + 1);
178
179       memcpy(s, &data[pos + 4], len);
180       s[len] = 0;
181       pos += 4 + len;
182       return s;
183     }
184   else
185     {
186       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
187       exit(1);
188     }
189 }
190 #define get_string() get_string(WHERE)
191
192 static char *
193 dump_nested_string(void)
194 {
195   char *s = NULL;
196
197   match_byte_assert (0);
198   match_byte_assert (0);
199   int outer_end = pos + get_u32();
200   int inner_end = pos + get_u32();
201   if (pos != inner_end)
202     {
203       match_u32_assert(0);
204       if (match_byte(0x31))
205         s = get_string();
206       else
207         match_byte_assert(0x58);
208       if (pos != inner_end)
209         {
210           fprintf(stderr, "inner end discrepancy\n");
211           exit(1);
212         }
213     }
214   match_byte_assert(0x58);
215   match_byte_assert(0x58);
216   if (pos != outer_end)
217     {
218       fprintf(stderr, "outer end discrepancy\n");
219       exit(1);
220     }
221
222   return s;
223 }
224
225 static void
226 dump_value_31(FILE *stream)
227 {
228   if (match_byte (0x31))
229     {
230       if (match_u32 (0))
231         {
232           if (match_u32 (1))
233             {
234               /* Only "a" observed as a sample value (although it appears 44 times in the corpus). */
235               get_string();
236             }
237           else
238             match_u32_assert (0);
239
240           int outer_end = pos + get_u32();
241           int inner_end = pos + get_u32();
242           match_u32_assert(0);
243           if (match_byte(0x31))
244             {
245               /* Appears to be a template string, e.g. '^1 cells (^2) expf < 5. Min exp = ^3...'.
246                  Probably doesn't actually appear in output because many examples look unpolished,
247                  e.g. 'partial list cases value ^1 shown upper...' */
248               get_string();
249             }
250           else
251             match_byte_assert(0x58);
252           if (pos != inner_end)
253             {
254               fprintf(stderr, "inner end discrepancy\n");
255               exit(1);
256             }
257
258           if (match_byte(0x31))
259             {
260               /* Only one example in the corpus. */
261               match_byte(0);
262               match_byte(0);
263               match_byte(0);
264               match_byte_assert(1);
265               get_string();     /* foreground */
266               get_string();     /* background */
267               get_string();     /* font */
268               match_byte_assert(12); /* size? */
269             }
270           else
271             match_byte_assert(0x58);
272           match_byte_assert(0x58);
273           if (pos != outer_end)
274             {
275               fprintf(stderr, "outer end discrepancy\n");
276               exit(1);
277             }
278         }
279       else if (match_u32 (1))
280         {
281           fprintf(stream, "(footnote %d) ", get_u32());
282           dump_nested_string();
283         }
284       else if (match_u32 (2))
285         {
286           fprintf(stream, "(special 2)");
287           match_byte_assert(0);
288           match_byte_assert(0);
289           if (!match_u32 (2))
290             match_u32_assert(1);
291           dump_nested_string(); /* Our corpus doesn't contain any examples with strings though. */
292         }
293       else
294         {
295           match_u32_assert(3);
296           fprintf(stream, "(special 3)");
297           match_byte_assert(0);
298           match_byte_assert(0);
299           match_byte_assert(1);
300           match_byte_assert(0);
301           match_u32_assert(2);
302           dump_nested_string(); /* Our corpus doesn't contain any examples with strings though. */
303         }
304     }
305   else
306     match_byte_assert (0x58);
307 }
308
309 static const char *
310 format_to_string (int type)
311 {
312   static char tmp[16];
313   switch (type)
314     {
315     case 1: return "A";
316     case 2: return "AHEX";
317     case 3: return "COMMA";
318     case 4: return "DOLLAR";
319     case 5: case 40: return "F";
320     case 6: return "IB";
321     case 7: return "PIBHEX";
322     case 8: return "P";
323     case 9: return "PIB";
324     case 10: return "PK";
325     case 11: return "RB";
326     case 12: return "RBHEX";
327     case 15: return "Z";
328     case 16: return "N";
329     case 17: return "E";
330     case 20: return "DATE";
331     case 21: return "TIME";
332     case 22: return "DATETIME";
333     case 23: return "ADATE";
334     case 24: return "JDATE";
335     case 25: return "DTIME";
336     case 26: return "WKDAY";
337     case 27: return "MONTH";
338     case 28: return "MOYR";
339     case 29: return "QYR";
340     case 30: return "WKYR";
341     case 31: return "PCT";
342     case 32: return "DOT";
343     case 33: return "CCA";
344     case 34: return "CCB";
345     case 35: return "CCC";
346     case 36: return "CCD";
347     case 37: return "CCE";
348     case 38: return "EDATE";
349     case 39: return "SDATE";
350     default:
351       abort();
352       sprintf(tmp, "<%d>", type);
353       return tmp;
354     }
355 }
356
357 static void
358 dump_value(FILE *stream, int level, bool match1)
359 {
360   match_byte(0);
361   match_byte(0);
362   match_byte(0);
363   match_byte(0);
364
365   for (int i = 0; i <= level; i++)
366     fprintf (stream, "    ");
367
368   if (match_byte (3))
369     {
370       char *text = get_string();
371       dump_value_31(stream);
372       char *identifier = get_string();
373       char *text_eng = get_string();
374       fprintf (stream, "<string c=\"%s\"", text_eng);
375       if (identifier[0])
376         fprintf (stream, " identifier=\"%s\"", identifier);
377       if (strcmp(text_eng, text))
378         fprintf (stream, " local=\"%s\"", text);
379       fprintf (stream, "/>\n");
380       if (!match_byte (0))
381         match_byte_assert(1);
382       if (match1)
383         match_byte (1);
384     }
385   else if (match_byte (5))
386     {
387       dump_value_31(stream);
388       char *name = get_string ();
389       char *label = get_string ();
390       fprintf (stream, "<variable name=\"%s\"", name);
391       if (label[0])
392         fprintf (stream, " label=\"%s\"", label);
393       fprintf (stream, "/>\n");
394       if (!match_byte(1) && !match_byte(2))
395         match_byte_assert(3);
396     }
397   else if (match_byte (2))
398     {
399       unsigned int format;
400       char *var, *vallab;
401       double value;
402
403       match_byte_assert (0x58);
404       format = get_u32 ();
405       value = get_double ();
406       var = get_string ();
407       vallab = get_string ();
408       fprintf (stream, "<numeric-datum value=\"%.*g\" format=\"%s%d.%d\"",
409               DBL_DIG, value, format_to_string(format >> 16), (format >> 8) & 0xff, format & 0xff);
410       if (var[0])
411         fprintf (stream, " variable=\"%s\"", var);
412       if (vallab[0])
413         fprintf (stream, " label=\"%s\"/>\n", vallab);
414       fprintf (stream, "/>\n");
415       if (!match_byte (1) && !match_byte(2))
416         match_byte_assert (3);
417     }
418   else if (match_byte (4))
419     {
420       unsigned int format;
421       char *var, *vallab, *value;
422
423       match_byte_assert (0x58);
424       format = get_u32 ();
425       vallab = get_string ();
426       var = get_string ();
427       if (!match_byte(1) && !match_byte(2))
428         match_byte_assert (3);
429       value = get_string ();
430       fprintf (stream, "<string-datum value=\"%s\" format=\"%s%d.%d\"",
431               value, format_to_string(format >> 16), (format >> 8) & 0xff, format & 0xff);
432       if (var[0])
433         fprintf (stream, " variable=\"%s\"", var);
434       if (vallab[0])
435         fprintf (stream, " label=\"%s\"/>\n", vallab);
436       fprintf (stream, "/>\n");
437     }
438   else if (match_byte (1))
439     {
440       unsigned int format;
441       double value;
442
443       dump_value_31(stream);
444       format = get_u32 ();
445       value = get_double ();
446       fprintf (stream, "<number value=\"%.*g\" format=\"%s%d.%d\"/>\n",
447                DBL_DIG, value, format_to_string(format >> 16), (format >> 8) & 0xff, format & 0xff);
448       if (match1)
449         match_byte (1);
450     }
451   else
452     {
453       dump_value_31(stream);
454
455       char *base = get_string();
456       int x = get_u32();
457       fprintf (stream, "<template format=\"%s\">\n", base);
458       for (int i = 0; i < x; i++)
459         {
460           int y = get_u32();
461           if (!y)
462             y = 1;
463           else
464             match_u32_assert(0);
465           for (int j = 0; j <= level + 1; j++)
466             fprintf (stream, "    ");
467           fprintf (stream, "<substitution index=\"%d\">\n", i + 1);
468           for (int j = 0; j < y; j++)
469             dump_value (stream, level + 2, false);
470           for (int j = 0; j <= level + 1; j++)
471             fprintf (stream, "    ");
472           fprintf (stream, "</substitution>\n");
473         }
474       for (int j = 0; j <= level; j++)
475         fprintf (stream, "    ");
476       fprintf (stream, "</template>\n");
477     }
478 }
479
480 static int
481 compare_int(const void *a_, const void *b_)
482 {
483   const int *a = a_;
484   const int *b = b_;
485   return *a < *b ? -1 : *a > *b;
486 }
487
488 static void
489 check_permutation(int *a, int n, const char *name)
490 {
491   int b[n];
492   memcpy(b, a, n * sizeof *a);
493   qsort(b, n, sizeof *b, compare_int);
494   for (int i = 0; i < n; i++)
495     if (b[i] != i)
496       {
497         fprintf(stderr, "bad %s permutation:", name);
498         for (int i = 0; i < n; i++)
499           fprintf(stderr, " %d", a[i]);
500         putc('\n', stderr);
501         exit(1);
502       }
503 }
504
505 static void
506 dump_category(int level, int *indexes, int *n_indexes)
507 {
508   for (int i = 0; i <= level; i++)
509     fprintf (stdout, "    ");
510   printf ("<category>\n");
511   dump_value (stdout, level + 1, true);
512   match_byte(0);
513   match_byte(0);
514   match_byte(0);
515
516   if (match_u32 (1))
517     match_byte (0);
518   else if (match_byte (1))
519     {
520       match_byte (0);
521       if (!match_u32 (2))
522         match_u32_assert (1);
523       match_byte (0);
524     }
525   else if (!match_u32(2))
526     match_u32_assert (0);
527
528   int indx = get_u32();
529   int n_categories = get_u32();
530   if (indx != -1)
531     {
532       if (n_categories != 0)
533         {
534           fprintf(stderr, "index not -1 but subcategories\n");
535           exit(1);
536         }
537       indexes[(*n_indexes)++] = indx;
538     }
539   if (n_categories == 0)
540     {
541       for (int i = 0; i <= level + 1; i++)
542         fprintf (stdout, "    ");
543       fprintf (stdout, "<category-index>%d</category-index>\n", indx);
544     }
545   for (int i = 0; i < n_categories; i++)
546     dump_category (level + 1, indexes, n_indexes);
547   for (int i = 0; i <= level; i++)
548     fprintf (stdout, "    ");
549   printf ("</category>\n");
550 }
551
552 static void
553 dump_dim(int indx)
554 {
555   int n_categories;
556
557   printf ("<dimension index=\"%d\">\n", indx);
558   dump_value (stdout, 0, false);
559
560   /* This byte is usually 0x02 but 0x00 and 0x75 (!) have also been spotted. */
561   pos++;
562
563   if (!match_byte(0) && !match_byte(1))
564     match_byte_assert(2);
565   if (!match_u32(0))
566     match_u32_assert(2);
567   if (!match_byte(0))
568     match_byte_assert(1);
569   if (!match_byte(0))
570     match_byte_assert(1);
571   match_byte_assert(1);
572   match_u32_assert(indx);
573   n_categories = get_u32();
574
575   int indexes[1024];
576   int n_indexes = 0;
577   for (int i = 0; i < n_categories; i++)
578     dump_category (0, indexes, &n_indexes);
579   check_permutation(indexes, n_indexes, "categories");
580
581   fprintf (stdout, "</dimension>\n");
582 }
583
584 int n_dims;
585 static void
586 dump_dims(void)
587 {
588   n_dims = get_u32();
589   for (int i = 0; i < n_dims; i++)
590     dump_dim (i);
591 }
592
593 static void
594 dump_data(void)
595 {
596   /* The first three numbers add to the number of dimensions. */
597   int t = get_u32();
598   t += get_u32();
599   match_u32_assert(n_dims - t);
600
601   /* The next n_dims numbers are a permutation of the dimension numbers. */
602   int a[n_dims];
603   for (int i = 0; i < n_dims; i++)
604     a[i] = get_u32();
605   check_permutation(a, n_dims, "dimensions");
606
607   int x = get_u32();
608   printf ("<data>\n");
609   for (int i = 0; i < x; i++)
610     {
611       printf ("    <datum index=\"%d\">\n", get_u32());
612       match_u32_assert(0);
613       dump_value(stdout, 1, false);
614       fprintf (stdout, "    </datum>\n");
615     }
616   printf ("</data>\n");
617 }
618
619 static void
620 dump_title(void)
621 {
622   pos = 0x27;
623   printf ("<title-local>\n");
624   dump_value(stdout, 0, true);
625   printf ("</title-local>\n");
626
627   printf ("<subtype>\n");
628   dump_value(stdout, 0, true);
629   printf ("</subtype>\n");
630
631   match_byte_assert(0x31);
632
633   printf ("<title-c>\n");
634   dump_value(stdout, 0, true);
635   printf ("</title-c>\n");
636
637   match_byte(0);
638   match_byte_assert(0x58);
639   if (match_byte(0x31))
640     {
641       printf ("<caption>\n");
642       dump_value(stdout, 0, false);
643       printf ("</caption>\n");
644     }
645   else
646     match_byte_assert(0x58);
647
648
649   int n_footnotes = get_u32();
650   for (int i = 0; i < n_footnotes; i++)
651     {
652       printf ("<footnote index=\"%d\">\n", i);
653       dump_value(stdout, 0, false);
654       if (match_byte (0x31))
655         {
656           /* Custom footnote marker string. */
657           match_byte_assert(3);
658           get_string();
659           match_byte_assert(0x58);
660           match_u32_assert(0);
661           get_string();
662         }
663       else
664         match_byte_assert (0x58);
665       printf("(%d)\n", get_u32());
666       printf ("</footnote>\n");
667     }
668 }
669
670 static void
671 dump_fonts(void)
672 {
673   match_byte(0);
674   for (int i = 1; i <= 8; i++)
675     {
676       printf ("<style index=\"%d\"", i);
677       match_byte_assert(i);
678       match_byte_assert(0x31);
679       printf(" font=\"%s\"", get_string());
680       match_byte_assert(0);
681       match_byte_assert(0);
682       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
683         match_byte_assert(0x50);
684       if (!match_byte(0x41))
685         match_byte_assert(0x51);
686       if (!match_u32(0))
687         match_u32_assert(1);
688       match_byte_assert(0);
689
690       /* OK, this seems really unlikely to be totally correct, but it matches my corpus... */
691       if (!match_u32(0) && !match_u32(2))
692         match_u32_assert(0xfaad);
693
694       if (!match_u32(0) && !match_u32(1) && !match_u32(2))
695         match_u32_assert(3);
696       printf (" fgcolor=\"%s\"", get_string());
697       printf (" bgcolor=\"%s\"", get_string());
698       match_u32_assert(0);
699       match_u32_assert(0);
700       match_byte_assert(0);
701
702       /* These seem unlikely to be correct too. */
703       if (i != 3)
704         {
705           match_u32_assert(8);
706           if (!match_u32(10))
707             match_u32_assert(11);
708           match_u32_assert(1);
709         }
710       else
711         {
712           get_u32();
713           if (!match_u32(-1) && !match_u32(8))
714             match_u32_assert(24);
715           if (!match_u32(-1) && !match_u32(2))
716             match_u32_assert(3);
717         }
718
719       /* Who knows? Ranges from -1 to 8 with no obvious pattern. */
720       get_u32();
721
722       printf ("/>\n");
723     }
724
725   match_u32_assert(240);
726   pos += 240;
727
728   match_u32_assert(18);
729   pos += 18;
730
731   if (match_u32(117))
732     pos += 117;
733   else
734     {
735       match_u32_assert(142);
736       pos += 142;
737     }
738
739   int count = get_u32();
740   pos += 4 * count;
741
742   printf ("<encoding>%s</encoding>\n", get_string ());
743
744   if (!match_u32(0))
745     match_u32_assert(UINT32_MAX);
746   if (!match_byte(0))
747     match_byte_assert(1);
748   match_byte_assert(0);
749   if (!match_byte(0))
750     match_byte_assert(1);
751   if (!match_byte(0x99) && !match_byte(0x98))
752     match_byte_assert(0x97);
753   match_byte_assert(7);
754   match_byte_assert(0);
755   match_byte_assert(0);
756   if (match_byte('.'))
757     match_byte_assert(',');
758   else
759     {
760       match_byte_assert(',');
761       if (!match_byte('.'))
762         match_byte_assert(' ');
763     }
764   match_u32_assert(5);
765   for (int i = 0; i < 5; i++)
766     get_string();
767   pos += get_u32();
768 }
769
770 int
771 main(int argc, char *argv[])
772 {
773   size_t start;
774   struct stat s;
775
776   if (isatty(STDIN_FILENO))
777     {
778       fprintf(stderr, "redirect stdin from a .bin file\n");
779       exit(1);
780     }
781   if (fstat(STDIN_FILENO, &s))
782     {
783       perror("fstat");
784       exit(1);
785     }
786   n = s.st_size;
787   data = malloc(n);
788   if (!data)
789     {
790       perror("malloc");
791       exit(1);
792     }
793   if (read(STDIN_FILENO, data, n) != n)
794     {
795       perror("read");
796       exit(1);
797     }
798
799   if (argc > 1)
800     {
801       if (!strcmp(argv[1], "title0"))
802         {
803           pos = 0x27;
804           if (match_byte (0x03)
805               || (match_byte (0x05) && match_byte (0x58)))
806             printf ("%s\n", get_string());
807           else
808             printf ("<unknown>\n");
809           return 0;
810         }
811       else if (!strcmp(argv[1], "title"))
812         {
813           dump_title();
814           exit(0);
815         }
816       else if (!strcmp(argv[1], "titleraw"))
817         {
818           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
819           start = 0x27;
820           n = find(fonts, sizeof fonts - 1);
821         }
822       else if (!strcmp(argv[1], "fonts"))
823         {
824           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
825           const char styles[] = "\xf0\0\0\0";
826           start = find(fonts, sizeof fonts - 1);
827           n = find(styles, sizeof styles - 1);
828         }
829       else if (!strcmp(argv[1], "styles"))
830         {
831           const char styles[] = "\xf0\0\0\0";
832           const char dimensions[] = "-,,,.\0";
833           start = find(styles, sizeof styles - 1);
834           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
835         }
836       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
837         {
838           pos = 0;
839           match_byte_assert(1);
840           match_byte_assert(0);
841           match_u32_assert(3);
842           match_byte_assert(1);
843           if (!match_byte(0))
844             match_byte_assert(1);
845           match_byte_assert(0);
846           match_byte_assert(0);
847           if (!match_byte(0))
848             match_byte_assert(1);
849           pos++;
850           match_byte_assert(0);
851           match_byte_assert(0);
852           match_byte_assert(0);
853           dump_title ();
854           dump_fonts();
855           dump_dims ();
856           dump_data ();
857           match_byte (1);
858           if (pos != n)
859             {
860               fprintf (stderr, "%x / %x\n", pos, n);
861               exit(1);
862             }
863           exit(0);
864         }
865       else
866         {
867           fprintf (stderr, "unknown section %s\n", argv[1]);
868           exit(1);
869         }
870     }
871   else
872     start = 0x27;
873
874   dump_raw(stdout, start, n, "\n");
875
876   return 0;
877 }