X-Git-Url: http://git.cascardo.eti.br/?a=blobdiff_plain;f=decfile.c;h=3856c2bd13c93eb9279ccd6953692fef2a83bf75;hb=26470029ecff8baa9b7a5a8162618c51e891fc8b;hp=f300d69b9c8ef9426a86e5c5d3d4ba8b87eb09a3;hpb=9b15de84f1628712b63134c70392a770fa021068;p=cascardo%2Frnetclient.git diff --git a/decfile.c b/decfile.c index f300d69..3856c2b 100644 --- a/decfile.c +++ b/decfile.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2012-2013 Thadeu Lima de Souza Cascardo + * Copyright (C) 2012-2014 Thadeu Lima de Souza Cascardo + * Copyright (C) 2014 Alexandre Oliva * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -73,110 +74,8 @@ static void decfile_release_lines(struct rnet_decfile *decfile) } static char * get_header(struct rnet_decfile *decfile); -static int parse_header_2013(struct pmhash *hash, char *buffer); -static int parse_header_2014(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 = get_header(decfile); - if (!buffer) - return -EINVAL; - switch (strlen(buffer)) { - case 765: - return parse_header_2013(decfile->header, buffer); - case 793: - return parse_header_2014(decfile->header, buffer); - default: - return -EINVAL; - } -} - -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; -} - -static int parse_header_2014(struct pmhash *hash, char *buffer) -{ - int r; - char *p = buffer; - char *key; - char *val; - #define parse(field, sz) \ r = -ENOMEM; \ val = malloc(sz + 1); \ @@ -191,12 +90,26 @@ static int parse_header_2014(struct pmhash *hash, char *buffer) if (pmhash_add(&hash, key, val)) \ goto out_add; +static int decfile_parse_header(struct rnet_decfile *decfile) +{ + char *buffer; + int r; + 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); - if (strcmp(val, "2014")) { - r = -EINVAL; - goto out_val; - } parse("ano", 4); parse("codigo_recnet", 4); parse("in_ret", 1); @@ -207,6 +120,29 @@ static int parse_header_2014(struct pmhash *hash, char *buffer) parse("nome", 60); parse("uf", 2); parse("hash", 10); + + /* 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; + } + + /* Retrieve exerc from the hash table. */ + exerc = atoi(pmhash_get(hash, "exerc")); + + /* 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 > 2016) { + 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); @@ -240,8 +176,14 @@ static int parse_header_2014(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); @@ -265,8 +207,18 @@ static int parse_header_2014(struct pmhash *hash, char *buffer) parse("cpf_invent", 11); parse("municipio", 40); parse("contribuinte", 60); - parse("cpf_empregada", 11); - parse("hashcode", 12); + + /* 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); parse("obrigatoriedade", 3); @@ -276,12 +228,114 @@ static int parse_header_2014(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); + + /* End of header in 2013. */ + + /* Fields added in 2014. */ + if (exerc >= 2014) { + parse("nr_conta", 13); + parse("nr_dv_conta", 2); + parse("in_dv_conta", 1); + } + + /* 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); + } + + /* End of header in 2015. */ + + /* Fields added in 2016. */ + if (exerc >= 2016) { + parse("dedutivelmaior1", 14); + parse("dedutivelmaior2", 14); + parse("dedutivelmaior3", 14); + parse("dedutivelmaior4", 14); + parse("dedutivelmaior5", 14); + parse("dedutivelmaior6", 14); + parse("funprespmaior", 14); + } + + /* End of header in 2016. */ + + /* Tail fields, which exist at least since 2013. */ + tail = p; parse("versaotestpgd", 3); parse("controle", 10); + 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 - 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; + case 2016: + if (p - buffer != RNET_HEADER_SIZE_2016) { + fprintf(stderr, "RNET_HEADER_SIZE_2016 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); @@ -291,120 +345,84 @@ 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; + 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; +} -#define parse(field, sz) \ - r = -ENOMEM; \ - val = malloc(sz + 1); \ - if (!val) \ - goto out_val; \ - val[sz] = 0; \ - memcpy(val, p, sz); \ - p += sz; \ - key = strdup(field); \ - if (!key) \ - goto out_key; \ - if (pmhash_add(&hash, key, val)) \ - goto out_add; +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("sistema", 8); - parse("exerc", 4); - if (strcmp(val, "2013")) { - r = -EINVAL; - goto out_val; - } - parse("ano", 4); - parse("codigo_recnet", 4); - parse("in_ret", 1); - parse("cpf", 11); - parse("filler", 3); - parse("tipo_ni", 1); - parse("nr_versao", 3); - parse("nome", 60); - parse("uf", 2); - parse("hash", 10); - 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); - 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); +} - return 0; -out_add: - free(key); -out_key: - free(val); -out_val: - return r; +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; } char *rnet_decfile_get_header_field(struct rnet_decfile *decfile, char *field)