X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=tests%2Fdissect-sysfile.c;h=444c15cc88bbec3544c017422aecb11b7b700bb3;hb=9ade26c8349b4434008c46cf09bc7473ec743972;hp=85f3644211d3171423239c59b14ab7136f28b36a;hpb=b5c82cc9aabe7e641011130240ae1b2e84348e23;p=pspp-builds.git diff --git a/tests/dissect-sysfile.c b/tests/dissect-sysfile.c index 85f36442..444c15cc 100644 --- a/tests/dissect-sysfile.c +++ b/tests/dissect-sysfile.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc. + Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,7 +36,7 @@ #include "gettext.h" #define _(msgid) gettext (msgid) -#define VAR_NAME_LEN 64 +#define ID_MAX_LEN 64 struct sfm_reader { @@ -45,8 +45,14 @@ struct sfm_reader int n_variable_records, n_variables; + int *var_widths; + size_t n_var_widths, allocated_var_widths; + enum integer_format integer_format; enum float_format float_format; + + bool compressed; + double bias; }; static void read_header (struct sfm_reader *); @@ -58,6 +64,7 @@ static void read_machine_integer_info (struct sfm_reader *, size_t size, size_t count); static void read_machine_float_info (struct sfm_reader *, size_t size, size_t count); +static void read_mrsets (struct sfm_reader *, size_t size, size_t count); static void read_display_parameters (struct sfm_reader *, size_t size, size_t count); static void read_long_var_name_map (struct sfm_reader *r, @@ -68,10 +75,14 @@ static void read_datafile_attributes (struct sfm_reader *r, size_t size, size_t count); static void read_variable_attributes (struct sfm_reader *r, size_t size, size_t count); +static void read_ncases64 (struct sfm_reader *, size_t size, size_t count); static void read_character_encoding (struct sfm_reader *r, size_t size, size_t count); static void read_long_string_value_labels (struct sfm_reader *r, size_t size, size_t count); +static void read_unknown_extension (struct sfm_reader *, + size_t size, size_t count); +static void read_compressed_data (struct sfm_reader *); static struct text_record *open_text_record ( struct sfm_reader *, size_t size); @@ -80,6 +91,8 @@ static bool read_variable_to_value_pair (struct text_record *, char **key, char **value); static char *text_tokenize (struct text_record *, int delimiter); static bool text_match (struct text_record *text, int c); +static const char *text_parse_counted_string (struct text_record *); +static size_t text_pos (const struct text_record *); static void usage (int exit_code); static void sys_warn (struct sfm_reader *, const char *, ...) @@ -89,7 +102,9 @@ static void sys_error (struct sfm_reader *, const char *, ...) NO_RETURN; static void read_bytes (struct sfm_reader *, void *, size_t); +static bool try_read_bytes (struct sfm_reader *, void *, size_t); static int read_int (struct sfm_reader *); +static int64_t read_int64 (struct sfm_reader *); static double read_float (struct sfm_reader *); static void read_string (struct sfm_reader *, char *, size_t); static void skip_bytes (struct sfm_reader *, size_t); @@ -112,9 +127,13 @@ main (int argc, char *argv[]) r.file_name = argv[i]; r.file = fopen (r.file_name, "rb"); if (r.file == NULL) - error (EXIT_FAILURE, errno, "error opening \"%s\"", r.file_name); + error (EXIT_FAILURE, errno, "error opening `%s'", r.file_name); r.n_variable_records = 0; r.n_variables = 0; + r.n_var_widths = 0; + r.allocated_var_widths = 0; + r.var_widths = 0; + r.compressed = false; if (argc > 2) printf ("Reading \"%s\":\n", r.file_name); @@ -147,9 +166,13 @@ main (int argc, char *argv[]) sys_error (&r, _("Unrecognized record type %d."), rec_type); } } - printf ("%08lx: end-of-dictionary record " - "(first byte of data at %08lx)\n", - ftell (r.file), ftell (r.file) + 4); + printf ("%08llx: end-of-dictionary record " + "(first byte of data at %08llx)\n", + (long long int) ftello (r.file), + (long long int) ftello (r.file) + 4); + + if (r.compressed) + read_compressed_data (&r); fclose (r.file); } @@ -169,7 +192,6 @@ read_header (struct sfm_reader *r) int32_t weight_index; int32_t ncases; uint8_t raw_bias[8]; - double bias; char creation_date[10]; char creation_time[9]; char file_label[65]; @@ -193,10 +215,12 @@ read_header (struct sfm_reader *r) raw_layout_code, sizeof raw_layout_code); nominal_case_size = read_int (r); - compressed = read_int (r) != 0; + compressed = read_int (r); weight_index = read_int (r); ncases = read_int (r); + r->compressed = compressed != 0; + /* Identify floating-point format and obtain compression bias. */ read_bytes (r, raw_bias, sizeof raw_bias); if (float_identify (100.0, raw_bias, sizeof raw_bias, &r->float_format) == 0) @@ -209,7 +233,7 @@ read_header (struct sfm_reader *r) else r->float_format = FLOAT_IEEE_DOUBLE_LE; } - bias = float_get_double (r->float_format, raw_bias); + r->bias = float_get_double (r->float_format, raw_bias); read_string (r, creation_date, sizeof creation_date); read_string (r, creation_time, sizeof creation_time); @@ -223,7 +247,7 @@ read_header (struct sfm_reader *r) printf ("\t%17s: %"PRId32"\n", "Compressed", compressed); printf ("\t%17s: %"PRId32"\n", "Weight index", weight_index); printf ("\t%17s: %"PRId32"\n", "Number of cases", ncases); - printf ("\t%17s: %g\n", "Compression bias", bias); + printf ("\t%17s: %g\n", "Compression bias", r->bias); printf ("\t%17s: %s\n", "Creation date", creation_date); printf ("\t%17s: %s\n", "Creation time", creation_time); printf ("\t%17s: \"%s\"\n", "File label", file_label); @@ -287,8 +311,8 @@ read_variable_record (struct sfm_reader *r) int write_format; char name[9]; - printf ("%08lx: variable record #%d\n", - ftell (r->file), r->n_variable_records++); + printf ("%08llx: variable record #%d\n", + (long long int) ftello (r->file), r->n_variable_records++); width = read_int (r); has_variable_label = read_int (r); @@ -301,6 +325,11 @@ read_variable_record (struct sfm_reader *r) if (width >= 0) r->n_variables++; + if (r->n_var_widths >= r->allocated_var_widths) + r->var_widths = x2nrealloc (r->var_widths, &r->allocated_var_widths, + sizeof *r->var_widths); + r->var_widths[r->n_var_widths++] = width; + printf ("\tWidth: %d (%s)\n", width, width > 0 ? "string" @@ -328,17 +357,21 @@ read_variable_record (struct sfm_reader *r) sys_error (r, _("Variable label indicator field is not 0 or 1.")); if (has_variable_label == 1) { - long int offset = ftell (r->file); - size_t len; + long long int offset = ftello (r->file); + size_t len, read_len; char label[255 + 1]; len = read_int (r); - if (len >= sizeof label) - sys_error (r, _("Variable %s has label of invalid length %zu."), - name, len); - read_string (r, label, len + 1); - printf("\t%08lx Variable label: \"%s\"\n", offset, label); + /* Read up to 255 bytes of label. */ + read_len = MIN (sizeof label - 1, len); + read_string (r, label, read_len + 1); + printf("\t%08llx Variable label: \"%s\"\n", offset, label); + + /* Skip unread label bytes. */ + skip_bytes (r, len - read_len); + + /* Skip label padding up to multiple of 4 bytes. */ skip_bytes (r, ROUND_UP (len, 4) - len); } @@ -347,7 +380,7 @@ read_variable_record (struct sfm_reader *r) { int i; - printf ("\t%08lx Missing values:", ftell (r->file)); + printf ("\t%08llx Missing values:", (long long int) ftello (r->file)); if (!width) { if (missing_value_code < -3 || missing_value_code > 3 @@ -380,6 +413,20 @@ read_variable_record (struct sfm_reader *r) } } +static void +print_untyped_value (struct sfm_reader *r, char raw_value[8]) +{ + int n_printable; + double value; + + value = float_get_double (r->float_format, raw_value); + for (n_printable = 0; n_printable < sizeof raw_value; n_printable++) + if (!isprint (raw_value[n_printable])) + break; + + printf ("%g/\"%.*s\"", value, n_printable, raw_value); +} + /* Reads value labels from sysfile R and inserts them into the associated dictionary. */ static void @@ -388,24 +435,18 @@ read_value_label_record (struct sfm_reader *r) int label_cnt, var_cnt; int i; - printf ("%08lx: value labels record\n", ftell (r->file)); + printf ("%08llx: value labels record\n", (long long int) ftello (r->file)); /* Read number of labels. */ label_cnt = read_int (r); for (i = 0; i < label_cnt; i++) { char raw_value[8]; - double value; - int n_printable; unsigned char label_len; size_t padded_len; char label[256]; read_bytes (r, raw_value, sizeof raw_value); - value = float_get_double (r->float_format, raw_value); - for (n_printable = 0; n_printable < sizeof raw_value; n_printable++) - if (!isprint (raw_value[n_printable])) - break; /* Read label length. */ read_bytes (r, &label_len, sizeof label_len); @@ -415,7 +456,9 @@ read_value_label_record (struct sfm_reader *r) read_bytes (r, label, padded_len - 1); label[label_len] = 0; - printf ("\t%g/\"%.*s\": \"%s\"\n", value, n_printable, raw_value, label); + printf ("\t"); + print_untyped_value (r, raw_value); + printf (": \"%s\"\n", label); } /* Now, read the type 4 record that has the list of variables @@ -428,7 +471,7 @@ read_value_label_record (struct sfm_reader *r) /* Read number of variables associated with value label from type 4 record. */ - printf ("\t%08lx: apply to variables", ftell (r->file)); + printf ("\t%08llx: apply to variables", (long long int) ftello (r->file)); var_cnt = read_int (r); for (i = 0; i < var_cnt; i++) printf (" #%d", read_int (r)); @@ -441,14 +484,14 @@ read_document_record (struct sfm_reader *r) int n_lines; int i; - printf ("%08lx: document record\n", ftell (r->file)); + printf ("%08llx: document record\n", (long long int) ftello (r->file)); n_lines = read_int (r); printf ("\t%d lines of documents\n", n_lines); for (i = 0; i < n_lines; i++) { char line[81]; - printf ("\t%08lx: ", ftell (r->file)); + printf ("\t%08llx: ", (long long int) ftello (r->file)); read_string (r, line, sizeof line); trim_spaces (line); printf ("line %d: \"%s\"\n", i, line); @@ -458,13 +501,13 @@ read_document_record (struct sfm_reader *r) static void read_extension_record (struct sfm_reader *r) { - long int offset = ftell (r->file); + long long int offset = ftello (r->file); int subtype = read_int (r); size_t size = read_int (r); size_t count = read_int (r); size_t bytes = size * count; - printf ("%08lx: Record 7, subtype %d, size=%zu, count=%zu\n", + printf ("%08llx: Record 7, subtype %d, size=%zu, count=%zu\n", offset, subtype, size, count); switch (subtype) @@ -489,8 +532,9 @@ read_extension_record (struct sfm_reader *r) break; case 7: - /* Unknown purpose. */ - break; + case 19: + read_mrsets (r, size, count); + return; case 11: read_display_parameters (r, size, count); @@ -505,8 +549,8 @@ read_extension_record (struct sfm_reader *r) return; case 16: - /* New in SPSS v14? Unknown purpose. */ - break; + read_ncases64 (r, size, count); + return; case 17: read_datafile_attributes (r, size, count); @@ -526,7 +570,8 @@ read_extension_record (struct sfm_reader *r) default: sys_warn (r, _("Unrecognized record type 7, subtype %d."), subtype); - break; + read_unknown_extension (r, size, count); + return; } skip_bytes (r, bytes); @@ -535,7 +580,7 @@ read_extension_record (struct sfm_reader *r) static void read_machine_integer_info (struct sfm_reader *r, size_t size, size_t count) { - long int offset = ftell (r->file); + long long int offset = ftello (r->file); int version_major = read_int (r); int version_minor = read_int (r); int version_revision = read_int (r); @@ -545,7 +590,7 @@ read_machine_integer_info (struct sfm_reader *r, size_t size, size_t count) int integer_representation = read_int (r); int character_code = read_int (r); - printf ("%08lx: machine integer info\n", offset); + printf ("%08llx: machine integer info\n", offset); if (size != 4 || count != 8) sys_error (r, _("Bad size (%zu) or count (%zu) field on record type 7, " "subtype 3."), @@ -571,12 +616,12 @@ read_machine_integer_info (struct sfm_reader *r, size_t size, size_t count) static void read_machine_float_info (struct sfm_reader *r, size_t size, size_t count) { - long int offset = ftell (r->file); + long long int offset = ftello (r->file); double sysmis = read_float (r); double highest = read_float (r); double lowest = read_float (r); - printf ("%08lx: machine float info\n", offset); + printf ("%08llx: machine float info\n", offset); if (size != 8 || count != 3) sys_error (r, _("Bad size (%zu) or count (%zu) on extension 4."), size, count); @@ -597,6 +642,108 @@ read_machine_float_info (struct sfm_reader *r, size_t size, size_t count) lowest, "LOWEST"); } +/* Read record type 7, subtype 7. */ +static void +read_mrsets (struct sfm_reader *r, size_t size, size_t count) +{ + struct text_record *text; + + printf ("%08llx: multiple response sets\n", + (long long int) ftello (r->file)); + text = open_text_record (r, size * count); + for (;;) + { + const char *name; + enum { MRSET_MC, MRSET_MD } type; + bool cat_label_from_counted_values = false; + bool label_from_var_label = false; + const char *counted; + const char *label; + const char *variables; + + name = text_tokenize (text, '='); + if (name == NULL) + break; + + if (text_match (text, 'C')) + { + type = MRSET_MC; + counted = NULL; + if (!text_match (text, ' ')) + { + sys_warn (r, "missing space following 'C' at offset %zu " + "in mrsets record", text_pos (text)); + break; + } + } + else if (text_match (text, 'D')) + { + type = MRSET_MD; + } + else if (text_match (text, 'E')) + { + char *number; + + type = MRSET_MD; + cat_label_from_counted_values = true; + + if (!text_match (text, ' ')) + { + sys_warn (r, _("Missing space following `%c' at offset %zu " + "in MRSETS record"), 'E', text_pos (text)); + break; + } + + number = text_tokenize (text, ' '); + if (!strcmp (number, "11")) + label_from_var_label = true; + else if (strcmp (number, "1")) + sys_warn (r, _("Unexpected label source value `%s' " + "following `E' at offset %zu in MRSETS record"), + number, text_pos (text)); + + } + else + { + sys_warn (r, "missing `C', `D', or `E' at offset %zu " + "in mrsets record", text_pos (text)); + break; + } + + if (type == MRSET_MD) + { + counted = text_parse_counted_string (text); + if (counted == NULL) + break; + } + + label = text_parse_counted_string (text); + if (label == NULL) + break; + + variables = text_tokenize (text, '\n'); + if (variables == NULL) + { + sys_warn (r, "missing variable names following label " + "at offset %zu in mrsets record", text_pos (text)); + break; + } + + printf ("\t\"%s\": multiple %s set", + name, type == MRSET_MC ? "category" : "dichotomy"); + if (counted != NULL) + printf (", counted value \"%s\"", counted); + if (cat_label_from_counted_values) + printf (", category labels from counted values"); + if (label[0] != '\0') + printf (", label \"%s\"", label); + if (label_from_var_label) + printf (", label from variable label"); + printf(", variables \"%s\"\n", variables); + } + close_text_record (text); +} + /* Read record type 7, subtype 11. */ static void read_display_parameters (struct sfm_reader *r, size_t size, size_t count) @@ -605,7 +752,8 @@ read_display_parameters (struct sfm_reader *r, size_t size, size_t count) bool includes_width; size_t i; - printf ("%08lx: variable display parameters\n", ftell (r->file)); + printf ("%08llx: variable display parameters\n", + (long long int) ftello (r->file)); if (size != 4) { sys_warn (r, _("Bad size %zu on extension 11."), size); @@ -656,7 +804,8 @@ read_long_var_name_map (struct sfm_reader *r, size_t size, size_t count) char *var; char *long_name; - printf ("%08lx: long variable names (short => long)\n", ftell (r->file)); + printf ("%08llx: long variable names (short => long)\n", + (long long int) ftello (r->file)); text = open_text_record (r, size * count); while (read_variable_to_value_pair (text, &var, &long_name)) printf ("\t%s => %s\n", var, long_name); @@ -672,7 +821,8 @@ read_long_string_map (struct sfm_reader *r, size_t size, size_t count) char *var; char *length_s; - printf ("%08lx: very long strings (variable => length)\n", ftell (r->file)); + printf ("%08llx: very long strings (variable => length)\n", + (long long int) ftello (r->file)); text = open_text_record (r, size * count); while (read_variable_to_value_pair (text, &var, &length_s)) printf ("\t%s => %d\n", var, atoi (length_s)); @@ -720,12 +870,37 @@ read_attributes (struct sfm_reader *r, struct text_record *text, } } +/* Read extended number of cases record. */ +static void +read_ncases64 (struct sfm_reader *r, size_t size, size_t count) +{ + int64_t unknown, ncases64; + + if (size != 8) + { + sys_warn (r, _("Bad size %zu for extended number of cases."), size); + skip_bytes (r, size * count); + return; + } + if (count != 2) + { + sys_warn (r, _("Bad count %zu for extended number of cases."), size); + skip_bytes (r, size * count); + return; + } + unknown = read_int64 (r); + ncases64 = read_int64 (r); + printf ("%08llx: extended number of cases: " + "unknown=%"PRId64", ncases64=%"PRId64"\n", + (long long int) ftello (r->file), unknown, ncases64); +} + static void read_datafile_attributes (struct sfm_reader *r, size_t size, size_t count) { struct text_record *text; - printf ("%08lx: datafile attributes\n", ftell (r->file)); + printf ("%08llx: datafile attributes\n", (long long int) ftello (r->file)); text = open_text_record (r, size * count); read_attributes (r, text, "datafile"); close_text_record (text); @@ -734,23 +909,23 @@ read_datafile_attributes (struct sfm_reader *r, size_t size, size_t count) static void read_character_encoding (struct sfm_reader *r, size_t size, size_t count) { - const unsigned long int posn = ftell (r->file); + long long int posn = ftello (r->file); char *encoding = xcalloc (size, count + 1); read_string (r, encoding, count + 1); - printf ("%08lx: Character Encoding: %s\n", posn, encoding); + printf ("%08llx: Character Encoding: %s\n", posn, encoding); } static void read_long_string_value_labels (struct sfm_reader *r, size_t size, size_t count) { - const long start = ftell (r->file); + long long int start = ftello (r->file); - printf ("%08lx: long string value labels\n", start); - while (ftell (r->file) - start < size * count) + printf ("%08llx: long string value labels\n", start); + while (ftello (r->file) - start < size * count) { - long posn = ftell (r->file); - char var_name[VAR_NAME_LEN + 1]; + long long posn = ftello (r->file); + char var_name[ID_MAX_LEN + 1]; int var_name_len; int n_values; int width; @@ -758,17 +933,17 @@ read_long_string_value_labels (struct sfm_reader *r, size_t size, size_t count) /* Read variable name. */ var_name_len = read_int (r); - if (var_name_len > VAR_NAME_LEN) + if (var_name_len > ID_MAX_LEN) sys_error (r, _("Variable name length in long string value label " "record (%d) exceeds %d-byte limit."), - var_name_len, VAR_NAME_LEN); + var_name_len, ID_MAX_LEN); read_string (r, var_name, var_name_len + 1); /* Read width, number of values. */ width = read_int (r); n_values = read_int (r); - printf ("\t%08lx: %s, width %d, %d values\n", + printf ("\t%08llx: %s, width %d, %d values\n", posn, var_name, width, n_values); /* Read values. */ @@ -780,7 +955,7 @@ read_long_string_value_labels (struct sfm_reader *r, size_t size, size_t count) char *label; int label_length; - posn = ftell (r->file); + posn = ftello (r->file); /* Read value. */ value_length = read_int (r); @@ -792,7 +967,7 @@ read_long_string_value_labels (struct sfm_reader *r, size_t size, size_t count) label = xmalloc (label_length + 1); read_string (r, label, label_length + 1); - printf ("\t\t%08lx: \"%s\" (%d bytes) => \"%s\" (%d bytes)\n", + printf ("\t\t%08llx: \"%s\" (%d bytes) => \"%s\" (%d bytes)\n", posn, value, value_length, label, label_length); free (value); @@ -801,12 +976,86 @@ read_long_string_value_labels (struct sfm_reader *r, size_t size, size_t count) } } +static void +hex_dump (size_t offset, const void *buffer_, size_t buffer_size) +{ + const uint8_t *buffer = buffer_; + + while (buffer_size > 0) + { + size_t n = MIN (buffer_size, 16); + size_t i; + + printf ("%04zx", offset); + for (i = 0; i < 16; i++) + { + if (i < n) + printf ("%c%02x", i == 8 ? '-' : ' ', buffer[i]); + else + printf (" "); + } + + printf (" |"); + for (i = 0; i < 16; i++) + { + unsigned char c = i < n ? buffer[i] : ' '; + putchar (isprint (c) ? c : '.'); + } + printf ("|\n"); + + offset += n; + buffer += n; + buffer_size -= n; + } +} + +/* Reads and prints any type 7 record that we don't understand. */ +static void +read_unknown_extension (struct sfm_reader *r, size_t size, size_t count) +{ + unsigned char *buffer; + size_t i; + + if (size == 0 || count > 65536 / size) + skip_bytes (r, size * count); + else if (size != 1) + { + buffer = xmalloc (size); + for (i = 0; i < count; i++) + { + read_bytes (r, buffer, size); + hex_dump (i * size, buffer, size); + } + free (buffer); + } + else + { + buffer = xmalloc (count); + read_bytes (r, buffer, count); + if (memchr (buffer, 0, count) == 0) + for (i = 0; i < count; i++) + { + unsigned char c = buffer[i]; + + if (c == '\\') + printf ("\\\\"); + else if (c == '\n' || isprint (c)) + putchar (c); + else + printf ("\\%02x", c); + } + else + hex_dump (0, buffer, count); + free (buffer); + } +} + static void read_variable_attributes (struct sfm_reader *r, size_t size, size_t count) { struct text_record *text; - printf ("%08lx: variable attributes\n", ftell (r->file)); + printf ("%08llx: variable attributes\n", (long long int) ftello (r->file)); text = open_text_record (r, size * count); for (;;) { @@ -816,6 +1065,96 @@ read_variable_attributes (struct sfm_reader *r, size_t size, size_t count) } close_text_record (text); } + +static void +read_compressed_data (struct sfm_reader *r) +{ + enum { N_OPCODES = 8 }; + uint8_t opcodes[N_OPCODES]; + long long int opcode_ofs; + int opcode_idx; + int case_num; + int i; + + read_int (r); + printf ("\n%08llx: compressed data:\n", (long long int) ftello (r->file)); + + opcode_idx = N_OPCODES; + opcode_ofs = 0; + case_num = 0; + for (case_num = 0; ; case_num++) + { + printf ("%08llx: case %d's uncompressible data begins\n", + (long long int) ftello (r->file), case_num); + for (i = 0; i < r->n_var_widths; ) + { + int width = r->var_widths[i]; + char raw_value[8]; + int opcode; + + if (opcode_idx >= N_OPCODES) + { + opcode_ofs = ftello (r->file); + if (i == 0) + { + if (!try_read_bytes (r, opcodes, 8)) + return; + } + else + read_bytes (r, opcodes, 8); + opcode_idx = 0; + } + opcode = opcodes[opcode_idx]; + printf ("%08llx: variable %d: opcode %d: ", + opcode_ofs + opcode_idx, i, opcode); + + switch (opcode) + { + default: + printf ("%g", opcode - r->bias); + if (width != 0) + printf (", but this is a string variable (width=%d)", width); + printf ("\n"); + i++; + break; + + case 0: + printf ("ignored padding\n"); + break; + + case 252: + printf ("end of data\n"); + return; + + case 253: + read_bytes (r, raw_value, 8); + printf ("uncompressible data: "); + print_untyped_value (r, raw_value); + printf ("\n"); + i++; + break; + + case 254: + printf ("spaces"); + if (width == 0) + printf (", but this is a numeric variable"); + printf ("\n"); + i++; + break; + + case 255: + printf ("SYSMIS"); + if (width != 0) + printf (", but this is a string variable (width=%d)", width); + printf ("\n"); + i++; + break; + } + + opcode_idx++; + } + } +} /* Helpers for reading records that consist of structured text strings. */ @@ -823,6 +1162,7 @@ read_variable_attributes (struct sfm_reader *r, size_t size, size_t count) /* State. */ struct text_record { + struct sfm_reader *reader; /* Reader. */ char *buffer; /* Record contents. */ size_t size; /* Size of buffer. */ size_t pos; /* Current position in buffer. */ @@ -836,6 +1176,8 @@ open_text_record (struct sfm_reader *r, size_t size) struct text_record *text = xmalloc (sizeof *text); char *buffer = xmalloc (size + 1); read_bytes (r, buffer, size); + buffer[size] = '\0'; + text->reader = r; text->buffer = buffer; text->size = size; text->pos = 0; @@ -878,6 +1220,54 @@ text_match (struct text_record *text, int c) return false; } +/* Reads a integer value expressed in decimal, then a space, then a string that + consists of exactly as many bytes as specified by the integer, then a space, + from TEXT. Returns the string, null-terminated, as a subset of TEXT's + buffer (so the caller should not free the string). */ +static const char * +text_parse_counted_string (struct text_record *text) +{ + size_t start; + size_t n; + char *s; + + start = text->pos; + n = 0; + while (isdigit ((unsigned char) text->buffer[text->pos])) + n = (n * 10) + (text->buffer[text->pos++] - '0'); + if (start == text->pos) + { + sys_error (text->reader, "expecting digit at offset %zu in record", + text->pos); + return NULL; + } + + if (!text_match (text, ' ')) + { + sys_error (text->reader, "expecting space at offset %zu in record", + text->pos); + return NULL; + } + + if (text->pos + n > text->size) + { + sys_error (text->reader, "%zu-byte string starting at offset %zu " + "exceeds record length %zu", n, text->pos, text->size); + return NULL; + } + + s = &text->buffer[text->pos]; + if (s[n] != ' ') + { + sys_error (text->reader, "expecting space at offset %zu following " + "%zu-byte string", text->pos + n, n); + return NULL; + } + s[n] = '\0'; + text->pos += n + 1; + return s; +} + /* Reads a variable=value pair from TEXT. Looks up the variable in DICT and stores it into *VAR. Stores a null-terminated value into *VALUE. */ @@ -896,6 +1286,13 @@ read_variable_to_value_pair (struct text_record *text, text->pos++; return true; } + +/* Returns the current byte offset inside the TEXT's string. */ +static size_t +text_pos (const struct text_record *text) +{ + return text->pos; +} static void usage (int exit_code) @@ -910,8 +1307,8 @@ usage (int exit_code) static void sys_msg (struct sfm_reader *r, const char *format, va_list args) { - printf ("\"%s\" near offset 0x%lx: ", - r->file_name, (unsigned long) ftell (r->file)); + printf ("\"%s\" near offset 0x%llx: ", + r->file_name, (long long int) ftello (r->file)); vprintf (format, args); putchar ('\n'); } @@ -971,6 +1368,16 @@ read_bytes (struct sfm_reader *r, void *buf, size_t byte_cnt) read_bytes_internal (r, false, buf, byte_cnt); } +/* Reads BYTE_CNT bytes into BUF. + Returns true if exactly BYTE_CNT bytes are successfully read. + Returns false if an immediate end-of-file is encountered. + Aborts if an I/O error or a partial read occurs. */ +static bool +try_read_bytes (struct sfm_reader *r, void *buf, size_t byte_cnt) +{ + return read_bytes_internal (r, true, buf, byte_cnt); +} + /* Reads a 32-bit signed integer from R and returns its value in host format. */ static int @@ -981,6 +1388,16 @@ read_int (struct sfm_reader *r) return integer_get (r->integer_format, integer, sizeof integer); } +/* Reads a 64-bit signed integer from R and returns its value in + host format. */ +static int64_t +read_int64 (struct sfm_reader *r) +{ + uint8_t integer[8]; + read_bytes (r, integer, sizeof integer); + return integer_get (r->integer_format, integer, sizeof integer); +} + /* Reads a 64-bit floating-point number from R and returns its value in host format. */ static double