From 2a94e880a9152f8a19fc0049304d49e71109f30e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20P=C3=B6ttker?= <25299532+hpoettker@users.noreply.github.com> Date: Sat, 12 Sep 2026 17:21:49 +0200 Subject: [PATCH 1/2] Fix build Adresses warning that "output may be truncated before the last format character." --- src/spss/readstat_sav_write.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spss/readstat_sav_write.c b/src/spss/readstat_sav_write.c index c261d546..9b85de1a 100644 --- a/src/spss/readstat_sav_write.c +++ b/src/spss/readstat_sav_write.c @@ -1309,7 +1309,7 @@ static void sav_generate_unique_name(char *output, size_t output_len, const char suffix[j] = suffix[len-1-j]; suffix[len-1-j] = tmp; } - snprintf(output, output_len, "%.*s%s", 8 - len, stem, suffix); + snprintf(output, output_len, "%.*s%.*s", 8 - len, stem, len, suffix); } while (ck_str_hash_lookup(output, table)); } From 331181281cb3eb6498258ea3ba1e51d9d85e3afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henning=20P=C3=B6ttker?= <25299532+hpoettker@users.noreply.github.com> Date: Sat, 12 Sep 2026 19:38:52 +0200 Subject: [PATCH 2/2] Read 64-bit multi-page SAS catalog files and skip format default values --- Makefile.am | 4 +- resources/format_with_default_32bit.sas7bcat | Bin 0 -> 17408 bytes resources/format_with_default_64bit.sas7bcat | Bin 0 -> 24576 bytes src/sas/readstat_sas7bcat_read.c | 119 +++++++++++-------- src/sas/readstat_sas7bcat_write.c | 110 ++++++++++++----- src/test/test_list.h | 96 +++++++++++++++ src/test/test_read.c | 4 +- src/test/test_readstat.h | 8 +- src/test/test_write.c | 1 + 9 files changed, 257 insertions(+), 85 deletions(-) create mode 100644 resources/format_with_default_32bit.sas7bcat create mode 100644 resources/format_with_default_64bit.sas7bcat diff --git a/Makefile.am b/Makefile.am index 1694e474..ffe50165 100644 --- a/Makefile.am +++ b/Makefile.am @@ -87,7 +87,9 @@ EXTRA_DIST = \ resources/compression_type_0x02.sas7bdat \ resources/compression_type_0x06.sas7bdat \ resources/compression_type_0x09.sas7bdat \ - resources/compression_type_0x0d.sas7bdat + resources/compression_type_0x0d.sas7bdat \ + resources/format_with_default_32bit.sas7bcat \ + resources/format_with_default_64bit.sas7bcat include_HEADERS = src/readstat.h diff --git a/resources/format_with_default_32bit.sas7bcat b/resources/format_with_default_32bit.sas7bcat new file mode 100644 index 0000000000000000000000000000000000000000..16882960ae17120cd62a4c1f1557d917aee47850 GIT binary patch literal 17408 zcmeI3ziU%b6vxlYueSbZQz$5O@KS%!p~y>9tlfMqO)aEN$V;FZ#FqLHYfK=uh~S{R zlaq`8gXqvjL0mfebEuQ9I*F5$dd|JMF-a4PbZAPyC%k*O=f{2bet03d=O#pd<@L7* ztIvl9-@JU(OMP33hbK?P2S-LInRG(qk$PCzX|gAs(@F8g!;TH{Jzu5f)3a(9vGJYs zjFYWj=fB*<|I|0Aw=Vtw!%Fa#C=NG(er}>81 zu^f)~ia(XfEqM7ERh;z~|5fXfoQn~PO3wR+{lD~=(leJuLx}nXlt`_v_~W>#k%&32 z>1nU2o6xA%^&+Top*`dTmePJM%nbMu((?7(Ctb(3nm@|T^CJ(ATsm-P;arj%$B}*~ zvDtRWPH}s&%erbM_u4S8qIq4Px0)!%o_?OhV~Nn~%D8S~+Fb6W%c8{9JdRk}N@_3b zvbnqIws(dR1V8`;KmY{xfq?2U`~CZ@f42y2-O&5}y55KR;g%o(0tb`8hnD@nSw+k- zdyVe{Yqjmk!CW0?4FVtl0w4eaAOHd&00JNY0w4eaARq)jw%z}aJLXL87uNyw=^dTX zKPphlQ1hN_>eIfXd}KLFJ^Uj6*MK>pG!eFyaZCxpWxXt&78#~t&gWx7N6ssorTMPS zKIuj1EVp6m*DiZpPm7f?Rj%At)nKJmx~qL0I<7;9b+>-2Vz54@=GAItqjXK@tX!`K zfx1PEobBf1-<)Jkc+mSu9$f&d7B00@8p T2!H?xfB*=900@9Umk{^^ADWj; literal 0 HcmV?d00001 diff --git a/resources/format_with_default_64bit.sas7bcat b/resources/format_with_default_64bit.sas7bcat new file mode 100644 index 0000000000000000000000000000000000000000..510e1c6d8d6d49cfff835d8603c0ca68d2c76252 GIT binary patch literal 24576 zcmeI3O>YuG7{`ZKi!YTXJ!mj-;!DDzlBLjEZf*%w5-5blq#PO{EiqMugi5R@<5^F} zgBL%AiC*f(i+V7gYt(x1p+2~a2980Bq{-n=I3;6cp zvshl@c6j~Ci%@NK*CZ)R`1Tb?a-l^T3rdH($s~-lkt}738=d}^>t^Xhav@<{nwv`{ zGD1)M7Lw}L!u<95wClC^8~?b|ptl=Q+UNepwyo`%pEn6M2!H?xfB*=900@8p2!H?x zfB*=900@8p2!H?xfB*=900@8p2!H?xfB*>mB?6!Hy0y2xvzQ+ZFh6UY^u}R>00@8p z2!H?xfB*=900@8p2!H?x{5b)uZdG$ul~D28P>_K13v=M30uvVt0l9R<@KDk zTpm^={8SeWv5=j%E6)ET_lFm(6th={NB^(L8G>hjZ1JptJ?MFZZzq&Z*G(wZ2rIvL zOL>gNWr%E&-h9{~a1aDmidNO`6VT1LmX(Juc6yCpeJpK)@D4}Gt>E`I-3@j3lYN$N>o?@2werxcKU%7&3#RBz+5AqKT`em5Kk z)r~Im2gQf_j3P&$9x_1y1V8`;KmY_l00a&rfiL@<|GRSz%YV4;dBazEPryz0ueqBF z4Fo^{1V8`;KmY_l00ck)1V8`;K;Tdj*co#F|KKEN<@vw6F7{Jj4^S&!7vzlxoU1zX znOY?~Cf4oZ?1*@&L960)6+yd&P<>@fVHrBm(9$bnEGBx2Y@e#2S$Xqpad1], ctx->bswap); - if (label_pos >= label_count_used) { + if (label_pos >= label_count_used || (default_label_pos > 0 && label_pos == default_label_pos - 1)) { retval = READSTAT_ERROR_PARSE; goto cleanup; } @@ -84,64 +96,73 @@ static readstat_error_t sas7bcat_parse_value_labels(const char *value_start, siz const char *lbp2 = lbp1; /* Pass 2 -- parse pairs of values & labels */ - for (i=0; i value_labels_len || - &lbp2[10] - value_start > value_labels_len) { - retval = READSTAT_ERROR_PARSE; - goto cleanup; - } - readstat_value_t value = { .type = is_string ? READSTAT_TYPE_STRING : READSTAT_TYPE_DOUBLE }; - if (is_string) { - size_t value_entry_len = 6 + sas_read2(&lbp1[2], ctx->bswap); - size_t string_start = 22 + string_offset; - if (string_start > value_entry_len || - &lbp1[value_entry_len] - value_start > value_labels_len) { + for (i=0; i 0 && i == default_label_pos - 1; + if (!is_default_label) { + /* A non-default label with no value entry pointing at it would + * otherwise read from offset 0 and emit a duplicate value */ + if (value_offset[i] == UINT32_MAX) { retval = READSTAT_ERROR_PARSE; goto cleanup; } - size_t string_len = value_entry_len - string_start; - char *new_string_val = realloc(string_val, 4 * string_len + 1); - if (new_string_val == NULL) { - retval = READSTAT_ERROR_MALLOC; + lbp1 = value_start + value_offset[i]; + + if (&lbp1[30] - value_start > value_labels_len) { + retval = READSTAT_ERROR_PARSE; goto cleanup; } - string_val = new_string_val; - retval = readstat_convert(string_val, 4 * string_len + 1, - &lbp1[string_start], string_len, ctx->converter); - if (retval != READSTAT_OK) - goto cleanup; + value.type = is_string ? READSTAT_TYPE_STRING : READSTAT_TYPE_DOUBLE; + if (is_string) { + size_t value_entry_len = 6 + sas_read2(&lbp1[2], ctx->bswap); + size_t string_start = 22 + string_offset; + if (string_start > value_entry_len || + &lbp1[value_entry_len] - value_start > value_labels_len) { + retval = READSTAT_ERROR_PARSE; + goto cleanup; + } + size_t string_len = value_entry_len - string_start; + char *new_string_val = realloc(string_val, 4 * string_len + 1); + if (new_string_val == NULL) { + retval = READSTAT_ERROR_MALLOC; + goto cleanup; + } + string_val = new_string_val; + retval = readstat_convert(string_val, 4 * string_len + 1, + &lbp1[string_start], string_len, ctx->converter); + if (retval != READSTAT_OK) + goto cleanup; - value.v.string_value = string_val; - } else { - uint64_t val = sas_read8(&lbp1[22], bswap_doubles); - double dval = NAN; - if ((val | 0xFF0000000000) == 0xFFFFFFFFFFFF) { - sas_assign_tag(&value, (val >> 40)); + value.v.string_value = string_val; } else { - memcpy(&dval, &val, 8); - if (dval > 0.0) { - val = ~val; - memcpy(&dval, &val, 8); + uint64_t val = sas_read8(&lbp1[22], bswap_doubles); + double dval = NAN; + if ((val | 0xFF0000000000) == 0xFFFFFFFFFFFF) { + sas_assign_tag(&value, (val >> 40)); } else { - dval *= -1.0; + memcpy(&dval, &val, 8); + if (dval > 0.0) { + val = ~val; + memcpy(&dval, &val, 8); + } else { + dval *= -1.0; + } } - } - value.v.double_value = dval; + value.v.double_value = dval; + } } - size_t label_len = sas_read2(&lbp2[8], ctx->bswap); if (&lbp2[10] > value_start + value_labels_len) { retval = READSTAT_ERROR_PARSE; goto cleanup; } + size_t label_len = sas_read2(&lbp2[8], ctx->bswap); /* Some labels seem to overflow the reported block length, truncate it */ /* (Observed with formats.sasbcat from GSS2021, produced with 9.0401M6X64_SR12R2 */ if (label_len > value_start + value_labels_len - &lbp2[10]) { label_len = value_start + value_labels_len - &lbp2[10]; } - if (ctx->value_label_handler) { + if (ctx->value_label_handler && !is_default_label) { label = realloc(label, 4 * label_len + 1); retval = readstat_convert(label, 4 * label_len + 1, &lbp2[10], label_len, ctx->converter); @@ -170,6 +191,7 @@ static readstat_error_t sas7bcat_parse_block(const char *data, size_t data_size, size_t pad = 0; uint64_t label_count_capacity = 0; uint64_t label_count_used = 0; + uint64_t default_label_pos = 0; int string_offset = 0; int payload_offset = 106; uint16_t flags = 0; @@ -185,6 +207,7 @@ static readstat_error_t sas7bcat_parse_block(const char *data, size_t data_size, goto cleanup; label_count_capacity = sas_read8(&data[42+pad], ctx->bswap); label_count_used = sas_read8(&data[50+pad], ctx->bswap); + default_label_pos = sas_read8(&data[74+pad], ctx->bswap); /* Nonzero when the format's values are strings longer than 16 bytes, * which shifts each value one byte to the right and NUL-pads it */ string_offset = sas_read4(&data[124+pad], ctx->bswap); @@ -195,10 +218,11 @@ static readstat_error_t sas7bcat_parse_block(const char *data, size_t data_size, goto cleanup; label_count_capacity = sas_read4(&data[38+pad], ctx->bswap); label_count_used = sas_read4(&data[42+pad], ctx->bswap); + default_label_pos = sas_read4(&data[66+pad], ctx->bswap); string_offset = sas_read2(&data[104+pad], ctx->bswap); } - if (string_offset < 0) { + if (string_offset < 0 || default_label_pos > label_count_used) { retval = READSTAT_ERROR_PARSE; goto cleanup; } @@ -227,7 +251,7 @@ static readstat_error_t sas7bcat_parse_block(const char *data, size_t data_size, goto cleanup; if ((retval = sas7bcat_parse_value_labels(&data[payload_offset+pad], data_size - payload_offset - pad, - label_count_used, label_count_capacity, string_offset, name, ctx)) != READSTAT_OK) + label_count_used, label_count_capacity, default_label_pos, string_offset, name, ctx)) != READSTAT_OK) goto cleanup; cleanup: @@ -508,8 +532,9 @@ readstat_error_t readstat_parse_sas7bcat(readstat_parser_t *parser, const char * retval = READSTAT_ERROR_READ; goto cleanup; } - if (memcmp(&page[16], "XLSR", sizeof("XLSR")-1) == 0) { - retval = sas7bcat_augment_index(&page[16], ctx->page_size - 16, ctx); + int offset = ctx->u64 ? 32 : 16; + if (memcmp(&page[offset], "XLSR", sizeof("XLSR")-1) == 0) { + retval = sas7bcat_augment_index(&page[offset], ctx->page_size - offset, ctx); if (retval != READSTAT_OK) goto cleanup; } diff --git a/src/sas/readstat_sas7bcat_write.c b/src/sas/readstat_sas7bcat_write.c index e902db9b..3528634c 100644 --- a/src/sas/readstat_sas7bcat_write.c +++ b/src/sas/readstat_sas7bcat_write.c @@ -14,20 +14,22 @@ typedef struct sas7bcat_block_s { char data[1]; // Flexible array; use [1] for C++-98 compatibility } sas7bcat_block_t; -static sas7bcat_block_t *sas7bcat_block_for_label_set(readstat_label_set_t *r_label_set) { - size_t len = 0; +static sas7bcat_block_t *sas7bcat_block_for_label_set( + readstat_label_set_t *r_label_set, readstat_writer_t *writer) { + size_t len = 106; size_t name_len = strlen(r_label_set->name); int j; char name[32]; - len += 106; - if (name_len > 8) { len += 32; // long name if (name_len > 32) { name_len = 32; } } + if (writer->is_64bit) { + len += 32; + } memcpy(&name[0], r_label_set->name, name_len); @@ -39,6 +41,9 @@ static sas7bcat_block_t *sas7bcat_block_for_label_set(readstat_label_set_t *r_la for (j=0; jvalue_labels_count; j++) { readstat_value_label_t *value_label = readstat_get_value_label(r_label_set, j); len += value_entry_size; + if (r_label_set->type == READSTAT_TYPE_STRING && value_label->string_key_len > 16) { + len += value_label->string_key_len - 16; + } len += 8 + 2 + value_label->label_len + 1; } @@ -47,16 +52,24 @@ static sas7bcat_block_t *sas7bcat_block_for_label_set(readstat_label_set_t *r_la block->len = len; off_t begin = 106; - int32_t count = r_label_set->value_labels_count; - memcpy(&block->data[38], &count, sizeof(int32_t)); - memcpy(&block->data[42], &count, sizeof(int32_t)); + int64_t count = r_label_set->value_labels_count; + if (writer->is_64bit) { + memcpy(&block->data[42], &count, sizeof(int64_t)); + memcpy(&block->data[50], &count, sizeof(int64_t)); + + begin += 32; + } else { + int32_t count32 = (int32_t)count; + memcpy(&block->data[38], &count32, sizeof(int32_t)); + memcpy(&block->data[42], &count32, sizeof(int32_t)); + } if (name_len > 8) { - int16_t flags = 0x80; + int16_t flags = writer->is_64bit ? 0x20 : 0x80; memcpy(&block->data[2], &flags, sizeof(int16_t)); memcpy(&block->data[8], name, 8); - memset(&block->data[106], ' ', 32); - memcpy(&block->data[106], name, name_len); + memset(&block->data[begin], ' ', 32); + memcpy(&block->data[begin], name, name_len); begin += 32; } else { @@ -65,18 +78,17 @@ static sas7bcat_block_t *sas7bcat_block_for_label_set(readstat_label_set_t *r_la } char *lbp1 = &block->data[begin]; - char *lbp2 = &block->data[begin+r_label_set->value_labels_count*value_entry_size]; for (j=0; jvalue_labels_count; j++) { readstat_value_label_t *value_label = readstat_get_value_label(r_label_set, j); int16_t value_entry_len = value_entry_size - 6; - memcpy(&lbp1[2], &value_entry_len, sizeof(int16_t)); int32_t index = j; memcpy(&lbp1[10], &index, sizeof(int32_t)); if (r_label_set->type == READSTAT_TYPE_STRING) { size_t string_len = value_label->string_key_len; - if (string_len > 16) - string_len = 16; + if (string_len > 16) { + value_entry_len += string_len - 16; + } memset(&lbp1[22], ' ', 16); memcpy(&lbp1[22], value_label->string_key, string_len); } else { @@ -95,11 +107,18 @@ static sas7bcat_block_t *sas7bcat_block_for_label_set(readstat_label_set_t *r_la memcpy(&lbp1[22], &big_endian_value, sizeof(uint64_t)); } + memcpy(&lbp1[2], &value_entry_len, sizeof(int16_t)); + lbp1 += 6 + value_entry_len; + } + + char *lbp2 = lbp1; + for (j=0; jvalue_labels_count; j++) { + readstat_value_label_t *value_label = readstat_get_value_label(r_label_set, j); + int16_t label_len = value_label->label_len; memcpy(&lbp2[8], &label_len, sizeof(int16_t)); memcpy(&lbp2[10], value_label->label, label_len); - lbp1 += 6 + value_entry_len; lbp2 += 8 + 2 + value_label->label_len + 1; } @@ -127,12 +146,12 @@ static readstat_error_t sas7bcat_begin_data(void *writer_ctx) { readstat_error_t retval = READSTAT_OK; int i; - sas_header_info_t *hinfo = sas_header_info_init(writer, 0); + sas_header_info_t *hinfo = sas_header_info_init(writer, writer->is_64bit); sas7bcat_block_t **blocks = malloc(writer->label_sets_count * sizeof(sas7bcat_block_t)); char *page = malloc(hinfo->page_size); for (i=0; ilabel_sets_count; i++) { - blocks[i] = sas7bcat_block_for_label_set(writer->label_sets[i]); + blocks[i] = sas7bcat_block_for_label_set(writer->label_sets[i], writer); } hinfo->page_count = 4; @@ -150,25 +169,42 @@ static readstat_error_t sas7bcat_begin_data(void *writer_ctx) { memset(page, '\0', hinfo->page_size); // Page 1 - char *xlsr = &page[856]; - int32_t block_idx = 4; + int64_t xlsr_size = 212; + int64_t xlsr_offset = 856; + int64_t xlsr_O_offset = 50; + size_t block_header_size = 16; + if (writer->is_64bit) { + xlsr_size += 72; + xlsr_offset += 144; + xlsr_O_offset += 24; + block_header_size += 16; + } + + char *xlsr = &page[xlsr_offset]; + int64_t block_idx = 4; int16_t block_off = 16; for (i=0; ilabel_sets_count; i++) { - if (xlsr + 212 > page + hinfo->page_size) + if (xlsr + xlsr_size > page + hinfo->page_size) break; memcpy(&xlsr[0], "XLSR", 4); - memcpy(&xlsr[4], &block_idx, sizeof(int32_t)); - memcpy(&xlsr[8], &block_off, sizeof(int16_t)); + if (writer->is_64bit) { + memcpy(&xlsr[8], &block_idx, sizeof(int64_t)); + memcpy(&xlsr[16], &block_off, sizeof(int16_t)); + } else { + int32_t block_idx32 = (int32_t)block_idx; + memcpy(&xlsr[4], &block_idx32, sizeof(int32_t)); + memcpy(&xlsr[8], &block_off, sizeof(int16_t)); + } - xlsr[50] = 'O'; + xlsr[xlsr_O_offset] = 'O'; /* Each block on the data page is preceded by a 16-byte block header, * which the offsets stored in the XLSR entries must account for */ - block_off += 16 + blocks[i]->len; + block_off += block_header_size + blocks[i]->len; - xlsr += 212; + xlsr += xlsr_size; } retval = readstat_write_bytes(writer, page, hinfo->page_size); @@ -183,23 +219,31 @@ static readstat_error_t sas7bcat_begin_data(void *writer_ctx) { // Page 3 memset(page, '\0', hinfo->page_size); - char block_header[16]; + char block_header[32]; block_off = 16; for (i=0; ilabel_sets_count; i++) { - if (block_off + sizeof(block_header) + blocks[i]->len > hinfo->page_size) + if (block_off + block_header_size + blocks[i]->len > hinfo->page_size) break; - memset(block_header, '\0', sizeof(block_header)); + memset(block_header, '\0', block_header_size); + + size_t next_page_offset = 0; + size_t next_page_pos_offset = 4; + size_t block_len_offset = 6; + if (writer->is_64bit) { + next_page_pos_offset += 4; + block_len_offset += 4; + } int32_t next_page = 0; int16_t next_off = 0; int16_t block_len = blocks[i]->len; - memcpy(&block_header[0], &next_page, sizeof(int32_t)); - memcpy(&block_header[4], &next_off, sizeof(int16_t)); - memcpy(&block_header[6], &block_len, sizeof(int16_t)); + memcpy(&block_header[next_page_offset], &next_page, sizeof(int32_t)); + memcpy(&block_header[next_page_pos_offset], &next_off, sizeof(int16_t)); + memcpy(&block_header[block_len_offset], &block_len, sizeof(int16_t)); - memcpy(&page[block_off], block_header, sizeof(block_header)); - block_off += sizeof(block_header); + memcpy(&page[block_off], block_header, block_header_size); + block_off += block_header_size; memcpy(&page[block_off], blocks[i]->data, blocks[i]->len); block_off += blocks[i]->len; diff --git a/src/test/test_list.h b/src/test/test_list.h index f59202e1..d0da0ddb 100644 --- a/src/test/test_list.h +++ b/src/test/test_list.h @@ -1503,6 +1503,44 @@ static rt_test_group_t _test_groups[] = { } }, + { + .label = "Multiple SAS label sets", + .test_formats = RT_FORMAT_SAS7BCAT, + .label_sets_count = 2, + .label_sets = { + { + .name = "OneTwo", + .type = READSTAT_TYPE_DOUBLE, + .value_labels_count = 2, + .value_labels = { + { + .value = { .type = READSTAT_TYPE_DOUBLE, .v = { .double_value = 1.0 } }, + .label = "One" + }, + { + .value = { .type = READSTAT_TYPE_DOUBLE, .v = { .double_value = 2.0 } }, + .label = "Two" + } + } + }, + { + .name = "ThreeFour", + .type = READSTAT_TYPE_DOUBLE, + .value_labels_count = 2, + .value_labels = { + { + .value = { .type = READSTAT_TYPE_DOUBLE, .v = { .double_value = 3.0 } }, + .label = "Three" + }, + { + .value = { .type = READSTAT_TYPE_DOUBLE, .v = { .double_value = 4.0 } }, + .label = "Four" + } + } + } + } + }, + { .label = "DTA ancient value labels", .write_error = READSTAT_ERROR_NUMERIC_VALUE_IS_OUT_OF_RANGE, @@ -2578,6 +2616,64 @@ static rt_test_group_t _test_groups[] = { } } } + }, + + { + .resource_name = "format_with_default_32bit.sas7bcat", + .test_formats = RT_FORMAT_SAS7BCAT_32BIT, + .label_sets_count = 1, + .label_sets = { + { + .name = "$YESNO", + .type = READSTAT_TYPE_STRING, + .value_labels_count = 2, + .value_labels = { + { + .value = { + .type = READSTAT_TYPE_STRING, + .v = { .string_value = "Yes, I could not agree more" } + }, + .label = "True" + }, + { + .value = { + .type = READSTAT_TYPE_STRING, + .v = { .string_value = "No, not really" } + }, + .label = "False" + } + } + } + } + }, + + { + .resource_name = "format_with_default_64bit.sas7bcat", + .test_formats = RT_FORMAT_SAS7BCAT_64BIT, + .label_sets_count = 1, + .label_sets = { + { + .name = "$YESNO", + .type = READSTAT_TYPE_STRING, + .value_labels_count = 2, + .value_labels = { + { + .value = { + .type = READSTAT_TYPE_STRING, + .v = { .string_value = "Yes, I could not agree more" } + }, + .label = "True" + }, + { + .value = { + .type = READSTAT_TYPE_STRING, + .v = { .string_value = "No, not really" } + }, + .label = "False" + } + } + } + } } } } diff --git a/src/test/test_read.c b/src/test/test_read.c index ee20ad2e..949727af 100644 --- a/src/test/test_read.c +++ b/src/test/test_read.c @@ -39,8 +39,10 @@ char *file_extension(long format) { return "zsav"; if (format == RT_FORMAT_POR) return "por"; - if (format == RT_FORMAT_SAS7BCAT) + if (format == RT_FORMAT_SAS7BCAT_32BIT) return "sas7bcat"; + if (format == RT_FORMAT_SAS7BCAT_64BIT) + return "sas7bcat64"; if (format == RT_FORMAT_SAS7BDAT_32BIT_COMP_NONE) return "sas7bdat32"; if (format == RT_FORMAT_SAS7BDAT_32BIT_COMP_ROWS) diff --git a/src/test/test_readstat.h b/src/test/test_readstat.h index 0aa30af6..e985b19a 100644 --- a/src/test/test_readstat.h +++ b/src/test/test_readstat.h @@ -50,10 +50,12 @@ #define RT_FORMAT_SAS7BDAT (RT_FORMAT_SAS7BDAT_32BIT | RT_FORMAT_SAS7BDAT_64BIT) -#define RT_FORMAT_SAS7BCAT 0x100000 +#define RT_FORMAT_SAS7BCAT_32BIT 0x100000 +#define RT_FORMAT_SAS7BCAT_64BIT 0x200000 +#define RT_FORMAT_SAS7BCAT (RT_FORMAT_SAS7BCAT_32BIT | RT_FORMAT_SAS7BCAT_64BIT) -#define RT_FORMAT_XPORT_5 0x200000 -#define RT_FORMAT_XPORT_8 0x400000 +#define RT_FORMAT_XPORT_5 0x400000 +#define RT_FORMAT_XPORT_8 0x800000 #define RT_FORMAT_XPORT (RT_FORMAT_XPORT_5 | RT_FORMAT_XPORT_8) diff --git a/src/test/test_write.c b/src/test/test_write.c index 4a6b0501..6c782396 100644 --- a/src/test/test_write.c +++ b/src/test/test_write.c @@ -168,6 +168,7 @@ readstat_error_t write_file_to_buffer(rt_test_file_t *file, rt_buffer_t *buffer, readstat_writer_set_file_format_is_64bit(writer, !!(format & RT_FORMAT_SAS7BDAT_64BIT)); error = readstat_begin_writing_sas7bdat(writer, buffer, file->rows); } else if ((format & RT_FORMAT_SAS7BCAT)) { + readstat_writer_set_file_format_is_64bit(writer, !!(format & RT_FORMAT_SAS7BCAT_64BIT)); error = readstat_begin_writing_sas7bcat(writer, buffer); } else if ((format & RT_FORMAT_XPORT)) { readstat_writer_set_file_format_version(writer, sas_file_format_version(format));