X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fhomebank;a=blobdiff_plain;f=src%2Fhb-import-csv.c;fp=src%2Fhb-import-csv.c;h=8d87f14ba53de9936ee794e09f5955df405632da;hp=0000000000000000000000000000000000000000;hb=996fa4ab9f6b836001f8ad0eecbfd3821687fea7;hpb=27f6e3b112df235c8e9afc9911b1f6bce208a001 diff --git a/src/hb-import-csv.c b/src/hb-import-csv.c new file mode 100644 index 0000000..8d87f14 --- /dev/null +++ b/src/hb-import-csv.c @@ -0,0 +1,382 @@ +/* HomeBank -- Free, easy, personal accounting for everyone. + * Copyright (C) 1995-2016 Maxime DOYEN + * + * This file is part of HomeBank. + * + * HomeBank 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 2 of the License, or + * (at your option) any later version. + * + * HomeBank 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, see . + */ + +#include "homebank.h" + + +#include "hb-import.h" + + +/****************************************************************************/ +/* Debug macros */ +/****************************************************************************/ +#define MYDEBUG 0 + +#if MYDEBUG +#define DB(x) (x); +#else +#define DB(x); +#endif + +/* our global datas */ +extern struct HomeBank *GLOBALS; +extern struct Preferences *PREFS; + + +static gchar *hb_csv_strndup (gchar *str, gsize n) +{ +gchar *new_str; +gchar *twoquote; + + if (str) + { + new_str = g_new (gchar, n + 1); + if(*str=='\"') + { + str++; n--; + } + if(str[n-1]=='\"') + n--; + + strncpy (new_str, str, n); + new_str[n] = '\0'; + + // replace "" + twoquote = strstr(new_str, "\"\""); + if(twoquote) + strcpy (twoquote, twoquote+1); + + //todo: replace & < > ' " ?? + + } + else + new_str = NULL; + + return new_str; +} + + +static gchar *hb_csv_find_delimiter(gchar *string) +{ + gchar *s = string; +gboolean enclosed = FALSE; + + while( *s != '\0' ) + { + if( *s == ';' && enclosed == FALSE ) + break; + + if( *s == '\"' ) + { + enclosed = !enclosed; + } + + s++; + } + + return s; +} + + +gboolean hb_csv_row_valid(gchar **str_array, guint nbcolumns, gint *csvtype) +{ +gboolean valid = TRUE; +guint i; +extern int errno; + +#if MYDEBUG == 1 +gchar *type[5] = { "string", "date", "int", "double" }; +gint lasttype; +#endif + + DB( g_print("\n** hb_string_csv_valid: init %d\n", valid) ); + + DB( g_print(" -> length %d, nbcolumns %d\n", g_strv_length( str_array ), nbcolumns) ); + + if( g_strv_length( str_array ) != nbcolumns ) + { + valid = FALSE; + goto csvend; + } + + for(i=0;i fail on column %d, type: %s\n", i, type[lasttype]) ); + break; + } + + DB( g_print(" -> control column %d, type: %d, valid: %d '%s'\n", i, lasttype, valid, str_array[i]) ); + + switch( csvtype[i] ) + { + case CSV_DATE: + valid = hb_string_isdate(str_array[i]); + break; + case CSV_STRING: + valid = hb_string_isprint(str_array[i]); + break; + case CSV_INT: + valid = hb_string_isdigit(str_array[i]); + break; + case CSV_DOUBLE : + + //todo: use strtod (to take care or . or ,) + g_ascii_strtod(str_array[i], NULL); + //todo : see this errno + if( errno ) + { + DB( g_print("errno: %d\n", errno) ); + valid = FALSE; + } + break; + } + } + +csvend: + + DB( g_print(" --> return %d\n", valid) ); + + return valid; +} + + +gchar **hb_csv_row_get(gchar *string, gchar *delimiter, gint max_tokens) +{ +GSList *string_list = NULL, *slist; +gchar **str_array, *s; +guint n = 0; +gchar *remainder; + + g_return_val_if_fail (string != NULL, NULL); + g_return_val_if_fail (delimiter != NULL, NULL); + g_return_val_if_fail (delimiter[0] != '\0', NULL); + + if (max_tokens < 1) + max_tokens = G_MAXINT; + + remainder = string; + s = hb_csv_find_delimiter (remainder); + if (s) + { + gsize delimiter_len = strlen (delimiter); + + while (--max_tokens && s && *s != '\0') + { + gsize len; + + len = s - remainder; + string_list = g_slist_prepend (string_list, hb_csv_strndup (remainder, len)); + DB( g_print(" stored=[%s]\n", (gchar *)string_list->data) ); + + n++; + remainder = s + delimiter_len; + s = hb_csv_find_delimiter (remainder); + } + } + if (*string) + { + gsize len; + + len = s - remainder; + n++; + string_list = g_slist_prepend (string_list, hb_csv_strndup (remainder, len)); + DB( g_print(" stored=[%s]\n", (gchar *)string_list->data) ); + } + + str_array = g_new (gchar*, n + 1); + + str_array[n--] = NULL; + for (slist = string_list; slist; slist = slist->next) + str_array[n--] = slist->data; + + g_slist_free (string_list); + + return str_array; +} + + +GList *homebank_csv_import(gchar *filename, ImportContext *ictx) +{ +GIOChannel *io; +GList *list = NULL; +static gint csvtype[7] = { + CSV_DATE, + CSV_INT, + CSV_STRING, + CSV_STRING, + CSV_STRING, + CSV_DOUBLE, + CSV_STRING, + }; + + DB( g_print("\n[import] homebank csv\n") ); + + io = g_io_channel_new_file(filename, "r", NULL); + if(io != NULL) + { + gchar *tmpstr; + gsize length; + gint io_stat; + gboolean isvalid; + gint count = 0; + gint error = 0; + Account *tmp_acc; + Payee *payitem; + Category *catitem; + GError *err = NULL; + + + gchar *accname = g_strdup_printf(_("(account %d)"), da_acc_get_max_key() + 1); + tmp_acc = import_create_account(accname, NULL); + g_free(accname); + + + if( ictx->encoding != NULL ) + { + g_io_channel_set_encoding(io, ictx->encoding, NULL); + } + + for(;;) + { + io_stat = g_io_channel_read_line(io, &tmpstr, &length, NULL, &err); + if( io_stat == G_IO_STATUS_EOF) + break; + if( io_stat == G_IO_STATUS_ERROR ) + { + DB (g_print(" + ERROR %s\n",err->message)); + break; + } + if( io_stat == G_IO_STATUS_NORMAL) + { + if( *tmpstr != '\0' ) + { + gchar **str_array; + + count++; + + hb_string_strip_crlf(tmpstr); + DB( g_print("\n (row-%04d) ->|%s|<-\n", count, tmpstr) ); + + // 0:date; 1:paymode; 2:info; 3:payee, 4:wording; 5:amount; 6:category; 7:tags + str_array = hb_csv_row_get(tmpstr, ";", 8); + isvalid = hb_csv_row_valid(str_array, 8, csvtype); + + DB( g_print(" valid %d, '%s'\n", isvalid, tmpstr) ); + + if( !isvalid ) + { + g_warning ("csv parse: line %d, invalid column count or data", count); + error++; + //todo log line in error to report user + } + else + { + Transaction *newope = da_transaction_malloc(); + + //DB( g_print(" ->%s\n", tmpstr ) ); + + newope->date = hb_date_get_julian(str_array[0], ictx->datefmt); + if( newope->date == 0 ) + { + g_warning ("csv parse: line %d, parse date failed", count); + ictx->cnt_err_date++; + } + + newope->paymode = atoi(str_array[1]); + newope->info = g_strdup(str_array[2]); + + /* payee */ + g_strstrip(str_array[3]); + payitem = da_pay_get_by_name(str_array[3]); + if(payitem == NULL) + { + payitem = da_pay_malloc(); + payitem->name = g_strdup(str_array[3]); + payitem->imported = TRUE; + da_pay_append(payitem); + + if( payitem->imported == TRUE ) + ictx->cnt_new_pay += 1; + } + + newope->kpay = payitem->key; + newope->wording = g_strdup(str_array[4]); + newope->amount = hb_qif_parser_get_amount(str_array[5]); + + /* category */ + g_strstrip(str_array[6]); + catitem = da_cat_append_ifnew_by_fullname(str_array[6], TRUE); + if( catitem != NULL ) + { + newope->kcat = catitem->key; + + if( catitem->imported == TRUE && catitem->key > 0 ) + ictx->cnt_new_cat += 1; + } + + /* tags */ + transaction_tags_parse(newope, str_array[7]); + + + newope->kacc = tmp_acc->key; + //newope->kxferacc = accnum; + + newope->flags |= OF_ADDED; + + if( newope->amount > 0) + newope->flags |= OF_INCOME; + + /* + DB( g_print(" storing %s : %s : %s :%s : %s : %s : %s : %s\n", + str_array[0], str_array[1], str_array[2], + str_array[3], str_array[4], str_array[5], + str_array[6], str_array[7] + ) ); + */ + + list = g_list_append(list, newope); + + g_strfreev (str_array); + } + } + g_free(tmpstr); + } + + } + g_io_channel_unref (io); + + /* + ui_dialog_msg_infoerror(data->window, error > 0 ? GTK_MESSAGE_ERROR : GTK_MESSAGE_INFO, + _("Transaction CSV import result"), + _("%d transactions inserted\n%d errors in the file"), + count, error); + */ + } + + + return list; +} + +