js-stream-sas7bdat 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/binding.gyp +58 -0
- package/dist/class/datasetSas7BDat.d.ts.map +1 -1
- package/dist/class/datasetSas7BDat.js +4 -3
- package/dist/class/datasetSas7BDat.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -7
- package/prebuilds/darwin-arm64/js-stream-sas7bdat.node +0 -0
- package/prebuilds/darwin-x64/js-stream-sas7bdat.node +0 -0
- package/prebuilds/linux-arm64/js-stream-sas7bdat.node +0 -0
- package/prebuilds/linux-x64/js-stream-sas7bdat.node +0 -0
- package/prebuilds/win32-ia32/js-stream-sas7bdat.node +0 -0
- package/prebuilds/win32-x64/js-stream-sas7bdat.node +0 -0
- package/src/binding/ReadStat/LICENSE +19 -0
- package/src/binding/ReadStat/README.md +483 -0
- package/src/binding/ReadStat/src/CKHashTable.c +309 -0
- package/src/binding/ReadStat/src/CKHashTable.h +37 -0
- package/src/binding/ReadStat/src/readstat.h +627 -0
- package/src/binding/ReadStat/src/readstat_bits.c +69 -0
- package/src/binding/ReadStat/src/readstat_bits.h +20 -0
- package/src/binding/ReadStat/src/readstat_convert.c +36 -0
- package/src/binding/ReadStat/src/readstat_convert.h +2 -0
- package/src/binding/ReadStat/src/readstat_error.c +126 -0
- package/src/binding/ReadStat/src/readstat_iconv.h +15 -0
- package/src/binding/ReadStat/src/readstat_io_unistd.c +147 -0
- package/src/binding/ReadStat/src/readstat_io_unistd.h +11 -0
- package/src/binding/ReadStat/src/readstat_malloc.c +34 -0
- package/src/binding/ReadStat/src/readstat_malloc.h +4 -0
- package/src/binding/ReadStat/src/readstat_metadata.c +53 -0
- package/src/binding/ReadStat/src/readstat_parser.c +121 -0
- package/src/binding/ReadStat/src/readstat_strings.h +6 -0
- package/src/binding/ReadStat/src/readstat_value.c +178 -0
- package/src/binding/ReadStat/src/readstat_variable.c +123 -0
- package/src/binding/ReadStat/src/readstat_writer.c +677 -0
- package/src/binding/ReadStat/src/readstat_writer.h +21 -0
- package/src/binding/ReadStat/src/sas/ieee.c +420 -0
- package/src/binding/ReadStat/src/sas/ieee.h +6 -0
- package/src/binding/ReadStat/src/sas/readstat_sas.c +528 -0
- package/src/binding/ReadStat/src/sas/readstat_sas.h +131 -0
- package/src/binding/ReadStat/src/sas/readstat_sas7bcat_read.c +515 -0
- package/src/binding/ReadStat/src/sas/readstat_sas7bcat_write.c +218 -0
- package/src/binding/ReadStat/src/sas/readstat_sas7bdat_read.c +1304 -0
- package/src/binding/ReadStat/src/sas/readstat_sas7bdat_write.c +812 -0
- package/src/binding/ReadStat/src/sas/readstat_sas_rle.c +286 -0
- package/src/binding/ReadStat/src/sas/readstat_sas_rle.h +8 -0
- package/src/binding/ReadStat/src/sas/readstat_xport.c +28 -0
- package/src/binding/ReadStat/src/sas/readstat_xport.h +47 -0
- package/src/binding/ReadStat/src/sas/readstat_xport_parse_format.c +265 -0
- package/src/binding/ReadStat/src/sas/readstat_xport_parse_format.h +4 -0
- package/src/binding/ReadStat/src/sas/readstat_xport_parse_format.rl +68 -0
- package/src/binding/ReadStat/src/sas/readstat_xport_read.c +777 -0
- package/src/binding/ReadStat/src/sas/readstat_xport_write.c +561 -0
- package/src/binding/readstat_binding.cc +393 -0
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
|
|
2
|
+
#include <stdio.h>
|
|
3
|
+
#include <stdlib.h>
|
|
4
|
+
#include <time.h>
|
|
5
|
+
|
|
6
|
+
#include "../readstat.h"
|
|
7
|
+
#include "../readstat_writer.h"
|
|
8
|
+
#include "readstat_sas.h"
|
|
9
|
+
#include "readstat_xport.h"
|
|
10
|
+
#include "readstat_xport_parse_format.h"
|
|
11
|
+
#include "ieee.h"
|
|
12
|
+
|
|
13
|
+
#define XPORT_DEFAULT_VERSION 8
|
|
14
|
+
#define RECORD_LEN 80
|
|
15
|
+
|
|
16
|
+
#if defined _MSC_VER
|
|
17
|
+
#define restrict __restrict
|
|
18
|
+
#endif
|
|
19
|
+
|
|
20
|
+
static void copypad(char * restrict dst, size_t dst_len, const char * restrict src) {
|
|
21
|
+
char *dst_end = dst + dst_len;
|
|
22
|
+
while (dst < dst_end && *src)
|
|
23
|
+
*dst++ = *src++;
|
|
24
|
+
while (dst < dst_end)
|
|
25
|
+
*dst++ = ' ';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static readstat_error_t xport_write_bytes(readstat_writer_t *writer, const void *bytes, size_t len) {
|
|
29
|
+
return readstat_write_bytes_as_lines(writer, bytes, len, RECORD_LEN, "");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static readstat_error_t xport_finish_record(readstat_writer_t *writer) {
|
|
33
|
+
return readstat_write_line_padding(writer, ' ', RECORD_LEN, "");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static readstat_error_t xport_write_record(readstat_writer_t *writer, const char *record) {
|
|
37
|
+
size_t len = strlen(record);
|
|
38
|
+
readstat_error_t retval = READSTAT_OK;
|
|
39
|
+
|
|
40
|
+
retval = xport_write_bytes(writer, record, len);
|
|
41
|
+
if (retval != READSTAT_OK)
|
|
42
|
+
goto cleanup;
|
|
43
|
+
|
|
44
|
+
retval = xport_finish_record(writer);
|
|
45
|
+
if (retval != READSTAT_OK)
|
|
46
|
+
goto cleanup;
|
|
47
|
+
|
|
48
|
+
cleanup:
|
|
49
|
+
return retval;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static readstat_error_t xport_write_header_record_v8(readstat_writer_t *writer,
|
|
53
|
+
xport_header_record_t *xrecord) {
|
|
54
|
+
char record[RECORD_LEN+1];
|
|
55
|
+
snprintf(record, sizeof(record),
|
|
56
|
+
"HEADER RECORD*******%-8sHEADER RECORD!!!!!!!" "%15d" "%15d",
|
|
57
|
+
xrecord->name, xrecord->num1, xrecord->num2);
|
|
58
|
+
return xport_write_record(writer, record);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static readstat_error_t xport_write_header_record(readstat_writer_t *writer,
|
|
62
|
+
xport_header_record_t *xrecord) {
|
|
63
|
+
char record[RECORD_LEN+1];
|
|
64
|
+
snprintf(record, sizeof(record),
|
|
65
|
+
"HEADER RECORD*******%-8sHEADER RECORD!!!!!!!" "%05d%05d%05d" "%05d%05d%05d",
|
|
66
|
+
xrecord->name, xrecord->num1, xrecord->num2, xrecord->num3,
|
|
67
|
+
xrecord->num4, xrecord->num5, xrecord->num6);
|
|
68
|
+
return xport_write_record(writer, record);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static size_t xport_variable_width(readstat_type_t type, size_t user_width) {
|
|
72
|
+
if (type == READSTAT_TYPE_STRING)
|
|
73
|
+
return user_width;
|
|
74
|
+
|
|
75
|
+
if (user_width >= XPORT_MAX_DOUBLE_SIZE || user_width == 0)
|
|
76
|
+
return XPORT_MAX_DOUBLE_SIZE;
|
|
77
|
+
|
|
78
|
+
if (user_width <= XPORT_MIN_DOUBLE_SIZE)
|
|
79
|
+
return XPORT_MIN_DOUBLE_SIZE;
|
|
80
|
+
|
|
81
|
+
return user_width;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static readstat_error_t xport_write_variables(readstat_writer_t *writer) {
|
|
85
|
+
readstat_error_t retval = READSTAT_OK;
|
|
86
|
+
int i;
|
|
87
|
+
long offset = 0;
|
|
88
|
+
int num_long_labels = 0;
|
|
89
|
+
int any_has_long_format = 0;
|
|
90
|
+
for (i=0; i<writer->variables_count; i++) {
|
|
91
|
+
int needs_long_record = 0;
|
|
92
|
+
readstat_variable_t *variable = readstat_get_variable(writer, i);
|
|
93
|
+
size_t width = xport_variable_width(variable->type, variable->user_width);
|
|
94
|
+
xport_namestr_t namestr = {
|
|
95
|
+
.nvar0 = i+1,
|
|
96
|
+
.nlng = width,
|
|
97
|
+
.npos = offset,
|
|
98
|
+
.niform = " ",
|
|
99
|
+
.nform = " "
|
|
100
|
+
};
|
|
101
|
+
if (readstat_variable_get_type_class(variable) == READSTAT_TYPE_CLASS_STRING) {
|
|
102
|
+
namestr.ntype = SAS_COLUMN_TYPE_CHR;
|
|
103
|
+
} else {
|
|
104
|
+
namestr.ntype = SAS_COLUMN_TYPE_NUM;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
copypad(namestr.nname, sizeof(namestr.nname), variable->name);
|
|
108
|
+
copypad(namestr.nlabel, sizeof(namestr.nlabel), variable->label);
|
|
109
|
+
|
|
110
|
+
if (variable->format[0]) {
|
|
111
|
+
xport_format_t format;
|
|
112
|
+
|
|
113
|
+
retval = xport_parse_format(variable->format, strlen(variable->format),
|
|
114
|
+
&format, NULL, NULL);
|
|
115
|
+
if (retval != READSTAT_OK)
|
|
116
|
+
goto cleanup;
|
|
117
|
+
|
|
118
|
+
copypad(namestr.nform, sizeof(namestr.nform), format.name);
|
|
119
|
+
namestr.nfl = format.width;
|
|
120
|
+
namestr.nfd = format.decimals;
|
|
121
|
+
|
|
122
|
+
copypad(namestr.niform, sizeof(namestr.niform), format.name);
|
|
123
|
+
namestr.nifl = format.width;
|
|
124
|
+
namestr.nifd = format.decimals;
|
|
125
|
+
|
|
126
|
+
if (strlen(format.name) > 8) {
|
|
127
|
+
any_has_long_format = 1;
|
|
128
|
+
needs_long_record = 1;
|
|
129
|
+
}
|
|
130
|
+
} else if (variable->display_width) {
|
|
131
|
+
namestr.nfl = variable->display_width;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
namestr.nfj = (variable->alignment == READSTAT_ALIGNMENT_RIGHT);
|
|
135
|
+
|
|
136
|
+
if (writer->version == 8) {
|
|
137
|
+
copypad(namestr.longname, sizeof(namestr.longname), variable->name);
|
|
138
|
+
|
|
139
|
+
size_t label_len = strlen(variable->label);
|
|
140
|
+
if (label_len > 40) {
|
|
141
|
+
needs_long_record = 1;
|
|
142
|
+
}
|
|
143
|
+
namestr.labeln = label_len;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (needs_long_record) {
|
|
147
|
+
num_long_labels++;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
offset += width;
|
|
151
|
+
|
|
152
|
+
xport_namestr_bswap(&namestr);
|
|
153
|
+
|
|
154
|
+
retval = xport_write_bytes(writer, &namestr, sizeof(xport_namestr_t));
|
|
155
|
+
if (retval != READSTAT_OK)
|
|
156
|
+
goto cleanup;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
retval = xport_finish_record(writer);
|
|
160
|
+
if (retval != READSTAT_OK)
|
|
161
|
+
goto cleanup;
|
|
162
|
+
|
|
163
|
+
if (writer->version == 8 && num_long_labels) {
|
|
164
|
+
xport_header_record_t header = {
|
|
165
|
+
.name = "LABELV8",
|
|
166
|
+
.num1 = num_long_labels };
|
|
167
|
+
if (any_has_long_format) {
|
|
168
|
+
strcpy(header.name, "LABELV9");
|
|
169
|
+
}
|
|
170
|
+
retval = xport_write_header_record_v8(writer, &header);
|
|
171
|
+
if (retval != READSTAT_OK)
|
|
172
|
+
goto cleanup;
|
|
173
|
+
|
|
174
|
+
for (i=0; i<writer->variables_count; i++) {
|
|
175
|
+
readstat_variable_t *variable = readstat_get_variable(writer, i);
|
|
176
|
+
size_t label_len = strlen(variable->label);
|
|
177
|
+
size_t name_len = strlen(variable->name);
|
|
178
|
+
size_t format_len = strlen(variable->format);
|
|
179
|
+
int has_long_label = 0;
|
|
180
|
+
int has_long_format = 0;
|
|
181
|
+
|
|
182
|
+
has_long_label = (label_len > 40);
|
|
183
|
+
|
|
184
|
+
if (variable->format[0]) {
|
|
185
|
+
xport_format_t format;
|
|
186
|
+
|
|
187
|
+
retval = xport_parse_format(variable->format, strlen(variable->format),
|
|
188
|
+
&format, NULL, NULL);
|
|
189
|
+
if (retval != READSTAT_OK)
|
|
190
|
+
goto cleanup;
|
|
191
|
+
|
|
192
|
+
if (strlen(format.name) > 8) {
|
|
193
|
+
has_long_format = 1;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (has_long_format) {
|
|
198
|
+
uint16_t labeldef[5] = { i+1, name_len, label_len, format_len, format_len };
|
|
199
|
+
|
|
200
|
+
if (machine_is_little_endian()) {
|
|
201
|
+
labeldef[0] = byteswap2(labeldef[0]);
|
|
202
|
+
labeldef[1] = byteswap2(labeldef[1]);
|
|
203
|
+
labeldef[2] = byteswap2(labeldef[2]);
|
|
204
|
+
labeldef[3] = byteswap2(labeldef[3]);
|
|
205
|
+
labeldef[4] = byteswap2(labeldef[4]);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
retval = readstat_write_bytes(writer, labeldef, sizeof(labeldef));
|
|
209
|
+
if (retval != READSTAT_OK)
|
|
210
|
+
goto cleanup;
|
|
211
|
+
|
|
212
|
+
retval = readstat_write_string(writer, variable->name);
|
|
213
|
+
if (retval != READSTAT_OK)
|
|
214
|
+
goto cleanup;
|
|
215
|
+
|
|
216
|
+
retval = readstat_write_string(writer, variable->label);
|
|
217
|
+
if (retval != READSTAT_OK)
|
|
218
|
+
goto cleanup;
|
|
219
|
+
|
|
220
|
+
retval = readstat_write_string(writer, variable->format);
|
|
221
|
+
if (retval != READSTAT_OK)
|
|
222
|
+
goto cleanup;
|
|
223
|
+
|
|
224
|
+
retval = readstat_write_string(writer, variable->format);
|
|
225
|
+
if (retval != READSTAT_OK)
|
|
226
|
+
goto cleanup;
|
|
227
|
+
|
|
228
|
+
} else if (has_long_label) {
|
|
229
|
+
uint16_t labeldef[3] = { i+1, name_len, label_len };
|
|
230
|
+
|
|
231
|
+
if (machine_is_little_endian()) {
|
|
232
|
+
labeldef[0] = byteswap2(labeldef[0]);
|
|
233
|
+
labeldef[1] = byteswap2(labeldef[1]);
|
|
234
|
+
labeldef[2] = byteswap2(labeldef[2]);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
retval = readstat_write_bytes(writer, labeldef, sizeof(labeldef));
|
|
238
|
+
if (retval != READSTAT_OK)
|
|
239
|
+
goto cleanup;
|
|
240
|
+
|
|
241
|
+
retval = readstat_write_string(writer, variable->name);
|
|
242
|
+
if (retval != READSTAT_OK)
|
|
243
|
+
goto cleanup;
|
|
244
|
+
|
|
245
|
+
retval = readstat_write_string(writer, variable->label);
|
|
246
|
+
if (retval != READSTAT_OK)
|
|
247
|
+
goto cleanup;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
retval = xport_finish_record(writer);
|
|
252
|
+
if (retval != READSTAT_OK)
|
|
253
|
+
goto cleanup;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
cleanup:
|
|
257
|
+
|
|
258
|
+
return retval;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
static readstat_error_t xport_write_first_header_record(readstat_writer_t *writer) {
|
|
262
|
+
xport_header_record_t xrecord = { .name = "LIBRARY" };
|
|
263
|
+
if (writer->version == 8) {
|
|
264
|
+
strcpy(xrecord.name, "LIBV8");
|
|
265
|
+
}
|
|
266
|
+
return xport_write_header_record(writer, &xrecord);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
static readstat_error_t xport_write_first_real_header_record(readstat_writer_t *writer,
|
|
270
|
+
const char *timestamp) {
|
|
271
|
+
char real_record[RECORD_LEN+1];
|
|
272
|
+
snprintf(real_record, sizeof(real_record),
|
|
273
|
+
"%-8.8s" "%-8.8s" "%-8.8s" "%-8.8s" "%-8.8s" "%-24.24s" "%16.16s",
|
|
274
|
+
"SAS", "SAS", "SASLIB", "6.06", "bsd4.2", "", timestamp);
|
|
275
|
+
|
|
276
|
+
return xport_write_record(writer, real_record);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
static readstat_error_t xport_write_member_header_record(readstat_writer_t *writer) {
|
|
280
|
+
xport_header_record_t xrecord = {
|
|
281
|
+
.name = "MEMBER",
|
|
282
|
+
.num4 = 160, .num6 = 140
|
|
283
|
+
};
|
|
284
|
+
if (writer->version == 8) {
|
|
285
|
+
strcpy(xrecord.name, "MEMBV8");
|
|
286
|
+
}
|
|
287
|
+
return xport_write_header_record(writer, &xrecord);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
static readstat_error_t xport_write_descriptor_header_record(readstat_writer_t *writer) {
|
|
291
|
+
xport_header_record_t xrecord = {
|
|
292
|
+
.name = "DSCRPTR"
|
|
293
|
+
};
|
|
294
|
+
if (writer->version == 8) {
|
|
295
|
+
strcpy(xrecord.name, "DSCPTV8");
|
|
296
|
+
}
|
|
297
|
+
return xport_write_header_record(writer, &xrecord);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
static readstat_error_t xport_write_member_record_v8(readstat_writer_t *writer,
|
|
301
|
+
char *timestamp) {
|
|
302
|
+
readstat_error_t retval = READSTAT_OK;
|
|
303
|
+
char member_header[RECORD_LEN+1];
|
|
304
|
+
char *ds_name = "DATASET";
|
|
305
|
+
if (writer->table_name[0])
|
|
306
|
+
ds_name = writer->table_name;
|
|
307
|
+
|
|
308
|
+
snprintf(member_header, sizeof(member_header),
|
|
309
|
+
"%-8.8s" "%-32.32s" "%-8.8s" "%-8.8s" "%-8.8s" "%16.16s",
|
|
310
|
+
"SAS", ds_name, "SASDATA", "6.06", "bsd4.2", timestamp);
|
|
311
|
+
|
|
312
|
+
retval = xport_write_record(writer, member_header);
|
|
313
|
+
|
|
314
|
+
return retval;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
static readstat_error_t xport_write_member_record(readstat_writer_t *writer,
|
|
318
|
+
char *timestamp) {
|
|
319
|
+
if (writer->version == 8)
|
|
320
|
+
return xport_write_member_record_v8(writer, timestamp);
|
|
321
|
+
|
|
322
|
+
readstat_error_t retval = READSTAT_OK;
|
|
323
|
+
char member_header[RECORD_LEN+1];
|
|
324
|
+
char *ds_name = "DATASET";
|
|
325
|
+
if (writer->table_name[0])
|
|
326
|
+
ds_name = writer->table_name;
|
|
327
|
+
|
|
328
|
+
snprintf(member_header, sizeof(member_header),
|
|
329
|
+
"%-8.8s" "%-8.8s" "%-8.8s" "%-8.8s" "%-8.8s" "%-24.24s" "%16.16s",
|
|
330
|
+
"SAS", ds_name, "SASDATA", "6.06", "bsd4.2", "", timestamp);
|
|
331
|
+
|
|
332
|
+
retval = xport_write_record(writer, member_header);
|
|
333
|
+
|
|
334
|
+
return retval;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
static readstat_error_t xport_write_file_label_record(readstat_writer_t *writer,
|
|
338
|
+
char *timestamp) {
|
|
339
|
+
char member_header[RECORD_LEN+1];
|
|
340
|
+
snprintf(member_header, sizeof(member_header),
|
|
341
|
+
"%16.16s" "%16.16s" "%-40.40s" "%-8.8s",
|
|
342
|
+
timestamp, "", writer->file_label, "" /* dstype? */);
|
|
343
|
+
|
|
344
|
+
return xport_write_record(writer, member_header);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
static readstat_error_t xport_write_namestr_header_record(readstat_writer_t *writer) {
|
|
348
|
+
xport_header_record_t xrecord = {
|
|
349
|
+
.name = "NAMESTR",
|
|
350
|
+
.num2 = writer->variables_count
|
|
351
|
+
};
|
|
352
|
+
if (writer->version == 8) {
|
|
353
|
+
strcpy(xrecord.name, "NAMSTV8");
|
|
354
|
+
}
|
|
355
|
+
return xport_write_header_record(writer, &xrecord);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
static readstat_error_t xport_write_obs_header_record(readstat_writer_t *writer) {
|
|
359
|
+
if (writer->version == 8) {
|
|
360
|
+
xport_header_record_t xrecord = {
|
|
361
|
+
.name = "OBSV8",
|
|
362
|
+
.num1 = writer->row_count
|
|
363
|
+
};
|
|
364
|
+
return xport_write_header_record_v8(writer, &xrecord);
|
|
365
|
+
}
|
|
366
|
+
xport_header_record_t xrecord = {
|
|
367
|
+
.name = "OBS"
|
|
368
|
+
};
|
|
369
|
+
return xport_write_header_record(writer, &xrecord);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
static readstat_error_t xport_format_timestamp(char *output, size_t output_len, time_t timestamp) {
|
|
373
|
+
struct tm *ts = localtime(×tamp);
|
|
374
|
+
|
|
375
|
+
if (!ts)
|
|
376
|
+
return READSTAT_ERROR_BAD_TIMESTAMP_VALUE;
|
|
377
|
+
|
|
378
|
+
snprintf(output, output_len,
|
|
379
|
+
"%02d%3.3s%02d:%02d:%02d:%02d",
|
|
380
|
+
(unsigned int)ts->tm_mday % 100,
|
|
381
|
+
_xport_months[ts->tm_mon],
|
|
382
|
+
(unsigned int)ts->tm_year % 100,
|
|
383
|
+
(unsigned int)ts->tm_hour % 100,
|
|
384
|
+
(unsigned int)ts->tm_min % 100,
|
|
385
|
+
(unsigned int)ts->tm_sec % 100
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
return READSTAT_OK;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
static readstat_error_t xport_begin_data(void *writer_ctx) {
|
|
392
|
+
readstat_writer_t *writer = (readstat_writer_t *)writer_ctx;
|
|
393
|
+
readstat_error_t retval = READSTAT_OK;
|
|
394
|
+
char timestamp[17];
|
|
395
|
+
|
|
396
|
+
retval = xport_format_timestamp(timestamp, sizeof(timestamp), writer->timestamp);
|
|
397
|
+
if (retval != READSTAT_OK)
|
|
398
|
+
goto cleanup;
|
|
399
|
+
|
|
400
|
+
retval = xport_write_first_header_record(writer);
|
|
401
|
+
if (retval != READSTAT_OK)
|
|
402
|
+
goto cleanup;
|
|
403
|
+
|
|
404
|
+
retval = xport_write_first_real_header_record(writer, timestamp);
|
|
405
|
+
if (retval != READSTAT_OK)
|
|
406
|
+
goto cleanup;
|
|
407
|
+
|
|
408
|
+
retval = xport_write_record(writer, timestamp);
|
|
409
|
+
if (retval != READSTAT_OK)
|
|
410
|
+
goto cleanup;
|
|
411
|
+
|
|
412
|
+
retval = xport_write_member_header_record(writer);
|
|
413
|
+
if (retval != READSTAT_OK)
|
|
414
|
+
goto cleanup;
|
|
415
|
+
|
|
416
|
+
retval = xport_write_descriptor_header_record(writer);
|
|
417
|
+
if (retval != READSTAT_OK)
|
|
418
|
+
goto cleanup;
|
|
419
|
+
|
|
420
|
+
retval = xport_write_member_record(writer, timestamp);
|
|
421
|
+
if (retval != READSTAT_OK)
|
|
422
|
+
goto cleanup;
|
|
423
|
+
|
|
424
|
+
retval = xport_write_file_label_record(writer, timestamp);
|
|
425
|
+
if (retval != READSTAT_OK)
|
|
426
|
+
goto cleanup;
|
|
427
|
+
|
|
428
|
+
retval = xport_write_namestr_header_record(writer);
|
|
429
|
+
if (retval != READSTAT_OK)
|
|
430
|
+
goto cleanup;
|
|
431
|
+
|
|
432
|
+
retval = xport_write_variables(writer);
|
|
433
|
+
if (retval != READSTAT_OK)
|
|
434
|
+
goto cleanup;
|
|
435
|
+
|
|
436
|
+
retval = xport_write_obs_header_record(writer);
|
|
437
|
+
if (retval != READSTAT_OK)
|
|
438
|
+
goto cleanup;
|
|
439
|
+
|
|
440
|
+
cleanup:
|
|
441
|
+
return retval;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
static readstat_error_t xport_end_data(void *writer_ctx) {
|
|
445
|
+
readstat_writer_t *writer = (readstat_writer_t *)writer_ctx;
|
|
446
|
+
readstat_error_t retval = READSTAT_OK;
|
|
447
|
+
|
|
448
|
+
retval = xport_finish_record(writer);
|
|
449
|
+
|
|
450
|
+
return retval;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
static readstat_error_t xport_write_row(void *writer_ctx, void *row, size_t row_len) {
|
|
454
|
+
readstat_writer_t *writer = (readstat_writer_t *)writer_ctx;
|
|
455
|
+
return xport_write_bytes(writer, row, row_len);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
static readstat_error_t xport_write_double(void *row, const readstat_variable_t *var, double value) {
|
|
459
|
+
char full_value[8];
|
|
460
|
+
int rc = cnxptiee(&value, CN_TYPE_NATIVE, full_value, CN_TYPE_XPORT);
|
|
461
|
+
|
|
462
|
+
if (rc)
|
|
463
|
+
return READSTAT_ERROR_CONVERT;
|
|
464
|
+
|
|
465
|
+
memcpy(row, full_value, var->storage_width);
|
|
466
|
+
|
|
467
|
+
return READSTAT_OK;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
static readstat_error_t xport_write_float(void *row, const readstat_variable_t *var, float value) {
|
|
471
|
+
return xport_write_double(row, var, value);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
static readstat_error_t xport_write_int32(void *row, const readstat_variable_t *var, int32_t value) {
|
|
475
|
+
return xport_write_double(row, var, value);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
static readstat_error_t xport_write_int16(void *row, const readstat_variable_t *var, int16_t value) {
|
|
479
|
+
return xport_write_double(row, var, value);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
static readstat_error_t xport_write_int8(void *row, const readstat_variable_t *var, int8_t value) {
|
|
483
|
+
return xport_write_double(row, var, value);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
static readstat_error_t xport_write_string(void *row, const readstat_variable_t *var, const char *string) {
|
|
487
|
+
memset(row, ' ', var->storage_width);
|
|
488
|
+
if (string != NULL && string[0]) {
|
|
489
|
+
size_t value_len = strlen(string);
|
|
490
|
+
if (value_len > var->storage_width)
|
|
491
|
+
return READSTAT_ERROR_STRING_VALUE_IS_TOO_LONG;
|
|
492
|
+
|
|
493
|
+
memcpy(row, string, value_len);
|
|
494
|
+
}
|
|
495
|
+
return READSTAT_OK;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
static readstat_error_t xport_write_missing_numeric(void *row, const readstat_variable_t *var) {
|
|
499
|
+
char *row_bytes = (char *)row;
|
|
500
|
+
row_bytes[0] = 0x2e;
|
|
501
|
+
return READSTAT_OK;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
static readstat_error_t xport_write_missing_string(void *row, const readstat_variable_t *var) {
|
|
505
|
+
return xport_write_string(row, var, NULL);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
static readstat_error_t xport_write_missing_tagged(void *row, const readstat_variable_t *var, char tag) {
|
|
509
|
+
char *row_bytes = (char *)row;
|
|
510
|
+
readstat_error_t error = sas_validate_tag(tag);
|
|
511
|
+
if (error == READSTAT_OK) {
|
|
512
|
+
row_bytes[0] = tag;
|
|
513
|
+
}
|
|
514
|
+
return error;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
static readstat_error_t xport_metadata_ok(void *writer_ctx) {
|
|
518
|
+
readstat_writer_t *writer = (readstat_writer_t *)writer_ctx;
|
|
519
|
+
|
|
520
|
+
if (writer->version != 5 && writer->version != 8)
|
|
521
|
+
return READSTAT_ERROR_UNSUPPORTED_FILE_FORMAT_VERSION;
|
|
522
|
+
|
|
523
|
+
if (writer->table_name[0]) {
|
|
524
|
+
if (writer->version == 8) {
|
|
525
|
+
return sas_validate_name(writer->table_name, 32);
|
|
526
|
+
}
|
|
527
|
+
if (writer->version == 5) {
|
|
528
|
+
return sas_validate_name(writer->table_name, 8);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return READSTAT_OK;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
readstat_error_t readstat_begin_writing_xport(readstat_writer_t *writer, void *user_ctx, long row_count) {
|
|
536
|
+
|
|
537
|
+
if (writer->version == 0)
|
|
538
|
+
writer->version = XPORT_DEFAULT_VERSION;
|
|
539
|
+
|
|
540
|
+
writer->callbacks.metadata_ok = &xport_metadata_ok;
|
|
541
|
+
writer->callbacks.write_int8 = &xport_write_int8;
|
|
542
|
+
writer->callbacks.write_int16 = &xport_write_int16;
|
|
543
|
+
writer->callbacks.write_int32 = &xport_write_int32;
|
|
544
|
+
writer->callbacks.write_float = &xport_write_float;
|
|
545
|
+
writer->callbacks.write_double = &xport_write_double;
|
|
546
|
+
|
|
547
|
+
writer->callbacks.write_string = &xport_write_string;
|
|
548
|
+
writer->callbacks.write_missing_string = &xport_write_missing_string;
|
|
549
|
+
writer->callbacks.write_missing_number = &xport_write_missing_numeric;
|
|
550
|
+
writer->callbacks.write_missing_tagged = &xport_write_missing_tagged;
|
|
551
|
+
|
|
552
|
+
writer->callbacks.variable_width = &xport_variable_width;
|
|
553
|
+
writer->callbacks.variable_ok = &sas_validate_variable;
|
|
554
|
+
|
|
555
|
+
writer->callbacks.begin_data = &xport_begin_data;
|
|
556
|
+
writer->callbacks.end_data = &xport_end_data;
|
|
557
|
+
|
|
558
|
+
writer->callbacks.write_row = &xport_write_row;
|
|
559
|
+
|
|
560
|
+
return readstat_begin_writing_file(writer, user_ctx, row_count);
|
|
561
|
+
}
|