--- /dev/null
+/*
+ * Copyright (C) 2015-2018 Thadeu Lima de Souza Cascardo <cascardo@minaslivre.org>
+ *
+ * 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
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "importa.h"
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <zlib.h>
+#include "declaracao.h"
+#include "cmd.h"
+#include "rendimento.h"
+#include "carne.h"
+#include "isento.h"
+#include "pagamento.h"
+#include "bem.h"
+#include "dependente.h"
+#include "totais.h"
+#include "sistema.h"
+#include "ano.h"
+
+#define COPY(size) \
+ if (size > sizeof(buffer) - 2) \
+ return -ENOSPC; \
+ if (pos + size >= len) \
+ return pos; \
+ memcpy(buffer, line + pos, size); \
+ buffer[size] = 0; \
+ pos += size;
+
+#define COPYI(field, size) \
+ COPY(size) \
+ field = atoi(buffer);
+
+#define COPYL(field, size) \
+ COPY(size) \
+ field = strtoull(buffer, NULL, 10);
+
+#define COPYS(field, size) \
+ COPY(size); \
+ field = strdup(buffer);
+
+#define COPYDI(field, size) \
+ COPYI(dec->field, size)
+
+#define COPYDL(field, size) \
+ COPYL(dec->field, size)
+
+#define COPYDS(field, size) \
+ COPYS(dec->field, size)
+
+#define COPYC(field, size) \
+ COPYS(dec->contribuinte.field, size)
+
+static int importa_header(struct declaracao *dec, char *line, size_t len)
+{
+ int pos = 0;
+ char buffer[256];
+
+ pos += 8;
+ COPYDI(ano, 4);
+ pos += 4;
+ pos += 4;
+ pos += 1;
+ COPYDS(cpf, 11);
+ pos += 3;
+ pos += 1;
+ pos += 3;
+ COPYDS(nome, 60);
+ COPYC(uf, 2);
+ pos += 10;
+ pos += 1;
+ COPYC(dn, 8);
+ COPY(1);
+ if (buffer[0] == 'S')
+ dec->tipo = COMPLETA;
+ else
+ dec->tipo = SIMPLES;
+ pos += 1;
+ pos += 1;
+ COPYDS(retifica, 10);
+ pos += 1;
+ COPYDS(sistema.so, 14);
+ COPYDS(sistema.so_versao, 7);
+ COPYDS(sistema.jvm_versao, 9);
+ pos += 10;
+ COPYDI(contribuinte.cd_municipio, 4);
+ pos += 11;
+ pos += 1;
+ pos += 13;
+ COPYDS(recibo, 10);
+
+ //fprintf(f, "%d", dec->retifica ? 0 : (dec->recibo ? 2 : 1));
+ pos += 1;
+
+ pos += 2;
+ pos += 1;
+ pos += 1;
+ COPYC(cep, 8);
+ pos += 1;
+ COPYDS(banco, 3);
+ COPYDS(agencia, 4);
+ pos += 1;
+ pos += 8;
+ pos += 13;
+ pos += 1;
+ pos += 11;
+ pos += 1;
+ pos += 11;
+ pos += 1;
+ pos += 11;
+ pos += 1;
+ pos += 11;
+ pos += 13;
+ pos += 14 * 4;
+ pos += 114;
+ pos += 14;
+ pos += 14;
+ pos += 11;
+ pos += 11;
+ COPYC(municipio, 40);
+ pos += 60;
+ pos += 11;
+ COPYDS(sistema.mac, 12);
+ pos += 8;
+ pos += 11;
+ pos += 3;
+ pos += 13;
+ pos += 14;
+ pos += 14;
+ pos += 13;
+ pos += 13;
+ pos += 13;
+ pos += 13;
+ COPYDS(dvconta, 2);
+ pos += 1;
+ COPYDI(contribuinte.natureza_ocupacao, 2);
+ pos += 66;
+ pos += 10;
+
+ /* Mudanças de 2016 */
+
+ if (dec->ano >= 2016) {
+ pos += 7 * 14;
+ }
+
+ /* Fim das mudanças de 2016 */
+
+ pos += 3;
+
+ return pos;
+}
+
+static int importa_contribuinte(struct declaracao *dec, char *line, size_t len)
+{
+ int pos = 0;
+ char buffer[256];
+
+ pos += 2;
+ pos += 11;
+
+ COPYDS(nome, 60);
+ COPYC(tipo_logradouro, 15);
+ COPYC(logradouro, 40);
+ COPYC(numero, 6);
+ COPYC(complemento, 21);
+ COPYC(bairro, 19);
+ COPYC(cep, 8);
+ pos += 1;
+ COPYDI(contribuinte.cd_municipio, 4);
+ COPYC(municipio, 40);
+ COPYC(uf, 2);
+ pos += 3;
+ pos += 3;
+
+ if (dec->ano == 2015) {
+ pos += 112;
+ } else if (dec->ano >= 2016) {
+ pos += 90;
+ COPYC(nit, 11);
+ COPYDS(conjuge.cpf, 11);
+ }
+ if (dec->ano <= 2017) {
+ COPYC(ddd, 4);
+ COPYC(telefone, 9);
+ } else {
+ pos += 13;
+ }
+ COPYC(dn, 8);
+ COPYC(titulo_eleitor, 13);
+ COPYDI(contribuinte.ocupacao_principal, 3);
+ COPYDI(contribuinte.natureza_ocupacao, 2);
+ pos += 1;
+ pos += 1;
+ pos += 1;
+ pos += 1;
+ pos += 1;
+ COPYDS(retifica, 12);
+ COPYDS(banco, 3);
+ COPYDS(agencia, 4);
+ pos += 1;
+ pos += 1;
+ COPYDS(contacorrente, 13);
+ COPYDS(dvconta, 2);
+ pos += 1;
+ pos += 1;
+ pos += 14;
+ COPYDS(recibo, 10);
+ pos += 1;
+ pos += 11;
+ if (dec->ano >= 2016)
+ pos += 20;
+ if (dec->ano >= 2017) {
+ COPYC(celular, 11);
+ pos += 1;
+ }
+ if (dec->ano >= 2018) {
+ COPYC(ddd, 2);
+ COPYC(telefone, 9);
+ }
+
+ return pos;
+}
+
+static int importa_conjuge(struct declaracao *dec, char *line, size_t len)
+{
+ int pos = 0;
+ char buffer[256];
+
+ pos += 2;
+ pos += 11;
+ COPYDS(conjuge.cpf, 11);
+ COPYDL(conjuge.base, 13);
+ COPYDL(conjuge.imposto, 13);
+ COPYDL(conjuge.isento, 13);
+ COPYDL(conjuge.exclusivo, 13);
+ COPYDL(conjuge.rendpj_exigibilidade_suspensa, 13);
+ COPYDL(conjuge.total, 13);
+ COPY(1);
+ if (buffer[0] == 'S')
+ dec->conjuge.entregou = 1;
+ else
+ dec->conjuge.entregou = 0;
+
+ return pos;
+}
+
+static int ignore_line(struct declaracao *dec, char *line, size_t len)
+{
+ return 0;
+}
+
+static int importa_rendimento(struct declaracao *dec, char *line, size_t len)
+{
+ struct rendimento *rend;
+ int pos = 0;
+ char buffer[256];
+ int r;
+
+ rend = malloc(sizeof(*rend));
+ if (!rend)
+ return -ENOMEM;
+ pos += 2;
+ pos += 11;
+ COPYS(rend->cnpj, 14);
+ COPYS(rend->nome, 60);
+ COPYL(rend->rendimento, 13);
+ COPYL(rend->previdencia, 13);
+ COPYL(rend->decimoterceiro, 13);
+ COPYL(rend->imposto, 13);
+ COPYS(rend->saida, 8);
+ COPYL(rend->imposto_13o, 13);
+ if ((r = list_add(&dec->rendimento, rend)) < 0) {
+ rendimento_free(rend);
+ return r;
+ }
+ return pos;
+}
+
+static int importa_pagamento(struct declaracao *dec, char *line, size_t len)
+{
+ struct pagamento *p;
+ int pos = 0;
+ char buffer[256];
+ int r;
+
+ p = malloc(sizeof(*p));
+ if (!p)
+ return -ENOMEM;
+
+ pos += 2;
+ pos += 11;
+ COPYI(p->codigo, 2);
+ COPYI(p->dependente, 5);
+ COPYS(p->cnpj, 14);
+ COPYS(p->nome, 60);
+ /* TODO: NIT */
+ pos += 11;
+ COPYL(p->pagamento, 13);
+ COPYL(p->reembolso, 13);
+ if (dec->ano >= 2016) {
+ /* TODO: contribuição do ente público patrocinador (FUNPRESP?) */
+ pos += 13;
+ }
+ /* PF ou PJ? */
+ pos += 1;
+ /* Titular (T), Dependente (D), Alimentando (A), FIXME Alimentando */
+ pos += 1;
+ if ((r = list_add(&dec->pagamentos, p)) < 0) {
+ pagamento_free(p);
+ return r;
+ }
+ return pos;
+}
+
+static int importa_isento(struct declaracao *dec, char *line, size_t len)
+{
+ struct isento *i;
+ int pos = 0;
+ char buffer[256];
+ int r;
+
+ i = malloc(sizeof(*i));
+ if (!i)
+ return -ENOMEM;
+
+ COPYI(i->codigo, 2);
+ pos += 11;
+ pos += 5;
+ /* Titular (T), Dependente (D), Alimentando (A), FIXME Alimentando */
+ pos += 1;
+ COPYS(i->cnpj, 14);
+ COPYS(i->nome, 60);
+ COPYL(i->valor, 13);
+ /* TODO: Suportar dependente */
+ pos += 11;
+ if ((r = list_add(&dec->isentos, i)) < 0) {
+ isento_free(i);
+ return r;
+ }
+ return pos;
+}
+
+static int importa_outrosisentos(struct declaracao *dec, char *line, size_t len)
+{
+ struct isento *i;
+ int pos = 0;
+ char buffer[256];
+ int r;
+
+ r = importa_isento(dec, line, len);
+ if (r > 0)
+ pos = r;
+ else
+ return r;
+ i = list_get(dec->isentos, list_size(dec->isentos));
+ if (!i)
+ return -EINVAL;
+ COPYI(i->exclusivo, 1);
+ i->exclusivo -= 1;
+ COPYS(i->descricao, 60);
+ return pos;
+}
+
+static int importa_isento2(struct declaracao *dec, char *line, size_t len)
+{
+ struct isento *i;
+ int pos = 0;
+ char buffer[256];
+ int r;
+
+ i = malloc(sizeof(*i));
+ if (!i)
+ return -ENOMEM;
+
+ pos += 2;
+ pos += 11;
+ /* TODO: Suporte a dependente */
+ pos += 1;
+ pos += 11;
+ /* Sub-código */
+ COPYI(i->codigo, 4);
+ COPYS(i->cnpj, 14);
+ COPYS(i->nome, 60);
+ COPYL(i->valor, 13);
+ if ((r = list_add(&dec->isentos, i)) < 0) {
+ isento_free(i);
+ return r;
+ }
+ return pos;
+}
+
+
+static int importa_dependente(struct declaracao *dec, char *line, size_t len)
+{
+ struct dependente *d;
+ int pos = 0;
+ char buffer[256];
+ int r;
+
+ d = malloc(sizeof(*d));
+ if (!d)
+ return -ENOMEM;
+
+ pos += 2;
+ pos += 11;
+ pos += 5;
+ COPYI(d->codigo, 2);
+ COPYS(d->nome, 60);
+ COPYS(d->dn, 8);
+ COPYS(d->cpf, 11);
+ pos += 1;
+ if (dec->ano >= 2016) {
+ /* TODO: NIT/PIS/PASEP de Pessoa Física no exterior */
+ pos += 11;
+ }
+ if ((r = list_add(&dec->dependentes, d)) < 0) {
+ dependente_free(d);
+ return r;
+ }
+ return pos;
+}
+
+static int importa_bem(struct declaracao *dec, char *line, size_t len)
+{
+ struct bem *b;
+ int pos = 0;
+ char buffer[514];
+ int r;
+
+ b = malloc(sizeof(*b));
+ if (!b)
+ return -ENOMEM;
+
+ pos += 2;
+ pos += 11;
+ COPYI(b->codigo, 2);
+
+ /* FIXME: exterior */
+ pos += 1;
+ pos += 3;
+
+ COPYS(b->descricao, 512);
+ COPYL(b->valor_anterior, 13);
+ COPYL(b->valor, 13);
+
+ /* Imóvel */
+ COPYS(b->logradouro, 40);
+ COPYS(b->numero, 6);
+ COPYS(b->complemento, 40);
+ COPYS(b->bairro, 40);
+ COPYS(b->cep, 9);
+ COPYS(b->uf, 2);
+ COPYI(b->cd_municipio, 4);
+ COPYS(b->municipio, 40);
+ /* FIXME: Registro de imóveis, Nao (0), Sim (1), Vazio (2) */
+ pos += 1;
+ COPYS(b->matricula, 40);
+ COPYS(b->registro, 40);
+ COPYL(b->area, 11);
+ /* FIXME: Area, M2 (0), Ha (1), Vazio (2) */
+ pos += 1;
+ COPYS(b->cartorio, 60);
+
+ /* Número de chave */
+ pos += 5;
+ if ((r = list_add(&dec->bens, b)) < 0) {
+ bem_free(b);
+ return r;
+ }
+ return pos;
+}
+
+static int importa_carne(struct declaracao *dec, char *line, size_t len)
+{
+ struct carne *carne;
+ int pos = 0;
+ char buffer[256];
+ int r;
+
+ carne = malloc(sizeof(*carne));
+ if (!carne)
+ return -ENOMEM;
+ pos += 2;
+ pos += 11;
+ COPY(1);
+ if (buffer[0] == 'S') {
+ return -ENOTSUP;
+ }
+ pos += 11;
+ COPYI(carne->mes, 2);
+ COPYL(carne->rendimento, 13);
+ COPYL(carne->alugueis, 13);
+ COPYL(carne->outros, 13);
+ COPYL(carne->exterior, 13);
+ COPYL(carne->caixa, 13);
+ COPYL(carne->alimentos, 13);
+ COPYL(carne->dependentes, 13);
+ COPYL(carne->previdencia, 13);
+ COPYL(carne->base, 13);
+ COPYL(carne->imposto, 13);
+ if ((r = list_add(&dec->carne, carne)) < 0) {
+ carne_free(carne);
+ return r;
+ }
+ return pos;
+}
+
+static struct {
+ char *prefix;
+ int (*func)(struct declaracao *, char *, size_t);
+} prefixes[] = {
+ { "IRPF", importa_header },
+ { "16", importa_contribuinte },
+ { "17", ignore_line },
+ { "18", ignore_line },
+ { "19", ignore_line },
+ { "20", ignore_line },
+ { "21", importa_rendimento },
+ { "22", importa_carne },
+ { "23", ignore_line },
+ { "24", ignore_line },
+ { "25", importa_dependente },
+ { "26", importa_pagamento },
+ { "27", importa_bem },
+ { "29", importa_conjuge },
+ { "82", importa_isento },
+ { "84", importa_isento2 },
+ { "88", importa_isento2 },
+ { "93", importa_isento },
+ { "96", importa_isento },
+ { "97", importa_outrosisentos },
+ { "98", importa_isento },
+ { "99", importa_isento },
+ { "T9", ignore_line },
+ { "HR", ignore_line },
+ { "DR", ignore_line },
+ { "R9", ignore_line },
+};
+
+static int importa_linha(struct declaracao *dec, char *line, size_t len)
+{
+ int i;
+
+ for (i = 0; i < sizeof(prefixes)/sizeof(prefixes[0]); i++)
+ if (!strncmp(prefixes[i].prefix, line, strlen(prefixes[i].prefix)))
+ return prefixes[i].func(dec, line, len);
+
+ return -EINVAL;
+}
+
+static int importa(struct declaracao *dec, char *filename)
+{
+ char *line = NULL;
+ size_t lsz = 0;
+ FILE *f;
+ int r = 0;
+ int n = 1;
+
+ f = fopen(filename, "r");
+ if (!f) {
+ r = -errno;
+ dec_set_error(dec, "Não foi possível abrir arquivo %s: %s.",
+ filename, strerror(errno));
+ goto out_file;
+ }
+ while ((r = getline(&line, &lsz, f)) > 0) {
+ r = importa_linha(dec, line, r);
+ if (r < 0) {
+ fprintf(stderr, "Não foi possível importar linha %d: %s\n",
+ n, dec->error ?: strerror(-r));
+ }
+ n++;
+ }
+
+ fclose(f);
+ return 0;
+out_file:
+ return r;
+}
+
+static int run_importa(struct declaracao *dec, char **args, int argc)
+{
+ if (argc != 2) {
+ dec_set_error(dec, "Comando %s recebe um nome de arquivo como parâmetro.",
+ args[0]);
+ return -EINVAL;
+ }
+ return importa(dec, args[1]);
+}
+
+static struct cmd cmd_importa = {
+ .name = "importa",
+ .run = run_importa,
+};
+
+int importa_cmd_init(void)
+{
+ cmd_add(&cmd_importa);
+ return 0;
+}