Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions csv_config.c
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
#ifdef WIN32
#include <io.h>
#define F_OK 0
#define access _access
#else
#include <unistd.h>
#endif

#include "csv_config.h"

CSVConfig* csv_config_create(Arena *arena) {
void *ptr;
ArenaResult result = arena_alloc(arena, sizeof(CSVConfig), &ptr);
if (result != ARENA_OK) return NULL;

CSVConfig *config = (CSVConfig*)ptr;
memset(config, 0, sizeof(CSVConfig));

config->delimiter = ',';
config->enclosure = '"';
config->escape = '"';
Expand All @@ -19,7 +27,7 @@ CSVConfig* csv_config_create(Arena *arena) {
config->trimFields = false;
config->preserveQuotes = false;
config->autoFlush = true;

return config;
}

Expand All @@ -31,11 +39,11 @@ void csv_config_free(CSVConfig *config) {

CSVConfig* csv_config_copy(Arena *arena, const CSVConfig *config) {
if (!config) return NULL;

void *ptr;
ArenaResult result = arena_alloc(arena, sizeof(CSVConfig), &ptr);
if (result != ARENA_OK) return NULL;

CSVConfig *copy = (CSVConfig*)ptr;
memcpy(copy, config, sizeof(CSVConfig));
return copy;
Expand Down Expand Up @@ -110,6 +118,11 @@ void csv_config_set_escape(CSVConfig *config, char escape) {
}

void csv_config_set_path(CSVConfig *config, const char *path) {
if (access(path, F_OK) != 0) {
printf("Error: File %s does not exist!", path);
exit(1);
}

Comment on lines +121 to +125
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider less aggressive error handling than exit(1).

While the file existence check aligns with the PR objectives, calling exit(1) directly is quite aggressive and prevents graceful error handling by the caller. Additionally, there are a couple of issues:

  1. The error message lacks a newline character
  2. The validation occurs before checking if path is NULL (though access() should handle NULL safely)

Consider returning an error code instead of terminating the program:

-void csv_config_set_path(CSVConfig *config, const char *path) {
+int csv_config_set_path(CSVConfig *config, const char *path) {
+    if (!path) return 0;
+    
     if (access(path, F_OK) != 0) {
-        printf("Error: File %s does not exist!", path);
-        exit(1);
+        printf("Error: File %s does not exist!\n", path);
+        return 0;
     }

     if (config && path) {
         strncpy(config->path, path, MAX_PATH_LENGTH - 1);
         config->path[MAX_PATH_LENGTH - 1] = '\0';
     }
+    return 1;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (access(path, F_OK) != 0) {
printf("Error: File %s does not exist!", path);
exit(1);
}
int csv_config_set_path(CSVConfig *config, const char *path) {
+ if (!path) {
+ return 0;
+ }
if (access(path, F_OK) != 0) {
- printf("Error: File %s does not exist!", path);
- exit(1);
+ printf("Error: File %s does not exist!\n", path);
+ return 0;
}
if (config && path) {
strncpy(config->path, path, MAX_PATH_LENGTH - 1);
config->path[MAX_PATH_LENGTH - 1] = '\0';
}
+ return 1;
}
🤖 Prompt for AI Agents
In csv_config.c around lines 121 to 125, the code checks file existence but
calls exit(1) on failure, which is too aggressive and prevents graceful error
handling. Also, the error message lacks a newline and the NULL check for path is
missing. Fix this by first checking if path is NULL and returning an error code
if so, then check file existence with access(), and if the file does not exist,
print the error message with a newline and return an error code instead of
calling exit(1). This allows the caller to handle errors more gracefully.

if (config && path) {
strncpy(config->path, path, MAX_PATH_LENGTH - 1);
config->path[MAX_PATH_LENGTH - 1] = '\0';
Expand Down Expand Up @@ -154,4 +167,4 @@ void csv_config_set_preserve_quotes(CSVConfig *config, bool preserveQuotes) {

void csv_config_set_auto_flush(CSVConfig *config, bool autoFlush) {
if (config) config->autoFlush = autoFlush;
}
}
65 changes: 27 additions & 38 deletions csv_reader.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ CSVReader* csv_reader_init_with_config(Arena *persistent_arena, Arena *temp_aren

CSVReader *reader = (CSVReader*)ptr;
reader->file = fopen(config->path, "r");
if (!reader->file) {
return NULL;
}

reader->persistent_arena = persistent_arena;
reader->temp_arena = temp_arena;
Expand Down Expand Up @@ -48,27 +45,27 @@ CSVReader* csv_reader_init_standalone(CSVConfig *config) {
if (!config) {
return NULL;
}

Arena *persistent_arena = malloc(sizeof(Arena));
Arena *temp_arena = malloc(sizeof(Arena));

if (!persistent_arena || !temp_arena) {
if (persistent_arena) free(persistent_arena);
if (temp_arena) free(temp_arena);
return NULL;
}

ArenaResult p_result = arena_create(persistent_arena, 1024 * 1024);
ArenaResult t_result = arena_create(temp_arena, 1024 * 1024);

if (p_result != ARENA_OK || t_result != ARENA_OK) {
if (p_result == ARENA_OK) arena_destroy(persistent_arena);
if (t_result == ARENA_OK) arena_destroy(temp_arena);
free(persistent_arena);
free(temp_arena);
return NULL;
}

CSVReader *reader = malloc(sizeof(CSVReader));
if (!reader) {
arena_destroy(persistent_arena);
Expand All @@ -79,14 +76,6 @@ CSVReader* csv_reader_init_standalone(CSVConfig *config) {
}

reader->file = fopen(config->path, "r");
if (!reader->file) {
arena_destroy(persistent_arena);
arena_destroy(temp_arena);
free(persistent_arena);
free(temp_arena);
free(reader);
return NULL;
}

reader->persistent_arena = persistent_arena;
reader->temp_arena = temp_arena;
Expand Down Expand Up @@ -152,7 +141,7 @@ void csv_reader_free(CSVReader *reader) {
fclose(reader->file);
reader->file = NULL;
}

if (reader->owns_arenas) {
if (reader->persistent_arena) {
arena_destroy(reader->persistent_arena);
Expand All @@ -176,12 +165,12 @@ char** csv_reader_get_headers(CSVReader *reader, int *header_count) {
if (!reader || !header_count) {
return NULL;
}

if (reader->headers_loaded) {
*header_count = reader->cached_header_count;
return reader->cached_headers;
}

*header_count = 0;
return NULL;
}
Expand All @@ -190,7 +179,7 @@ void csv_reader_rewind(CSVReader *reader) {
if (reader && reader->file) {
rewind(reader->file);
reader->line_number = 0;

if (reader->config->hasHeader && reader->headers_loaded) {
char *line = read_full_record(reader->file, reader->persistent_arena);
if (line) {
Expand All @@ -204,7 +193,7 @@ int csv_reader_set_config(CSVReader *reader, Arena *persistent_arena, Arena *tem
if (!reader || !config || !persistent_arena || !temp_arena) {
return 0;
}

reader->config = (CSVConfig*)config;
reader->persistent_arena = persistent_arena;
reader->temp_arena = temp_arena;
Expand All @@ -215,30 +204,30 @@ long csv_reader_get_record_count(CSVReader *reader) {
if (!reader || !reader->file) {
return -1;
}

long current_pos = ftell(reader->file);
if (current_pos == -1) {
return -1;
}

rewind(reader->file);

long record_count = 0;

if (reader->config && reader->config->hasHeader) {
char *header_line = read_full_record(reader->file, reader->persistent_arena);
if (!header_line) {
fseek(reader->file, current_pos, SEEK_SET);
return 0;
}
}

while (1) {
char *line = read_full_record(reader->file, reader->persistent_arena);
if (!line) {
break;
}

if (reader->config && reader->config->skipEmptyLines) {
bool is_empty = true;
for (int i = 0; line[i] != '\0'; i++) {
Expand All @@ -251,30 +240,30 @@ long csv_reader_get_record_count(CSVReader *reader) {
continue;
}
}

record_count++;
}

fseek(reader->file, current_pos, SEEK_SET);

return record_count;
}

long csv_reader_get_position(CSVReader *reader) {
if (!reader || !reader->file) {
return -1;
}

return reader->line_number;
}

int csv_reader_seek(CSVReader *reader, long position) {
if (!reader || !reader->file || position < 0) {
return 0;
}

csv_reader_rewind(reader);

for (long i = 0; i < position; i++) {
arena_reset(reader->temp_arena);
char *line = read_full_record(reader->file, reader->temp_arena);
Expand All @@ -283,22 +272,22 @@ int csv_reader_seek(CSVReader *reader, long position) {
}
reader->line_number++;
}

return 1;
}

int csv_reader_has_next(CSVReader *reader) {
if (!reader || !reader->file) {
return 0;
}

long current_pos = ftell(reader->file);
if (current_pos == -1) {
return 0;
}

int c = fgetc(reader->file);
fseek(reader->file, current_pos, SEEK_SET);

return c != EOF;
}
}