From 489afa94c4a7f8244ba0a2960b3ebffbb482b223 Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Thu, 27 Jun 2024 14:44:43 +0200 Subject: [PATCH] add MSCSV, group and tab (array) support --- src/cbook/cbook.h | 5 ++- src/cbook/cbook_csv.c | 91 ++++++++++++++++++++++-------------------- src/cbook/cbook_main.c | 28 ++++++++----- src/cbook/cbook_pl1.c | 60 +++++++++++++++++++++++----- src/cutils | 2 +- 5 files changed, 120 insertions(+), 66 deletions(-) diff --git a/src/cbook/cbook.h b/src/cbook/cbook.h index f9796b7..7d69c06 100755 --- a/src/cbook/cbook.h +++ b/src/cbook/cbook.h @@ -53,7 +53,8 @@ typedef int CBOOK_FORMAT; typedef int CBOOK_OUT; #define CBOOK_OUT_CSV 1 -#define CBOOK_OUT_FIELDS 2 +#define CBOOK_OUT_MSCSV 2 +#define CBOOK_OUT_FIELDS 3 typedef struct { char *name; @@ -92,7 +93,7 @@ int read_book(FILE *book_file, book_t *book); /* cbook_csv */ int write_header(FILE *outfile, book_t *book); -int write_csv(FILE *output, book_t *book, cstring_t *data, size_t lino); +int write_line(FILE *output, book_t *book, cstring_t *data, size_t lino); void write_done(); #endif /* CBOOK_H */ diff --git a/src/cbook/cbook_csv.c b/src/cbook/cbook_csv.c index 782a219..86e0424 100755 --- a/src/cbook/cbook_csv.c +++ b/src/cbook/cbook_csv.c @@ -29,13 +29,9 @@ static cstring_t *ascii_line = NULL; -// Actual write code, but may write either data or header -static int write_line(FILE *outfile, book_t *book, cstring_t *data, - int header, size_t lino); - // Write one field (including subfields), consume data and report errors static char *write_field(FILE *outfile, book_t *book, line_t *field, - char *data, size_t *remaining, size_t lino); + char *data, size_t *remaining, size_t lino, size_t fino); // Write this one field (data is ready), report errors in book->err_mess static void one_field(FILE *outfile, book_t *book, line_t *field, char *data); @@ -50,14 +46,11 @@ static char ascii_car(unsigned char ebcdic); int write_header(FILE *outfile, book_t *book) { // Only for CSV - if (book->out_fmt != CBOOK_OUT_CSV) + if ((book->out_fmt != CBOOK_OUT_CSV) + && (book->out_fmt != CBOOK_OUT_MSCSV)) return 1; - return write_line(outfile, book, NULL, 1, 0); -} - -int write_csv(FILE *outfile, book_t *book, cstring_t *data, size_t lino) { - return write_line(outfile, book, data, 0, lino); + return write_line(outfile, book, NULL, 0); } void write_done() { @@ -65,10 +58,7 @@ void write_done() { ascii_line = NULL; } -// PRIVATE - -static int write_line(FILE *outfile, book_t *book, cstring_t *dataline, - int header, size_t lino) { +int write_line(FILE *outfile, book_t *book, cstring_t *dataline, size_t lino) { size_t remaining = 0; char *data = NULL; @@ -77,24 +67,10 @@ static int write_line(FILE *outfile, book_t *book, cstring_t *dataline, data = dataline->string; } - int first = 1; array_loop(book->lines, field, line_t *) { - if (!first) { - switch(book->out_fmt) { - case CBOOK_OUT_CSV : fwrite(",", 1,1,outfile); break; - case CBOOK_OUT_FIELDS: fwrite("\n", 1,1,outfile); break; - } - } - first = 0; - - if (header) { - char *n = (*field)->name; - if (n) - fwrite(n, strlen(n), 1, outfile); - } else { - data = write_field(outfile, book, *field, data, - &remaining, lino); - } + // Works for data (lino > 0) and header (lino == 0) + data = write_field(outfile, book, *field, data, + &remaining, lino, 0); if (book->err_mess) { book->err_line = lino; @@ -121,20 +97,47 @@ static int write_line(FILE *outfile, book_t *book, cstring_t *dataline, return 1; } +// PRIVATE + static char *write_field(FILE *outfile, book_t *book, line_t *field, - char *data, size_t *remaining, size_t lino) { + char *data, size_t *remaining, size_t lino, size_t fino) { if (field->type == CBOOK_FMT_GROUP) { - array_loop(field->children, subfield, line_t *) { - data = write_field(outfile, book, *subfield, - data, remaining, lino); - if (book->err_mess) - return 0; + for (size_t i = 0 ; i < field->tab ; i++) { + array_loop(field->children, subfield, line_t *) { + data = write_field(outfile, book, *subfield, + data, remaining, lino, i + 1); + if (book->err_mess) + return 0; + } } return data; } + + int first = (field == *((line_t **)array_first(book->lines))); + if (!first) { + switch(book->out_fmt) { + case CBOOK_OUT_CSV : fwrite(",", 1,1,outfile); break; + case CBOOK_OUT_MSCSV : fwrite(";", 1,1,outfile); break; + case CBOOK_OUT_FIELDS: fwrite("\n", 1,1,outfile); break; + } + } - if (*remaining >= field->bytes) { + if (!lino) { + // Header + char *n = field->name; + if (n) + fwrite(n, 1, strlen(n), outfile); + + // TODO: find a way to support multiple levels deep + if (fino) { + char tab[100]; + sprintf(tab, "%zu", fino); + fwrite("(", 1, 1, outfile); + fwrite(tab, 1, strlen(tab), outfile); + fwrite(")", 1, 1, outfile); + } + } else if (*remaining >= field->bytes) { char car = data[field->bytes]; data[field->bytes] = '\0'; @@ -167,6 +170,7 @@ static void one_field(FILE *outfile, book_t *book, line_t *field, char *data) { switch(book->out_fmt) { case CBOOK_OUT_CSV : break; + case CBOOK_OUT_MSCSV: break; case CBOOK_OUT_FIELDS: fwrite(field->name, 1, strlen(field->name), outfile); fwrite(": ", 1, 2, outfile); @@ -225,12 +229,13 @@ static void one_field(FILE *outfile, book_t *book, line_t *field, char *data) { break; case CBOOK_FMT_VARCHAR: - // first 2 bytes -> BIN FIXED(15) ; - // TODO: - size_t coded = field->bytes - 2; - + // first 2 bytes -> BIN FIXED(15) + size_t coded = 0; + coded = (unsigned char)data[0] * 256 + + (unsigned char)data[1]; data = data + 2; + if (coded > (field->bytes - 2)) { char buf[100]; sprintf(buf, "%zu", coded); diff --git a/src/cbook/cbook_main.c b/src/cbook/cbook_main.c index 4d54c25..49b6e9f 100755 --- a/src/cbook/cbook_main.c +++ b/src/cbook/cbook_main.c @@ -36,7 +36,7 @@ int main(int argc, char **argv) { char *input = NULL; char *output = NULL; - CBOOK_OUT out_fmt = CBOOK_OUT_CSV; + CBOOK_OUT out_fmt = CBOOK_OUT_MSCSV; int ignore_errors = 0; int debug = 0; int raw_ebcdic = 0; @@ -45,6 +45,10 @@ int main(int argc, char **argv) { char *arg = argv[i]; if (!strcmp("--debug", arg) || !strcmp("-d", arg)) { debug = 1; + } else if (!strcmp("--output-csv", arg) || !strcmp("-c", arg)){ + out_fmt = CBOOK_OUT_CSV; + } else if (!strcmp("--output-ms", arg) || !strcmp("-m", arg)){ + out_fmt = CBOOK_OUT_MSCSV; } else if (!strcmp("--output-flat", arg) || !strcmp("-o", arg)){ out_fmt = CBOOK_OUT_FIELDS; } else if (!strcmp("--raw-ebcdic", arg) || !strcmp("-r", arg)) { @@ -61,12 +65,12 @@ int main(int argc, char **argv) { return 5; } if (book_filename) { - fprintf(stderr, "Syntax error\n"); + fprintf(stderr,"Syntax error (try '--help')\n"); return 5; } book_filename = argv[++i]; } else if (input && output) { - fprintf(stderr, "Syntax error\n"); + fprintf(stderr, "Syntax error (try '--help')\n"); return 5; } else if (input) { output = argv[i]; @@ -76,7 +80,7 @@ int main(int argc, char **argv) { } if (!book_filename) { - fprintf(stderr, "Syntax error\n"); + fprintf(stderr, "Syntax error (try '--help')\n"); return 5; } @@ -131,7 +135,7 @@ int main(int argc, char **argv) { cstring_t *line = new_cstring(); int write_ok = 1; while(readline(book, line, infile, ++lino)) { - if (!write_csv(outfile, book, line, lino)) { + if (!write_line(outfile, book, line, lino)) { write_ok = 0; break; } @@ -198,9 +202,15 @@ void help(char *program) { printf("\t%s --b BOOK.PLI (IN.DATA (OUT.CSV))\n", program); printf("\n"); printf("Parameters:\n"); - printf("\t> --help (or -h): this help message\n"); - printf("\t> BOOK.PLI: a data description include book (in PL/1)\n"); - printf("\t> IN.DATA: the text/binary input file, defaults to stdin\n"); - printf("\t> OUT.CSV: the CSV output file, defaults to stdout\n"); + printf("\t-h / --help : this help message\n"); + printf("\t-o / --output-flat : output is one line per field\n"); + printf("\t-m / --output-ms : output is Microsoft CSV (default)\n"); + printf("\t-c / --output-csv : output is CSV\n"); + printf("\t-d / --debug : add lot of DEBUG information\n"); + printf("\t-i / --ignore-errors : ignore errors and continue\n"); + printf("\t-r / --raw-ebcdic : consider input to be raw EBCDIC\n"); + printf("\tBOOK.PLI : a data description include book (in PL/1)\n"); + printf("\tIN.DATA : the text/binary input file, defaults to stdin\n"); + printf("\tOUT.CSV : the CSV output file, defaults to stdout\n"); } diff --git a/src/cbook/cbook_pl1.c b/src/cbook/cbook_pl1.c index 45fd9e5..7d65daf 100755 --- a/src/cbook/cbook_pl1.c +++ b/src/cbook/cbook_pl1.c @@ -43,7 +43,10 @@ int add_line(book_t *book, const char data[]); line_t *process_line(book_t *book, const char data[]); /* Reorder the lines inside the book according to their level */ -void reorder_lines(book_t *book); +array_t *reorder_lines(array_t *lines); + +/* Compute the total size of all the given lines */ +size_t compute_size(array_t *lines); /* Util: get the first word found in the given string */ char *get_word(char *line); @@ -177,18 +180,12 @@ skip: } } - // Compute only the first level (reorder_lines must ensure that - // the size of sublevels are reported to higher levels) - book->bytes = 0; - array_loop(book->lines, line, line_t *) { - book->bytes += (*line)->bytes; - } - if (book->debug) { fprintf(stderr, "Book has a size of: %zu bytes\n", book->bytes); } - reorder_lines(book); + book->lines = reorder_lines(book->lines); + book->bytes = compute_size(book->lines); return 1; } @@ -317,6 +314,7 @@ line_t *process_line(book_t *book, const char data[]) { size_t i; cstring_t *str = new_cstring(); line_t *line = calloc(1, sizeof(line_t)); + line->tab = 1; // default line->children = new_array(sizeof(line_t *), 20); // Copy of data @@ -615,9 +613,49 @@ skip_pline: return line; } +size_t compute_size(array_t *lines) { + size_t sz = 0; + + array_loop(lines, line, line_t *) { + if ((*line)->type == CBOOK_FMT_GROUP) { + (*line)->bytes = compute_size((*line)->children); + } + + sz += (*line)->bytes; + } + + return sz; +} + +array_t *reorder_lines(array_t *lines) { + int change = 1; + line_t **prev = NULL; + array_t *tmp = new_array(sizeof(line_t *), 20); + while (change) { + change = 0; + array_loop(lines, line, line_t *) { + if (prev && *prev && (*line)-> level > (*prev)->level) { + array_push((*prev)->children, line); + change = 1; + } else { + array_push(tmp, line); + } + + prev = line; + } + array_t *aroo = lines; + lines = tmp; + tmp = aroo; + + array_cut_at(tmp, 0); + } + free_array(tmp); + + array_loop(lines, line, line_t *) { + (*line)->children = reorder_lines((*line)->children); + } -void reorder_lines(book_t *book) { - // TODO + return lines; } char *get_word(char *line) { diff --git a/src/cutils b/src/cutils index 0dbfba1..493c8a6 160000 --- a/src/cutils +++ b/src/cutils @@ -1 +1 @@ -Subproject commit 0dbfba1b2c91562142588c6ebf0538233eccd1ef +Subproject commit 493c8a6cd70e7e4b520d15edcf9922576506ca59 -- 2.27.0