+static char * get_header(struct rnet_decfile *decfile);
+static int decfile_parse_file(struct rnet_decfile *decfile);
+
+#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;
+
+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);
+ 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);
+
+ /* 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);
+ 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);
+
+ /* 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("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);
+
+ /* 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);
+ parse("rendtrib", 13);
+ parse("cnpj_prev", 14);
+ parse("cnpj_prev2", 14);
+ parse("vr_totisentos", 13);
+ parse("vr_totexclusivo", 13);
+ parse("vr_totpagamentos", 13);
+
+ /* 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);
+out_key:
+ free(val);
+out_val:
+ return r;
+}
+
+#undef parse
+