Parse the header in a single function
[cascardo/rnetclient.git] / 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);