From 00eaccbb8110c9116a545b179fc57e0401ad94f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20COLANGELO?= Date: Sun, 26 Jul 2020 11:52:28 +0200 Subject: [PATCH] Implemeted CSV backend, add it to makefile, correct some errors in smart_records.c --- src/csv_backend/Makefile | 15 ++ src/csv_backend/csv_backend.a | Bin 0 -> 11382 bytes src/csv_backend/csv_backend.c | 179 ++++++++++++++++++++++++ src/csv_backend/csv_backend.h | 13 ++ src/csv_backend/csv_backend.o | Bin 0 -> 11104 bytes src/csv_backend/csv_backend_internals.h | 11 ++ src/csv_backend/csv_backend_test | Bin 0 -> 22040 bytes 7 files changed, 218 insertions(+) create mode 100644 src/csv_backend/Makefile create mode 100644 src/csv_backend/csv_backend.a create mode 100644 src/csv_backend/csv_backend.c create mode 100644 src/csv_backend/csv_backend.h create mode 100644 src/csv_backend/csv_backend.o create mode 100644 src/csv_backend/csv_backend_internals.h create mode 100755 src/csv_backend/csv_backend_test diff --git a/src/csv_backend/Makefile b/src/csv_backend/Makefile new file mode 100644 index 0000000..cd2cc1e --- /dev/null +++ b/src/csv_backend/Makefile @@ -0,0 +1,15 @@ +CFLAGS=-ggdb -Wall + +all: test csv_backend.a + +clean: + rm -f *.o *.a csv_backend_test + +test: csv_backend_test + +csv_backend.a: csv_backend.o + $(AR) -rcv $@ $? + +csv_backend_test: csv_backend.c + $(CC) $(CFLAGS) -D_TEST -o $@ $? + diff --git a/src/csv_backend/csv_backend.a b/src/csv_backend/csv_backend.a new file mode 100644 index 0000000000000000000000000000000000000000..089731bed048dbd6cacab957a27897fa1f79fd79 GIT binary patch literal 11382 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 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../smart_records_internals.h" +#include "csv_backend_internals.h" + +void *csv_backend_init(char **error) +{ + csv_backend_ctx_t *ctx=NULL; + ctx = malloc(sizeof(csv_backend_ctx_t)); + memset(ctx, 0, sizeof(csv_backend_ctx_t)); + ctx->mode = 0644; + ctx->fmt = NULL; + ctx->pathname = NULL; + return ctx; +} + +int csv_backend_open_records_base(void *ctx, char **error, const char *uri) +{ + csv_backend_ctx_t *c; + if (!ctx || !uri) { + set_error_and_fails("invalid arguments while opening records base"); + } + c = (csv_backend_ctx_t *)ctx; + + // XXX: Implement mode parsing example: csv://: + c->pathname = strdup(strchr(uri,':')+1); + c->fd = open(c->pathname, O_CREAT|O_APPEND|O_WRONLY, c->mode); + if (c->fd < 0) { + /* And then, we're not POSIX anymore */ +#define _GNU_SOURCE + asprintf(error,"failed opening CSV file: %s", strerror(errno)); + goto failed; + } + return 0; +failed: + return -1; +} + +int csv_backend_set_format(void *ctx, char **error, const char *fmt) +{ + csv_backend_ctx_t *c; + if (!ctx || !fmt) { + set_error_and_fails("invalid parameters while setting format.") + } + c = (csv_backend_ctx_t *) ctx; + c->fmt = strdup(fmt); + return 0; +failed: + return -1; +} + +int csv_backend_write_record(void *ctx, char **error, ...) +{ + csv_backend_ctx_t *c; + int size=0; + va_list ap; + + if (!ctx) { + set_error_and_fails("invalid parameters while setting format.") + } + c = (csv_backend_ctx_t *) ctx; + + if (!c->fmt) { + set_error_and_fails("no format provided, please set it before writing any record"); + } + + va_start(ap,error); + size = vsnprintf(NULL, size, c->fmt, ap); + va_end(ap); + + va_start(ap,error); + if (vdprintf(c->fd, c->fmt, ap) != size) { + set_error_and_fails("vdprintf() failed"); + } + va_end(ap); + + return 0; +failed: + return -1; +} + +int csv_backend_close_records_base(void *ctx) +{ + csv_backend_ctx_t *c; + if(!ctx) + return -1; + c = (csv_backend_ctx_t *)ctx; + + if(c->fd > 0) + close(c->fd); + if(c->pathname) + free(c->pathname); + if(c->fmt) + free(c->fmt); + free(c); + + return 0; +} + + + +#ifdef _TEST + +#define dump_error_and_quit(func,error) \ + { \ + if (error) { \ + fprintf(stderr, "[ERROR] %s(): %s\n", func, error); \ + } else { \ + fprintf(stderr, "[ERROR] %s(): unknown error\n", func); \ + } \ + free(error); \ + exit(EXIT_FAILURE); \ + } + + +int main(int ac, char **av) +{ + void *ctx; + char *error=NULL; + int i=0; + + if(ac != 2) { + printf("Usage: %s \n \ + mysql_uri format is mysql://user[:password]@host[:port]/database.table\n \ + database shall exists and table must obey the following scheme:\n \ + CREATE TABLE (a INT, b INT, c VARCHAR(40))\n",av[0]); + exit(EXIT_FAILURE); + } + + ctx = csv_backend_init(&error); + if (ctx == NULL) { + dump_error_and_quit("csv_backend_init", error); + } + + if (csv_backend_set_format(ctx, &error, "%d;%d;%s;\n") < 0){ + dump_error_and_quit("csv_backend_set_format", error); + } + + if(csv_backend_open_records_base(ctx, &error, av[1]) < 0) { + dump_error_and_quit("csv_backend_set_open_records_base", error); + } + + for (i=0; i<4; i++) { + if (csv_backend_write_record(ctx, &error, i, i+10, "Text here") <0 ){ + dump_error_and_quit("csv_backend_write_record", error); + } + } + csv_backend_close_records_base(ctx); +} +#endif diff --git a/src/csv_backend/csv_backend.h b/src/csv_backend/csv_backend.h new file mode 100644 index 0000000..8fbcb4a --- /dev/null +++ b/src/csv_backend/csv_backend.h @@ -0,0 +1,13 @@ +#ifndef _SMART_RECORDS_CSV_BACKEND_H +#define _SMART_RECORDS_CSV_BACKEND_H + +#include + + +void *csv_backend_init(char **error); +int csv_backend_open_records_base(void *ctx, char **error, const char *uri); +int csv_backend_write_record(void *ctx, char **error, va_list ap); +int csv_backend_set_format(void *ctx, char **error, const char *format); +int csv_backend_close_records_base(void *ctx); + +#endif diff --git a/src/csv_backend/csv_backend.o b/src/csv_backend/csv_backend.o new file mode 100644 index 0000000000000000000000000000000000000000..e0a99f379734c72dda79bf3852fa1cf31de9caed GIT binary patch literal 11104 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 EADx)t1ONa4 literal 0 HcmV?d00001 diff --git a/src/csv_backend/csv_backend_internals.h b/src/csv_backend/csv_backend_internals.h new file mode 100644 index 0000000..28facbb --- /dev/null +++ b/src/csv_backend/csv_backend_internals.h @@ -0,0 +1,11 @@ +#ifndef CSV_BACKEND_INTERNALS_H +#define CSV_BACKEND_INTERNALS_H + +typedef struct csv_backend_ctx_t { + int fd; + char *pathname; + char *fmt; + int mode; +} csv_backend_ctx_t; + +#endif diff --git a/src/csv_backend/csv_backend_test b/src/csv_backend/csv_backend_test new file mode 100755 index 0000000000000000000000000000000000000000..9e553250da2fbcc67cd9f9e728d5ee918bf9240d GIT binary patch literal 22040 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 Bkxc*q literal 0 HcmV?d00001