/src/readstat/src/sas/readstat_sas7bcat_read.c
Line | Count | Source |
1 | | #include <stdio.h> |
2 | | #include <stdlib.h> |
3 | | #include <errno.h> |
4 | | #include <string.h> |
5 | | #include <math.h> |
6 | | #include "readstat_sas.h" |
7 | | #include "../readstat_iconv.h" |
8 | | #include "../readstat_convert.h" |
9 | | #include "../readstat_malloc.h" |
10 | | |
11 | 1.16k | #define SAS_CATALOG_FIRST_INDEX_PAGE 1 |
12 | 1.06k | #define SAS_CATALOG_USELESS_PAGES 3 |
13 | | |
14 | | typedef struct sas7bcat_ctx_s { |
15 | | readstat_metadata_handler metadata_handler; |
16 | | readstat_value_label_handler value_label_handler; |
17 | | void *user_ctx; |
18 | | readstat_io_t *io; |
19 | | int u64; |
20 | | int pad1; |
21 | | int bswap; |
22 | | int64_t xlsr_size; |
23 | | int64_t xlsr_offset; |
24 | | int64_t xlsr_O_offset; |
25 | | int64_t page_count; |
26 | | int64_t page_size; |
27 | | int64_t header_size; |
28 | | uint64_t *block_pointers; |
29 | | int block_pointers_used; |
30 | | int block_pointers_capacity; |
31 | | const char *input_encoding; |
32 | | const char *output_encoding; |
33 | | iconv_t converter; |
34 | | } sas7bcat_ctx_t; |
35 | | |
36 | 1.69k | static void sas7bcat_ctx_free(sas7bcat_ctx_t *ctx) { |
37 | 1.69k | if (ctx->converter) |
38 | 703 | iconv_close(ctx->converter); |
39 | 1.69k | if (ctx->block_pointers) |
40 | 1.69k | free(ctx->block_pointers); |
41 | | |
42 | 1.69k | free(ctx); |
43 | 1.69k | } |
44 | | |
45 | | static readstat_error_t sas7bcat_parse_value_labels(const char *value_start, size_t value_labels_len, |
46 | 1.63k | int label_count_used, int label_count_capacity, const char *name, sas7bcat_ctx_t *ctx) { |
47 | 1.63k | readstat_error_t retval = READSTAT_OK; |
48 | 1.63k | int i; |
49 | 1.63k | const char *lbp1 = value_start; |
50 | 1.63k | uint32_t *value_offset = readstat_calloc(label_count_used, sizeof(uint32_t)); |
51 | | /* Doubles appear to be stored as big-endian, always */ |
52 | 1.63k | int bswap_doubles = machine_is_little_endian(); |
53 | 1.63k | int is_string = (name[0] == '$'); |
54 | 1.63k | char *label = NULL; |
55 | | |
56 | 1.63k | if (value_offset == NULL) { |
57 | 150 | retval = READSTAT_ERROR_MALLOC; |
58 | 150 | goto cleanup; |
59 | 150 | } |
60 | | |
61 | | /* Pass 1 -- find out the offset of the labels */ |
62 | 4.85M | for (i=0; i<label_count_capacity; i++) { |
63 | 4.85M | if (&lbp1[4] - value_start > value_labels_len || sas_read2(&lbp1[2], ctx->bswap) < 0) { |
64 | 49 | retval = READSTAT_ERROR_PARSE; |
65 | 49 | goto cleanup; |
66 | 49 | } |
67 | 4.85M | if (i<label_count_used) { |
68 | 142k | if (&lbp1[10+ctx->pad1+4] - value_start > value_labels_len) { |
69 | 13 | retval = READSTAT_ERROR_PARSE; |
70 | 13 | goto cleanup; |
71 | 13 | } |
72 | 142k | uint32_t label_pos = sas_read4(&lbp1[10+ctx->pad1], ctx->bswap); |
73 | 142k | if (label_pos >= label_count_used) { |
74 | 58 | retval = READSTAT_ERROR_PARSE; |
75 | 58 | goto cleanup; |
76 | 58 | } |
77 | 142k | value_offset[label_pos] = lbp1 - value_start; |
78 | 142k | } |
79 | 4.85M | lbp1 += 6 + sas_read2(&lbp1[2], ctx->bswap); |
80 | 4.85M | } |
81 | | |
82 | 1.36k | const char *lbp2 = lbp1; |
83 | | |
84 | | /* Pass 2 -- parse pairs of values & labels */ |
85 | 45.1k | for (i=0; i<label_count_used && i<label_count_capacity; i++) { |
86 | 43.9k | lbp1 = value_start + value_offset[i]; |
87 | | |
88 | 43.9k | if (&lbp1[30] - value_start > value_labels_len || |
89 | 43.9k | &lbp2[10] - value_start > value_labels_len) { |
90 | 130 | retval = READSTAT_ERROR_PARSE; |
91 | 130 | goto cleanup; |
92 | 130 | } |
93 | 43.8k | readstat_value_t value = { .type = is_string ? READSTAT_TYPE_STRING : READSTAT_TYPE_DOUBLE }; |
94 | 43.8k | char string_val[4*16+1]; |
95 | 43.8k | if (is_string) { |
96 | 529 | size_t value_entry_len = 6 + sas_read2(&lbp1[2], ctx->bswap); |
97 | 529 | retval = readstat_convert(string_val, sizeof(string_val), |
98 | 529 | &lbp1[value_entry_len-16], 16, ctx->converter); |
99 | 529 | if (retval != READSTAT_OK) |
100 | 1 | goto cleanup; |
101 | | |
102 | 528 | value.v.string_value = string_val; |
103 | 43.3k | } else { |
104 | 43.3k | uint64_t val = sas_read8(&lbp1[22], bswap_doubles); |
105 | 43.3k | double dval = NAN; |
106 | 43.3k | if ((val | 0xFF0000000000) == 0xFFFFFFFFFFFF) { |
107 | 2.19k | sas_assign_tag(&value, (val >> 40)); |
108 | 41.1k | } else { |
109 | 41.1k | memcpy(&dval, &val, 8); |
110 | 41.1k | if (dval > 0.0) { |
111 | 37.2k | val = ~val; |
112 | 37.2k | memcpy(&dval, &val, 8); |
113 | 37.2k | } else { |
114 | 3.87k | dval *= -1.0; |
115 | 3.87k | } |
116 | 41.1k | } |
117 | | |
118 | 43.3k | value.v.double_value = dval; |
119 | 43.3k | } |
120 | 43.8k | size_t label_len = sas_read2(&lbp2[8], ctx->bswap); |
121 | 43.8k | if (&lbp2[10] > value_start + value_labels_len) { |
122 | 0 | retval = READSTAT_ERROR_PARSE; |
123 | 0 | goto cleanup; |
124 | 0 | } |
125 | | /* Some labels seem to overflow the reported block length, truncate it */ |
126 | | /* (Observed with formats.sasbcat from GSS2021, produced with 9.0401M6X64_SR12R2 */ |
127 | 43.8k | if (label_len > value_start + value_labels_len - &lbp2[10]) { |
128 | 368 | label_len = value_start + value_labels_len - &lbp2[10]; |
129 | 368 | } |
130 | 43.8k | if (ctx->value_label_handler) { |
131 | 43.8k | label = realloc(label, 4 * label_len + 1); |
132 | 43.8k | retval = readstat_convert(label, 4 * label_len + 1, |
133 | 43.8k | &lbp2[10], label_len, ctx->converter); |
134 | 43.8k | if (retval != READSTAT_OK) |
135 | 30 | goto cleanup; |
136 | | |
137 | 43.8k | if (ctx->value_label_handler(name, value, label, ctx->user_ctx) != READSTAT_HANDLER_OK) { |
138 | 0 | retval = READSTAT_ERROR_USER_ABORT; |
139 | 0 | goto cleanup; |
140 | 0 | } |
141 | 43.8k | } |
142 | | |
143 | 43.8k | lbp2 += 8 + 2 + label_len + 1; |
144 | 43.8k | } |
145 | | |
146 | 1.63k | cleanup: |
147 | 1.63k | free(label); |
148 | 1.63k | free(value_offset); |
149 | 1.63k | return retval; |
150 | 1.36k | } |
151 | | |
152 | 3.62k | static readstat_error_t sas7bcat_parse_block(const char *data, size_t data_size, sas7bcat_ctx_t *ctx) { |
153 | 3.62k | readstat_error_t retval = READSTAT_OK; |
154 | | |
155 | 3.62k | size_t pad = 0; |
156 | 3.62k | uint64_t label_count_capacity = 0; |
157 | 3.62k | uint64_t label_count_used = 0; |
158 | 3.62k | int payload_offset = 106; |
159 | 3.62k | uint16_t flags = 0; |
160 | 3.62k | char name[4*32+1]; |
161 | | |
162 | 3.62k | if (data_size < payload_offset) |
163 | 763 | goto cleanup; |
164 | | |
165 | 2.86k | flags = sas_read2(&data[2], ctx->bswap); |
166 | 2.86k | pad = (flags & 0x08) ? 4 : 0; // might be 0x10, not sure |
167 | 2.86k | if (ctx->u64) { |
168 | 494 | label_count_capacity = sas_read8(&data[42+pad], ctx->bswap); |
169 | 494 | label_count_used = sas_read8(&data[50+pad], ctx->bswap); |
170 | | |
171 | 494 | payload_offset += 32; |
172 | 2.37k | } else { |
173 | 2.37k | label_count_capacity = sas_read4(&data[38+pad], ctx->bswap); |
174 | 2.37k | label_count_used = sas_read4(&data[42+pad], ctx->bswap); |
175 | 2.37k | } |
176 | | |
177 | 2.86k | if ((retval = readstat_convert(name, sizeof(name), &data[8], 8, ctx->converter)) != READSTAT_OK) |
178 | 5 | goto cleanup; |
179 | | |
180 | 2.86k | if (pad) { |
181 | 1.19k | pad += 16; |
182 | 1.19k | } |
183 | | |
184 | 2.86k | if (((flags & 0x80) && !ctx->u64) || ((flags & 0x20) && ctx->u64)) { // has long name |
185 | 1.52k | if (data_size < payload_offset + pad + 32) |
186 | 291 | goto cleanup; |
187 | | |
188 | 1.23k | retval = readstat_convert(name, sizeof(name), &data[payload_offset+pad], 32, ctx->converter); |
189 | 1.23k | if (retval != READSTAT_OK) |
190 | 2 | goto cleanup; |
191 | 1.23k | pad += 32; |
192 | 1.23k | } |
193 | | |
194 | 2.56k | if (data_size < payload_offset + pad) |
195 | 213 | goto cleanup; |
196 | | |
197 | 2.35k | if (label_count_used == 0) |
198 | 722 | goto cleanup; |
199 | | |
200 | 1.63k | if ((retval = sas7bcat_parse_value_labels(&data[payload_offset+pad], data_size - payload_offset - pad, |
201 | 1.63k | label_count_used, label_count_capacity, name, ctx)) != READSTAT_OK) |
202 | 431 | goto cleanup; |
203 | | |
204 | 3.62k | cleanup: |
205 | 3.62k | return retval; |
206 | 1.63k | } |
207 | | |
208 | 9.00k | static readstat_error_t sas7bcat_augment_index(const char *index, size_t len, sas7bcat_ctx_t *ctx) { |
209 | 9.00k | const char *xlsr = index; |
210 | 9.00k | readstat_error_t retval = READSTAT_OK; |
211 | 38.6k | while (xlsr + ctx->xlsr_size <= index + len) { |
212 | 30.9k | if (memcmp(xlsr, "XLSR", 4) != 0) // some block pointers seem to have 8 bytes of extra padding |
213 | 14.8k | xlsr += 8; |
214 | 30.9k | if (memcmp(xlsr, "XLSR", 4) != 0) |
215 | 1.35k | break; |
216 | | |
217 | 29.6k | if (xlsr[ctx->xlsr_O_offset] == 'O') { |
218 | 27.4k | uint64_t page = 0, pos = 0; |
219 | 27.4k | if (ctx->u64) { |
220 | 867 | page = sas_read8(&xlsr[8], ctx->bswap); |
221 | 867 | pos = sas_read2(&xlsr[16], ctx->bswap); |
222 | 26.5k | } else { |
223 | 26.5k | page = sas_read4(&xlsr[4], ctx->bswap); |
224 | 26.5k | pos = sas_read2(&xlsr[8], ctx->bswap); |
225 | 26.5k | } |
226 | 27.4k | ctx->block_pointers[ctx->block_pointers_used++] = (page << 32) + pos; |
227 | 27.4k | } |
228 | | |
229 | 29.6k | if (ctx->block_pointers_used == ctx->block_pointers_capacity) { |
230 | 68 | ctx->block_pointers = readstat_realloc(ctx->block_pointers, (ctx->block_pointers_capacity *= 2) * sizeof(uint64_t)); |
231 | 68 | if (ctx->block_pointers == NULL) { |
232 | 0 | retval = READSTAT_ERROR_MALLOC; |
233 | 0 | goto cleanup; |
234 | 0 | } |
235 | 68 | } |
236 | | |
237 | 29.6k | xlsr += ctx->xlsr_size; |
238 | 29.6k | } |
239 | 9.00k | cleanup: |
240 | 9.00k | return retval; |
241 | 9.00k | } |
242 | | |
243 | 107k | static int compare_block_pointers(const void *elem1, const void *elem2) { |
244 | 107k | uint64_t v1 = *(const uint64_t *)elem1; |
245 | 107k | uint64_t v2 = *(const uint64_t *)elem2; |
246 | 107k | return v1 - v2; |
247 | 107k | } |
248 | | |
249 | 976 | static void sas7bcat_sort_index(sas7bcat_ctx_t *ctx) { |
250 | 976 | if (ctx->block_pointers_used == 0) |
251 | 110 | return; |
252 | | |
253 | 866 | int i; |
254 | 1.24k | for (i=1; i<ctx->block_pointers_used; i++) { |
255 | 592 | if (ctx->block_pointers[i] < ctx->block_pointers[i-1]) { |
256 | 210 | qsort(ctx->block_pointers, ctx->block_pointers_used, sizeof(uint64_t), &compare_block_pointers); |
257 | 210 | break; |
258 | 210 | } |
259 | 592 | } |
260 | 866 | } |
261 | | |
262 | 976 | static void sas7bcat_uniq_index(sas7bcat_ctx_t *ctx) { |
263 | 976 | if (ctx->block_pointers_used == 0) |
264 | 110 | return; |
265 | | |
266 | 866 | int i; |
267 | 866 | int out_i = 1; |
268 | 19.1k | for (i=1; i<ctx->block_pointers_used; i++) { |
269 | 18.2k | if (ctx->block_pointers[i] != ctx->block_pointers[i-1]) { |
270 | 13.7k | if (out_i != i) { |
271 | 12.2k | ctx->block_pointers[out_i] = ctx->block_pointers[i]; |
272 | 12.2k | } |
273 | 13.7k | out_i++; |
274 | 13.7k | } |
275 | 18.2k | } |
276 | 866 | ctx->block_pointers_used = out_i; |
277 | 866 | } |
278 | | |
279 | 14.5k | static int sas7bcat_block_size(int start_page, int start_page_pos, sas7bcat_ctx_t *ctx, readstat_error_t *outError) { |
280 | 14.5k | readstat_error_t retval = READSTAT_OK; |
281 | 14.5k | readstat_io_t *io = ctx->io; |
282 | 14.5k | int next_page = start_page; |
283 | 14.5k | int next_page_pos = start_page_pos; |
284 | 14.5k | int link_count = 0; |
285 | | |
286 | 14.5k | int buffer_len = 0; |
287 | 14.5k | int chain_link_len = 0; |
288 | | |
289 | 14.5k | char chain_link[32]; |
290 | 14.5k | int chain_link_header_len = 16; |
291 | 14.5k | if (ctx->u64) { |
292 | 646 | chain_link_header_len = 32; |
293 | 646 | } |
294 | | |
295 | | // calculate buffer size needed |
296 | 51.6k | while (next_page > 0 && next_page_pos > 0 && next_page <= ctx->page_count && link_count++ < ctx->page_count) { |
297 | 37.1k | if (io->seek(ctx->header_size+(next_page-1)*ctx->page_size+next_page_pos, READSTAT_SEEK_SET, io->io_ctx) == -1) { |
298 | 14 | retval = READSTAT_ERROR_SEEK; |
299 | 14 | goto cleanup; |
300 | 14 | } |
301 | 37.1k | if (io->read(chain_link, chain_link_header_len, io->io_ctx) < chain_link_header_len) { |
302 | 19 | retval = READSTAT_ERROR_READ; |
303 | 19 | goto cleanup; |
304 | 19 | } |
305 | | |
306 | 37.1k | if (ctx->u64) { |
307 | 1.39k | next_page = sas_read4(&chain_link[0], ctx->bswap); |
308 | 1.39k | next_page_pos = sas_read2(&chain_link[8], ctx->bswap); |
309 | 1.39k | chain_link_len = sas_read2(&chain_link[10], ctx->bswap); |
310 | 35.7k | } else { |
311 | 35.7k | next_page = sas_read4(&chain_link[0], ctx->bswap); |
312 | 35.7k | next_page_pos = sas_read2(&chain_link[4], ctx->bswap); |
313 | 35.7k | chain_link_len = sas_read2(&chain_link[6], ctx->bswap); |
314 | 35.7k | } |
315 | | |
316 | 37.1k | buffer_len += chain_link_len; |
317 | 37.1k | } |
318 | | |
319 | 14.5k | cleanup: |
320 | 14.5k | if (outError) |
321 | 14.5k | *outError = retval; |
322 | | |
323 | 14.5k | return retval == READSTAT_OK ? buffer_len : -1; |
324 | 14.5k | } |
325 | | |
326 | | static readstat_error_t sas7bcat_read_block(char *buffer, size_t buffer_len, |
327 | 3.67k | int start_page, int start_page_pos, sas7bcat_ctx_t *ctx) { |
328 | 3.67k | readstat_error_t retval = READSTAT_OK; |
329 | 3.67k | readstat_io_t *io = ctx->io; |
330 | 3.67k | int next_page = start_page; |
331 | 3.67k | int next_page_pos = start_page_pos; |
332 | 3.67k | int link_count = 0; |
333 | | |
334 | 3.67k | int chain_link_len = 0; |
335 | 3.67k | int buffer_offset = 0; |
336 | | |
337 | 3.67k | char chain_link[32]; |
338 | 3.67k | int chain_link_header_len = 16; |
339 | 3.67k | if (ctx->u64) { |
340 | 573 | chain_link_header_len = 32; |
341 | 573 | } |
342 | | |
343 | 37.2k | while (next_page > 0 && next_page_pos > 0 && next_page <= ctx->page_count && link_count++ < ctx->page_count) { |
344 | 33.6k | if (io->seek(ctx->header_size+(next_page-1)*ctx->page_size+next_page_pos, READSTAT_SEEK_SET, io->io_ctx) == -1) { |
345 | 0 | retval = READSTAT_ERROR_SEEK; |
346 | 0 | goto cleanup; |
347 | 0 | } |
348 | 33.6k | if (io->read(chain_link, chain_link_header_len, io->io_ctx) < chain_link_header_len) { |
349 | 0 | retval = READSTAT_ERROR_READ; |
350 | 0 | goto cleanup; |
351 | 0 | } |
352 | 33.6k | if (ctx->u64) { |
353 | 1.38k | next_page = sas_read4(&chain_link[0], ctx->bswap); |
354 | 1.38k | next_page_pos = sas_read2(&chain_link[8], ctx->bswap); |
355 | 1.38k | chain_link_len = sas_read2(&chain_link[10], ctx->bswap); |
356 | 32.2k | } else { |
357 | 32.2k | next_page = sas_read4(&chain_link[0], ctx->bswap); |
358 | 32.2k | next_page_pos = sas_read2(&chain_link[4], ctx->bswap); |
359 | 32.2k | chain_link_len = sas_read2(&chain_link[6], ctx->bswap); |
360 | 32.2k | } |
361 | 33.6k | if (buffer_offset + chain_link_len > buffer_len) { |
362 | 0 | retval = READSTAT_ERROR_PARSE; |
363 | 0 | goto cleanup; |
364 | 0 | } |
365 | 33.6k | if (io->read(buffer + buffer_offset, chain_link_len, io->io_ctx) < chain_link_len) { |
366 | 51 | retval = READSTAT_ERROR_READ; |
367 | 51 | goto cleanup; |
368 | 51 | } |
369 | 33.6k | buffer_offset += chain_link_len; |
370 | 33.6k | } |
371 | 3.67k | cleanup: |
372 | | |
373 | 3.67k | return retval; |
374 | 3.67k | } |
375 | | |
376 | 1.69k | readstat_error_t readstat_parse_sas7bcat(readstat_parser_t *parser, const char *path, void *user_ctx) { |
377 | 1.69k | readstat_error_t retval = READSTAT_OK; |
378 | 1.69k | readstat_io_t *io = parser->io; |
379 | 1.69k | int64_t i; |
380 | 1.69k | char *page = NULL; |
381 | 1.69k | char *buffer = NULL; |
382 | | |
383 | 1.69k | sas7bcat_ctx_t *ctx = calloc(1, sizeof(sas7bcat_ctx_t)); |
384 | 1.69k | sas_header_info_t *hinfo = calloc(1, sizeof(sas_header_info_t)); |
385 | | |
386 | 1.69k | ctx->block_pointers = malloc((ctx->block_pointers_capacity = 200) * sizeof(uint64_t)); |
387 | | |
388 | 1.69k | ctx->value_label_handler = parser->handlers.value_label; |
389 | 1.69k | ctx->metadata_handler = parser->handlers.metadata; |
390 | 1.69k | ctx->input_encoding = parser->input_encoding; |
391 | 1.69k | ctx->output_encoding = parser->output_encoding; |
392 | 1.69k | ctx->user_ctx = user_ctx; |
393 | 1.69k | ctx->io = io; |
394 | | |
395 | 1.69k | if (io->open(path, io->io_ctx) == -1) { |
396 | 0 | retval = READSTAT_ERROR_OPEN; |
397 | 0 | goto cleanup; |
398 | 0 | } |
399 | | |
400 | 1.69k | if ((retval = sas_read_header(io, hinfo, parser->handlers.error, user_ctx)) != READSTAT_OK) { |
401 | 517 | goto cleanup; |
402 | 517 | } |
403 | | |
404 | 1.17k | ctx->u64 = hinfo->u64; |
405 | 1.17k | ctx->pad1 = hinfo->pad1; |
406 | 1.17k | ctx->bswap = machine_is_little_endian() ^ hinfo->little_endian; |
407 | 1.17k | ctx->header_size = hinfo->header_size; |
408 | 1.17k | ctx->page_count = hinfo->page_count; |
409 | 1.17k | ctx->page_size = hinfo->page_size; |
410 | 1.17k | if (ctx->input_encoding == NULL) { |
411 | 1.17k | ctx->input_encoding = hinfo->encoding; |
412 | 1.17k | } |
413 | | |
414 | 1.17k | ctx->xlsr_size = 212 + ctx->pad1; |
415 | 1.17k | ctx->xlsr_offset = 856 + 2 * ctx->pad1; |
416 | 1.17k | ctx->xlsr_O_offset = 50 + ctx->pad1; |
417 | 1.17k | if (ctx->u64) { |
418 | 275 | ctx->xlsr_offset += 144; |
419 | 275 | ctx->xlsr_size += 72; |
420 | 275 | ctx->xlsr_O_offset += 24; |
421 | 275 | } |
422 | | |
423 | 1.17k | if (ctx->input_encoding && ctx->output_encoding && strcmp(ctx->input_encoding, ctx->output_encoding) != 0) { |
424 | 705 | iconv_t converter = iconv_open(ctx->output_encoding, ctx->input_encoding); |
425 | 705 | if (converter == (iconv_t)-1) { |
426 | 2 | retval = READSTAT_ERROR_UNSUPPORTED_CHARSET; |
427 | 2 | goto cleanup; |
428 | 2 | } |
429 | 703 | ctx->converter = converter; |
430 | 703 | } |
431 | | |
432 | 1.17k | if (ctx->metadata_handler) { |
433 | 1.17k | char table_name[4*32+1]; |
434 | 1.17k | readstat_metadata_t metadata = { |
435 | 1.17k | .file_encoding = ctx->input_encoding, /* orig encoding? */ |
436 | 1.17k | .modified_time = hinfo->modification_time, |
437 | 1.17k | .creation_time = hinfo->creation_time, |
438 | 1.17k | .file_format_version = hinfo->major_version, |
439 | 1.17k | .endianness = hinfo->little_endian ? READSTAT_ENDIAN_LITTLE : READSTAT_ENDIAN_BIG, |
440 | 1.17k | .is64bit = ctx->u64 |
441 | 1.17k | }; |
442 | 1.17k | retval = readstat_convert(table_name, sizeof(table_name), |
443 | 1.17k | hinfo->table_name, sizeof(hinfo->table_name), ctx->converter); |
444 | 1.17k | if (retval != READSTAT_OK) |
445 | 3 | goto cleanup; |
446 | | |
447 | 1.16k | metadata.table_name = table_name; |
448 | | |
449 | 1.16k | if (ctx->metadata_handler(&metadata, ctx->user_ctx) != READSTAT_HANDLER_OK) { |
450 | 0 | retval = READSTAT_ERROR_USER_ABORT; |
451 | 0 | goto cleanup; |
452 | 0 | } |
453 | 1.16k | } |
454 | | |
455 | 1.16k | if ((page = readstat_malloc(ctx->page_size)) == NULL) { |
456 | 0 | retval = READSTAT_ERROR_MALLOC; |
457 | 0 | goto cleanup; |
458 | 0 | } |
459 | 1.16k | if (io->seek(ctx->header_size+SAS_CATALOG_FIRST_INDEX_PAGE*ctx->page_size, READSTAT_SEEK_SET, io->io_ctx) == -1) { |
460 | 63 | retval = READSTAT_ERROR_SEEK; |
461 | 63 | goto cleanup; |
462 | 63 | } |
463 | 1.10k | if (io->read(page, ctx->page_size, io->io_ctx) < ctx->page_size) { |
464 | 41 | retval = READSTAT_ERROR_READ; |
465 | 41 | goto cleanup; |
466 | 41 | } |
467 | | |
468 | 1.06k | retval = sas7bcat_augment_index(&page[ctx->xlsr_offset], ctx->page_size - ctx->xlsr_offset, ctx); |
469 | 1.06k | if (retval != READSTAT_OK) |
470 | 0 | goto cleanup; |
471 | | |
472 | | // Pass 1 -- find the XLSR entries |
473 | 22.5k | for (i=SAS_CATALOG_USELESS_PAGES; i<ctx->page_count; i++) { |
474 | 21.5k | if (io->seek(ctx->header_size+i*ctx->page_size, READSTAT_SEEK_SET, io->io_ctx) == -1) { |
475 | 15 | retval = READSTAT_ERROR_SEEK; |
476 | 15 | goto cleanup; |
477 | 15 | } |
478 | 21.5k | if (io->read(page, ctx->page_size, io->io_ctx) < ctx->page_size) { |
479 | 74 | retval = READSTAT_ERROR_READ; |
480 | 74 | goto cleanup; |
481 | 74 | } |
482 | 21.5k | if (memcmp(&page[16], "XLSR", sizeof("XLSR")-1) == 0) { |
483 | 7.94k | retval = sas7bcat_augment_index(&page[16], ctx->page_size - 16, ctx); |
484 | 7.94k | if (retval != READSTAT_OK) |
485 | 0 | goto cleanup; |
486 | 7.94k | } |
487 | 21.5k | } |
488 | | |
489 | 976 | sas7bcat_sort_index(ctx); |
490 | 976 | sas7bcat_uniq_index(ctx); |
491 | | |
492 | | // Pass 2 -- look up the individual block pointers |
493 | 14.9k | for (i=0; i<ctx->block_pointers_used; i++) { |
494 | 14.5k | int start_page = ctx->block_pointers[i] >> 32; |
495 | 14.5k | int start_page_pos = (ctx->block_pointers[i]) & 0xFFFF; |
496 | | |
497 | 14.5k | int buffer_len = sas7bcat_block_size(start_page, start_page_pos, ctx, &retval); |
498 | 14.5k | if (buffer_len == -1) { |
499 | 33 | goto cleanup; |
500 | 14.4k | } else if (buffer_len == 0) { |
501 | 10.8k | continue; |
502 | 10.8k | } |
503 | 3.68k | if ((buffer = readstat_realloc(buffer, buffer_len)) == NULL) { |
504 | 4 | retval = READSTAT_ERROR_MALLOC; |
505 | 4 | goto cleanup; |
506 | 4 | } |
507 | 3.67k | if ((retval = sas7bcat_read_block(buffer, buffer_len, start_page, start_page_pos, ctx)) != READSTAT_OK) |
508 | 51 | goto cleanup; |
509 | 3.62k | if ((retval = sas7bcat_parse_block(buffer, buffer_len, ctx)) != READSTAT_OK) |
510 | 438 | goto cleanup; |
511 | 3.62k | } |
512 | | |
513 | 1.69k | cleanup: |
514 | 1.69k | io->close(io->io_ctx); |
515 | 1.69k | if (page) |
516 | 1.16k | free(page); |
517 | 1.69k | if (buffer) |
518 | 706 | free(buffer); |
519 | 1.69k | if (ctx) |
520 | 1.69k | sas7bcat_ctx_free(ctx); |
521 | 1.69k | if (hinfo) |
522 | 1.69k | free(hinfo); |
523 | | |
524 | 1.69k | return retval; |
525 | 976 | } |