/* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2018 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 .
*/
//nota: this file should be renamed hb-utils
#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 const double fac[7] = { 1, 10, 100, 1000, 10000, 100000, 1000000 };
double hb_amount_round(const double x, unsigned int digits)
{
digits = MAX(digits, 6);
return floor((x * fac[digits]) + 0.5) / fac[digits];
}
// used to convert from national to euro currency
// used in hb_account.c :: only when convert account to euro
// round option is to 0.5 case so 1.32 is 1.3, but 1.35 is 1.4
gdouble hb_amount_to_euro(gdouble amount)
{
return hb_amount_round((amount * PREFS->euro_value), PREFS->minor_cur.frac_digits);
}
/* new >5.1 currency fct
*
* convert an amount in base currency
*
*/
gdouble hb_amount_base(gdouble value, guint32 kcur)
{
gdouble newvalue;
Currency *cur;
if(kcur == GLOBALS->kcur)
return value;
cur = da_cur_get(kcur);
if(cur == NULL || cur->rate == 0.0)
return 0;
newvalue = value / cur->rate;
return hb_amount_round(newvalue, cur->frac_digits);
}
static Currency *hb_strfmon_check(gchar *outstr, guint32 kcur)
{
Currency *cur = da_cur_get(kcur);
if(cur == NULL)
g_stpcpy(outstr, "nan");
return cur;
}
gchar *hb_str_rate(gchar *outstr, gint outlen, gdouble rate)
{
gint count, i;
gchar *p;
count = g_snprintf(outstr, outlen, "%.6f", rate);
//remove trailing 0 and decimal point
p = &outstr[count-1];
for(i=count;i>0;i--)
{
if(*p == '0')
*p = '\0';
else
break;
p--;
}
if(*p == '.' || *p == ',')
*p = '\0';
return outstr;
}
/* this function copy a number 99999.99 at s into d and count
* number of digits for integer part and decimal part
*/
static gchar * _strfnumcopycount(gchar *s, gchar *d, gchar *decchar, gint *plen, gint *pnbint, gint *pnbdec)
{
gint len=0, nbint=0, nbdec=0;
// sign part
if(*s == '-') {
*d++ = *s++;
len++;
}
// integer part
while(*s != 0 && *s != '.') {
*d++ = *s++;
nbint++;
len++;
}
// decimal separator
if(*s == '.') {
d = g_stpcpy(d, decchar);
len++;
s++;
}
// decimal part
while(*s != 0) {
*d++ = *s++;
nbdec++;
len++;
}
// end string | fill external count
*d = 0;
*plen = len;
*pnbint = nbint;
*pnbdec = nbdec;
return d;
}
//todo: used only in ui_prefs.c
gchar *hb_str_formatd(gchar *outstr, gint outlen, gchar *buf1, Currency *cur, gboolean showsymbol)
{
gint len, nbd, nbi;
gchar *s, *d, *tmp;
d = tmp = outstr;
if(showsymbol && cur->sym_prefix)
{
d = g_stpcpy (d, cur->symbol);
*d++ = ' ';
tmp = d;
}
d = _strfnumcopycount(buf1, d, cur->decimal_char, &len, &nbi, &nbd);
if( cur->grouping_char != NULL && strlen(cur->grouping_char) > 0 )
{
gint i, grpcnt;
s = buf1;
d = tmp;
if(*s == '-')
*d++ = *s++;
grpcnt = 4 - nbi;
for(i=0;igrouping_char);
}
grpcnt++;
}
if(nbd > 0)
{
d = g_stpcpy(d, cur->decimal_char);
d = g_stpcpy(d, s+1);
}
*d = 0;
}
if(showsymbol && !cur->sym_prefix)
{
*d++ = ' ';
d = g_stpcpy (d, cur->symbol);
}
*d = 0;
return d;
}
void hb_strfmon(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor)
{
gchar formatd_buf[outlen];
Currency *cur;
gdouble monval;
if(minor == FALSE)
{
cur = hb_strfmon_check(outstr, kcur);
if(cur != NULL)
{
monval = hb_amount_round(value, cur->frac_digits);
g_ascii_formatd(formatd_buf, outlen, cur->format, monval);
hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
}
}
else
{
monval = hb_amount_base(value, kcur);
monval = hb_amount_to_euro(monval);
cur = &PREFS->minor_cur;
g_ascii_formatd(formatd_buf, outlen, cur->format, monval);
hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
}
}
void hb_strfmon_int(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor)
{
gchar formatd_buf[outlen];
Currency *cur;
gdouble monval;
if(minor == FALSE)
{
cur = hb_strfmon_check(outstr, kcur);
if(cur != NULL)
{
monval = hb_amount_round(value, cur->frac_digits);
g_ascii_formatd(formatd_buf, outlen, "%0.f", monval);
hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
}
}
else
{
monval = hb_amount_base(value, kcur);
monval = hb_amount_to_euro(monval);
cur = &PREFS->minor_cur;
g_ascii_formatd(formatd_buf, outlen, cur->format, monval);
hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
}
}
void hb_strfnum(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor)
{
gchar formatd_buf[outlen];
Currency *cur;
gdouble monval;
if(minor == FALSE)
{
cur = hb_strfmon_check(outstr, kcur);
if(cur != NULL)
{
monval = hb_amount_round(value, cur->frac_digits);
g_ascii_formatd(formatd_buf, outlen, "%.2f", monval);
hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
}
}
else
{
cur = &PREFS->minor_cur;
monval = hb_amount_to_euro(value);
g_ascii_formatd(formatd_buf, outlen, "%.2f", monval);
hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
}
}
gchar *get_normal_color_amount(gdouble value)
{
gchar *color = NULL;
//fix: 400483
value = hb_amount_round(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 = hb_amount_round(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, guint32 kcur, gboolean minor)
{
gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE];
hb_strfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, value, kcur, 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, guint32 kcur, gboolean minor)
{
gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE];
gchar *markuptxt;
gchar *color = NULL;
hb_strfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, value, kcur, 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);
}
/*
** String utility
*/
gint hb_string_compare(gchar *s1, gchar *s2)
{
gint retval = 0;
if (s1 == NULL || s2 == NULL)
{
if (s1 == NULL && s2 == NULL)
goto end;
retval = (s1 == NULL) ? -1 : 1;
}
else
{
retval = strcasecmp(s1, s2);
}
end:
return retval;
}
/*
* 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++;
}
}
}
void hb_string_replace_char(gchar c, gchar *str)
{
gchar *s = str;
gchar *d = str;
if(str)
{
while( *s )
{
if( *s != c )
{
*d++ = *s;
}
s++;
}
*d = 0;
}
}
gchar *hb_string_copy_jsonpair(gchar *dst, gchar *str)
{
while( *str!='\0' )
{
if( *str=='}' )
break;
if( *str==',' )
{
*dst = '\0';
return str + 1;
}
if( *str!='{' && *str!='\"' )
{
*dst++ = *str;
}
str++;
}
*dst = '\0';
return NULL;
}
void hb_string_inline(gchar *str)
{
gchar *s = str;
gchar *d = str;
if(str)
{
while( *s )
{
if( !(*s==' ' || *s=='\t' || *s=='\n' || *s=='\r') )
{
*d++ = *s;
}
s++;
}
*d = 0;
}
}
/*void strip_extra_spaces(char* str) {
int i,x;
for(i=x=1; str[i]; ++i)
if(!isspace(str[i]) || (i>0 && !isspace(str[i-1])))
str[x++] = str[i];
str[x] = '\0';
}*/
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("\n[utils] hb_date_get_julian\n") );
DB( g_print(" - '%s' dateorder=%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;
}
if(d <= 31 && m <= 12)
{
if( g_date_valid_dmy(d, m, y) )
{
date = g_date_new_dmy(d, m, y);
julian = g_date_get_julian (date);
g_date_free(date);
}
}
DB( g_print(" > %d %d %d julian=%d\n", d, m, y, julian) );
}
return julian;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
gchar *hb_filename_new_with_extension(gchar *filename, const gchar *extension)
{
gchar *lastdot, *fwe;
gchar *newfilename;
DB( g_print("\n[util] filename with extension\n") );
DB( g_print(" - orig: '%s' => '%s'\n", filename, extension) );
//duplicate without extensions
lastdot = g_strrstr(filename, ".");
if(lastdot != NULL)
{
fwe = g_strndup(filename, strlen(filename) - strlen(lastdot));
DB( g_print(" - fwe: '%s'\n", fwe) );
newfilename = g_strdup_printf("%s.%s", fwe, extension);
g_free(fwe);
}
else
{
newfilename = g_strdup_printf("%s.%s", filename, extension);
}
DB( g_print(" - new: '%s'\n", newfilename) );
return newfilename;
}
/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
gboolean hb_string_isdate(gchar *str)
{
gint d, m, y;
return(hb_date_parser_get_nums(str, &d, &m, &y));
}
gboolean hb_string_isdigit(gchar *str)
{
gboolean valid = TRUE;
while(*str && valid)
valid = g_ascii_isdigit(*str++);
return valid;
}
/*
gboolean hb_string_isprint(gchar *str)
{
gboolean valid = TRUE;
while(*str && valid)
valid = g_ascii_isprint(*str++);
return valid;
}
*/
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;
}
gchar *hb_sprint_date(gchar *outstr, guint32 julian)
{
GDate date;
g_date_clear(&date, 1);
g_date_set_julian (&date, julian);
switch(PREFS->dtex_datefmt)
{
case PRF_DATEFMT_MDY:
{
g_sprintf(outstr, "%02d/%02d/%04d",
g_date_get_month(&date),
g_date_get_day(&date),
g_date_get_year(&date)
);
}
break;
case PRF_DATEFMT_DMY:
{
g_sprintf(outstr, "%02d/%02d/%04d",
g_date_get_day(&date),
g_date_get_month(&date),
g_date_get_year(&date)
);
}
break;
default:
g_sprintf(outstr, "%04d/%02d/%02d",
g_date_get_year(&date),
g_date_get_month(&date),
g_date_get_day(&date)
);
break;
}
return outstr;
}
//used only in DB() macro !!
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