sav_ctx_t *sav_ctx_init(sav_file_header_record_t *header, readstat_io_t *io) { sav_ctx_t *ctx = NULL; if ((ctx = malloc(sizeof(sav_ctx_t))) == NULL) { return NULL; } memset(ctx, 0, sizeof(sav_ctx_t)); ctx->bswap = !(header->layout_code == 2 || header->layout_code == 3); ctx->data_is_compressed = (header->compressed != 0); ctx->record_count = ctx->bswap ? byteswap4(header->ncases) : header->ncases; ctx->fweight_index = ctx->bswap ? byteswap4(header->weight_index) : header->weight_index; ctx->missing_double = SAV_MISSING_DOUBLE; ctx->lowest_double = SAV_LOWEST_DOUBLE; ctx->highest_double = SAV_HIGHEST_DOUBLE; double bias = ctx->bswap ? byteswap_double(header->bias) : header->bias; if (bias != 100.0) { sav_ctx_free(ctx); return NULL; } ctx->varinfo_capacity = SAV_VARINFO_INITIAL_CAPACITY; if ((ctx->varinfo = calloc(ctx->varinfo_capacity, sizeof(spss_varinfo_t))) == NULL) { sav_ctx_free(ctx); return NULL; } ctx->io = io; return ctx; }
readstat_error_t sav_parse_timestamp(sav_ctx_t *ctx, sav_file_header_record_t *header) { readstat_error_t retval = READSTAT_OK; struct tm timestamp = { .tm_isdst = -1 }; if ((retval = sav_parse_time(header->creation_time, sizeof(header->creation_time), ×tamp, ctx)) != READSTAT_OK) goto cleanup; if ((retval = sav_parse_date(header->creation_date, sizeof(header->creation_date), ×tamp, ctx)) != READSTAT_OK) goto cleanup; ctx->timestamp = mktime(×tamp); cleanup: return retval; } readstat_error_t readstat_parse_sav(readstat_parser_t *parser, const char *path, void *user_ctx) { readstat_error_t retval = READSTAT_OK; readstat_io_t *io = parser->io; sav_file_header_record_t header; sav_ctx_t *ctx = NULL; size_t file_size = 0; if (io->open(path, io->io_ctx) == -1) { return READSTAT_ERROR_OPEN; } file_size = io->seek(0, READSTAT_SEEK_END, io->io_ctx); if (file_size == -1) { retval = READSTAT_ERROR_SEEK; goto cleanup; } if (io->seek(0, READSTAT_SEEK_SET, io->io_ctx) == -1) { retval = READSTAT_ERROR_SEEK; goto cleanup; } if (io->read(&header, sizeof(sav_file_header_record_t), io->io_ctx) < sizeof(sav_file_header_record_t)) { retval = READSTAT_ERROR_READ; goto cleanup; } ctx = sav_ctx_init(&header, io); if (ctx == NULL) { retval = READSTAT_ERROR_PARSE; goto cleanup; } ctx->progress_handler = parser->progress_handler; ctx->error_handler = parser->error_handler; ctx->note_handler = parser->note_handler; ctx->value_handler = parser->value_handler; ctx->value_label_handler = parser->value_label_handler; ctx->input_encoding = parser->input_encoding; ctx->output_encoding = parser->output_encoding; ctx->user_ctx = user_ctx; ctx->file_size = file_size; if (ctx->record_count == -1 || (parser->row_limit > 0 && parser->row_limit < ctx->record_count)) { ctx->row_limit = parser->row_limit; } else { ctx->row_limit = ctx->record_count; } if ((retval = sav_parse_timestamp(ctx, &header)) != READSTAT_OK) goto cleanup; if ((retval = sav_parse_records_pass1(ctx)) != READSTAT_OK) goto cleanup; if (io->seek(sizeof(sav_file_header_record_t), READSTAT_SEEK_SET, io->io_ctx) == -1) { retval = READSTAT_ERROR_SEEK; goto cleanup; } if ((retval = sav_update_progress(ctx)) != READSTAT_OK) goto cleanup; if ((retval = sav_parse_records_pass2(ctx)) != READSTAT_OK) goto cleanup; sav_set_n_segments_and_var_count(ctx); if (parser->info_handler) { if (parser->info_handler(ctx->record_count == -1 ? -1 : ctx->row_limit, ctx->var_count, ctx->user_ctx)) { retval = READSTAT_ERROR_USER_ABORT; goto cleanup; } } if (parser->metadata_handler) { if ((retval = readstat_convert(ctx->file_label, sizeof(ctx->file_label), header.file_label, sizeof(header.file_label), ctx->converter)) != READSTAT_OK) goto cleanup; if (parser->metadata_handler(ctx->file_label, ctx->timestamp, 2, ctx->user_ctx)) { retval = READSTAT_ERROR_USER_ABORT; goto cleanup; } } sav_parse_variable_display_parameter_record(ctx); if ((retval = sav_handle_variables(parser, ctx)) != READSTAT_OK) goto cleanup; if ((retval = sav_handle_fweight(parser, ctx)) != READSTAT_OK) goto cleanup; if (ctx->value_handler) { retval = sav_read_data(ctx); } cleanup: io->close(io->io_ctx); if (ctx) sav_ctx_free(ctx); return retval; }