1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2019 Maxime DOYEN
4 * This file is part of HomeBank.
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.
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.
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/>.
21 //nota: this file should be renamed hb-utils
34 /* our global datas */
35 extern struct HomeBank
*GLOBALS
;
36 extern struct Preferences
*PREFS
;
38 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
40 static const double fac
[9] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
42 double hb_amount_round(const double x
, unsigned int digits
)
44 digits
= MAX(digits
, 8);
45 return floor((x
* fac
[digits
]) + 0.5) / fac
[digits
];
49 // used to convert from national to euro currency
50 // used in hb_account.c :: only when convert account to euro
51 // round option is to 0.5 case so 1.32 is 1.3, but 1.35 is 1.4
53 gdouble
hb_amount_to_euro(gdouble amount
)
55 return hb_amount_round((amount
* PREFS
->euro_value
), PREFS
->minor_cur
.frac_digits
);
59 /* new >5.1 currency fct
61 * convert an amount in base currency
64 gdouble
hb_amount_base(gdouble value
, guint32 kcur
)
69 if(kcur
== GLOBALS
->kcur
)
72 cur
= da_cur_get(kcur
);
73 if(cur
== NULL
|| cur
->rate
== 0.0)
76 newvalue
= value
/ cur
->rate
;
77 return hb_amount_round(newvalue
, cur
->frac_digits
);
81 static Currency
*hb_strfmon_check(gchar
*outstr
, guint32 kcur
)
83 Currency
*cur
= da_cur_get(kcur
);
86 g_stpcpy(outstr
, "nan");
91 gchar
*hb_str_rate(gchar
*outstr
, gint outlen
, gdouble rate
)
96 count
= g_snprintf(outstr
, outlen
, "%.6f", rate
);
97 //remove trailing 0 and decimal point
107 if(*p
== '.' || *p
== ',')
113 /* this function copy a number 99999.99 at s into d and count
114 * number of digits for integer part and decimal part
116 static gchar
* _strfnumcopycount(gchar
*s
, gchar
*d
, gchar
*decchar
, gint
*plen
, gint
*pnbint
, gint
*pnbdec
)
118 gint len
=0, nbint
=0, nbdec
=0;
126 while(*s
!= 0 && *s
!= '.') {
133 d
= g_stpcpy(d
, decchar
);
143 // end string | fill external count
152 //todo: used only in ui_prefs.c
153 gchar
*hb_str_formatd(gchar
*outstr
, gint outlen
, gchar
*buf1
, Currency
*cur
, gboolean showsymbol
)
159 if(showsymbol
&& cur
->sym_prefix
)
161 d
= g_stpcpy (d
, cur
->symbol
);
166 d
= _strfnumcopycount(buf1
, d
, cur
->decimal_char
, &len
, &nbi
, &nbd
);
168 if( cur
->grouping_char
!= NULL
&& strlen(cur
->grouping_char
) > 0 )
181 if( !(grpcnt
% 3) && i
<(nbi
-1))
183 d
= g_stpcpy(d
, cur
->grouping_char
);
190 d
= g_stpcpy(d
, cur
->decimal_char
);
191 d
= g_stpcpy(d
, s
+1);
196 if(showsymbol
&& !cur
->sym_prefix
)
199 d
= g_stpcpy (d
, cur
->symbol
);
208 void hb_strfmon(gchar
*outstr
, gint outlen
, gdouble value
, guint32 kcur
, gboolean minor
)
210 gchar formatd_buf
[outlen
];
216 cur
= hb_strfmon_check(outstr
, kcur
);
219 monval
= hb_amount_round(value
, cur
->frac_digits
);
220 g_ascii_formatd(formatd_buf
, outlen
, cur
->format
, monval
);
221 hb_str_formatd(outstr
, outlen
, formatd_buf
, cur
, TRUE
);
226 monval
= hb_amount_base(value
, kcur
);
227 monval
= hb_amount_to_euro(monval
);
228 cur
= &PREFS
->minor_cur
;
229 g_ascii_formatd(formatd_buf
, outlen
, cur
->format
, monval
);
230 hb_str_formatd(outstr
, outlen
, formatd_buf
, cur
, TRUE
);
236 void hb_strfmon_int(gchar
*outstr
, gint outlen
, gdouble value
, guint32 kcur
, gboolean minor
)
238 gchar formatd_buf
[outlen
];
244 cur
= hb_strfmon_check(outstr
, kcur
);
247 monval
= hb_amount_round(value
, cur
->frac_digits
);
248 g_ascii_formatd(formatd_buf
, outlen
, "%0.f", monval
);
249 hb_str_formatd(outstr
, outlen
, formatd_buf
, cur
, TRUE
);
254 monval
= hb_amount_base(value
, kcur
);
255 monval
= hb_amount_to_euro(monval
);
256 cur
= &PREFS
->minor_cur
;
257 g_ascii_formatd(formatd_buf
, outlen
, cur
->format
, monval
);
258 hb_str_formatd(outstr
, outlen
, formatd_buf
, cur
, TRUE
);
264 void hb_strfnum(gchar
*outstr
, gint outlen
, gdouble value
, guint32 kcur
, gboolean minor
)
266 gchar formatd_buf
[outlen
];
272 cur
= hb_strfmon_check(outstr
, kcur
);
275 monval
= hb_amount_round(value
, cur
->frac_digits
);
276 g_ascii_formatd(formatd_buf
, outlen
, "%.2f", monval
);
277 hb_str_formatd(outstr
, outlen
, formatd_buf
, cur
, TRUE
);
282 cur
= &PREFS
->minor_cur
;
283 monval
= hb_amount_to_euro(value
);
284 g_ascii_formatd(formatd_buf
, outlen
, "%.2f", monval
);
285 hb_str_formatd(outstr
, outlen
, formatd_buf
, cur
, TRUE
);
292 gchar
*get_normal_color_amount(gdouble value
)
297 value
= hb_amount_round(value
, 2);
299 if(value
!= 0.0 && PREFS
->custom_colors
== TRUE
)
301 color
= (value
> 0.0) ? PREFS
->color_inc
: PREFS
->color_exp
;
307 gchar
*get_minimum_color_amount(gdouble value
, gdouble minvalue
)
312 value
= hb_amount_round(value
, 2);
313 if(value
!= 0.0 && PREFS
->custom_colors
== TRUE
)
315 color
= (value
> 0.0) ? PREFS
->color_inc
: PREFS
->color_exp
;
316 if( value
< minvalue
)
317 color
= PREFS
->color_warn
;
322 void hb_label_set_amount(GtkLabel
*label
, gdouble value
, guint32 kcur
, gboolean minor
)
324 gchar strbuffer
[G_ASCII_DTOSTR_BUF_SIZE
];
326 hb_strfmon(strbuffer
, G_ASCII_DTOSTR_BUF_SIZE
-1, value
, kcur
, minor
);
327 gtk_label_set_text(GTK_LABEL(label
), strbuffer
);
333 ** format/color and set a label text with a amount value
335 void hb_label_set_colvalue(GtkLabel
*label
, gdouble value
, guint32 kcur
, gboolean minor
)
337 gchar strbuffer
[G_ASCII_DTOSTR_BUF_SIZE
];
341 hb_strfmon(strbuffer
, G_ASCII_DTOSTR_BUF_SIZE
-1, value
, kcur
, minor
);
343 if(value
!= 0.0 && PREFS
->custom_colors
== TRUE
)
345 color
= get_normal_color_amount(value
);
347 //g_print("color: %s\n", color);
351 markuptxt
= g_strdup_printf("<span color='%s'>%s</span>", color
, strbuffer
);
352 gtk_label_set_markup(GTK_LABEL(label
), markuptxt
);
358 gtk_label_set_text(GTK_LABEL(label
), strbuffer
);
369 gint
hb_string_compare(gchar
*s1
, gchar
*s2
)
373 if (s1
== NULL
|| s2
== NULL
)
375 if (s1
== NULL
&& s2
== NULL
)
378 retval
= (s1
== NULL
) ? -1 : 1;
382 retval
= strcasecmp(s1
, s2
);
389 gint
hb_string_utf8_strstr(gchar
*haystack
, gchar
*needle
, gboolean exact
)
395 if( g_strstr_len(haystack
, -1, needle
) != NULL
)
397 DB( g_print(" found case '%s'\n", needle
) );
403 gchar
*nchaystack
= g_utf8_casefold(haystack
, -1);
404 gchar
*ncneedle
= g_utf8_casefold(needle
, -1);
406 if( g_strrstr(nchaystack
, ncneedle
) != NULL
)
408 DB( g_print(" found nocase '%s'\n", ncneedle
) );
423 * compare 2 utf8 string
425 gint
hb_string_utf8_compare(gchar
*s1
, gchar
*s2
)
430 if (s1
== NULL
|| s2
== NULL
)
432 if (s1
== NULL
&& s2
== NULL
)
435 retval
= (s1
== NULL
) ? -1 : 1;
440 //retval = g_utf8_collate(s1 != NULL ? s1 : "", s2 != NULL ? s2 : "");
441 ns1
= g_utf8_normalize(s1
, -1, G_NORMALIZE_DEFAULT
);
442 ns2
= g_utf8_normalize(s2
, -1, G_NORMALIZE_DEFAULT
);
443 retval
= strcasecmp(ns1
, ns2
);
452 void hb_string_strip_crlf(gchar
*str
)
460 if( *p
== '\n' || *p
== '\r')
470 void hb_string_replace_char(gchar c
, gchar
*str
)
490 gchar
*hb_string_copy_jsonpair(gchar
*dst
, gchar
*str
)
504 if( *str
!='{' && *str
!='\"' )
515 void hb_string_inline(gchar
*str
)
524 if( !(*s
==' ' || *s
=='\t' || *s
=='\n' || *s
=='\r') )
535 /*void strip_extra_spaces(char* str) {
537 for(i=x=1; str[i]; ++i)
538 if(!isspace(str[i]) || (i>0 && !isspace(str[i-1])))
546 hb_strdup_nobrackets (const gchar
*str
)
554 length
= strlen (str
) + 1;
555 new_str
= g_new (char, length
);
560 if( *s
!= '[' && *s
!= ']' )
573 /* if we found a . or , within last x digits it might be a dchar */
574 static gchar
hb_string_raw_amount_guess_dchar(const gchar
*s
, gint len
, gshort digit
)
579 DB( g_print(" digit=%d maxidx=%d\n", digit
, len
-digit
-1) );
582 for(i
=len
-1;i
>=0;i
--)
584 DB( g_print(" [%2d] '%c' %d %d '%c'\n", i
, s
[i
], nbc
, nbd
, gdc
) );
585 //store rightmost ,. within digit-1
586 if( i
>=(len
-digit
-1) )
588 if(s
[i
]=='.' || s
[i
]==',')
592 else if(s
[i
]==',') nbc
++;
594 if( gdc
=='.' && nbd
> 1) gdc
='?';
595 else if( gdc
==',' && nbc
> 1) gdc
='?';
601 gchar
*hb_string_dup_raw_amount_clean(const gchar
*string
, gint digits
)
606 const gchar
*p
= string
;
609 gdc
= hb_string_raw_amount_guess_dchar(string
, l
, digits
);
611 new_str
= d
= g_malloc (l
+1);
614 if(*p
=='-' || g_ascii_isdigit(*p
) )
630 hb_date_parser_get_nums(gchar
*string
, gint
*n1
, gint
*n2
, gint
*n3
)
635 //DB( g_print("(qif) hb_qif_parser_get_dmy for '%s'\n", string) );
640 str_array
= g_strsplit (string
, "/", 3);
641 if( g_strv_length( str_array
) != 3 )
643 g_strfreev (str_array
);
644 str_array
= g_strsplit (string
, ".", 3);
647 if( g_strv_length( str_array
) != 3 )
649 g_strfreev (str_array
);
650 str_array
= g_strsplit (string
, "-", 3);
654 if( g_strv_length( str_array
) == 3 )
656 *n1
= atoi(str_array
[0]);
657 *n2
= atoi(str_array
[1]);
658 *n3
= atoi(str_array
[2]);
662 g_strfreev (str_array
);
668 guint32
hb_date_get_julian(gchar
*string
, gint datefmt
)
671 gint n1
, n2
, n3
, d
, m
, y
;
674 DB( g_print("\n[utils] hb_date_get_julian\n") );
676 DB( g_print(" - '%s' dateorder=%d\n", string
, datefmt
) );
678 if( hb_date_parser_get_nums(string
, &n1
, &n2
, &n3
) )
680 DB( g_print(" - '%d' '%d' '%d'\n", n1
, n2
, n3
) );
684 case PRF_DATEFMT_MDY
:
689 case PRF_DATEFMT_DMY
:
695 case PRF_DATEFMT_YMD
:
702 //correct for 2 digits year
711 if(d
<= 31 && m
<= 12)
713 if( g_date_valid_dmy(d
, m
, y
) )
715 date
= g_date_new_dmy(d
, m
, y
);
716 julian
= g_date_get_julian (date
);
721 DB( g_print(" > %d %d %d julian=%d\n", d
, m
, y
, julian
) );
729 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
731 gint
hb_filename_type_get_by_extension(gchar
*filepath
)
733 gint retval
= FILETYPE_UNKNOWN
;
736 g_return_val_if_fail(filepath
!= NULL
, FILETYPE_UNKNOWN
);
738 str_len
= strlen(filepath
);
741 if( strcasecmp(filepath
+ str_len
- 4, ".ofx") == 0)
742 retval
= FILETYPE_OFX
;
744 if( strcasecmp(filepath
+ str_len
- 4, ".qif") == 0)
745 retval
= FILETYPE_QIF
;
747 if( strcasecmp(filepath
+ str_len
- 4, ".qfx") == 0)
748 retval
= FILETYPE_OFX
;
750 if( strcasecmp(filepath
+ str_len
- 4, ".csv") == 0)
751 retval
= FILETYPE_CSV_HB
;
753 if( strcasecmp(filepath
+ str_len
- 4, ".xhb") == 0)
754 retval
= FILETYPE_HOMEBANK
;
760 static gchar
*hb_filename_new_without_extension(gchar
*filename
)
764 lastdot
= g_strrstr (filename
, ".");
767 return g_strndup(filename
, strlen(filename
) - strlen(lastdot
));
769 return g_strdup(filename
);
773 static gint
hb_filename_backup_list_sort_func(gchar
**a
, gchar
**b
)
775 gint da
= atoi( *a
+ strlen(*a
) - 12);
776 gint db
= atoi( *b
+ strlen(*b
) - 12);
782 GPtrArray
*hb_filename_backup_list(gchar
*filename
)
784 gchar
*dirname
, *basename
;
785 gchar
*rawfilename
, *pattern
;
787 const gchar
*tmpname
;
791 DB( g_print("\n[util] filename backup list\n") );
793 dirname
= g_path_get_dirname(filename
);
794 basename
= g_path_get_basename(filename
);
796 DB( g_print(" dir='%s' base='%s'\n", dirname
, basename
) );
798 rawfilename
= hb_filename_new_without_extension(basename
);
799 pattern
= g_strdup_printf("%s-????????.bak", rawfilename
);
801 pspec
= g_pattern_spec_new(pattern
);
804 DB( g_print(" pattern='%s'\n", pattern
) );
806 array
= g_ptr_array_new_with_free_func(g_free
);
808 dir
= g_dir_open (PREFS
->path_hbfile
, 0, NULL
);
811 while ((tmpname
= g_dir_read_name (dir
)) != NULL
)
815 match
= g_pattern_match_string(pspec
, tmpname
);
818 DB( g_print(" %d => '%s'\n", match
, tmpname
) );
819 g_ptr_array_add(array
, g_strdup(tmpname
));
825 g_pattern_spec_free(pspec
);
831 g_ptr_array_sort(array
, (GCompareFunc
)hb_filename_backup_list_sort_func
);
837 gchar
*hb_filename_backup_get_filtername(gchar
*filename
)
839 gchar
*dirname
, *basename
;
840 gchar
*rawfilename
, *pattern
;
842 DB( g_print("\n[util] filename backup get filtername\n") );
844 dirname
= g_path_get_dirname(filename
);
845 basename
= g_path_get_basename(filename
);
847 DB( g_print(" dir='%s' base='%s'\n", dirname
, basename
) );
849 rawfilename
= hb_filename_new_without_extension(basename
);
851 pattern
= g_strdup_printf("%s*.[Bb][Aa][Kk]", rawfilename
);
861 gchar
*hb_filename_new_for_backup(gchar
*filename
)
863 gchar
*rawfilename
, *newfilename
;
866 DB( g_print("\n[util] filename new for backup\n") );
868 rawfilename
= hb_filename_new_without_extension(filename
);
870 g_date_clear(&date
, 1);
871 g_date_set_julian (&date
, GLOBALS
->today
);
873 newfilename
= g_strdup_printf("%s-%04d%02d%02d.bak",
875 g_date_get_year(&date
),
876 g_date_get_month(&date
),
877 g_date_get_day(&date
)
882 DB( g_print(" - '%s' => '%s'\n", filename
, newfilename
) );
888 gchar
*hb_filename_new_with_extension(gchar
*filename
, const gchar
*extension
)
890 gchar
*rawfilename
, *newfilename
;
892 DB( g_print("\n[util] filename new with extension\n") );
894 rawfilename
= hb_filename_new_without_extension(filename
);
895 newfilename
= g_strdup_printf("%s.%s", rawfilename
, extension
);
898 DB( g_print(" - '%s' => '%s'\n", filename
, newfilename
) );
904 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
907 gboolean
hb_string_isdate(gchar
*str
)
911 return(hb_date_parser_get_nums(str
, &d
, &m
, &y
));
915 gboolean
hb_string_isdigit(gchar
*str
)
917 gboolean valid
= TRUE
;
919 valid
= g_ascii_isdigit(*str
++);
924 gboolean hb_string_isprint(gchar *str)
926 gboolean valid = TRUE;
928 valid = g_ascii_isprint(*str++);
935 gboolean
hb_string_isprint(gchar
*str
)
937 gboolean valid
= TRUE
;
941 if(g_utf8_validate(str
, -1, NULL
))
946 c
= g_utf8_get_char(p
);
947 valid
= g_unichar_isprint(c
);
948 p
= g_utf8_next_char(p
);
955 gchar
*hb_sprint_date(gchar
*outstr
, guint32 julian
)
959 g_date_clear(&date
, 1);
960 g_date_set_julian (&date
, julian
);
961 switch(PREFS
->dtex_datefmt
)
963 case PRF_DATEFMT_MDY
:
965 g_sprintf(outstr
, "%02d/%02d/%04d",
966 g_date_get_month(&date
),
967 g_date_get_day(&date
),
968 g_date_get_year(&date
)
972 case PRF_DATEFMT_DMY
:
974 g_sprintf(outstr
, "%02d/%02d/%04d",
975 g_date_get_day(&date
),
976 g_date_get_month(&date
),
977 g_date_get_year(&date
)
982 g_sprintf(outstr
, "%04d/%02d/%02d",
983 g_date_get_year(&date
),
984 g_date_get_month(&date
),
985 g_date_get_day(&date
)
993 //used only in DB() macro !!
994 void hb_print_date(guint32 jdate
, gchar
*label
)
999 date
= g_date_new_julian(jdate
);
1000 g_date_strftime (buffer1
, 128-1, "%x", date
);
1002 g_print(" - %s %s\n", label
!= NULL
? label
:"date is", buffer1
);
1008 ** parse a string an retrieve an iso date (dd-mm-yy(yy) or dd/mm/yy(yy))
1012 guint32 hb_date_get_julian_parse(gchar *str)
1014 gchar **str_array = NULL;
1017 guint32 julian = GLOBALS->today;
1019 // try with - separator
1020 if( g_strrstr(str, "-") != NULL )
1022 str_array = g_strsplit (str, "-", 3);
1026 if( g_strrstr(str, "/") != NULL )
1028 str_array = g_strsplit (str, "/", 3);
1032 if( g_strv_length( str_array ) == 3 )
1034 d = atoi(str_array[0]);
1035 m = atoi(str_array[1]);
1036 y = atoi(str_array[2]);
1038 //correct for 2 digits year
1047 //todo: here if month is > 12 then the format is probably mm/dd/yy(yy)
1048 //or maybe check with g_date_valid_julian(julian)
1052 date = g_date_new();
1053 g_date_set_dmy(date, d, m, y);
1054 julian = g_date_get_julian (date);
1057 DB( g_print("date: %s :: %d %d %d :: %d\n", str, d, m, y, julian ) );
1061 g_strfreev (str_array);
1067 /* -------------------- */
1075 void hex_dump(guchar
*ptr
, guint length
)
1077 guchar ascii
[MAX_DUMP
+4];
1080 g_print("**hex_dump - %d bytes\n", length
);
1084 g_print("%08x: ", (guint
)ptr
+i
);
1086 for(j
=0;j
<MAX_DUMP
;j
++)
1088 if(i
>= length
) break;
1091 if(ptr
[i
] >= 32 && ptr
[i
] <= 126)
1096 g_print("%02x ", ptr
[i
]);
1101 g_print(" '%s'\n", ascii
);