Fix bug when expanding message.
[cascardo/rnetclient.git] / decfile.c
1 /*
2  *  Copyright (C) 2012-2013  Thadeu Lima de Souza Cascardo <cascardo@minaslivre.org>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #define _GNU_SOURCE
20 #include "decfile.h"
21 #include <string.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include "pmhash.h"
27
28 struct rnet_decfile {
29         char *filename;
30         FILE *file;
31         char **lines;
32         int lines_len;
33         struct pmhash *header;
34 };
35
36 /*
37  * line should be an allocated buffer given to append_line
38  * this means, free(line) will be called when decfile is released
39  */
40 static int append_line(struct rnet_decfile *decfile, char *line)
41 {
42         size_t len;
43         char **old_lines;
44         decfile->lines_len += 1;
45         len = sizeof(*decfile->lines) * decfile->lines_len;
46         old_lines = decfile->lines;
47         decfile->lines = realloc(decfile->lines, len);
48         if (!decfile->lines) {
49                 decfile->lines = old_lines;
50                 goto out;
51         }
52         decfile->lines[decfile->lines_len - 1] = line;
53         return 0;
54 out:
55         decfile->lines_len -= 1;
56         return -1;
57 }
58
59 static void decfile_release_lines(struct rnet_decfile *decfile)
60 {
61         int i;
62         for (i = 0; i < decfile->lines_len; i++)
63                 free(decfile->lines[i]);
64         free(decfile->lines);
65         decfile->lines = NULL;
66 }
67
68 static char * get_header(struct rnet_decfile *decfile);
69 static int parse_header(struct pmhash *hash, char *buffer);
70
71 static int decfile_parse_header(struct rnet_decfile *decfile)
72 {
73         char *buffer = get_header(decfile);
74         if (!buffer || strlen(buffer) != 765)
75                 return 1;
76         return parse_header(decfile->header, buffer);
77 }
78
79 static int decfile_parse(struct rnet_decfile *decfile)
80 {
81         char *buffer = NULL;
82         size_t len = 0;
83         int r;
84         while ((r = getline(&buffer, &len, decfile->file)) > 0) {
85                 r = append_line(decfile, buffer);
86                 if (r) {
87                         free(buffer);
88                         goto out;
89                 }
90                 buffer = NULL;
91                 len = 0;
92         }
93         if (!decfile_parse_header(decfile))
94                 return 0;
95 out:
96         decfile_release_lines(decfile);
97         return -1;
98 }
99
100 struct rnet_decfile * rnet_decfile_open(char *filename)
101 {
102         struct rnet_decfile *decfile;
103         decfile = malloc(sizeof(*decfile));
104         if (!decfile)
105                 return NULL;
106         decfile->header = pmhash_new();
107         if (!decfile->header)
108                 goto out_header;
109         decfile->filename = strdup(filename);
110         if (!decfile->filename)
111                 goto out_filename;
112         decfile->file = fopen(filename, "r");
113         if (!decfile->file)
114                 goto out_file;
115         decfile->lines_len = 0;
116         decfile->lines = NULL;
117         if (decfile_parse(decfile))
118                 goto out_parse;
119         return decfile;
120 out_parse:
121         fclose(decfile->file);
122 out_file:
123         free(decfile->filename);
124 out_filename:
125         pmhash_del(decfile->header);
126 out_header:
127         free(decfile);
128         return NULL;
129 }
130
131 void rnet_decfile_close(struct rnet_decfile *decfile)
132 {
133         decfile_release_lines(decfile);
134         fclose(decfile->file);
135         free(decfile->filename);
136         free(decfile);
137 }
138
139 static char * get_header(struct rnet_decfile *decfile)
140 {
141         int i;
142         for (i = 0; i < decfile->lines_len; i++) {
143                 if (!strncmp(decfile->lines[i], "IRPF", 4)) {
144                         return decfile->lines[i];
145                 }
146         }
147         return NULL;
148 }
149
150 static int parse_header(struct pmhash *hash, char *buffer)
151 {
152         char *p = buffer;
153         char *key;
154         char *val;
155
156 #define parse(field, sz) \
157         val = malloc(sz + 1); \
158         if (!val) \
159                 goto out_val; \
160         val[sz] = 0; \
161         memcpy(val, p, sz); \
162         p += sz; \
163         key = strdup(field); \
164         if (!key) \
165                 goto out_key; \
166         if (pmhash_add(&hash, key, val)) \
167                 goto out_add;
168
169         parse("sistema", 8);
170         parse("exerc", 4);
171         parse("ano", 4);
172         parse("codigo_recnet", 4);
173         parse("in_ret", 1);
174         parse("cpf", 11);
175         parse("filler", 3);
176         parse("tipo_ni", 1);
177         parse("nr_versao", 3);
178         parse("nome", 60);
179         parse("uf", 2);
180         parse("hash", 10);
181         parse("in_cert", 1);
182         parse("dt_nasc", 8);
183         parse("in_comp", 1);
184         parse("in_res", 1);
185         parse("in_gerada", 1);
186         parse("nr_recibo_anterior", 10);
187         parse("in_pgd", 1);
188         parse("so", 14);
189         parse("versao_so", 7);
190         parse("jvm", 9);
191         parse("nr_recibo", 10);
192         parse("municipio", 4);
193         parse("conjuge", 11);
194         parse("obrig", 1);
195         parse("impdevido", 13);
196         parse("nr_recibo", 10);
197         parse("in_seg", 1);
198         parse("imppago", 2);
199         parse("impant", 1);
200         parse("mudend", 1);
201         parse("cep", 8);
202         parse("debito", 1);
203         parse("banco", 3);
204         parse("agencia", 4);
205         parse("filler", 1);
206         parse("data_julgado", 8);
207         parse("imppagar", 13);
208         parse("tribfonte", 1);
209         parse("cpfrra", 11);
210         parse("trib_rra", 1);
211         parse("cpf_rra2", 11);
212         parse("trib_3rra", 1);
213         parse("cpf_rra3", 11);
214         parse("vr_doacao", 13);
215         parse("cnpj1", 14);
216         parse("cnpj2", 14);
217         parse("cnpj3", 14);
218         parse("cnpj4", 14);
219         parse("cpf_dep1", 11);
220         parse("dnas_dep1", 8);
221         parse("cpf_dep2", 11);
222         parse("dnas_dep2", 8);
223         parse("cpf_dep3", 11);
224         parse("dnas_dep3", 8);
225         parse("cpf_dep4", 11);
226         parse("dnas_dep4", 8);
227         parse("cpf_dep5", 11);
228         parse("dnas_dep5", 8);
229         parse("cpf_dep6", 11);
230         parse("dnas_dep6", 8);
231         parse("cnpj_med1", 14);
232         parse("cnpj_med2", 14);
233         parse("cpf_alim", 11);
234         parse("cpf_invent", 11);
235         parse("municipio", 40);
236         parse("contribuinte", 60);
237         parse("cpf_empregada", 11);
238         parse("hashcode", 12);
239         parse("data_nao_residente", 8);
240         parse("cpf_procurador", 11);
241         parse("obrigatoriedade", 3);
242         parse("rendtrib", 13);
243         parse("cnpj_prev", 14);
244         parse("cnpj_prev2", 14);
245         parse("vr_totisentos", 13);
246         parse("vr_totexclusivo", 13);
247         parse("vr_totpagamentos", 13);
248         parse("versaotestpgd", 3);
249         parse("controle", 10);
250
251         return 0;
252 out_add:
253         free(key);
254 out_key:
255         free(val);
256 out_val:
257         return -1;
258 }
259
260 char *rnet_decfile_get_header_field(struct rnet_decfile *decfile, char *field)
261 {
262         return pmhash_get(decfile->header, field);
263 }