add MSCSV, group and tab (array) support
authorNiki Roo <niki@nikiroo.be>
Thu, 27 Jun 2024 12:44:43 +0000 (14:44 +0200)
committerNiki Roo <niki@nikiroo.be>
Thu, 27 Jun 2024 12:44:43 +0000 (14:44 +0200)
src/cbook/cbook.h
src/cbook/cbook_csv.c
src/cbook/cbook_main.c
src/cbook/cbook_pl1.c
src/cutils

index f9796b71a6340b81a86df5d81a483263de80f9dd..7d69c06763cd9ab30fa73c29348e7902f0009dce 100755 (executable)
@@ -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 */
index 782a2196e27d8a3ef8b6a901d503196782f450c9..86e0424ec8ff982eb684156df69618ab3feb807d 100755 (executable)
 
 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);
index 4d54c253ef8b732cc31fc23e7c269567807ebb4e..49b6e9fc9cc47206810f3352193a23dcc66511f9 100755 (executable)
@@ -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");
 }
 
index 45fd9e562430547b2e4b981982e8dccb9a4ddc36..7d65dafbb0a4a45cca48184d24b645ee7ee27148 100755 (executable)
@@ -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) {
index 0dbfba1b2c91562142588c6ebf0538233eccd1ef..493c8a6cd70e7e4b520d15edcf9922576506ca59 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 0dbfba1b2c91562142588c6ebf0538233eccd1ef
+Subproject commit 493c8a6cd70e7e4b520d15edcf9922576506ca59