-
Notifications
You must be signed in to change notification settings - Fork 74
/
Copy pathmod_csv.c
154 lines (143 loc) · 5.35 KB
/
mod_csv.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include "../../readstat.h"
#include "module_util.h"
#include "module.h"
#include "../util/readstat_dta_days.h"
#include "../util/readstat_sav_date.h"
#include "double_decimals.h"
typedef struct mod_csv_ctx_s {
FILE *out_file;
long var_count;
} mod_csv_ctx_t;
static int accept_file(const char *filename);
static void *ctx_init(const char *filename);
static void finish_file(void *ctx);
static int handle_metadata(readstat_metadata_t *metadata, void *ctx);
static int handle_variable(int index, readstat_variable_t *variable,
const char *val_labels, void *ctx);
static int handle_value(int obs_index, readstat_variable_t *variable, readstat_value_t value, void *ctx);
rs_module_t rs_mod_csv = {
.accept = accept_file,
.init = ctx_init,
.finish = finish_file,
.handle = {
.metadata = handle_metadata,
.variable = handle_variable,
.value = handle_value
}
};
static int accept_file(const char *filename) {
return strcmp(filename, "-") == 0 || rs_ends_with(filename, ".csv");
}
static void *ctx_init(const char *filename) {
mod_csv_ctx_t *mod_ctx = malloc(sizeof(mod_csv_ctx_t));
if (strcmp(filename, "-") == 0) {
mod_ctx->out_file = stdout;
} else {
mod_ctx->out_file = fopen(filename, "w");
}
if (mod_ctx->out_file == NULL) {
fprintf(stderr, "Error opening %s for writing: %s\n", filename, strerror(errno));
return NULL;
}
return mod_ctx;
}
static void finish_file(void *ctx) {
mod_csv_ctx_t *mod_ctx = (mod_csv_ctx_t *)ctx;
if (mod_ctx) {
if (mod_ctx->out_file == stdout) {
fflush(mod_ctx->out_file);
} else if (mod_ctx->out_file != NULL) {
fclose(mod_ctx->out_file);
}
}
}
static int handle_metadata(readstat_metadata_t *metadata, void *ctx) {
mod_csv_ctx_t *mod_ctx = (mod_csv_ctx_t *)ctx;
mod_ctx->var_count = readstat_get_var_count(metadata);
return mod_ctx->var_count == 0;
}
static void emit_escaped_string(mod_csv_ctx_t *mod_ctx, const char *string) {
if (string == NULL) {
fprintf(mod_ctx->out_file, "\"\"");
} else {
fprintf(mod_ctx->out_file, "\"");
const char *p = NULL;
while ((string = strchr(p = string, '"'))) {
fwrite(p, string - p, 1, mod_ctx->out_file);
fprintf(mod_ctx->out_file, "\"\"");
string++;
}
fprintf(mod_ctx->out_file, "%s\"", p);
}
}
static int handle_variable(int index, readstat_variable_t *variable,
const char *val_labels, void *ctx) {
mod_csv_ctx_t *mod_ctx = (mod_csv_ctx_t *)ctx;
const char *name = readstat_variable_get_name(variable);
if (index > 0) {
fprintf(mod_ctx->out_file, ",");
}
emit_escaped_string(mod_ctx, name);
if (index == mod_ctx->var_count - 1) {
fprintf(mod_ctx->out_file, "\n");
}
return 0;
}
static int handle_value(int obs_index, readstat_variable_t *variable, readstat_value_t value, void *ctx) {
mod_csv_ctx_t *mod_ctx = (mod_csv_ctx_t *)ctx;
readstat_type_t type = readstat_value_type(value);
const char *format = readstat_variable_get_format(variable);
int var_index = readstat_variable_get_index(variable);
if (var_index > 0) {
fprintf(mod_ctx->out_file, ",");
}
if (readstat_value_is_system_missing(value)) {
/* void */
} else if (readstat_value_is_tagged_missing(value)) {
/* void */
} else if (type == READSTAT_TYPE_STRING) {
emit_escaped_string(mod_ctx, readstat_string_value(value));
} else if (type == READSTAT_TYPE_INT8) {
#ifdef __MINGW32__
__mingw_fprintf(mod_ctx->out_file, "%hhd", readstat_int8_value(value));
#else
fprintf(mod_ctx->out_file, "%hhd", readstat_int8_value(value));
#endif
} else if (type == READSTAT_TYPE_INT16) {
fprintf(mod_ctx->out_file, "%hd", readstat_int16_value(value));
} else if (type == READSTAT_TYPE_INT32 && format && 0 == strncmp("%td", format, strlen("%td"))) {
int days = readstat_int32_value(value);
char days_str[255];
readstat_dta_days_string(days, days_str, sizeof(days_str)-1);
fprintf(mod_ctx->out_file, "%s", days_str);
} else if (type == READSTAT_TYPE_DOUBLE && format && 0 == strncmp("EDATE40", format, strlen("EDATE40"))) {
double v = readstat_double_value(value);
char date_str[255];
char *s = readstat_sav_date_string(v, date_str, sizeof(date_str)-1);
if (!s) {
fprintf(stderr, "%s:%d Could not parse SPSS date double: %lf\n", __FILE__, __LINE__, v);
exit(EXIT_FAILURE);
}
fprintf(mod_ctx->out_file, "%s", s);
} else if (type == READSTAT_TYPE_INT32) {
fprintf(mod_ctx->out_file, "%d", readstat_int32_value(value));
} else if (type == READSTAT_TYPE_FLOAT) {
fprintf(mod_ctx->out_file, "%f", readstat_float_value(value));
} else if (type == READSTAT_TYPE_DOUBLE) {
double v = readstat_double_value(value);
int decimals = double_decimals(v);
if (decimals <= 6) {
fprintf(mod_ctx->out_file, "%lf", v);
} else {
fprintf(mod_ctx->out_file, "%.14f", v);
}
}
if (var_index == mod_ctx->var_count - 1) {
fprintf(mod_ctx->out_file, "\n");
}
return 0;
}