]> begriffs open source - sa-parse/blob - src/csv_parser.c
ical parser
[sa-parse] / src / csv_parser.c
1 #include "csv_parser.h"
2 #include "csv.tab.h"
3 #include <stdlib.h>
4 #include <string.h>
5
6 /* External declarations from Bison/Flex - updated for reentrant API */
7 typedef void *yyscan_t;
8
9 extern int csvlex_init(yyscan_t *scanner);
10 extern int csvlex_destroy(yyscan_t scanner);
11 extern void csvset_in(FILE *file, yyscan_t scanner);
12 extern int csvparse(void *parser_state, yyscan_t scanner);
13
14 /* CSV parser structure - now contains all the state that was previously global */
15 struct csv_parser {
16     char error_message[256];
17     bool has_error;
18     vector *current_records;    /* Vector of csv_record_t* */
19     bool parse_error;
20     char parse_error_message[256];
21     int error_line;
22     int error_column;
23     int expected_field_count;   /* Field count from first record (-1 if not set) */
24     int current_record_number;  /* Track record number for error reporting */
25 };
26
27 /* Helper functions */
28 static csv_record_t *create_record_from_vector(vector *field_vector);
29 static void csv_record_destructor(void *record_ptr, void *aux);
30
31 /* Functions called by the Bison parser */
32 void csv_parser_add_record(void *parser_state, vector *fields);
33 void csv_parser_set_error_with_location(void *parser_state, const char *error, int line, int column);
34
35 /* Implementation */
36
37 csv_parser_t *csv_parser_create(void)
38 {
39     csv_parser_t *parser = malloc(sizeof(csv_parser_t));
40     if (!parser) {
41         return NULL;
42     }
43     
44     parser->error_message[0] = '\0';
45     parser->has_error = false;
46     parser->current_records = NULL;
47     parser->parse_error = false;
48     parser->parse_error_message[0] = '\0';
49     parser->error_line = 0;
50     parser->error_column = 0;
51     parser->expected_field_count = -1;
52     parser->current_record_number = 0;
53     
54     return parser;
55 }
56
57 void csv_parser_destroy(csv_parser_t *parser)
58 {
59     if (parser) {
60         free(parser);
61     }
62 }
63
64 csv_error_t csv_parser_parse_string(csv_parser_t *parser, const char *input, vector **records)
65 {
66     if (!parser || !input || !records) {
67         return CSV_ERROR_INVALID_PARAMETER;
68     }
69     
70     *records = NULL;
71     
72     /* Create a temporary file from the string input */
73     FILE *temp_file = tmpfile();
74     if (!temp_file) {
75         strncpy(parser->error_message, "Failed to create temporary file", sizeof(parser->error_message) - 1);
76         parser->has_error = true;
77         return CSV_ERROR_IO;
78     }
79     
80     /* Write input to temporary file */
81     if (fputs(input, temp_file) == EOF) {
82         fclose(temp_file);
83         strncpy(parser->error_message, "Failed to write to temporary file", sizeof(parser->error_message) - 1);
84         parser->has_error = true;
85         return CSV_ERROR_IO;
86     }
87     
88     /* Reset file position to beginning */
89     rewind(temp_file);
90     
91     /* Parse from the temporary file */
92     csv_error_t result = csv_parser_parse_file(parser, temp_file, records);
93     fclose(temp_file);
94     
95     return result;
96 }
97
98 csv_error_t csv_parser_parse_file(csv_parser_t *parser, FILE *file, vector **records)
99 {
100     if (!parser || !file || !records) {
101         return CSV_ERROR_INVALID_PARAMETER;
102     }
103     
104     *records = NULL;
105     parser->has_error = false;
106     parser->error_message[0] = '\0';
107     
108     /* Initialize parser state */
109     parser->current_records = v_new();
110     if (!parser->current_records) {
111         strncpy(parser->error_message, "Memory allocation failed", sizeof(parser->error_message) - 1);
112         parser->has_error = true;
113         return CSV_ERROR_MEMORY;
114     }
115     
116     /* Set destructor for automatic cleanup */
117     v_dtor(parser->current_records, csv_record_destructor, NULL);
118     
119     parser->parse_error = false;
120     parser->parse_error_message[0] = '\0';
121     parser->error_line = 0;
122     parser->error_column = 0;
123     parser->expected_field_count = -1;
124     parser->current_record_number = 0;
125     
126     /* Initialize the reentrant scanner */
127     yyscan_t scanner;
128     int scanner_init_result = csvlex_init(&scanner);
129     if (scanner_init_result != 0) {
130         v_free(parser->current_records);
131         parser->current_records = NULL;
132         return CSV_ERROR_MEMORY;
133     }
134     
135     /* Set up the lexer input */
136     csvset_in(file, scanner);
137     
138     /* Parse the input */
139     int parse_result = csvparse(parser, scanner);
140     
141     /* Clean up lexer state */
142     csvlex_destroy(scanner);
143     
144     /* Check for errors */
145     if (parse_result != 0 || parser->parse_error) {
146         csv_error_t error_code;
147         
148         switch (parse_result) {
149             case 2:
150                 /* Memory exhaustion in parser */
151                 strncpy(parser->error_message, "Parser memory exhaustion", sizeof(parser->error_message) - 1);
152                 error_code = CSV_ERROR_MEMORY;
153                 break;
154                 
155             case 1:
156                 /* Parse error (syntax error or YYABORT) */
157                 if (parser->parse_error_message[0] != '\0') {
158                     strncpy(parser->error_message, parser->parse_error_message, sizeof(parser->error_message) - 1);
159                 } else {
160                     strncpy(parser->error_message, "Syntax error", sizeof(parser->error_message) - 1);
161                 }
162                 error_code = CSV_ERROR_PARSE;
163                 break;
164                 
165             default:
166                 /* Other non-zero return or parser->parse_error flag set */
167                 if (parser->parse_error_message[0] != '\0') {
168                     strncpy(parser->error_message, parser->parse_error_message, sizeof(parser->error_message) - 1);
169                 } else {
170                     strncpy(parser->error_message, "Parse error", sizeof(parser->error_message) - 1);
171                 }
172                 error_code = CSV_ERROR_PARSE;
173                 break;
174         }
175         
176         parser->has_error = true;
177         v_free(parser->current_records);
178         parser->current_records = NULL;
179         return error_code;
180     }
181     
182     /* Success - transfer ownership of records vector */
183     *records = parser->current_records;
184     parser->current_records = NULL;
185     
186     return CSV_SUCCESS;
187 }
188
189 csv_error_info_t csv_parser_get_error_info(csv_parser_t *parser)
190 {
191     csv_error_info_t info = {0};
192     
193     if (!parser) {
194         info.message = "Invalid parser";
195         info.line = 0;
196         info.column = 0;
197         info.has_location = false;
198         return info;
199     }
200     
201     if (parser->has_error) {
202         info.message = parser->error_message;
203         info.line = parser->error_line;
204         info.column = parser->error_column;
205         info.has_location = (parser->error_line > 0);
206     } else {
207         info.message = "No error";
208         info.line = 0;
209         info.column = 0;
210         info.has_location = false;
211     }
212     
213     return info;
214 }
215
216 const char *csv_error_string(csv_error_t error)
217 {
218     switch (error) {
219         case CSV_SUCCESS:
220             return "Success";
221         case CSV_ERROR_MEMORY:
222             return "Memory allocation error";
223         case CSV_ERROR_PARSE:
224             return "Parse error";
225         case CSV_ERROR_INVALID_PARAMETER:
226             return "Invalid parameter";
227         case CSV_ERROR_IO:
228             return "I/O error";
229         default:
230             return "Unknown error";
231     }
232 }
233
234 /* Internal helper functions */
235
236 static csv_record_t *create_record_from_vector(vector *field_vector)
237 {
238     if (!field_vector) {
239         return NULL;
240     }
241     
242     csv_record_t *record = malloc(sizeof(csv_record_t));
243     if (!record) {
244         return NULL;
245     }
246     
247     /* Take ownership of the vector directly */
248     record->fields = field_vector;
249     
250     return record;
251 }
252
253 static void csv_record_destructor(void *record_ptr, void *aux)
254 {
255     (void)aux; /* Unused parameter */
256     csv_record_t *record = (csv_record_t *)record_ptr;
257     if (record) {
258         if (record->fields) {
259             /* The fields vector already has derp_free as its destructor set in yacc,
260                so v_free will automatically clean up the strings */
261             v_free(record->fields);
262         }
263         free(record);
264     }
265 }
266
267 /* Functions called by the Bison parser */
268
269 void csv_parser_add_record(void *parser_state, vector *fields)
270 {
271     csv_parser_t *parser = (csv_parser_t *)parser_state;
272     if (!parser || !parser->current_records || parser->parse_error) {
273         return;
274     }
275     
276     /* Skip empty records (single empty field) */
277     if (fields && v_length(fields) == 1) {
278         char *first_field = v_at(fields, 0);
279         if (first_field && first_field[0] == '\0') {
280             return;
281         }
282     }
283     
284     parser->current_record_number++;
285     int field_count = v_length(fields);
286     
287     /* Set expected field count from first record */
288     if (parser->expected_field_count == -1) {
289         parser->expected_field_count = field_count;
290     } else if (field_count != parser->expected_field_count) {
291         /* Field count mismatch - RFC 4180 violation */
292         parser->parse_error = true;
293         snprintf(parser->parse_error_message, sizeof(parser->parse_error_message) - 1,
294                 "Field count mismatch: record %d has %d fields, expected %d",
295                 parser->current_record_number, field_count, parser->expected_field_count);
296         return;
297     }
298     
299     csv_record_t *record = create_record_from_vector(fields);
300     if (!record) {
301         parser->parse_error = true;
302         strncpy(parser->parse_error_message, "Memory allocation failed while creating record", sizeof(parser->parse_error_message) - 1);
303         return;
304     }
305     
306     /* Add to records vector */
307     if (!v_append(parser->current_records, record)) {
308         parser->parse_error = true;
309         strncpy(parser->parse_error_message, "Failed to add record to vector", sizeof(parser->parse_error_message) - 1);
310         csv_record_destructor(record, NULL);
311         return;
312     }
313 }
314
315 void csv_parser_set_error_with_location(void *parser_state, const char *error, int line, int column)
316 {
317     csv_parser_t *parser = (csv_parser_t *)parser_state;
318     if (!parser) {
319         return;
320     }
321     
322     parser->parse_error = true;
323     parser->error_line = line;
324     parser->error_column = column;
325     if (error) {
326         strncpy(parser->parse_error_message, error, sizeof(parser->parse_error_message) - 1);
327         parser->parse_error_message[sizeof(parser->parse_error_message) - 1] = '\0';
328     }
329 }
330