/* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2014 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-misc.h"
#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 gdouble fint(gdouble amount)
{
gdouble fi;
modf(amount, &fi);
return(fi);
}
static unsigned dix_puissance_n(unsigned n)
{
unsigned i, retval = 1;
for(i = 0; i < n; i++)
retval *= 10;
return retval;
}
double arrondi(const double x, unsigned n)
{
unsigned N = dix_puissance_n(n);
return floor((x * N) + 0.5) / N;
}
// new for v4.5
/*
* convert an amount in base currency
*
*/
gdouble to_base_amount(gdouble value, guint32 kcur)
{
/*
gdouble newvalue;
Currency *cur;
if(kcur == GLOBALS->kcur)
return value;
cur = da_cur_get(kcur);
if(cur == NULL)
return 0;
newvalue = value * cur->rate;
return newvalue;
*/
return value;
}
/* new currency fct
static gint real_mystrfmoncurr(gchar *outstr, gint outlen, gchar *buf1, Currency *cur)
{
gint size = 0;
gchar groupbuf[G_ASCII_DTOSTR_BUF_SIZE];
gchar **str_array;
guint i, length;
gchar *monstr;
str_array = g_strsplit(buf1, ".", 2);
monstr = NULL;
length = strlen(str_array[0]);
if( cur->grouping_char == NULL || !strlen(cur->grouping_char) )
{
monstr = g_strjoinv(cur->decimal_char, str_array);
}
else
{
gchar *s = str_array[0];
gchar *d = groupbuf;
i = 0;
// avoid the - for negative amount
if( *s == '-')
{
*d++ = *s++;
length--;
}
// do the grouping
do
{
if( i!=0 && (length % 3) == 0 )
{
gchar *gc = cur->grouping_char;
while( *gc )
*d++ = *gc++;
}
*d++ = *s;
length--;
i++;
}
while (length && *s++ != '\0');
*d = 0;
monstr = g_strjoin(cur->decimal_char, groupbuf, str_array[1], NULL);
}
//debug
//g_print("**\nmystrfmon %.2f\n 0=%s\n 1=%s\n [%d]\n", value, str_array[0], str_array[1], length );
//g_print(" => %s :: %s\n", monstr, groupbuf);
g_strfreev(str_array);
// insert our formated number with symbol
g_snprintf(outstr, outlen, cur->monfmt, monstr);
g_free(monstr);
return size;
}
static Currency *hb_strfmon_check(gchar *outstr, guint32 kcur)
{
Currency *cur = da_cur_get(kcur);
if(cur == NULL)
g_stpcpy(outstr, "error");
return cur;
}
void hb_strfmon(gchar *outstr, gint outlen, gdouble value, guint32 kcur)
{
gchar formatd_buf[G_ASCII_DTOSTR_BUF_SIZE];
Currency *cur;
gdouble monval;
cur = hb_strfmon_check(outstr, kcur);
if(cur != NULL)
{
monval = arrondi(value, cur->frac_digits);
g_ascii_formatd(formatd_buf, G_ASCII_DTOSTR_BUF_SIZE-1, cur->format, monval);
real_mystrfmoncurr(outstr, outlen, formatd_buf, cur);
}
}
void hb_strfmon_int(gchar *outstr, gint outlen, gdouble value, guint32 kcur)
{
gchar formatd_buf[G_ASCII_DTOSTR_BUF_SIZE];
Currency *cur;
gdouble monval;
cur = hb_strfmon_check(outstr, kcur);
if(cur != NULL)
{
monval = arrondi(value, cur->frac_digits);
g_ascii_formatd(formatd_buf, sizeof (formatd_buf), "%0.f", monval);
real_mystrfmoncurr(outstr, outlen, formatd_buf, cur);
}
}
//todo: remove this
// test for currecny choose dialog
void mystrfmoncurrcurr(gchar *outstr, gint outlen, gdouble value, Currency *cur)
{
gchar formatd_buf[G_ASCII_DTOSTR_BUF_SIZE];
gdouble monval;
monval = arrondi(value, cur->frac_digits);
g_ascii_formatd(formatd_buf, G_ASCII_DTOSTR_BUF_SIZE-1, cur->format, monval);
real_mystrfmoncurr(outstr, outlen, formatd_buf, cur);
}
*/
/* obsolete before currencies */
gint real_mystrfmon(gchar *outstr, gint outlen, gchar *buf1, struct CurrencyFmt *cur)
{
gint size = 0;
gchar groupbuf[G_ASCII_DTOSTR_BUF_SIZE];
gchar **str_array;
guint i, length;
gchar *monstr;
str_array = g_strsplit(buf1, ".", 2);
monstr = NULL;
length = strlen(str_array[0]);
if( cur->grouping_char == NULL || !strlen(cur->grouping_char) )
{
monstr = g_strjoinv(cur->decimal_char, str_array);
}
else
{
gchar *s = str_array[0];
gchar *d = groupbuf;
i = 0;
// avoid the - for negative amount
if( *s == '-')
{
*d++ = *s++;
length--;
}
// do the grouping
do
{
if( i!=0 && (length % 3) == 0 )
{
gchar *gc = cur->grouping_char;
while( *gc )
*d++ = *gc++;
}
*d++ = *s;
length--;
i++;
}
while (length && *s++ != '\0');
*d = 0;
monstr = g_strjoin(cur->decimal_char, groupbuf, str_array[1], NULL);
}
//debug
//g_print("**\nmystrfmon %.2f\n 0=%s\n 1=%s\n [%d]\n", value, str_array[0], str_array[1], length );
//g_print(" => %s :: %s\n", monstr, groupbuf);
g_strfreev(str_array);
// insert our formated number with symbol
g_snprintf(outstr, outlen, cur->monfmt, monstr);
g_free(monstr);
return size;
}
gint mystrfmon(gchar *outstr, gint outlen, gdouble value, gboolean minor)
{
struct CurrencyFmt *cur;
gchar formatd_buf[G_ASCII_DTOSTR_BUF_SIZE];
gdouble monval;
gint size;
cur = minor ? &PREFS->minor_cur : &PREFS->base_cur;
monval = arrondi(value, cur->frac_digits);
if(minor == TRUE)
{
monval = (value * PREFS->euro_value);
monval += (monval > 0.0) ? 0.005 : -0.005;
monval = (fint(monval * 100) / 100);
}
//DB( g_print("fmt = %s\n", cur->format) );
g_ascii_formatd(formatd_buf, sizeof (formatd_buf), cur->format, monval);
size = real_mystrfmon(outstr, outlen, formatd_buf, cur);
return size;
}
gint mystrfmon_int(gchar *outstr, gint outlen, gdouble value, gboolean minor)
{
struct CurrencyFmt *cur;
gchar formatd_buf[G_ASCII_DTOSTR_BUF_SIZE];
gdouble monval = value;
gint size;
cur = minor ? &PREFS->minor_cur : &PREFS->base_cur;
if(minor == TRUE)
{
monval = (value * PREFS->euro_value);
monval += (monval > 0.0) ? 0.005 : -0.005;
monval = (fint(monval * 100) / 100);
}
g_ascii_formatd(formatd_buf, sizeof (formatd_buf), "%0.f", monval);
size = real_mystrfmon(outstr, outlen, formatd_buf, cur);
return size;
}
/* end obsolete call */
gchar *get_normal_color_amount(gdouble value)
{
gchar *color = NULL;
//fix: 400483
value = arrondi(value, 2);
if(value != 0.0 && PREFS->custom_colors == TRUE)
{
color = (value > 0.0) ? PREFS->color_inc : PREFS->color_exp;
}
return color;
}
gchar *get_minimum_color_amount(gdouble value, gdouble minvalue)
{
gchar *color = NULL;
//fix: 400483
value = arrondi(value, 2);
if(value != 0.0 && PREFS->custom_colors == TRUE)
{
color = (value > 0.0) ? PREFS->color_inc : PREFS->color_exp;
if( value < minvalue)
color = PREFS->color_warn;
}
return color;
}
void hb_label_set_amount(GtkLabel *label, gdouble value, gboolean minor)
{
gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE];
mystrfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, value, minor);
gtk_label_set_text(GTK_LABEL(label), strbuffer);
}
/*
** format/color and set a label text with a amount value
*/
void hb_label_set_colvalue(GtkLabel *label, gdouble value, gboolean minor)
{
gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE];
gchar *markuptxt;
gchar *color = NULL;
mystrfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, value, minor);
if(value != 0.0 && PREFS->custom_colors == TRUE)
{
color = get_normal_color_amount(value);
//g_print("color: %s\n", color);
if(color)
{
markuptxt = g_strdup_printf("%s", color, strbuffer);
gtk_label_set_markup(GTK_LABEL(label), markuptxt);
g_free(markuptxt);
return;
}
}
gtk_label_set_text(GTK_LABEL(label), strbuffer);
}
/*
void hb_label_set_colvaluecurr(GtkLabel *label, gdouble value, guint32 currkey)
{
gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE];
gchar *markuptxt;
gchar *color = NULL;
hb_strfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, value, currkey);
if(value != 0.0 && PREFS->custom_colors == TRUE)
{
color = get_normal_color_amount(value);
if(color)
{
markuptxt = g_strdup_printf("%s", color, strbuffer);
gtk_label_set_markup(GTK_LABEL(label), markuptxt);
g_free(markuptxt);
return;
}
}
gtk_label_set_text(GTK_LABEL(label), strbuffer);
}
*/
/*
void get_range_minmax(guint32 refdate, gint range, guint32 *mindate, guint32 *maxdate)
{
GDate *date;
guint month, year, qnum;
if(refdate > *maxdate)
refdate = *maxdate;
date = g_date_new_julian(refdate);
month = g_date_get_month(date);
year = g_date_get_year(date);
qnum = ((month-1)/3)+1;
DB( g_print("m=%d, y=%d, qnum=%d\n", month, year, qnum) );
switch( range )
{
case 0: // this month
g_date_set_day(date, 1);
*mindate = g_date_get_julian(date);
g_date_add_days(date, g_date_get_days_in_month(month, year)-1);
*maxdate = g_date_get_julian(date);
break;
case 1: // last month
g_date_set_day(date, 1);
g_date_subtract_months(date, 1);
*mindate = g_date_get_julian(date);
month = g_date_get_month(date);
year = g_date_get_year(date);
g_date_add_days(date, g_date_get_days_in_month(month, year)-1);
*maxdate = g_date_get_julian(date);
break;
case 2: // this quarter
g_date_set_day(date, 1);
g_date_set_month(date, (qnum-1)*3+1);
*mindate = g_date_get_julian(date);
g_date_add_months(date, 3);
g_date_subtract_days(date, 1);
*maxdate = g_date_get_julian(date);
break;
case 3: // last quarter
g_date_set_day(date, 1);
g_date_set_month(date, (qnum-1)*3+1);
g_date_subtract_months(date, 3);
*mindate = g_date_get_julian(date);
g_date_add_months(date, 3);
g_date_subtract_days(date, 1);
*maxdate = g_date_get_julian(date);
break;
case 4: // this year
g_date_set_dmy(date, 1, 1, year);
*mindate = g_date_get_julian(date);
g_date_set_dmy(date, 31, 12, year);
*maxdate = g_date_get_julian(date);
break;
// separator
case 6: // last 30 days
*mindate = refdate - 30;
*maxdate = refdate;
break;
case 7: // last 60 days
*mindate = refdate - 60;
*maxdate = refdate;
break;
case 8: // last 90 days
*mindate = refdate - 90;
*maxdate = refdate;
break;
case 9: // last 12 months
g_date_subtract_months(date, 12);
*mindate = g_date_get_julian(date);
*maxdate = refdate;
break;
}
g_date_free(date);
}
*/
/*
** String utility
*/
/*
* compare 2 utf8 string
*/
gint hb_string_utf8_compare(gchar *s1, gchar *s2)
{
gint retval = 0;
gchar *ns1, *ns2;
if (s1 == NULL || s2 == NULL)
{
if (s1 == NULL && s2 == NULL)
goto end;
retval = (s1 == NULL) ? -1 : 1;
}
else
{
//#1325969
//retval = g_utf8_collate(s1 != NULL ? s1 : "", s2 != NULL ? s2 : "");
ns1 = g_utf8_normalize(s1, -1, G_NORMALIZE_DEFAULT);
ns2 = g_utf8_normalize(s2, -1, G_NORMALIZE_DEFAULT);
retval = strcasecmp(ns1, ns2);
g_free(ns2);
g_free(ns1);
}
end:
return retval;
}
void hb_string_strip_crlf(gchar *str)
{
gchar *p = str;
if(str)
{
while( *p )
{
if( *p == '\n' || *p == '\r')
{
*p = '\0';
}
p++;
}
}
}
gchar*
hb_strdup_nobrackets (const gchar *str)
{
const gchar *s;
gchar *new_str, *d;
gsize length;
if (str)
{
length = strlen (str) + 1;
new_str = g_new (char, length);
s = str;
d = new_str;
while(*s != '\0')
{
if( *s != '[' && *s != ']' )
*d++ = *s;
s++;
}
*d = '\0';
}
else
new_str = NULL;
return new_str;
}
static gboolean
hb_date_parser_get_nums(gchar *string, gint *n1, gint *n2, gint *n3)
{
gboolean retval;
gchar **str_array;
//DB( g_print("(qif) hb_qif_parser_get_dmy for '%s'\n", string) );
retval = FALSE;
str_array = g_strsplit (string, "/", 3);
if( g_strv_length( str_array ) != 3 )
{
g_strfreev (str_array);
str_array = g_strsplit (string, ".", 3);
// fix 371381
//todo test
if( g_strv_length( str_array ) != 3 )
{
g_strfreev (str_array);
str_array = g_strsplit (string, "-", 3);
}
}
if( g_strv_length( str_array ) == 3 )
{
*n1 = atoi(str_array[0]);
*n2 = atoi(str_array[1]);
*n3 = atoi(str_array[2]);
retval = TRUE;
}
g_strfreev (str_array);
return retval;
}
guint32 hb_date_get_julian(gchar *string, gint datefmt)
{
GDate *date;
gint n1, n2, n3, d, m, y;
guint32 julian = 0;
DB( g_print("hb_date_get_julian: %s, %d\n", string, datefmt) );
if( hb_date_parser_get_nums(string, &n1, &n2, &n3) )
{
DB( g_print("-> %d %d %d\n", n1, n2, n3) );
switch(datefmt)
{
case PRF_DATEFMT_MDY:
d = n2;
m = n1;
y = n3;
break;
case PRF_DATEFMT_DMY:
d = n1;
m = n2;
y = n3;
break;
default:
case PRF_DATEFMT_YMD:
d = n3;
m = n2;
y = n1;
break;
}
//correct for 2 digits year
if(y < 1970)
{
if(y < 60)
y += 2000;
else
y += 1900;
}
DB( g_print("-> %d %d %d\n", d, m, y) );
if(d <= 31 && m <= 12)
{
date = g_date_new();
g_date_set_dmy(date, d, m, y);
if( g_date_valid (date) )
{
julian = g_date_get_julian (date);
}
g_date_free(date);
}
}
return julian;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
gchar *hb_filename_new_with_extention(gchar *filename, const gchar *extension)
{
gchar *dirname;
gchar *basename;
gchar *newbasename;
gchar *newfilename;
gchar **str_array;
dirname = g_path_get_dirname (filename);
basename = g_path_get_basename(filename);
str_array = g_strsplit(basename, ".", 0);
newbasename = g_strdup_printf("%s.%s", str_array[0], extension);
newfilename = g_build_filename(dirname, newbasename, NULL);
g_strfreev(str_array);
g_free(basename);
g_free(dirname);
g_free(newbasename);
return newfilename;
}
/* file backup, qif export */
/*gchar *homebank_filename_without_extention(gchar *path)
{
gchar *basename;
gchar *newname;
gchar **str_array;
basename = g_path_get_basename(path);
str_array = g_strsplit(basename, ".", 0);
newname = g_strdup(str_array[0]);
g_strfreev(str_array);
g_free(basename);
return newname;
}*/
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
static gboolean hb_string_isdate(gchar *str)
{
gint d, m, y;
return(hb_date_parser_get_nums(str, &d, &m, &y));
}
static gboolean hb_string_isdigit(gchar *str)
{
gboolean valid = TRUE;
while(*str && valid)
valid = g_ascii_isdigit(*str++);
return valid;
}
/*
static gboolean hb_string_isprint(gchar *str)
{
gboolean valid = TRUE;
while(*str && valid)
valid = g_ascii_isprint(*str++);
return valid;
}
*/
static gboolean hb_string_isprint(gchar *str)
{
gboolean valid = TRUE;
gchar *p;
gunichar c;
if(g_utf8_validate(str, -1, NULL))
{
p = str;
while(*p && valid)
{
c = g_utf8_get_char(p);
valid = g_unichar_isprint(c);
p = g_utf8_next_char(p);
}
}
return valid;
}
gboolean hb_string_csv_valid(gchar *str, guint nbcolumns, gint *csvtype)
{
gchar **str_array;
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) );
hb_string_strip_crlf(str);
str_array = g_strsplit (str, ";", 0);
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 :
g_ascii_strtod(str_array[i], NULL);
//todo : see this errno
if( errno )
{
DB( g_print("errno: %d\n", errno) );
valid = FALSE;
}
break;
}
}
csvend:
g_strfreev (str_array);
DB( g_print(" --> return %d\n", valid) );
return valid;
}
void hb_print_date(guint32 jdate, gchar *label)
{
gchar buffer1[128];
GDate *date;
date = g_date_new_julian(jdate);
g_date_strftime (buffer1, 128-1, "%x", date);
g_date_free(date);
g_print(" - %s %s\n", label != NULL ? label:"date is", buffer1);
}
/*
** parse a string an retrieve an iso date (dd-mm-yy(yy) or dd/mm/yy(yy))
**
*/
/* obsolete 4.5
guint32 hb_date_get_julian_parse(gchar *str)
{
gchar **str_array = NULL;
GDate *date;
guint d, m, y;
guint32 julian = GLOBALS->today;
// try with - separator
if( g_strrstr(str, "-") != NULL )
{
str_array = g_strsplit (str, "-", 3);
}
else
{
if( g_strrstr(str, "/") != NULL )
{
str_array = g_strsplit (str, "/", 3);
}
}
if( g_strv_length( str_array ) == 3 )
{
d = atoi(str_array[0]);
m = atoi(str_array[1]);
y = atoi(str_array[2]);
//correct for 2 digits year
if(y < 1970)
{
if(y < 60)
y += 2000;
else
y += 1900;
}
//todo: here if month is > 12 then the format is probably mm/dd/yy(yy)
//or maybe check with g_date_valid_julian(julian)
date = g_date_new();
g_date_set_dmy(date, d, m, y);
julian = g_date_get_julian (date);
g_date_free(date);
DB( g_print("date: %s :: %d %d %d :: %d\n", str, d, m, y, julian ) );
}
g_strfreev (str_array);
return julian;
}
*/
/* -------------------- */
#if MYDEBUG == 1
/*
** hex memory dump
*/
#define MAX_DUMP 16
void hex_dump(guchar *ptr, guint length)
{
guchar ascii[MAX_DUMP+4];
guint i,j;
g_print("**hex_dump - %d bytes\n", length);
for(i=0;i= length) break;
//store ascii value
if(ptr[i] >= 32 && ptr[i] <= 126)
ascii[j] = ptr[i];
else
ascii[j] = '.';
g_print("%02x ", ptr[i]);
i++;
}
//newline
ascii[j] = 0;
g_print(" '%s'\n", ascii);
}
}
#endif