From 98ea89a9161af89345344afde49f7cb35c2b10e1 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Thibaut=20VAR=C3=88NE?= Date: 2021年8月21日 00:25:44 +0200 Subject: [PATCH] carve out output formatting from the parser --- Makefile | 4 +- tic2json.c | 366 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tic2json.h | 7 + tic2json.y | 367 +++-------------------------------------------------- 4 files changed, 391 insertions(+), 353 deletions(-) create mode 100644 tic2json.c diff --git a/Makefile b/Makefile index 280d752..61c8669 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,13 @@ all: tic2json -%.lex.c: %.l %.tab.h +%.lex.c: %.l tic2json.tab.h # The ideal size for the flex buffer is the length of the longest token expected, in bytes, plus a little more. flex -DYY_BUF_SIZE=128 -o$@ $< %.tab.h %.tab.c: %.y bison -Wno-other -d $< -tic2json: %: %.tab.c %.lex.c +tic2json: %: %.tab.c ticv02.lex.c %.c $(CC) -DBINNAME='"$@"' -Os -Wall $^ -o $@ clean: diff --git a/tic2json.c b/tic2json.c new file mode 100644 index 0000000..2bd3dcd --- /dev/null +++ b/tic2json.c @@ -0,0 +1,366 @@ +// +// tic2json.c +// A tool to turn ENEDIS TIC data into pure JSON +// +// (C) 2021 Thibaut VARENE +// License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html +// + +/** + * @file + * Outputs as JSON a series of frames formatted as a list of fields or a dictionary. + * - for list mode, fields are { "label": "xxx", "data": "xxx", horodate: "xxx", "desc": "xxx", "unit": "xxx" } + * - for dict mode, the keys are the label, followed by { "data": "xxx", "horodate": "xxx", "desc": "xxx", "unit": "xxx" } + * with horodate optional, unit and data optional and possibly empty and data being either quoted string or number. + * + * Data errors can result in some/all datasets being omitted in the output frame (e.g. invalid datasets or datasets + * that did not pass checksum are not emitted): the JSON root object can then be empty but is still emitted. + * In dictionary mode the parser will report the frame status as "_tvalide" ("trame valide") followed by either 1 + * for a valid frame or 0 for a frame containing errors (including dataset errors). + * + * Output JSON is guaranteed to always be valid for each frame. By default only frames are separated with newlines. + */ + +#include +#include +#include +#include + +#include "tic2json.h" +#include "tic2json.tab.h" + +int filter_mode; +uint8_t *etiq_en; // type: < 255 tokens. This could be made a bit field if memory is a concern + +static struct { + const char *idtag; + char framedelims[2]; + char fdelim; + int optflags; + unsigned int skipframes, framecount; + char ferr; +} tp; + +enum { + OPT_MASKZEROES = 0x01, + OPT_CRFIELD = 0x02, + OPT_DESCFORM = 0x04, + OPT_DICTOUT = 0x08, + OPT_LONGDATE = 0x10, + OPT_PARSESTGE = 0x20, +}; + +static const char * tic_units[] = { + [U_SANS] = "", + [U_WH] = "Wh", + [U_VARH] = "VArh", + [U_A] = "A", + [U_V] = "V", + [U_KVA] = "kVA", + [U_VA] = "VA", + [U_W] = "W", +}; + +int yylex_destroy(); + +void make_field(struct tic_field *field, const struct tic_etiquette *etiq, char *horodate, char *data) +{ + // args come from the bison stack + int base; + + field->horodate = horodate; + memcpy(&field->etiq, etiq, sizeof(field->etiq)); + + switch ((etiq->unittype & 0xF0)) { + case T_STRING: + field->data.s = data; + return; + case T_HEX: + base = 16; + break; + default: + base = 10; + break; + } + field->data.i = (int)strtol(data, NULL, base); + free(data); +} + +static void print_stge_data(int data) +{ + const char sep = (tp.optflags & OPT_CRFIELD) ? '\n' : ' '; + uint32_t d = (uint32_t)data; + + + const char *const of[] = { + "fermé", + "ouvert", + }; + + const char *const coupure[] = { + "fermé", + "ouvert sur surpuissance", + "ouvert sur surtension", + "ouvert sur délestage", + "ouvert sur ordre CPL ou Euridis", + "ouvert sur une surchauffe avec une valeur de courant supérieure au courant de commutation maximal", + "ouvert sur une surchauffe avec une valeur de courant inférieure au courant de commutation maximal", + NULL, + }; + + const char *const euridis[] = { + "désactivée", + "activée sans sécurité", + NULL, + "activée avec sécurité", + }; + + const char *const cpl[] = { + "New/Unlock", + "New/Lock", + "Registered", + NULL, + }; + + const char *const tempo[] = { + "Pas d'annonce", + "Bleu", + "Blanc", + "Rouge", + }; + + const char *const pm[] = { + "pas", + "PM1", + "PM2", + "PM3", + }; + + printf("{ " + "\"Contact sec\": \"%s\",%c" + "\"Organe de coupure\": \"%s\",%c" + "\"État du cache-bornes distributeur\": \"%s\",%c" + "\"Surtension sur une des phases\": \"%ssurtension\",%c" + "\"Dépassement de la puissance de référence\": \"%s\",%c" + "\"Fonctionnement producteur/consommateur\": \"%s\",%c" + "\"Sens de l'énergie active\": \"énergie active %s\",%c" + "\"Tarif en cours sur le contrat fourniture\": \"énergie ventilée sur Index %d\",%c" + "\"Tarif en cours sur le contrat distributeur\": \"énergie ventilée sur Index %d\",%c" + "\"Mode dégradé de l'horloge\": \"horloge %s\",%c" + "\"État de la sortie télé-information\": \"mode %s\",%c" + "\"État de la sortie communication Euridis\": \"%s\",%c" + "\"Statut du CPL\": \"%s\",%c" + "\"Synchronisation CPL\": \"compteur%s synchronisé\",%c" + "\"Couleur du jour pour le contrat historique tempo\": \"%s\",%c" + "\"Couleur du lendemain pour le contrat historique tempo\": \"%s\",%c" + "\"Préavis pointes mobiles\": \"%s en cours\",%c" + "\"Pointe mobile\": \"%s en cours\" }%c" + , + of[d & 0x01], sep, + coupure[(d>>1) & 0x07], sep, + of[(d>>4) & 0x01], sep, + (d>>6) & 0x01 ? "" : "pas de ", sep, + (d>>7) & 0x01 ? "dépassement en cours" : "pas de dépassement", sep, + (d>>8) & 0x01 ? "producteur" : "consommateur", sep, + (d>>9) & 0x01 ? "négative" : "positive", sep, + ((d>>10) & 0x0F) + 1, sep, + ((d>>14) & 0x07) + 1, sep, + (d>>16) & 0x01 ? "en mode dégradée" : "correcte", sep, + (d>>17) & 0x01 ? "standard" : "historique", sep, + euridis[(d>>19) & 0x03], sep, + cpl[(d>>21) & 0x03], sep, + (d>>23) & 0x01 ? "" : " non", sep, + tempo[(d>>24) & 0x03], sep, + tempo[(d>>26) & 0x03], sep, + pm[(d>>28) & 0x03], sep, + pm[(d>>30) & 0x03], sep + ); +} + +void print_field(struct tic_field *field) +{ + const char fdictout[] = "%c \"%.8s\": { \"data\": "; + const char flistout[] = "%c{ \"label\": \"%.8s\", \"data\": "; + const char *format; + uint8_t type; + + // filters + if (tp.framecount || + ((tp.optflags & OPT_MASKZEROES) && (T_STRING != (field->etiq.unittype & 0xF0)) && (0 == field->data.i)) || + (etiq_en && !etiq_en[field->etiq.tok])) + return; + + format = (tp.optflags & OPT_DICTOUT) ? fdictout : flistout; + + printf(format, tp.fdelim, field->etiq.label); + switch (field->etiq.unittype & 0x0F) { + case U_SANS: + type = field->etiq.unittype & 0xF0; + if (T_STRING == type) { + printf("\"%s\"", field->data.s ? field->data.s : ""); + break; + } + else if ((T_HEX == type) && (tp.optflags & OPT_PARSESTGE)) { + // XXX abuse the fact that STGE is the only U_SANS|T_HEX field + print_stge_data(field->data.i); + break; + } + // fallthrough + default: + printf("%d", field->data.i); + break; + } + + if (field->horodate) { + if (tp.optflags & OPT_LONGDATE) { + const char *o, *d = field->horodate; + switch (d[0]) { + default: + case ' ': + o = ""; + break; + case 'E': + case 'e': + o = "+02:00"; + break; + case 'H': + case 'h': + o = "+01:00"; + break; + } + printf(", \"horodate\": \"20%.2s-%.2s-%.2sT%.2s:%.2s:%.2s%s\"", d+1, d+3, d+5, d+7, d+9, d+11, o); + } + else + printf(", \"horodate\": \"%s\"", field->horodate); + } + + if (tp.optflags & OPT_DESCFORM) + printf(", \"desc\": \"%s\", \"unit\": \"%s\"", field->etiq.desc, tic_units[(field->etiq.unittype & 0x0F)]); + + if (tp.idtag) + printf(", \"id\": \"%s\"", tp.idtag); + + putchar('}'); + if (tp.optflags & OPT_CRFIELD) + putchar('\n'); + + tp.fdelim = ','; +} + +void free_field(struct tic_field *field) +{ + free(field->horodate); + switch ((field->etiq.unittype & 0xF0)) { + case T_STRING: + free(field->data.s); + break; + default: + break; + } +} + +void frame_sep(void) +{ + if (!tp.framecount--) { + tp.framecount = tp.skipframes; + if (tp.optflags & OPT_DICTOUT) + printf("%c \"_tvalide\": %d", tp.fdelim, !tp.ferr); + printf ("%c\n%c", tp.framedelims[1], tp.framedelims[0]); + } + tp.fdelim = ' '; + tp.ferr = 0; +} + +void frame_err(void) +{ + tp.ferr = 1; +} + +#ifndef BAREBUILD +static void usage(void) +{ + printf( BINNAME " version " TIC2JSON_VER "\n" + "usage: " BINNAME " [-dhlnruz] [-e fichier] [-i id] [-s N]\n" + " -d\t\t" "Émet les trames sous forme de dictionaire plutà ́t que de liste\n" + " -e fichier\t" "Utilise pour configurer le filtre d'étiquettes\n" + " -h\t\t" "Montre ce message d'aide\n" + " -i id\t\t" "Ajoute une balise \"id\" avec la valeur à chaque groupe\n" + " -l\t\t" "Ajoute les descriptions longues et les unitées de chaque groupe\n" + " -n\t\t" "Insà ̈re une nouvelle ligne aprà ̈s chaque groupe\n" + " -r\t\t" "Interprà ̈te les horodates en format RFC3339\n" + " -s N\t\t" "Émet une trame toutes les reçues\n" + " -u\t\t" "Décode le registre de statut sous forme de dictionnaire\n" + " -z\t\t" "Masque les groupes numériques à zéro\n" + "\n" + "Note: le fichier de configuration du filtre d'étiquettes doit commencer par une ligne comportant\n" + "uniquement la séquence de caractà ̈res suivante: `#ticfilter` (sans les apostrophes), suivi à partir de\n" + "la ligne suivante d'un nombre quelconque d'étiquettes TIC séparées par du blanc (espace, nouvelle ligne, etc).\n" + "Seuls les groupes dont les étiquettes sont ainsi listées seront alors émis par le programme.\n" + ); +} + +void parse_config(const char *filename); +#endif /* !BAREBUILD */ + +int main(int argc, char **argv) +{ + int ch; + + filter_mode = 0; + etiq_en = NULL; + memset(&tp, 0, sizeof(tp)); + + tp.framedelims[0] = '['; tp.framedelims[1] = ']'; + tp.fdelim = ' '; + +#ifndef BAREBUILD + while ((ch = getopt(argc, argv, "de:hi:lnrs:uz")) != -1) { + switch (ch) { + case 'd': + tp.optflags |= OPT_DICTOUT; + tp.framedelims[0] = '{'; tp.framedelims[1] = '}'; + break; + case 'e': + parse_config(optarg); + break; + case 'h': + usage(); + return 0; + case 'i': + tp.idtag = optarg; + break; + case 'l': + tp.optflags |= OPT_DESCFORM; + break; + case 'n': + tp.optflags |= OPT_CRFIELD; + break; + case 'r': + tp.optflags |= OPT_LONGDATE; + break; + case 's': + tp.skipframes = (unsigned int)strtol(optarg, NULL, 10); + break; + case 'u': + tp.optflags |= OPT_PARSESTGE; + break; + case 'z': + tp.optflags |= OPT_MASKZEROES; + break; + default: + usage(); + exit(-1); + } + } + argc -= optind; + argv += optind; +#endif /* !BAREBUILD */ + + putchar(tp.framedelims[0]); + yyparse(); + printf("%c\n", tp.framedelims[1]); + yylex_destroy(); + + free(etiq_en); + return 0; +} diff --git a/tic2json.h b/tic2json.h index 588cf47..a45ff15 100644 --- a/tic2json.h +++ b/tic2json.h @@ -9,6 +9,7 @@ #ifndef tic2json_h #define tic2json_h +#include #include #ifdef BAREBUILD @@ -54,4 +55,10 @@ struct tic_field { char *horodate; }; +void make_field(struct tic_field *field, const struct tic_etiquette *etiq, char *horodate, char *data); +void print_field(struct tic_field *field); +void free_field(struct tic_field *field); +void frame_sep(void); +void frame_err(void); + #endif /* tic2json_h */ diff --git a/tic2json.y b/tic2json.y index 311ae1a..31089f4 100644 --- a/tic2json.y +++ b/tic2json.y @@ -1,36 +1,25 @@ // // tic2json.y -// A parser to turn ENEDIS TIC data into pure JSON +// A parser for ENEDIS TIC version 02 protocol // // (C) 2021 Thibaut VARENE // License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html // -/* - * Outputs as JSON a series of frames formatted as a list of fields or a dictionary. - * - for list mode, fields are { "label": "xxx", "data": "xxx", horodate: "xxx", "desc": "xxx", "unit": "xxx" } - * - for dict mode, the keys are the label, followed by { "data": "xxx", "horodate": "xxx", "desc": "xxx", "unit": "xxx" } - * with horodate optional, unit and data optional and possibly empty and data being either quoted string or number. +/** + * @file + * This parser implements a complete grammar that supports TIC version 02 + * as specified in Enedis-NOI-CPT_54E.pdf version 3. * - * Data errors can result in some/all datasets being omitted in the output frame (e.g. invalid datasets or datasets - * that did not pass checksum are not emitted): the JSON root object can then be empty but is still emitted. - * In dictionary mode the parser will report the frame status as "_tvalide" ("trame valide") followed by either 1 - * for a valid frame or 0 for a frame containing errors (including dataset errors). - * - * Output JSON is guaranteed to always be valid for each frame. By default only frames are separated with newlines. - * This parser implements a complete grammar that supports TIC version 02 as specified in Enedis-NOI-CPT_54E.pdf version 3. - * - * This parser does not allocate memory, except if a filter configuration is used in which case the etiq_en array will - * be allocated (it's a few hundred bytes). - * A left-recursion grammar has been implemented to keep the memory usage to the bare minimum as well. As a tradeoff, - * valid datasets are always emitted regardless of the overall status of the containing frame. + * This parser does not allocate memory, except if a filter configuration is used in + * which case the etiq_en array will be allocated (it's a few hundred bytes). + * A left-recursion grammar has been implemented to keep the memory usage to the bare + * minimum as well. As a tradeoff, valid datasets are always emitted regardless of the + * overall status of the containing frame. */ %{ -#include #include -#include -#include #include "tic2json.h" int yylex(); @@ -38,154 +27,8 @@ int yylex_destroy(); extern FILE *yyin; static void yyerror(const char *); -int filter_mode; - -static const char *idtag; -static char framedelims[2]; -static char fdelim; -static int optflags; -static unsigned int skipframes, framecount; -static uint8_t *etiq_en; // type: < 255 tokens. This could be made a bit field if memory is a concern -static char ferr; - -enum { - OPT_MASKZEROES = 0x01, - OPT_CRFIELD = 0x02, - OPT_DESCFORM = 0x04, - OPT_DICTOUT = 0x08, - OPT_LONGDATE = 0x10, - OPT_PARSESTGE = 0x20, -}; - -static const char * tic_units[] = { - [U_SANS] = "", - [U_WH] = "Wh", - [U_VARH] = "VArh", - [U_A] = "A", - [U_V] = "V", - [U_KVA] = "kVA", - [U_VA] = "VA", - [U_W] = "W", -}; - -static void make_field(struct tic_field *field, const struct tic_etiquette *etiq, char *horodate, char *data) -{ - // args come from the bison stack - int base; - - field->horodate = horodate; - memcpy(&field->etiq, etiq, sizeof(field->etiq)); - - switch ((etiq->unittype & 0xF0)) { - case T_STRING: - field->data.s = data; - return; - case T_HEX: - base = 16; - break; - default: - base = 10; - break; - } - field->data.i = (int)strtol(data, NULL, base); - free(data); -} - -static void print_stge_data(int data); - -static void print_field(struct tic_field *field) -{ - const char fdictout[] = "%c \"%.8s\": { \"data\": "; - const char flistout[] = "%c{ \"label\": \"%.8s\", \"data\": "; - const char *format; - uint8_t type; - - // filters - if (framecount || - ((optflags & OPT_MASKZEROES) && (T_STRING != (field->etiq.unittype & 0xF0)) && (0 == field->data.i)) || - (etiq_en && !etiq_en[field->etiq.tok])) - return; - - format = (optflags & OPT_DICTOUT) ? fdictout : flistout; - - printf(format, fdelim, field->etiq.label); - switch (field->etiq.unittype & 0x0F) { - case U_SANS: - type = field->etiq.unittype & 0xF0; - if (T_STRING == type) { - printf("\"%s\"", field->data.s ? field->data.s : ""); - break; - } - else if ((T_HEX == type) && (optflags & OPT_PARSESTGE)) { - // XXX abuse the fact that STGE is the only U_SANS|T_HEX field - print_stge_data(field->data.i); - break; - } - // fallthrough - default: - printf("%d", field->data.i); - break; - } - - if (field->horodate) { - if (optflags & OPT_LONGDATE) { - const char *o, *d = field->horodate; - switch (d[0]) { - default: - case ' ': - o = ""; - break; - case 'E': - case 'e': - o = "+02:00"; - break; - case 'H': - case 'h': - o = "+01:00"; - break; - } - printf(", \"horodate\": \"20%.2s-%.2s-%.2sT%.2s:%.2s:%.2s%s\"", d+1, d+3, d+5, d+7, d+9, d+11, o); - } - else - printf(", \"horodate\": \"%s\"", field->horodate); - } - - if (optflags & OPT_DESCFORM) - printf(", \"desc\": \"%s\", \"unit\": \"%s\"", field->etiq.desc, tic_units[(field->etiq.unittype & 0x0F)]); - - if (idtag) - printf(", \"id\": \"%s\"", idtag); - - putchar('}'); - if (optflags & OPT_CRFIELD) - putchar('\n'); - - fdelim = ','; -} - -static void free_field(struct tic_field *field) -{ - free(field->horodate); - switch ((field->etiq.unittype & 0xF0)) { - case T_STRING: - free(field->data.s); - break; - default: - break; - } -} - -static void frame_sep(void) -{ - if (!framecount--) { - framecount = skipframes; - if (optflags & OPT_DICTOUT) - printf("%c \"_tvalide\": %d", fdelim, !ferr); - printf ("%c\n%c", framedelims[1], framedelims[0]); - } - fdelim=' '; - ferr=0; -} +extern int filter_mode; +extern uint8_t *etiq_en; %} @@ -248,18 +91,18 @@ frames: frame: TOK_STX datasets TOK_ETX { frame_sep(); } - | error TOK_ETX { ferr=1; frame_sep(); pr_err("frame error\n"); yyerrok; } + | error TOK_ETX { frame_err(); frame_sep(); pr_err("frame error\n"); yyerrok; } ; datasets: - error { ferr=1; pr_err("dataset error\n"); } + error { frame_err(); pr_err("dataset error\n"); } | dataset | datasets dataset ; dataset: FIELD_START field FIELD_OK { print_field(&2ドル); free_field(&2ドル); } - | FIELD_START field FIELD_KO { ferr=1; pr_err("dataset invalid checksum\n"); free_field(&2ドル); } + | FIELD_START field FIELD_KO { frame_err(); pr_err("dataset invalid checksum\n"); free_field(&2ドル); } | FIELD_START error FIELD_OK { /*not a frame error*/ pr_err("unrecognized dataset\n"); yyerrok; } ; @@ -356,121 +199,8 @@ etiquette_nodate: %% -static void print_stge_data(int data) -{ - const char sep = (optflags & OPT_CRFIELD) ? '\n' : ' '; - uint32_t d = (uint32_t)data; - - - const char *const of[] = { - "fermé", - "ouvert", - }; - - const char *const coupure[] = { - "fermé", - "ouvert sur surpuissance", - "ouvert sur surtension", - "ouvert sur délestage", - "ouvert sur ordre CPL ou Euridis", - "ouvert sur une surchauffe avec une valeur de courant supérieure au courant de commutation maximal", - "ouvert sur une surchauffe avec une valeur de courant inférieure au courant de commutation maximal", - NULL, - }; - - const char *const euridis[] = { - "désactivée", - "activée sans sécurité", - NULL, - "activée avec sécurité", - }; - - const char *const cpl[] = { - "New/Unlock", - "New/Lock", - "Registered", - NULL, - }; - - const char *const tempo[] = { - "Pas d'annonce", - "Bleu", - "Blanc", - "Rouge", - }; - - const char *const pm[] = { - "pas", - "PM1", - "PM2", - "PM3", - }; - - printf("{ " - "\"Contact sec\": \"%s\",%c" - "\"Organe de coupure\": \"%s\",%c" - "\"État du cache-bornes distributeur\": \"%s\",%c" - "\"Surtension sur une des phases\": \"%ssurtension\",%c" - "\"Dépassement de la puissance de référence\": \"%s\",%c" - "\"Fonctionnement producteur/consommateur\": \"%s\",%c" - "\"Sens de l'énergie active\": \"énergie active %s\",%c" - "\"Tarif en cours sur le contrat fourniture\": \"énergie ventilée sur Index %d\",%c" - "\"Tarif en cours sur le contrat distributeur\": \"énergie ventilée sur Index %d\",%c" - "\"Mode dégradé de l'horloge\": \"horloge %s\",%c" - "\"État de la sortie télé-information\": \"mode %s\",%c" - "\"État de la sortie communication Euridis\": \"%s\",%c" - "\"Statut du CPL\": \"%s\",%c" - "\"Synchronisation CPL\": \"compteur%s synchronisé\",%c" - "\"Couleur du jour pour le contrat historique tempo\": \"%s\",%c" - "\"Couleur du lendemain pour le contrat historique tempo\": \"%s\",%c" - "\"Préavis pointes mobiles\": \"%s en cours\",%c" - "\"Pointe mobile\": \"%s en cours\" }%c" - , - of[d & 0x01], sep, - coupure[(d>>1) & 0x07], sep, - of[(d>>4) & 0x01], sep, - (d>>6) & 0x01 ? "" : "pas de ", sep, - (d>>7) & 0x01 ? "dépassement en cours" : "pas de dépassement", sep, - (d>>8) & 0x01 ? "producteur" : "consommateur", sep, - (d>>9) & 0x01 ? "négative" : "positive", sep, - ((d>>10) & 0x0F) + 1, sep, - ((d>>14) & 0x07) + 1, sep, - (d>>16) & 0x01 ? "en mode dégradée" : "correcte", sep, - (d>>17) & 0x01 ? "standard" : "historique", sep, - euridis[(d>>19) & 0x03], sep, - cpl[(d>>21) & 0x03], sep, - (d>>23) & 0x01 ? "" : " non", sep, - tempo[(d>>24) & 0x03], sep, - tempo[(d>>26) & 0x03], sep, - pm[(d>>28) & 0x03], sep, - pm[(d>>30) & 0x03], sep - ); -} - #ifndef BAREBUILD -static void usage(void) -{ - printf( BINNAME " version " TIC2JSON_VER "\n" - "usage: " BINNAME " [-dhlnruz] [-e fichier] [-i id] [-s N]\n" - " -d\t\t" "Émet les trames sous forme de dictionaire plutà ́t que de liste\n" - " -e fichier\t" "Utilise pour configurer le filtre d'étiquettes\n" - " -h\t\t" "Montre ce message d'aide\n" - " -i id\t\t" "Ajoute une balise \"id\" avec la valeur à chaque groupe\n" - " -l\t\t" "Ajoute les descriptions longues et les unitées de chaque groupe\n" - " -n\t\t" "Insà ̈re une nouvelle ligne aprà ̈s chaque groupe\n" - " -r\t\t" "Interprà ̈te les horodates en format RFC3339\n" - " -s N\t\t" "Émet une trame toutes les reçues\n" - " -u\t\t" "Décode le registre de statut sous forme de dictionnaire\n" - " -z\t\t" "Masque les groupes numériques à zéro\n" - "\n" - "Note: le fichier de configuration du filtre d'étiquettes doit commencer par une ligne comportant\n" - "uniquement la séquence de caractà ̈res suivante: `#ticfilter` (sans les apostrophes), suivi à partir de\n" - "la ligne suivante d'un nombre quelconque d'étiquettes TIC séparées par du blanc (espace, nouvelle ligne, etc).\n" - "Seuls les groupes dont les étiquettes sont ainsi listées seront alors émis par le programme.\n" - ); -} - -static void parse_config(const char *filename) +void parse_config(const char *filename) { if (!(yyin = fopen(filename, "r"))) { perror(filename); @@ -494,71 +224,6 @@ static void parse_config(const char *filename) } #endif /* !BAREBUILD */ -int main(int argc, char **argv) -{ - int ch; - - idtag = NULL; - framedelims[0] = '['; framedelims[1] = ']'; - fdelim = ' '; - optflags = 0; - skipframes = framecount = 0; - filter_mode = 0; - etiq_en = NULL; - ferr = 0; - -#ifndef BAREBUILD - while ((ch = getopt(argc, argv, "de:hi:lnrs:uz")) != -1) { - switch (ch) { - case 'd': - optflags |= OPT_DICTOUT; - framedelims[0] = '{'; framedelims[1] = '}'; - break; - case 'e': - parse_config(optarg); - break; - case 'h': - usage(); - return 0; - case 'i': - idtag = optarg; - break; - case 'l': - optflags |= OPT_DESCFORM; - break; - case 'n': - optflags |= OPT_CRFIELD; - break; - case 'r': - optflags |= OPT_LONGDATE; - break; - case 's': - skipframes = (unsigned int)strtol(optarg, NULL, 10); - break; - case 'u': - optflags |= OPT_PARSESTGE; - break; - case 'z': - optflags |= OPT_MASKZEROES; - break; - default: - usage(); - exit(-1); - } - } - argc -= optind; - argv += optind; -#endif /* !BAREBUILD */ - - putchar(framedelims[0]); - yyparse(); - printf("%c\n", framedelims[1]); - yylex_destroy(); - - free(etiq_en); - return 0; -} - static void yyerror(const char * s) { } -- 2.39.5

AltStyle によって変換されたページ (->オリジナル) /