]> Dogcows Code - chaz/homebank/blob - src/hb-import-csv.c
Merge branch 'upstream'
[chaz/homebank] / src / hb-import-csv.c
1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2019 Maxime DOYEN
3 *
4 * This file is part of HomeBank.
5 *
6 * HomeBank is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * HomeBank is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "homebank.h"
21
22
23 #include "hb-import.h"
24
25
26 /****************************************************************************/
27 /* Debug macros */
28 /****************************************************************************/
29 #define MYDEBUG 0
30
31 #if MYDEBUG
32 #define DB(x) (x);
33 #else
34 #define DB(x);
35 #endif
36
37 /* our global datas */
38 extern struct HomeBank *GLOBALS;
39 extern struct Preferences *PREFS;
40
41
42 static gint csvtype[7] = {
43 CSV_DATE,
44 CSV_INT,
45 CSV_STRING,
46 CSV_STRING,
47 CSV_STRING,
48 CSV_DOUBLE,
49 CSV_STRING,
50 };
51
52
53
54 static gchar *hb_csv_strndup (gchar *str, gsize n)
55 {
56 gchar *new_str;
57 gchar *twoquote;
58
59 if (str)
60 {
61 new_str = g_new (gchar, n + 1);
62 if(*str=='\"')
63 {
64 str++; n--;
65 }
66 if(str[n-1]=='\"')
67 n--;
68
69 strncpy (new_str, str, n);
70 new_str[n] = '\0';
71
72 // replace ""
73 twoquote = strstr(new_str, "\"\"");
74 if(twoquote)
75 strcpy (twoquote, twoquote+1);
76
77 //todo: replace &amp; &lt; &gt; &apos; &quot; ??
78
79 }
80 else
81 new_str = NULL;
82
83 return new_str;
84 }
85
86
87 static gchar *hb_csv_find_delimiter(gchar *string, gchar delimiter)
88 {
89 gchar *s = string;
90 gboolean enclosed = FALSE;
91
92 while( *s != '\0' )
93 {
94 if( (*s == delimiter) && (enclosed == FALSE) )
95 break;
96
97 if( *s == '\"' )
98 {
99 enclosed = !enclosed;
100 }
101
102 s++;
103 }
104
105 return s;
106 }
107
108
109 static gboolean hb_csv_row_valid(gchar **str_array, guint nbcolumns, gint *csvtype)
110 {
111 gboolean valid = TRUE;
112 guint i;
113 extern int errno;
114
115 #if MYDEBUG == 1
116 gchar *type[5] = { "string", "date", "int", "double" };
117 gint lasttype;
118 #endif
119
120 DB( g_print("\n** hb_string_csv_valid: init %d\n", valid) );
121
122 DB( g_print(" -> length %d, nbcolumns %d\n", g_strv_length( str_array ), nbcolumns) );
123
124 if( g_strv_length( str_array ) != nbcolumns )
125 {
126 valid = FALSE;
127 goto csvend;
128 }
129
130 for(i=0;i<nbcolumns;i++)
131 {
132 #if MYDEBUG == 1
133 lasttype = csvtype[i];
134 #endif
135
136 if(valid == FALSE)
137 {
138 DB( g_print(" -> fail on column %d, type: %s\n", i, type[lasttype]) );
139 break;
140 }
141
142 DB( g_print(" -> control column %d, type: %d, valid: %d '%s'\n", i, lasttype, valid, str_array[i]) );
143
144 switch( csvtype[i] )
145 {
146 case CSV_DATE:
147 valid = hb_string_isdate(str_array[i]);
148 break;
149 case CSV_STRING:
150 valid = hb_string_isprint(str_array[i]);
151 break;
152 case CSV_INT:
153 valid = hb_string_isdigit(str_array[i]);
154 break;
155 case CSV_DOUBLE :
156
157 //todo: use strtod (to take care or . or ,)
158 g_ascii_strtod(str_array[i], NULL);
159 //todo : see this errno
160 if( errno )
161 {
162 DB( g_print("errno: %d\n", errno) );
163 valid = FALSE;
164 }
165 break;
166 }
167 }
168
169 csvend:
170
171 DB( g_print(" --> return %d\n", valid) );
172
173 return valid;
174 }
175
176
177 static gchar **hb_csv_row_get(gchar *string, gchar delimiter, gint max_tokens)
178 {
179 GSList *string_list = NULL, *slist;
180 gchar **str_array, *s;
181 guint n = 0;
182 gchar *remainder;
183
184 g_return_val_if_fail (string != NULL, NULL);
185 g_return_val_if_fail (delimiter != '\0', NULL);
186
187 if (max_tokens < 1)
188 max_tokens = G_MAXINT;
189
190 remainder = string;
191 s = hb_csv_find_delimiter (remainder, delimiter);
192 if (s)
193 {
194 while (--max_tokens && s && *s != '\0')
195 {
196 gsize len;
197
198 len = s - remainder;
199 string_list = g_slist_prepend (string_list, hb_csv_strndup (remainder, len));
200 DB( g_print(" stored=[%s]\n", (gchar *)string_list->data) );
201
202 n++;
203 remainder = s + 1;
204 s = hb_csv_find_delimiter (remainder, delimiter);
205 }
206 }
207 if (*string)
208 {
209 gsize len;
210
211 len = s - remainder;
212 n++;
213 string_list = g_slist_prepend (string_list, hb_csv_strndup (remainder, len));
214 DB( g_print(" stored=[%s]\n", (gchar *)string_list->data) );
215 }
216
217 str_array = g_new (gchar*, n + 1);
218
219 str_array[n--] = NULL;
220 for (slist = string_list; slist; slist = slist->next)
221 str_array[n--] = slist->data;
222
223 g_slist_free (string_list);
224
225 return str_array;
226 }
227
228
229 static gchar hb_csv_get_separator(void)
230 {
231 static const gchar sep[] = PRF_DTEX_CSVSEP_BUFFER;
232 return sep[PREFS->dtex_csvsep];
233 }
234
235
236 gboolean hb_csv_test_line(gchar *rawline)
237 {
238 gchar **str_array;
239 gchar sep;
240 gboolean isvalid = FALSE;
241
242 hb_string_strip_crlf(rawline);
243 sep = hb_csv_get_separator();
244 str_array = hb_csv_row_get(rawline, sep, 8);
245 isvalid = hb_csv_row_valid(str_array, 8, csvtype);
246
247 g_strfreev (str_array);
248
249 return isvalid;
250 }
251
252
253 GList *homebank_csv_import(ImportContext *ictx, GenFile *genfile)
254 {
255 GIOChannel *io;
256 //GList *list = NULL;
257
258 DB( g_print("\n[import] homebank csv\n") );
259
260 io = g_io_channel_new_file(genfile->filepath, "r", NULL);
261 if(io != NULL)
262 {
263 gchar *tmpstr;
264 gchar sep;
265 gsize length;
266 gint io_stat;
267 gboolean isvalid;
268 gint count = 0;
269 gint error = 0;
270 GenAcc *newacc;
271 GError *err = NULL;
272
273
274 newacc = hb_import_gen_acc_get_next(ictx, FILETYPE_CSV_HB, NULL, NULL);
275
276 if( genfile->encoding != NULL )
277 {
278 g_io_channel_set_encoding(io, genfile->encoding, NULL);
279 }
280
281 for(;;)
282 {
283 io_stat = g_io_channel_read_line(io, &tmpstr, &length, NULL, &err);
284 if( io_stat == G_IO_STATUS_EOF)
285 break;
286 if( io_stat == G_IO_STATUS_ERROR )
287 {
288 DB (g_print(" + ERROR %s\n",err->message));
289 break;
290 }
291 if( io_stat == G_IO_STATUS_NORMAL)
292 {
293 if( *tmpstr != '\0' )
294 {
295 gchar **str_array;
296
297 count++;
298
299 sep = hb_csv_get_separator();
300
301 hb_string_strip_crlf(tmpstr);
302 str_array = hb_csv_row_get(tmpstr, sep, 8);
303
304 // 0:date; 1:paymode; 2:info; 3:payee, 4:wording; 5:amount; 6:category; 7:tags
305 isvalid = hb_csv_row_valid(str_array, 8, csvtype);
306
307 DB( g_print("\n (row-%04d) ->|%s|<-\n", count, tmpstr) );
308 DB( g_print(" valid %d, '%s'\n", isvalid, tmpstr) );
309
310 if( !isvalid )
311 {
312 g_warning ("csv parse: line %d, invalid column count or data", count);
313 error++;
314 //todo log line in error to report user
315 }
316 else
317 {
318 GenTxn *newope = da_gen_txn_malloc();;
319
320 DB( g_print(" adding txn\n" ) );
321
322 /* convert to generic transaction */
323 newope->date = g_strdup(str_array[0]);
324 newope->paymode = atoi(str_array[1]);
325 //todo: reinforce controls here
326 // csv file are standalone, so no way to link a target txn
327 //added 5.1.8 forbid to import 5=internal xfer
328 if(newope->paymode == PAYMODE_INTXFER)
329 newope->paymode = PAYMODE_XFER;
330 newope->rawinfo = g_strdup(str_array[2]);
331 newope->rawpayee = g_strdup(g_strstrip(str_array[3]));
332 newope->rawmemo = g_strdup(str_array[4]);
333 newope->amount = hb_qif_parser_get_amount(str_array[5]);
334 newope->category = g_strdup(g_strstrip(str_array[6]));
335 newope->tags = g_strdup(str_array[7]);
336 newope->account = g_strdup(newacc->name);
337
338 /* todo: move this eval date valid */
339 //guint32 juliantmp = hb_date_get_julian(str_array[0], ictx->datefmt);
340 ///if( juliantmp == 0 )
341 // ictx->cnt_err_date++;
342
343 /*
344 DB( g_print(" storing %s : %s : %s :%s : %s : %s : %s : %s\n",
345 str_array[0], str_array[1], str_array[2],
346 str_array[3], str_array[4], str_array[5],
347 str_array[6], str_array[7]
348 ) );
349 */
350
351 da_gen_txn_append(ictx, newope);
352
353 g_strfreev (str_array);
354 }
355 }
356 g_free(tmpstr);
357 }
358
359 }
360 g_io_channel_unref (io);
361
362 /*
363 ui_dialog_msg_infoerror(data->window, error > 0 ? GTK_MESSAGE_ERROR : GTK_MESSAGE_INFO,
364 _("Transaction CSV import result"),
365 _("%d transactions inserted\n%d errors in the file"),
366 count, error);
367 */
368 }
369
370
371 return ictx->gen_lst_txn;
372 }
373
374
This page took 0.044089 seconds and 4 git commands to generate.