}
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); \
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);
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);
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);
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);
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);
"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);
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);