X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fsys-file-writer.c;h=1b73e737c1bca6efcaf1ccecf799ae0c8d2c18bf;hb=79100bdd30230890345c1c5a09ae57a19c1e408b;hp=a67cf7dd2f609ef3ce724acde611ad6497ef9ee1;hpb=099b41646452789d8a5b31218fa6522d1ea46584;p=pspp diff --git a/src/data/sys-file-writer.c b/src/data/sys-file-writer.c index a67cf7dd2f..1b73e737c1 100644 --- a/src/data/sys-file-writer.c +++ b/src/data/sys-file-writer.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2010, 2011 Free Software Foundation, Inc. + Copyright (C) 1997-2000, 2006-2013 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 @@ -74,6 +74,7 @@ struct sfm_writer bool compress; /* 1=compressed, 0=not compressed. */ casenumber case_cnt; /* Number of cases written so far. */ + uint8_t space; /* ' ' in the file's character encoding. */ /* Compression buffering. @@ -98,9 +99,10 @@ static const struct casewriter_class sys_file_casewriter_class; static void write_header (struct sfm_writer *, const struct dictionary *); static void write_variable (struct sfm_writer *, const struct variable *); -static void write_value_labels (struct sfm_writer *, struct variable *, - int idx); -static void write_integer_info_record (struct sfm_writer *); +static void write_value_labels (struct sfm_writer *, + const struct dictionary *); +static void write_integer_info_record (struct sfm_writer *, + const struct dictionary *); static void write_float_info_record (struct sfm_writer *); static void write_longvar_table (struct sfm_writer *w, @@ -175,9 +177,9 @@ struct casewriter * sfm_open_writer (struct file_handle *fh, struct dictionary *d, struct sfm_write_options opts) { + struct encoding_info encoding_info; struct sfm_writer *w; mode_t mode; - int idx; int i; /* Check version. */ @@ -227,6 +229,9 @@ sfm_open_writer (struct file_handle *fh, struct dictionary *d, goto error; } + get_encoding_info (&encoding_info, dict_get_encoding (d)); + w->space = encoding_info.space[0]; + /* Write the file header. */ write_header (w, d); @@ -235,20 +240,12 @@ sfm_open_writer (struct file_handle *fh, struct dictionary *d, for (i = 0; i < dict_get_var_cnt (d); i++) write_variable (w, dict_get_var (d, i)); - /* Write out value labels. */ - idx = 0; - for (i = 0; i < dict_get_var_cnt (d); i++) - { - struct variable *v = dict_get_var (d, i); - - write_value_labels (w, v, idx); - idx += sfm_width_to_octs (var_get_width (v)); - } + write_value_labels (w, d); if (dict_get_document_line_cnt (d) > 0) write_documents (w, d); - write_integer_info_record (w); + write_integer_info_record (w, d); write_float_info_record (w); write_mrsets (w, d, true); @@ -262,9 +259,12 @@ sfm_open_writer (struct file_handle *fh, struct dictionary *d, write_long_string_value_labels (w, d); - if (attrset_count (dict_get_attributes (d))) - write_data_file_attributes (w, d); - write_variable_attributes (w, d); + if (opts.version >= 3) + { + if (attrset_count (dict_get_attributes (d))) + write_data_file_attributes (w, d); + write_variable_attributes (w, d); + } write_mrsets (w, d, false); @@ -319,6 +319,7 @@ calc_oct_idx (const struct dictionary *d, struct variable *target_var) static void write_header (struct sfm_writer *w, const struct dictionary *d) { + const char *dict_encoding = dict_get_encoding (d); char prod_name[61]; char creation_date[10]; char creation_time[9]; @@ -328,12 +329,15 @@ write_header (struct sfm_writer *w, const struct dictionary *d) time_t t; /* Record-type code. */ - write_string (w, "$FL2", 4); + if (is_encoding_ebcdic_compatible (dict_encoding)) + write_string (w, EBCDIC_MAGIC, 4); + else + write_string (w, ASCII_MAGIC, 4); /* Product identification. */ snprintf (prod_name, sizeof prod_name, "@(#) SPSS DATA FILE %s - %s", version, host_system); - write_string (w, prod_name, 60); + write_utf8_string (w, dict_encoding, prod_name, 60); /* Layout code. */ write_int (w, 2); @@ -382,14 +386,14 @@ write_header (struct sfm_writer *w, const struct dictionary *d) snprintf (creation_time, sizeof creation_time, "%02d:%02d:%02d", hour - 1, min - 1, sec - 1); } - write_string (w, creation_date, 9); - write_string (w, creation_time, 8); + write_utf8_string (w, dict_encoding, creation_date, 9); + write_utf8_string (w, dict_encoding, creation_time, 8); /* File label. */ file_label = dict_get_label (d); if (file_label == NULL) file_label = ""; - write_utf8_string (w, dict_get_encoding (d), file_label, 64); + write_utf8_string (w, dict_encoding, file_label, 64); /* Padding. */ write_zeros (w, 3); @@ -514,47 +518,117 @@ write_variable (struct sfm_writer *w, const struct variable *v) mv_destroy (&mv); } -/* Writes the value labels for variable V having system file - variable index IDX to system file W. +/* Writes the value labels to system file W. Value labels for long string variables are written separately, by write_long_string_value_labels. */ static void -write_value_labels (struct sfm_writer *w, struct variable *v, int idx) +write_value_labels (struct sfm_writer *w, const struct dictionary *d) { - const struct val_labs *val_labs; - const struct val_lab **labels; - size_t n_labels; + struct label_set + { + struct hmap_node hmap_node; + const struct val_labs *val_labs; + int *indexes; + size_t n_indexes, allocated_indexes; + }; + + size_t n_sets, allocated_sets; + struct label_set **sets; + struct hmap same_sets; size_t i; + int idx; - val_labs = var_get_value_labels (v); - n_labels = val_labs_count (val_labs); - if (n_labels == 0 || var_get_width (v) > 8) - return; + n_sets = allocated_sets = 0; + sets = NULL; + hmap_init (&same_sets); - /* Value label record. */ - write_int (w, 3); /* Record type. */ - write_int (w, val_labs_count (val_labs)); - labels = val_labs_sorted (val_labs); - for (i = 0; i < n_labels; i++) + idx = 0; + for (i = 0; i < dict_get_var_cnt (d); i++) { - const struct val_lab *vl = labels[i]; - char *label = recode_string (var_get_encoding (v), UTF8, - val_lab_get_escaped_label (vl), -1); - uint8_t len = MIN (strlen (label), 255); - - write_value (w, val_lab_get_value (vl), var_get_width (v)); - write_bytes (w, &len, 1); - write_bytes (w, label, len); - write_zeros (w, REM_RND_UP (len + 1, 8)); - free (label); + struct variable *v = dict_get_var (d, i); + + if (var_has_value_labels (v) && var_get_width (v) <= 8) + { + const struct val_labs *val_labs = var_get_value_labels (v); + unsigned int hash = val_labs_hash (val_labs, 0); + struct label_set *set; + + HMAP_FOR_EACH_WITH_HASH (set, struct label_set, hmap_node, + hash, &same_sets) + { + if (val_labs_equal (set->val_labs, val_labs)) + { + if (set->n_indexes >= set->allocated_indexes) + set->indexes = x2nrealloc (set->indexes, + &set->allocated_indexes, + sizeof *set->indexes); + set->indexes[set->n_indexes++] = idx; + goto next_var; + } + } + + set = xmalloc (sizeof *set); + set->val_labs = val_labs; + set->indexes = xmalloc (sizeof *set->indexes); + set->indexes[0] = idx; + set->n_indexes = 1; + set->allocated_indexes = 1; + hmap_insert (&same_sets, &set->hmap_node, hash); + + if (n_sets >= allocated_sets) + sets = x2nrealloc (sets, &allocated_sets, sizeof *sets); + sets[n_sets++] = set; + } + + next_var: + idx += sfm_width_to_octs (var_get_width (v)); } - free (labels); - /* Value label variable record. */ - write_int (w, 4); /* Record type. */ - write_int (w, 1); /* Number of variables. */ - write_int (w, idx + 1); /* Variable's dictionary index. */ + for (i = 0; i < n_sets; i++) + { + const struct label_set *set = sets[i]; + const struct val_labs *val_labs = set->val_labs; + size_t n_labels = val_labs_count (val_labs); + int width = val_labs_get_width (val_labs); + const struct val_lab **labels; + size_t j; + + /* Value label record. */ + write_int (w, 3); /* Record type. */ + write_int (w, n_labels); + labels = val_labs_sorted (val_labs); + for (j = 0; j < n_labels; j++) + { + const struct val_lab *vl = labels[j]; + char *label = recode_string (dict_get_encoding (d), UTF8, + val_lab_get_escaped_label (vl), -1); + uint8_t len = MIN (strlen (label), 255); + + write_value (w, val_lab_get_value (vl), width); + write_bytes (w, &len, 1); + write_bytes (w, label, len); + write_zeros (w, REM_RND_UP (len + 1, 8)); + free (label); + } + free (labels); + + /* Value label variable record. */ + write_int (w, 4); /* Record type. */ + write_int (w, set->n_indexes); + for (j = 0; j < set->n_indexes; j++) + write_int (w, set->indexes[j] + 1); + } + + for (i = 0; i < n_sets; i++) + { + struct label_set *set = sets[i]; + + free (set->indexes); + free (set); + } + free (sets); + hmap_destroy (&same_sets); } /* Writes record type 6, document record. */ @@ -609,6 +683,46 @@ write_data_file_attributes (struct sfm_writer *w, ds_destroy (&s); } +static void +add_role_attribute (enum var_role role, struct attrset *attrs) +{ + struct attribute *attr; + const char *s; + + switch (role) + { + case ROLE_NONE: + default: + s = "0"; + break; + + case ROLE_INPUT: + s = "1"; + break; + + case ROLE_OUTPUT: + s = "2"; + break; + + case ROLE_BOTH: + s = "3"; + break; + + case ROLE_PARTITION: + s = "4"; + break; + + case ROLE_SPLIT: + s = "5"; + break; + } + attrset_delete (attrs, "$@Role"); + + attr = attribute_create ("$@Role"); + attribute_add_value (attr, s); + attrset_add (attrs, attr); +} + static void write_variable_attributes (struct sfm_writer *w, const struct dictionary *d) { @@ -620,14 +734,16 @@ write_variable_attributes (struct sfm_writer *w, const struct dictionary *d) for (i = 0; i < n_vars; i++) { struct variable *v = dict_get_var (d, i); - struct attrset *attrs = var_get_attributes (v); - if (attrset_count (attrs)) - { - if (n_attrsets++) - ds_put_byte (&s, '/'); - ds_put_format (&s, "%s:", var_get_name (v)); - put_attrset (&s, attrs); - } + struct attrset attrs; + + attrset_clone (&attrs, var_get_attributes (v)); + + add_role_attribute (var_get_role (v), &attrs); + if (n_attrsets++) + ds_put_byte (&s, '/'); + ds_put_format (&s, "%s:", var_get_name (v)); + put_attrset (&s, &attrs); + attrset_destroy (&attrs); } if (n_attrsets) write_utf8_record (w, dict_get_encoding (d), &s, 18); @@ -646,6 +762,12 @@ write_mrsets (struct sfm_writer *w, const struct dictionary *dict, size_t n_mrsets; size_t i; + if (is_encoding_ebcdic_compatible (encoding)) + { + /* FIXME. */ + return; + } + n_mrsets = dict_get_n_mrsets (dict); if (n_mrsets == 0) return; @@ -697,15 +819,18 @@ write_mrsets (struct sfm_writer *w, const struct dictionary *dict, for (j = 0; j < mrset->n_vars; j++) { const char *short_name_utf8 = var_get_short_name (mrset->vars[j], 0); + char *lower_name_utf8 = utf8_to_lower (short_name_utf8); char *short_name = recode_string (encoding, "UTF-8", - short_name_utf8, -1); + lower_name_utf8, -1); ds_put_format (&s, " %s", short_name); free (short_name); + free (lower_name_utf8); } ds_put_byte (&s, '\n'); } - write_string_record (w, ds_ss (&s), pre_v14 ? 7 : 19); + if (!ds_is_empty (&s)) + write_string_record (w, ds_ss (&s), pre_v14 ? 7 : 19); ds_destroy (&s); } @@ -851,23 +976,18 @@ static void write_encoding_record (struct sfm_writer *w, const struct dictionary *d) { - const char *enc = dict_get_encoding (d); - - if ( NULL == enc) - return; - - write_int (w, 7); /* Record type. */ - write_int (w, 20); /* Record subtype. */ - write_int (w, 1); /* Data item (char) size. */ - - /* IANA says "...character set names may be up to 40 characters taken from - the printable characters of US-ASCII," so character set names don't need - to be recoded. */ - write_int (w, strlen (enc)); /* Number of data items. */ - write_string (w, enc, strlen (enc)); + /* IANA says "...character set names may be up to 40 characters taken + from the printable characters of US-ASCII," so character set names + don't need to be recoded to be in UTF-8. + + We convert encoding names to uppercase because SPSS writes encoding + names in uppercase. */ + char *encoding = xstrdup (dict_get_encoding (d)); + str_uppercase (encoding); + write_string_record (w, ss_cstr (encoding), 20); + free (encoding); } - /* Writes the long variable name table. */ static void write_longvar_table (struct sfm_writer *w, const struct dictionary *dict) @@ -890,10 +1010,13 @@ write_longvar_table (struct sfm_writer *w, const struct dictionary *dict) /* Write integer information record. */ static void -write_integer_info_record (struct sfm_writer *w) +write_integer_info_record (struct sfm_writer *w, + const struct dictionary *d) { + const char *dict_encoding = dict_get_encoding (d); int version_component[3]; int float_format; + int codepage; /* Parse the version string. */ memset (version_component, 0, sizeof version_component); @@ -911,6 +1034,24 @@ write_integer_info_record (struct sfm_writer *w) else abort (); + /* Choose codepage. */ + codepage = sys_get_codepage_from_encoding (dict_encoding); + if (codepage == 0) + { + /* The codepage is unknown. Choose a default. + + For an EBCDIC-compatible encoding, use the value for EBCDIC. + + For an ASCII-compatible encoding, default to "7-bit ASCII", because + many files use this codepage number regardless of their actual + encoding. + */ + if (is_encoding_ascii_compatible (dict_encoding)) + codepage = 2; + else if (is_encoding_ebcdic_compatible (dict_encoding)) + codepage = 1; + } + /* Write record. */ write_int (w, 7); /* Record type. */ write_int (w, 3); /* Record subtype. */ @@ -923,7 +1064,7 @@ write_integer_info_record (struct sfm_writer *w) write_int (w, float_format); write_int (w, 1); /* Compression code. */ write_int (w, INTEGER_NATIVE == INTEGER_MSB_FIRST ? 1 : 2); - write_int (w, 2); /* 7-bit ASCII. */ + write_int (w, codepage); } /* Write floating-point information record. */ @@ -1167,7 +1308,7 @@ put_cmp_string (struct sfm_writer *w, const void *data, size_t size) assert (w->data_cnt < 8); assert (size <= 8); - memset (w->data[w->data_cnt], ' ', 8); + memset (w->data[w->data_cnt], w->space, 8); memcpy (w->data[w->data_cnt], data, size); w->data_cnt++; } @@ -1229,7 +1370,7 @@ write_string (struct sfm_writer *w, const char *string, size_t width) size_t pad_bytes = width - data_bytes; write_bytes (w, string, data_bytes); while (pad_bytes-- > 0) - putc (' ', w->file); + putc (w->space, w->file); } /* Recodes null-terminated UTF-8 encoded STRING into ENCODING, and writes the @@ -1290,5 +1431,5 @@ static void write_spaces (struct sfm_writer *w, size_t n) { while (n-- > 0) - putc (' ', w->file); + putc (w->space, w->file); }