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
zjF
YWj=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(Ju}Tpr?uZX9!Vi7kN?33e014BfAWtb
zsk>c6yCpeJpK)@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$Xq4oc5*rW{5
z-q~_@hIhJmc(2uLx9_Qpad1], 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));