Parse the header in a single function
authorGabriel F. T. Gomes <gabriel@gftg.com.br>
Sun, 13 Mar 2016 22:16:22 +0000 (19:16 -0300)
committerThadeu Lima de Souza Cascardo <cascardo@cascardo.eti.br>
Fri, 18 Mar 2016 10:27:36 +0000 (07:27 -0300)
The header of the declaration files is composed of several fields. Each year
this header changes, however most of the fields are the same throughout the
years supported by rnetclient. This patch refactors the code so that the
parsing of the headers fields is done in a single function.

Changes since v1:

* Merge decfile_parse_header and parse_header_common into the former.
* Undeleted some assertions:
- (p - buffer != RNET_HEADER_HEAD_COMMON)
- (p - tail != RNET_HEADER_TAIL_COMMON)
* Changed some comments regarding fields orlder than 2013.
* Assert exerc is a valid year and sooner.
* Moved last checks out of the else statement.

decfile.c

index bc2a2ea..3aab382 100644 (file)
--- a/decfile.c
+++ b/decfile.c
@@ -74,117 +74,8 @@ static void decfile_release_lines(struct rnet_decfile *decfile)
 }
 
 static char * get_header(struct rnet_decfile *decfile);
-static int parse_header_common(struct pmhash *hash, char **buffer);
-static int parse_header_2013(struct pmhash *hash, char *buffer);
-static int parse_header_2014(struct pmhash *hash, char *buffer);
-static int parse_header_2015(struct pmhash *hash, char *buffer);
 static int decfile_parse_file(struct rnet_decfile *decfile);
 
-static int decfile_parse_header(struct rnet_decfile *decfile)
-{
-       char *buffer;
-       int r;
-       char *p;
-
-       p = buffer = get_header(decfile);
-       if (!buffer)
-               return -EINVAL;
-
-       r = parse_header_common(decfile->header, &p);
-       if (r)
-               return r;
-
-       switch (strlen(buffer)) {
-       case RNET_HEADER_SIZE_2013:
-               return parse_header_2013(decfile->header, p);
-       case RNET_HEADER_SIZE_2014:
-               return parse_header_2014(decfile->header, p);
-       case RNET_HEADER_SIZE_2015:
-               return parse_header_2015(decfile->header, p);
-       default:
-               fprintf(stderr, "Unknown file version, but proceeding anyway.\n");
-       }
-       return 0;
-}
-
-static int decfile_parse(struct rnet_decfile *decfile)
-{
-       char *buffer = NULL;
-       size_t len = 0;
-       int r;
-       while ((r = getline(&buffer, &len, decfile->file)) > 0) {
-               r = append_line(decfile, buffer);
-               if (r) {
-                       free(buffer);
-                       goto out;
-               }
-               buffer = NULL;
-               len = 0;
-       }
-       if (!(r = decfile_parse_header(decfile)) && !(r = decfile_parse_file(decfile)))
-               return 0;
-out:
-       decfile_release_lines(decfile);
-       return r;
-}
-
-struct rnet_decfile * rnet_decfile_open(char *filename)
-{
-       struct rnet_decfile *decfile;
-       int r = -ENOMEM;
-       decfile = malloc(sizeof(*decfile));
-       if (!decfile)
-               return NULL;
-       decfile->header = pmhash_new();
-       if (!decfile->header)
-               goto out_header;
-       decfile->message = rnet_message_new();
-       if (!decfile->message)
-               goto out_message;
-       decfile->filename = strdup(filename);
-       if (!decfile->filename)
-               goto out_filename;
-       decfile->file = fopen(filename, "r");
-       if (!decfile->file)
-               goto out_file;
-       decfile->lines_len = 0;
-       decfile->lines = NULL;
-       if ((r = decfile_parse(decfile)))
-               goto out_parse;
-       return decfile;
-out_parse:
-       fclose(decfile->file);
-out_file:
-       free(decfile->filename);
-out_filename:
-       rnet_message_del(decfile->message);
-out_message:
-       pmhash_del(decfile->header);
-out_header:
-       free(decfile);
-       errno = -r;
-       return NULL;
-}
-
-void rnet_decfile_close(struct rnet_decfile *decfile)
-{
-       decfile_release_lines(decfile);
-       fclose(decfile->file);
-       free(decfile->filename);
-       free(decfile);
-}
-
-static char * get_header(struct rnet_decfile *decfile)
-{
-       int i;
-       for (i = 0; i < decfile->lines_len; i++) {
-               if (!strncmp(decfile->lines[i], "IRPF", 4)) {
-                       return decfile->lines[i];
-               }
-       }
-       return NULL;
-}
-
 #define parse(field, sz) \
        r = -ENOMEM; \
        val = malloc(sz + 1); \
@@ -199,13 +90,24 @@ static char * get_header(struct rnet_decfile *decfile)
        if (pmhash_add(&hash, key, val)) \
                goto out_add;
 
-static int parse_header_common(struct pmhash *hash, char **buffer)
+static int decfile_parse_header(struct rnet_decfile *decfile)
 {
+       char *buffer;
        int r;
-       char *p = *buffer;
+       int exerc;
+       char *p;
+       char *tail;
        char *key;
        char *val;
+       struct pmhash *hash;
+
+       p = buffer = get_header(decfile);
+       if (!buffer)
+               return -EINVAL;
+
+       hash = decfile->header;
 
+       /* Common header fields. Most of these are used by rnet_encode. */
        parse("sistema", 8);
        parse("exerc", 4);
        parse("ano", 4);
@@ -219,37 +121,28 @@ static int parse_header_common(struct pmhash *hash, char **buffer)
        parse("uf", 2);
        parse("hash", 10);
 
-       if (p - *buffer != RNET_HEADER_HEAD_COMMON) {
-               fprintf(stderr, "RNET_HEADER_HEAD_COMMON in decfile.h needs to be adjusted to %ti\n", p - *buffer);
+       /* Assert the size of the common header matches the expectation. */
+       if (p - buffer != RNET_HEADER_HEAD_COMMON) {
+               fprintf(stderr, "RNET_HEADER_HEAD_COMMON in decfile.h needs to be adjusted to %ti\n", p - buffer);
                goto out_val;
        }
 
-       *buffer = p;
+       /* Retrieve exerc from the hash table. */
+       exerc = atoi(pmhash_get(hash, "exerc"));
 
-       return 0;
-out_add:
-       free(key);
-out_key:
-       free(val);
-out_val:
-       return r;
-}
-
-static int parse_header_2015(struct pmhash *hash, char *buffer)
-{
-       int r;
-       char *p = buffer;
-       char *key;
-       char *val;
-       char *tail;
-       char *exerc;
-
-       exerc = pmhash_get(hash, "exerc");
-       if (strcmp(exerc, "2015")) {
-               r = -EINVAL;
+       /* Assert exerc is no less than the minimum supported version */
+       if (exerc < 2013) {
+               fprintf(stderr, "This software does not support declarations older than 2013.\n");
                goto out_val;
        }
 
+       /* Check for tested versions. */
+       if (exerc > 2015) {
+               fprintf(stderr, "Unknown file version, but proceeding anyway.\n");
+               return 0;
+       }
+
+       /* These fields exist at least since 2013. */
        parse("in_cert", 1);
        parse("dt_nasc", 8);
        parse("in_comp", 1);
@@ -283,8 +176,14 @@ static int parse_header_2015(struct pmhash *hash, char *buffer)
        parse("cpf_rra2", 11);
        parse("trib_3rra", 1);
        parse("cpf_rra3", 11);
-       parse("trib_4rra", 1);
-       parse("cpf_rra4", 11);
+
+       /* Fields added in 2014. */
+       if (exerc >= 2014) {
+               parse("trib_4rra", 1);
+               parse("cpf_rra4", 11);
+       }
+
+       /* These fields exist at least since 2013. */
        parse("vr_doacao", 13);
        parse("cnpj1", 14);
        parse("cnpj2", 14);
@@ -308,7 +207,17 @@ static int parse_header_2015(struct pmhash *hash, char *buffer)
        parse("cpf_invent", 11);
        parse("municipio", 40);
        parse("contribuinte", 60);
-       parse("filler", 11);
+
+       /* The contents of this field until 2014 (cpf_empregada) were moved to
+        * the end of the header in 2015 (cpfdomestic@). This field has then
+        * been converted into a filler field. */
+       if (exerc <= 2014) {
+               parse("cpf_empregada", 11);
+       } else {
+               parse("filler", 11);
+       }
+
+       /* These fields exist at least since 2013. */
        parse("mac", 12);
        parse("data_nao_residente", 8);
        parse("cpf_procurador", 11);
@@ -319,154 +228,43 @@ static int parse_header_2015(struct pmhash *hash, char *buffer)
        parse("vr_totisentos", 13);
        parse("vr_totexclusivo", 13);
        parse("vr_totpagamentos", 13);
-       parse("nr_conta", 13);
-       parse("nr_dv_conta", 2);
-       parse("in_dv_conta", 1);
-
-       parse("codnaturezaocup", 2);
-       parse("cpfdomestic@", 11);
-       parse("nitdomestic@", 11);
-       parse("cpfdomestic@2", 11);
-       parse("nitdomestic@2", 11);
-       parse("cpfdomestic@3", 11);
-       parse("nitdomestic@3", 11);
-       parse("deciniciada", 1);
-       parse("utilpgd", 1);
-       parse("utilapp", 1);
-       parse("utilonline", 1);
-       parse("utilrascunho", 1);
-       parse("utilprepreenchida", 1);
-       parse("utilfontes", 1);
-       parse("utilplanosaude", 1);
-       parse("utilrecuperar", 1);
-       parse("dectransmitida", 1);
-       tail = p;
 
-       parse("versaotestpgd", 3);
-       parse("controle", 10);
+       /* End of header in 2013. */
 
-       if (*p++ != '\r') {
-               fprintf(stderr,
-                       "missing CR at the %tith header character\n",
-                       p - buffer);
-               goto out_val;
-       } else if (*p++ != '\n') {
-               fprintf(stderr,
-                       "missing LF at the %tith header character\n",
-                       p - buffer);
-               goto out_val;
-       } else if (*p != 0) {
-               fprintf(stderr,
-                       "missing NUL at the %tith header character\n",
-                       p - buffer);
-               goto out_val;
-       } else if (p - buffer != RNET_HEADER_SIZE_2015) {
-               fprintf(stderr, "RNET_HEADER_SIZE_2015 in decfile.h needs to be adjusted to %ti,\nor parse_header in decfile.c needs updating\n", p - buffer);
-               goto out_val;
-       } else if (p - tail != RNET_HEADER_TAIL_COMMON) {
-               fprintf(stderr, "RNET_HEADER_TAIL_COMMON in decfile.h needs to be adjusted to %ti\n", p - tail);
-               goto out_val;
+       /* Fields added in 2014. */
+       if (exerc >= 2014) {
+               parse("nr_conta", 13);
+               parse("nr_dv_conta", 2);
+               parse("in_dv_conta", 1);
        }
 
-       return 0;
-out_add:
-       free(key);
-out_key:
-       free(val);
-out_val:
-       return r;
-}
-
-static int parse_header_2014(struct pmhash *hash, char *buffer)
-{
-       int r;
-       char *p = buffer;
-       char *key;
-       char *val;
-       char *tail;
-       char *exerc;
-
-       exerc = pmhash_get(hash, "exerc");
-       if (strcmp(exerc, "2014")) {
-               r = -EINVAL;
-               goto out_val;
+       /* End of header in 2014. */
+
+       /* Fields added in 2015. */
+       if (exerc >= 2015) {
+               parse("codnaturezaocup", 2);
+               parse("cpfdomestic@", 11);
+               parse("nitdomestic@", 11);
+               parse("cpfdomestic@2", 11);
+               parse("nitdomestic@2", 11);
+               parse("cpfdomestic@3", 11);
+               parse("nitdomestic@3", 11);
+               parse("deciniciada", 1);
+               parse("utilpgd", 1);
+               parse("utilapp", 1);
+               parse("utilonline", 1);
+               parse("utilrascunho", 1);
+               parse("utilprepreenchida", 1);
+               parse("utilfontes", 1);
+               parse("utilplanosaude", 1);
+               parse("utilrecuperar", 1);
+               parse("dectransmitida", 1);
        }
 
-       parse("in_cert", 1);
-       parse("dt_nasc", 8);
-       parse("in_comp", 1);
-       parse("in_res", 1);
-       parse("in_gerada", 1);
-       parse("nr_recibo_anterior", 10);
-       parse("in_pgd", 1);
-       parse("so", 14);
-       parse("versao_so", 7);
-       parse("jvm", 9);
-       parse("nr_recibo", 10);
-       parse("municipio", 4);
-       parse("conjuge", 11);
-       parse("obrig", 1);
-       parse("impdevido", 13);
-       parse("nr_recibo", 10);
-       parse("in_seg", 1);
-       parse("imppago", 2);
-       parse("impant", 1);
-       parse("mudend", 1);
-       parse("cep", 8);
-       parse("debito", 1);
-       parse("banco", 3);
-       parse("agencia", 4);
-       parse("filler", 1);
-       parse("data_julgado", 8);
-       parse("imppagar", 13);
-       parse("tribfonte", 1);
-       parse("cpfrra", 11);
-       parse("trib_rra", 1);
-       parse("cpf_rra2", 11);
-       parse("trib_3rra", 1);
-       parse("cpf_rra3", 11);
-       parse("trib_4rra", 1);
-       parse("cpf_rra4", 11);
-       parse("vr_doacao", 13);
-       parse("cnpj1", 14);
-       parse("cnpj2", 14);
-       parse("cnpj3", 14);
-       parse("cnpj4", 14);
-       parse("cpf_dep1", 11);
-       parse("dnas_dep1", 8);
-       parse("cpf_dep2", 11);
-       parse("dnas_dep2", 8);
-       parse("cpf_dep3", 11);
-       parse("dnas_dep3", 8);
-       parse("cpf_dep4", 11);
-       parse("dnas_dep4", 8);
-       parse("cpf_dep5", 11);
-       parse("dnas_dep5", 8);
-       parse("cpf_dep6", 11);
-       parse("dnas_dep6", 8);
-       parse("cnpj_med1", 14);
-       parse("cnpj_med2", 14);
-       parse("cpf_alim", 11);
-       parse("cpf_invent", 11);
-       parse("municipio", 40);
-       parse("contribuinte", 60);
-       parse("cpf_empregada", 11);
-       parse("hashcode", 12);
-       parse("data_nao_residente", 8);
-       parse("cpf_procurador", 11);
-       parse("obrigatoriedade", 3);
-       parse("rendtrib", 13);
-       parse("cnpj_prev", 14);
-       parse("cnpj_prev2", 14);
-       parse("vr_totisentos", 13);
-       parse("vr_totexclusivo", 13);
-       parse("vr_totpagamentos", 13);
-       parse("nr_conta", 13);
-       parse("nr_dv_conta", 2);
-       parse("in_dv_conta", 1);
+       /* End of header in 2015. */
 
+       /* Tail fields, which exist at least since 2013. */
        tail = p;
-
        parse("versaotestpgd", 3);
        parse("controle", 10);
 
@@ -485,14 +283,40 @@ static int parse_header_2014(struct pmhash *hash, char *buffer)
                        "missing NUL at the %tith header character\n",
                        p - buffer);
                goto out_val;
-       } else if (p - buffer != RNET_HEADER_SIZE_2014) {
-               fprintf(stderr, "RNET_HEADER_SIZE_2014 in decfile.h needs to be adjusted to %ti,\nor parse_header in decfile.c needs updating\n", p - buffer);
-               goto out_val;
        } else if (p - tail != RNET_HEADER_TAIL_COMMON) {
                fprintf(stderr, "RNET_HEADER_TAIL_COMMON in decfile.h needs to be adjusted to %ti\n", p - tail);
                goto out_val;
        }
 
+       /* Verify header size */
+       switch (exerc) {
+               case 2013:
+                       if (p - buffer != RNET_HEADER_SIZE_2013) {
+                               fprintf(stderr, "RNET_HEADER_SIZE_2013 in decfile.h needs to be adjusted to %ti,\nor parse_header in decfile.c needs updating\n", p - buffer);
+                               goto out_val;
+                       }
+                       break;
+               case 2014:
+                       if (p - buffer != RNET_HEADER_SIZE_2014) {
+                               fprintf(stderr, "RNET_HEADER_SIZE_2014 in decfile.h needs to be adjusted to %ti,\nor parse_header in decfile.c needs updating\n", p - buffer);
+                               goto out_val;
+                       }
+                       break;
+               case 2015:
+                       if (p - buffer != RNET_HEADER_SIZE_2015) {
+                               fprintf(stderr, "RNET_HEADER_SIZE_2015 in decfile.h needs to be adjusted to %ti,\nor parse_header in decfile.c needs updating\n", p - buffer);
+                               goto out_val;
+                       }
+                       break;
+               default:
+                       /* This case should never be reached even for later
+                          years, because later years should not be parsed
+                          further than the common header. */
+                       fprintf(stderr, "Error while processing header. Unrecognized version\n");
+                       goto out_val;
+                       break;
+       }
+
        return 0;
 out_add:
        free(key);
@@ -502,128 +326,86 @@ out_val:
        return r;
 }
 
-static int parse_header_2013(struct pmhash *hash, char *buffer)
+#undef parse
+
+static int decfile_parse(struct rnet_decfile *decfile)
 {
+       char *buffer = NULL;
+       size_t len = 0;
        int r;
-       char *p = buffer;
-       char *key;
-       char *val;
-       char *tail;
-       char *exerc;
-
-       exerc = pmhash_get(hash, "exerc");
-       if (strcmp(exerc, "2013")) {
-               r = -EINVAL;
-               goto out_val;
+       while ((r = getline(&buffer, &len, decfile->file)) > 0) {
+               r = append_line(decfile, buffer);
+               if (r) {
+                       free(buffer);
+                       goto out;
+               }
+               buffer = NULL;
+               len = 0;
        }
+       if (!(r = decfile_parse_header(decfile)) && !(r = decfile_parse_file(decfile)))
+               return 0;
+out:
+       decfile_release_lines(decfile);
+       return r;
+}
 
-       parse("in_cert", 1);
-       parse("dt_nasc", 8);
-       parse("in_comp", 1);
-       parse("in_res", 1);
-       parse("in_gerada", 1);
-       parse("nr_recibo_anterior", 10);
-       parse("in_pgd", 1);
-       parse("so", 14);
-       parse("versao_so", 7);
-       parse("jvm", 9);
-       parse("nr_recibo", 10);
-       parse("municipio", 4);
-       parse("conjuge", 11);
-       parse("obrig", 1);
-       parse("impdevido", 13);
-       parse("nr_recibo", 10);
-       parse("in_seg", 1);
-       parse("imppago", 2);
-       parse("impant", 1);
-       parse("mudend", 1);
-       parse("cep", 8);
-       parse("debito", 1);
-       parse("banco", 3);
-       parse("agencia", 4);
-       parse("filler", 1);
-       parse("data_julgado", 8);
-       parse("imppagar", 13);
-       parse("tribfonte", 1);
-       parse("cpfrra", 11);
-       parse("trib_rra", 1);
-       parse("cpf_rra2", 11);
-       parse("trib_3rra", 1);
-       parse("cpf_rra3", 11);
-       parse("vr_doacao", 13);
-       parse("cnpj1", 14);
-       parse("cnpj2", 14);
-       parse("cnpj3", 14);
-       parse("cnpj4", 14);
-       parse("cpf_dep1", 11);
-       parse("dnas_dep1", 8);
-       parse("cpf_dep2", 11);
-       parse("dnas_dep2", 8);
-       parse("cpf_dep3", 11);
-       parse("dnas_dep3", 8);
-       parse("cpf_dep4", 11);
-       parse("dnas_dep4", 8);
-       parse("cpf_dep5", 11);
-       parse("dnas_dep5", 8);
-       parse("cpf_dep6", 11);
-       parse("dnas_dep6", 8);
-       parse("cnpj_med1", 14);
-       parse("cnpj_med2", 14);
-       parse("cpf_alim", 11);
-       parse("cpf_invent", 11);
-       parse("municipio", 40);
-       parse("contribuinte", 60);
-       parse("cpf_empregada", 11);
-       parse("hashcode", 12);
-       parse("data_nao_residente", 8);
-       parse("cpf_procurador", 11);
-       parse("obrigatoriedade", 3);
-       parse("rendtrib", 13);
-       parse("cnpj_prev", 14);
-       parse("cnpj_prev2", 14);
-       parse("vr_totisentos", 13);
-       parse("vr_totexclusivo", 13);
-       parse("vr_totpagamentos", 13);
-
-       tail = p;
+struct rnet_decfile * rnet_decfile_open(char *filename)
+{
+       struct rnet_decfile *decfile;
+       int r = -ENOMEM;
+       decfile = malloc(sizeof(*decfile));
+       if (!decfile)
+               return NULL;
+       decfile->header = pmhash_new();
+       if (!decfile->header)
+               goto out_header;
+       decfile->message = rnet_message_new();
+       if (!decfile->message)
+               goto out_message;
+       decfile->filename = strdup(filename);
+       if (!decfile->filename)
+               goto out_filename;
+       decfile->file = fopen(filename, "r");
+       if (!decfile->file)
+               goto out_file;
+       decfile->lines_len = 0;
+       decfile->lines = NULL;
+       if ((r = decfile_parse(decfile)))
+               goto out_parse;
+       return decfile;
+out_parse:
+       fclose(decfile->file);
+out_file:
+       free(decfile->filename);
+out_filename:
+       rnet_message_del(decfile->message);
+out_message:
+       pmhash_del(decfile->header);
+out_header:
+       free(decfile);
+       errno = -r;
+       return NULL;
+}
 
-       parse("versaotestpgd", 3);
-       parse("controle", 10);
+void rnet_decfile_close(struct rnet_decfile *decfile)
+{
+       decfile_release_lines(decfile);
+       fclose(decfile->file);
+       free(decfile->filename);
+       free(decfile);
+}
 
-       if (*p++ != '\r') {
-               fprintf(stderr,
-                       "missing CR at the %tith header character\n",
-                       p - buffer);
-               goto out_val;
-       } else if (*p++ != '\n') {
-               fprintf(stderr,
-                       "missing LF at the %tith header character\n",
-                       p - buffer);
-               goto out_val;
-       } else if (*p != 0) {
-               fprintf(stderr,
-                       "missing NUL at the %tith header character\n",
-                       p - buffer);
-               goto out_val;
-       } else if (p - buffer != RNET_HEADER_SIZE_2013) {
-               fprintf(stderr, "RNET_HEADER_SIZE_2013 in decfile.h needs to be adjusted to %ti,\nor parse_header in decfile.c needs updating\n", p - buffer);
-               goto out_val;
-       } else if (p - tail != RNET_HEADER_TAIL_COMMON) {
-               fprintf(stderr, "RNET_HEADER_TAIL_COMMON in decfile.h needs to be adjusted to %ti\n", p - tail);
-               goto out_val;
+static char * get_header(struct rnet_decfile *decfile)
+{
+       int i;
+       for (i = 0; i < decfile->lines_len; i++) {
+               if (!strncmp(decfile->lines[i], "IRPF", 4)) {
+                       return decfile->lines[i];
+               }
        }
-
-       return 0;
-out_add:
-       free(key);
-out_key:
-       free(val);
-out_val:
-       return r;
+       return NULL;
 }
 
-#undef parse
-
 char *rnet_decfile_get_header_field(struct rnet_decfile *decfile, char *field)
 {
        return pmhash_get(decfile->header, field);