diff --git a/NEWS.md b/NEWS.md index ddc10f9e..e1677efb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,7 @@ * SAS: support Win baltic code page (#231) * SPSS: support uppercase time stamps (#230) * SPSS: fixes for 252-255 byte strings (#226) + * SPSS: fixes for 0 byte strings (#245) # haven 1.0.0 diff --git a/src/readstat/sas/readstat_sas7bdat_write.c b/src/readstat/sas/readstat_sas7bdat_write.c index 9d7f572b..bcf0f9b1 100644 --- a/src/readstat/sas/readstat_sas7bdat_write.c +++ b/src/readstat/sas/readstat_sas7bdat_write.c @@ -144,7 +144,18 @@ static readstat_error_t sas7bdat_emit_header(readstat_writer_t *writer, sas_head }; memcpy(&header_start.magic, sas7bdat_magic_number, sizeof(header_start.magic)); - strncpy(header_start.file_label, writer->file_label, sizeof(header_start.file_label)); + + memset(header_start.file_label, ' ', sizeof(header_start.file_label)); + + size_t file_label_len = strlen(writer->file_label); + if (file_label_len > sizeof(header_start.file_label)) + file_label_len = sizeof(header_start.file_label); + + if (file_label_len) { + memcpy(header_start.file_label, writer->file_label, file_label_len); + } else { + memcpy(header_start.file_label, "DATASET", sizeof("DATASET")-1); + } return sas_write_header(writer, hinfo, header_start); } diff --git a/src/readstat/spss/readstat_sav_date.c b/src/readstat/spss/readstat_sav_date.c new file mode 100644 index 00000000..c02e8590 --- /dev/null +++ b/src/readstat/spss/readstat_sav_date.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include + +static inline int is_leap(int year) { + return ((year % 4 == 0 && year % 100 != 0) || year % 400 ==0); +} + +double readstat_sav_date_parse(const char *s, char **dest) { + // A SPSS date stored as the number of seconds since the start of the Gregorian calendar (midnight, Oct 14, 1582) + // Through the C interface in savReaderWriter I've verifed that leap seconds is ignored + int daysPerMonth[] = {31,28,31,30,31,30,31,31,30,31,30,31}; + int daysPerMonthLeap[] = {31,29,31,30,31,30,31,31,30,31,30,31}; + int year, month, day; + if (strlen(s) == 0) { + *dest = (char*) s; + return 0; + } + int ret = sscanf(s, "%d-%d-%d", &year, &month, &day); + month--; + if (month < 0 || month > 11 || ret!=3) { + *dest = (char*)s; + return 0; + } + int maxdays = (is_leap(year) ? daysPerMonthLeap : daysPerMonth)[month]; + if (day < 1 || day > maxdays) { + *dest =(char*)s; + return 0; + } else { + int days = 0; + + for (int i=1582; i 0) { + int days_in_year = is_leap(yr) ? 366 : 365; + if (days >= days_in_year) { + yr+=1; + days-=days_in_year; + continue; + } + int days_in_month = is_leap(yr) ? daysPerMonthLeap[month] : daysPerMonth[month]; + if (days >= days_in_month) { + month+=1; + days-=days_in_month; + continue; + } + day+= days; + days = 0; + } + snprintf(dest, size, "%04d-%02d-%02d", yr, month+1, day); + return dest; +} diff --git a/src/readstat/spss/readstat_sav_date.h b/src/readstat/spss/readstat_sav_date.h new file mode 100644 index 00000000..0ca10b4d --- /dev/null +++ b/src/readstat/spss/readstat_sav_date.h @@ -0,0 +1,7 @@ +#ifndef __READSTAT_SAV_DATE_H +#define __READSTAT_SAV_DATE_H + +double readstat_sav_date_parse(const char *s, char **dest); +char* readstat_sav_date_string(double seconds, char* dest, int size); + +#endif diff --git a/src/readstat/spss/readstat_sav_write.c b/src/readstat/spss/readstat_sav_write.c index c7d59b9d..d5ca9a01 100644 --- a/src/readstat/spss/readstat_sav_write.c +++ b/src/readstat/spss/readstat_sav_write.c @@ -907,7 +907,9 @@ static size_t sav_variable_width(readstat_type_t type, size_t user_width) { size_t last_segment_width = ((user_width - (n_segments - 1) * 252) + 7)/8*8; return (n_segments-1)*256 + last_segment_width; } - + if (user_width == 0) { + return 8; + } return (user_width + 7) / 8 * 8; } return 8; diff --git a/src/readstat/stata/readstat_dta.h b/src/readstat/stata/readstat_dta.h index a44e9cf7..93ea2811 100644 --- a/src/readstat/stata/readstat_dta.h +++ b/src/readstat/stata/readstat_dta.h @@ -11,11 +11,19 @@ typedef struct dta_header_s { int32_t nobs; } dta_header_t; -typedef struct dta_strl_header_s { - unsigned char vo_bytes[8]; +typedef struct dta_117_strl_header_s { + uint32_t v; + uint32_t o; unsigned char type; int32_t len; -} dta_strl_header_t; +} dta_117_strl_header_t; + +typedef struct dta_118_strl_header_s { + uint32_t v; + uint64_t o; + unsigned char type; + int32_t len; +} dta_118_strl_header_t; #pragma pack(pop) diff --git a/src/readstat/stata/readstat_dta_days.c b/src/readstat/stata/readstat_dta_days.c new file mode 100644 index 00000000..d560d096 --- /dev/null +++ b/src/readstat/stata/readstat_dta_days.c @@ -0,0 +1,96 @@ +#include +#include +#include + +static inline int is_leap(int year) { + return ((year % 4 == 0 && year % 100 != 0) || year % 400 ==0); +} + +int readstat_dta_num_days(const char *s, char **dest) { + int daysPerMonth[] = {31,28,31,30,31,30,31,31,30,31,30,31}; + int daysPerMonthLeap[] = {31,29,31,30,31,30,31,31,30,31,30,31}; + int year, month, day; + if (strlen(s) == 0) { + *dest = (char*) s; + return 0; + } + int ret = sscanf(s, "%d-%d-%d", &year, &month, &day); + month--; + if (month < 0 || month > 11 || ret!=3) { + *dest = (char*)s; + return 0; + } + int maxdays = (is_leap(year) ? daysPerMonthLeap : daysPerMonth)[month]; + if (day < 1 || day > maxdays) { + *dest = (char*)s; + return 0; + } else { + int days = 0; + + for (int i=year; i<1960; i++) { + days -= is_leap(i) ? 366 : 365; + } + + for (int i=1960; i 0) { + int days_in_year = is_leap(yr) ? 366 : 365; + if (days > days_in_year) { + yr-=1; + days-=days_in_year; + continue; + } + int days_in_month = is_leap(yr) ? daysPerMonthLeap[month] : daysPerMonth[month]; + if (days > days_in_month) { + month-=1; + days-=days_in_month; + continue; + } + day = days_in_month-days + 1; + days = 0; + } + } else { + while (days > 0) { + int days_in_year = is_leap(yr) ? 366 : 365; + if (days >= days_in_year) { + yr+=1; + days-=days_in_year; + continue; + } + int days_in_month = is_leap(yr) ? daysPerMonthLeap[month] : daysPerMonth[month]; + if (days >= days_in_month) { + month+=1; + days-=days_in_month; + continue; + } + day+= days; + days = 0; + } + } + snprintf(dest, size, "%04d-%02d-%02d", yr, month+1, day); + return dest; +} diff --git a/src/readstat/stata/readstat_dta_days.h b/src/readstat/stata/readstat_dta_days.h new file mode 100644 index 00000000..c2ac9fd1 --- /dev/null +++ b/src/readstat/stata/readstat_dta_days.h @@ -0,0 +1,7 @@ +#ifndef __READSTAT_DTA_DAYS_H +#define __READSTAT_DTA_DAYS_H + +int readstat_dta_num_days(const char *s, char** dest); +char* readstat_dta_days_string(int days, char* dest, int size); + +#endif diff --git a/src/readstat/stata/readstat_dta_parse_timestamp.c b/src/readstat/stata/readstat_dta_parse_timestamp.c index 64418df4..9593743a 100644 --- a/src/readstat/stata/readstat_dta_parse_timestamp.c +++ b/src/readstat/stata/readstat_dta_parse_timestamp.c @@ -19,37 +19,37 @@ static const char _dta_timestamp_parse_actions[] = { }; static const char _dta_timestamp_parse_key_offsets[] = { - 0, 0, 3, 5, 8, 16, 20, 21, - 22, 24, 27, 30, 33, 35, 36, 37, - 38, 39, 41, 42, 43, 44, 46, 47, - 48, 49, 53, 54, 55, 57, 58, 59, - 60, 62, 64, 66, 67, 68, 70, 72, - 73, 74, 75, 77, 78, 79, 80, 82, - 83, 84, 85 + 0, 0, 3, 5, 8, 24, 28, 30, + 31, 33, 36, 39, 42, 44, 46, 47, + 49, 53, 54, 56, 58, 59, 63, 65, + 66, 70, 71, 72, 74, 80, 81, 82, + 84, 86, 87, 91, 93, 94, 96, 98, + 99 }; static const char _dta_timestamp_parse_trans_keys[] = { 32, 48, 57, 48, 57, 32, 48, 57, 65, 68, 70, 74, 77, 78, 79, 83, - 80, 85, 112, 117, 82, 32, 48, 57, - 32, 48, 57, 32, 48, 57, 58, 48, - 57, 48, 57, 71, 32, 114, 103, 69, - 101, 67, 32, 99, 69, 101, 66, 32, - 98, 65, 85, 97, 117, 78, 32, 76, - 78, 32, 32, 110, 108, 110, 65, 97, - 82, 89, 32, 32, 114, 121, 79, 111, - 86, 32, 118, 67, 99, 84, 32, 116, - 69, 101, 80, 32, 112, 48, 57, 0 + 97, 100, 102, 106, 109, 110, 111, 115, + 80, 85, 112, 117, 82, 114, 32, 48, + 57, 32, 48, 57, 32, 48, 57, 58, + 48, 57, 48, 57, 71, 103, 32, 69, + 101, 67, 90, 99, 122, 32, 69, 101, + 66, 98, 32, 65, 85, 97, 117, 78, + 110, 32, 76, 78, 108, 110, 32, 32, + 65, 97, 73, 82, 89, 105, 114, 121, + 32, 32, 79, 111, 86, 118, 32, 67, + 75, 99, 107, 84, 116, 32, 69, 101, + 80, 112, 32, 48, 57, 0 }; static const char _dta_timestamp_parse_single_lengths[] = { - 0, 1, 0, 1, 8, 4, 1, 1, - 0, 1, 1, 1, 0, 1, 1, 1, - 1, 2, 1, 1, 1, 2, 1, 1, - 1, 4, 1, 1, 2, 1, 1, 1, - 2, 2, 2, 1, 1, 2, 2, 1, - 1, 1, 2, 1, 1, 1, 2, 1, - 1, 1, 0 + 0, 1, 0, 1, 16, 4, 2, 1, + 0, 1, 1, 1, 0, 2, 1, 2, + 4, 1, 2, 2, 1, 4, 2, 1, + 4, 1, 1, 2, 6, 1, 1, 2, + 2, 1, 4, 2, 1, 2, 2, 1, + 0 }; static const char _dta_timestamp_parse_range_lengths[] = { @@ -58,58 +58,56 @@ static const char _dta_timestamp_parse_range_lengths[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1 + 1 }; static const unsigned char _dta_timestamp_parse_index_offsets[] = { - 0, 0, 3, 5, 8, 17, 22, 24, - 26, 28, 31, 34, 37, 39, 41, 43, - 45, 47, 50, 52, 54, 56, 59, 61, - 63, 65, 70, 72, 74, 77, 79, 81, - 83, 86, 89, 92, 94, 96, 99, 102, - 104, 106, 108, 111, 113, 115, 117, 120, - 122, 124, 126 + 0, 0, 3, 5, 8, 25, 30, 33, + 35, 37, 40, 43, 46, 48, 51, 53, + 56, 61, 63, 66, 69, 71, 76, 79, + 81, 86, 88, 90, 93, 100, 102, 104, + 107, 110, 112, 117, 120, 122, 125, 128, + 130 +}; + +static const char _dta_timestamp_parse_indicies[] = { + 0, 2, 1, 2, 1, 3, 4, 1, + 5, 6, 7, 8, 9, 10, 11, 12, + 5, 6, 7, 8, 9, 10, 11, 12, + 1, 13, 14, 13, 14, 1, 15, 15, + 1, 16, 1, 17, 1, 18, 19, 1, + 20, 21, 1, 23, 22, 1, 24, 1, + 25, 25, 1, 26, 1, 27, 27, 1, + 28, 28, 28, 28, 1, 29, 1, 30, + 30, 1, 31, 31, 1, 32, 1, 33, + 34, 33, 34, 1, 35, 35, 1, 36, + 1, 37, 38, 37, 38, 1, 39, 1, + 40, 1, 41, 41, 1, 42, 43, 42, + 42, 43, 42, 1, 44, 1, 45, 1, + 46, 46, 1, 47, 47, 1, 48, 1, + 49, 49, 49, 49, 1, 50, 50, 1, + 51, 1, 52, 52, 1, 53, 53, 1, + 54, 1, 55, 1, 0 }; static const char _dta_timestamp_parse_trans_targs[] = { - 2, 3, 0, 3, 0, 4, 3, 0, - 5, 17, 21, 25, 33, 38, 42, 46, - 0, 6, 13, 15, 16, 0, 7, 0, - 8, 0, 9, 0, 10, 9, 0, 10, - 11, 0, 12, 11, 0, 50, 0, 14, - 0, 8, 0, 7, 0, 14, 0, 18, - 20, 0, 19, 0, 8, 0, 19, 0, - 22, 24, 0, 23, 0, 8, 0, 23, - 0, 26, 28, 31, 32, 0, 27, 0, - 8, 0, 29, 30, 0, 8, 0, 8, - 0, 27, 0, 29, 30, 0, 34, 37, - 0, 35, 36, 0, 8, 0, 8, 0, - 35, 36, 0, 39, 41, 0, 40, 0, - 8, 0, 40, 0, 43, 45, 0, 44, - 0, 8, 0, 44, 0, 47, 49, 0, - 48, 0, 8, 0, 48, 0, 50, 0, - 0 + 2, 0, 3, 4, 3, 5, 15, 18, + 21, 27, 31, 34, 37, 6, 13, 7, + 8, 9, 10, 9, 10, 11, 11, 12, + 40, 14, 8, 16, 17, 8, 19, 20, + 8, 22, 24, 23, 8, 25, 26, 8, + 8, 28, 29, 30, 8, 8, 32, 33, + 8, 35, 36, 8, 38, 39, 8, 40 }; static const char _dta_timestamp_parse_trans_actions[] = { - 0, 35, 0, 35, 0, 3, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 35, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 11, 0, 35, 0, 29, 1, 0, 0, - 35, 0, 31, 1, 0, 35, 0, 0, - 0, 19, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 27, 0, 0, 0, - 0, 0, 0, 0, 0, 7, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 5, 0, 0, 0, 0, 17, 0, 15, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 9, 0, 13, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 25, 0, 0, 0, 0, 0, 0, 0, - 0, 23, 0, 0, 0, 0, 0, 0, - 0, 0, 21, 0, 0, 0, 1, 0, - 0 + 11, 35, 29, 1, 0, 35, 1, 31, + 35, 0, 19, 0, 0, 27, 0, 0, + 7, 0, 0, 0, 5, 0, 0, 17, + 15, 0, 0, 0, 13, 9, 0, 0, + 25, 0, 0, 23, 0, 0, 21, 1 }; static const char _dta_timestamp_parse_eof_actions[] = { @@ -118,8 +116,7 @@ static const char _dta_timestamp_parse_eof_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 33 + 33 }; static const int dta_timestamp_parse_start = 1; @@ -138,12 +135,12 @@ readstat_error_t dta_parse_timestamp(const char *data, size_t len, struct tm *ti int cs; int temp_val = 0; -#line 142 "src/stata/readstat_dta_parse_timestamp.c" +#line 139 "src/stata/readstat_dta_parse_timestamp.c" { cs = dta_timestamp_parse_start; } -#line 147 "src/stata/readstat_dta_parse_timestamp.c" +#line 144 "src/stata/readstat_dta_parse_timestamp.c" { int _klen; unsigned int _trans; @@ -205,6 +202,7 @@ readstat_error_t dta_parse_timestamp(const char *data, size_t len, struct tm *ti } _match: + _trans = _dta_timestamp_parse_indicies[_trans]; cs = _dta_timestamp_parse_trans_targs[_trans]; if ( _dta_timestamp_parse_trans_actions[_trans] == 0 ) @@ -286,7 +284,7 @@ readstat_error_t dta_parse_timestamp(const char *data, size_t len, struct tm *ti #line 46 "src/stata/readstat_dta_parse_timestamp.rl" { timestamp->tm_hour = temp_val; } break; -#line 290 "src/stata/readstat_dta_parse_timestamp.c" +#line 288 "src/stata/readstat_dta_parse_timestamp.c" } } @@ -306,7 +304,7 @@ readstat_error_t dta_parse_timestamp(const char *data, size_t len, struct tm *ti #line 48 "src/stata/readstat_dta_parse_timestamp.rl" { timestamp->tm_min = temp_val; } break; -#line 310 "src/stata/readstat_dta_parse_timestamp.c" +#line 308 "src/stata/readstat_dta_parse_timestamp.c" } } } @@ -317,7 +315,7 @@ readstat_error_t dta_parse_timestamp(const char *data, size_t len, struct tm *ti #line 54 "src/stata/readstat_dta_parse_timestamp.rl" - if (cs < 50|| p != pe) { + if (cs < 40|| p != pe) { if (ctx->error_handler) { snprintf(ctx->error_buf, sizeof(ctx->error_buf), "Invalid timestamp string (length=%d): %*s", (int)len, (int)-len, data); ctx->error_handler(ctx->error_buf, ctx->user_ctx); diff --git a/src/readstat/stata/readstat_dta_read.c b/src/readstat/stata/readstat_dta_read.c index c2875544..1bb7d3cb 100644 --- a/src/readstat/stata/readstat_dta_read.c +++ b/src/readstat/stata/readstat_dta_read.c @@ -335,17 +335,18 @@ static void dta_interpret_strl_vo_bytes(dta_ctx_t *ctx, unsigned char *vo_bytes, } } -static readstat_error_t dta_read_strl(dta_ctx_t *ctx, dta_strl_t *strl) { +static readstat_error_t dta_117_read_strl(dta_ctx_t *ctx, dta_strl_t *strl) { readstat_error_t retval = READSTAT_OK; readstat_io_t *io = ctx->io; - dta_strl_header_t header; + dta_117_strl_header_t header; - if (io->read(&header, sizeof(header), io->io_ctx) != sizeof(dta_strl_header_t)) { + if (io->read(&header, sizeof(header), io->io_ctx) != sizeof(dta_117_strl_header_t)) { retval = READSTAT_ERROR_READ; goto cleanup; } - dta_interpret_strl_vo_bytes(ctx, header.vo_bytes, strl); + strl->v = ctx->bswap ? byteswap4(header.v) : header.v; + strl->o = ctx->bswap ? byteswap4(header.o) : header.o; strl->type = header.type; strl->len = ctx->bswap ? byteswap4(header.len) : header.len; @@ -353,6 +354,32 @@ static readstat_error_t dta_read_strl(dta_ctx_t *ctx, dta_strl_t *strl) { return retval; } +static readstat_error_t dta_118_read_strl(dta_ctx_t *ctx, dta_strl_t *strl) { + readstat_error_t retval = READSTAT_OK; + readstat_io_t *io = ctx->io; + dta_118_strl_header_t header; + + if (io->read(&header, sizeof(header), io->io_ctx) != sizeof(dta_118_strl_header_t)) { + retval = READSTAT_ERROR_READ; + goto cleanup; + } + + strl->v = ctx->bswap ? byteswap4(header.v) : header.v; + strl->o = ctx->bswap ? byteswap8(header.o) : header.o; + strl->type = header.type; + strl->len = ctx->bswap ? byteswap4(header.len) : header.len; + +cleanup: + return retval; +} + +static readstat_error_t dta_read_strl(dta_ctx_t *ctx, dta_strl_t *strl) { + if (ctx->strl_o_len > 4) { + return dta_118_read_strl(ctx, strl); + } + return dta_117_read_strl(ctx, strl); +} + static readstat_error_t dta_read_strls(dta_ctx_t *ctx) { if (!ctx->file_is_xmlish) return READSTAT_OK; diff --git a/src/readstat/stata/readstat_dta_write.c b/src/readstat/stata/readstat_dta_write.c index 314b5910..1b8662bd 100644 --- a/src/readstat/stata/readstat_dta_write.c +++ b/src/readstat/stata/readstat_dta_write.c @@ -99,8 +99,13 @@ static readstat_error_t dta_emit_header_time_stamp(readstat_writer_t *writer, dt time_t now = writer->timestamp; struct tm *time_s = localtime(&now); char *timestamp = calloc(1, ctx->timestamp_len); - uint8_t actual_timestamp_len = strftime(timestamp, ctx->timestamp_len, - "%d %b %Y %H:%M", time_s); + /* There are locale/portability issues with strftime so hack something up */ + char months[][4] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + uint8_t actual_timestamp_len = snprintf(timestamp, ctx->timestamp_len, "%02d %3s %04d %02d:%02d", + time_s->tm_mday, months[time_s->tm_mon], time_s->tm_year + 1900, + time_s->tm_hour, time_s->tm_min); if (actual_timestamp_len == 0) { error = READSTAT_ERROR_WRITE; goto cleanup; @@ -523,6 +528,28 @@ static readstat_error_t dta_emit_characteristics(readstat_writer_t *writer, dta_ return error; } +static readstat_error_t dta_117_emit_strl_header(readstat_writer_t *writer, readstat_string_ref_t *ref) { + dta_117_strl_header_t header = { + .v = ref->first_v, + .o = ref->first_o, + .type = DTA_GSO_TYPE_ASCII, + .len = ref->len + }; + + return readstat_write_bytes(writer, &header, sizeof(dta_117_strl_header_t)); +} + +static readstat_error_t dta_118_emit_strl_header(readstat_writer_t *writer, readstat_string_ref_t *ref) { + dta_118_strl_header_t header = { + .v = ref->first_v, + .o = ref->first_o, + .type = DTA_GSO_TYPE_ASCII, + .len = ref->len + }; + + return readstat_write_bytes(writer, &header, sizeof(dta_118_strl_header_t)); +} + static readstat_error_t dta_emit_strls(readstat_writer_t *writer, dta_ctx_t *ctx) { if (!ctx->file_is_xmlish) return READSTAT_OK; @@ -541,25 +568,11 @@ static readstat_error_t dta_emit_strls(readstat_writer_t *writer, dta_ctx_t *ctx if (retval != READSTAT_OK) goto cleanup; - dta_strl_header_t header; - if (ctx->strl_v_len == 2) { - int16_t v = ref->first_v; - int64_t o = ref->first_o; - memcpy(&header.vo_bytes[0], &v, sizeof(int16_t)); - if (!machine_is_little_endian()) { - o <<= 16; - } - memcpy(&header.vo_bytes[2], &o, 6); + if (ctx->strl_o_len > 4) { + retval = dta_118_emit_strl_header(writer, ref); } else { - int32_t v = ref->first_v; - int32_t o = ref->first_o; - memcpy(&header.vo_bytes[0], &v, sizeof(int32_t)); - memcpy(&header.vo_bytes[4], &o, sizeof(int32_t)); + retval = dta_117_emit_strl_header(writer, ref); } - header.type = DTA_GSO_TYPE_ASCII; - header.len = ref->len; - - retval = readstat_write_bytes(writer, &header, sizeof(dta_strl_header_t)); if (retval != READSTAT_OK) goto cleanup; @@ -956,7 +969,11 @@ static size_t dta_measure_strls(readstat_writer_t *writer, dta_ctx_t *ctx) { for (i=0; istring_refs_count; i++) { readstat_string_ref_t *ref = writer->string_refs[i]; - strls_len += 16 + ref->len; + if (ctx->strl_o_len > 4) { + strls_len += 20 + ref->len; + } else { + strls_len += 16 + ref->len; + } } return (dta_measure_tag(ctx, "")