]> 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 gchar *hb_csv_strndup (gchar *str, gsize n)
43 {
44 gchar *new_str;
45 gchar *twoquote;
46
47 if (str)
48 {
49 new_str = g_new (gchar, n + 1);
50 if(*str=='\"')
51 {
52 str++; n--;
53 }
54 if(str[n-1]=='\"')
55 n--;
56
57 strncpy (new_str, str, n);
58 new_str[n] = '\0';
59
60 // replace ""
61 twoquote = strstr(new_str, "\"\"");
62 if(twoquote)
63 strcpy (twoquote, twoquote+1);
64
65 //todo: replace &amp; &lt; &gt; &apos; &quot; ??
66
67 }
68 else
69 new_str = NULL;
70
71 return new_str;
72 }
73
74
75 static gchar *hb_csv_find_delimiter(gchar *string)
76 {
77 gchar *s = string;
78 gboolean enclosed = FALSE;
79
80 while( *s != '\0' )
81 {
82 if( *s == ';' && enclosed == FALSE )
83 break;
84
85 if( *s == '\"' )
86 {
87 enclosed = !enclosed;
88 }
89
90 s++;
91 }
92
93 return s;
94 }
95
96
97 gboolean hb_csv_row_valid(gchar **str_array, guint nbcolumns, gint *csvtype)
98 {
99 gboolean valid = TRUE;
100 guint i;
101 extern int errno;
102
103 #if MYDEBUG == 1
104 gchar *type[5] = { "string", "date", "int", "double" };
105 gint lasttype;
106 #endif
107
108 DB( g_print("\n** hb_string_csv_valid: init %d\n", valid) );
109
110 DB( g_print(" -> length %d, nbcolumns %d\n", g_strv_length( str_array ), nbcolumns) );
111
112 if( g_strv_length( str_array ) != nbcolumns )
113 {
114 valid = FALSE;
115 goto csvend;
116 }
117
118 for(i=0;i<nbcolumns;i++)
119 {
120 #if MYDEBUG == 1
121 lasttype = csvtype[i];
122 #endif
123
124 if(valid == FALSE)
125 {
126 DB( g_print(" -> fail on column %d, type: %s\n", i, type[lasttype]) );
127 break;
128 }
129
130 DB( g_print(" -> control column %d, type: %d, valid: %d '%s'\n", i, lasttype, valid, str_array[i]) );
131
132 switch( csvtype[i] )
133 {
134 case CSV_DATE:
135 valid = hb_string_isdate(str_array[i]);
136 break;
137 case CSV_STRING:
138 valid = hb_string_isprint(str_array[i]);
139 break;
140 case CSV_INT:
141 valid = hb_string_isdigit(str_array[i]);
142 break;
143 case CSV_DOUBLE :
144
145 //todo: use strtod (to take care or . or ,)
146 g_ascii_strtod(str_array[i], NULL);
147 //todo : see this errno
148 if( errno )
149 {
150 DB( g_print("errno: %d\n", errno) );
151 valid = FALSE;
152 }
153 break;
154 }
155 }
156
157 csvend:
158
159 DB( g_print(" --> return %d\n", valid) );
160
161 return valid;
162 }
163
164
165 gchar **hb_csv_row_get(gchar *string, gchar *delimiter, gint max_tokens)
166 {
167 GSList *string_list = NULL, *slist;
168 gchar **str_array, *s;
169 guint n = 0;
170 gchar *remainder;
171
172 g_return_val_if_fail (string != NULL, NULL);
173 g_return_val_if_fail (delimiter != NULL, NULL);
174 g_return_val_if_fail (delimiter[0] != '\0', NULL);
175
176 if (max_tokens < 1)
177 max_tokens = G_MAXINT;
178
179 remainder = string;
180 s = hb_csv_find_delimiter (remainder);
181 if (s)
182 {
183 gsize delimiter_len = strlen (delimiter);
184
185 while (--max_tokens && s && *s != '\0')
186 {
187 gsize len;
188
189 len = s - remainder;
190 string_list = g_slist_prepend (string_list, hb_csv_strndup (remainder, len));
191 DB( g_print(" stored=[%s]\n", (gchar *)string_list->data) );
192
193 n++;
194 remainder = s + delimiter_len;
195 s = hb_csv_find_delimiter (remainder);
196 }
197 }
198 if (*string)
199 {
200 gsize len;
201
202 len = s - remainder;
203 n++;
204 string_list = g_slist_prepend (string_list, hb_csv_strndup (remainder, len));
205 DB( g_print(" stored=[%s]\n", (gchar *)string_list->data) );
206 }
207
208 str_array = g_new (gchar*, n + 1);
209
210 str_array[n--] = NULL;
211 for (slist = string_list; slist; slist = slist->next)
212 str_array[n--] = slist->data;
213
214 g_slist_free (string_list);
215
216 return str_array;
217 }
218
219
220 GList *homebank_csv_import(ImportContext *ictx, GenFile *genfile)
221 {
222 GIOChannel *io;
223 //GList *list = NULL;
224 static gint csvtype[7] = {
225 CSV_DATE,
226 CSV_INT,
227 CSV_STRING,
228 CSV_STRING,
229 CSV_STRING,
230 CSV_DOUBLE,
231 CSV_STRING,
232 };
233
234 DB( g_print("\n[import] homebank csv\n") );
235
236 io = g_io_channel_new_file(genfile->filepath, "r", NULL);
237 if(io != NULL)
238 {
239 gchar *tmpstr;
240 gsize length;
241 gint io_stat;
242 gboolean isvalid;
243 gint count = 0;
244 gint error = 0;
245 GenAcc *newacc;
246 GError *err = NULL;
247
248
249 newacc = hb_import_gen_acc_get_next(ictx, FILETYPE_CSV_HB, NULL, NULL);
250
251 if( genfile->encoding != NULL )
252 {
253 g_io_channel_set_encoding(io, genfile->encoding, NULL);
254 }
255
256 for(;;)
257 {
258 io_stat = g_io_channel_read_line(io, &tmpstr, &length, NULL, &err);
259 if( io_stat == G_IO_STATUS_EOF)
260 break;
261 if( io_stat == G_IO_STATUS_ERROR )
262 {
263 DB (g_print(" + ERROR %s\n",err->message));
264 break;
265 }
266 if( io_stat == G_IO_STATUS_NORMAL)
267 {
268 if( *tmpstr != '\0' )
269 {
270 gchar **str_array;
271
272 count++;
273
274 hb_string_strip_crlf(tmpstr);
275 DB( g_print("\n (row-%04d) ->|%s|<-\n", count, tmpstr) );
276
277 // 0:date; 1:paymode; 2:info; 3:payee, 4:wording; 5:amount; 6:category; 7:tags
278 str_array = hb_csv_row_get(tmpstr, ";", 8);
279 isvalid = hb_csv_row_valid(str_array, 8, csvtype);
280
281 DB( g_print(" valid %d, '%s'\n", isvalid, tmpstr) );
282
283 if( !isvalid )
284 {
285 g_warning ("csv parse: line %d, invalid column count or data", count);
286 error++;
287 //todo log line in error to report user
288 }
289 else
290 {
291 GenTxn *newope = da_gen_txn_malloc();;
292
293 DB( g_print(" ->%s\n", tmpstr ) );
294
295 /* convert to generic transaction */
296 newope->date = g_strdup(str_array[0]);
297 newope->paymode = atoi(str_array[1]);
298 //added 5.1.8 forbid to import 5=internal xfer
299 if(newope->paymode == PAYMODE_INTXFER)
300 newope->paymode = PAYMODE_XFER;
301 newope->rawinfo = g_strdup(str_array[2]);
302 newope->rawpayee = g_strdup(g_strstrip(str_array[3]));
303 newope->rawmemo = g_strdup(str_array[4]);
304 newope->amount = hb_qif_parser_get_amount(str_array[5]);
305 newope->category = g_strdup(g_strstrip(str_array[6]));
306 newope->tags = g_strdup(str_array[7]);
307 newope->account = g_strdup(newacc->name);
308
309 /* todo: move this eval date valid */
310 //guint32 juliantmp = hb_date_get_julian(str_array[0], ictx->datefmt);
311 ///if( juliantmp == 0 )
312 // ictx->cnt_err_date++;
313
314 /*
315 DB( g_print(" storing %s : %s : %s :%s : %s : %s : %s : %s\n",
316 str_array[0], str_array[1], str_array[2],
317 str_array[3], str_array[4], str_array[5],
318 str_array[6], str_array[7]
319 ) );
320 */
321 /* csv file are standalone, so no way to link a target txn */
322 if(newope->paymode == PAYMODE_INTXFER)
323 newope->paymode = PAYMODE_XFER;
324
325 da_gen_txn_append(ictx, newope);
326
327 g_strfreev (str_array);
328 }
329 }
330 g_free(tmpstr);
331 }
332
333 }
334 g_io_channel_unref (io);
335
336 /*
337 ui_dialog_msg_infoerror(data->window, error > 0 ? GTK_MESSAGE_ERROR : GTK_MESSAGE_INFO,
338 _("Transaction CSV import result"),
339 _("%d transactions inserted\n%d errors in the file"),
340 count, error);
341 */
342 }
343
344
345 return ictx->gen_lst_txn;
346 }
347
348
This page took 0.048899 seconds and 4 git commands to generate.