1 /*  HomeBank -- Free, easy, personal accounting for everyone.
2  *  Copyright (C) 1995-2021 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 
21 //nota: this file should be renamed hb-utils
22 
23 #include "homebank.h"
24 #include "hb-misc.h"
25 
26 #define MYDEBUG 0
27 
28 #if MYDEBUG
29 #define DB(x) (x);
30 #else
31 #define DB(x);
32 #endif
33 
34 /* our global datas */
35 extern struct HomeBank *GLOBALS;
36 extern struct Preferences *PREFS;
37 
38 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
39 
40 static const double fac[9] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
41 
hb_amount_round(const double x,unsigned int digits)42 double hb_amount_round(const double x, unsigned int digits)
43 {
44 	digits = MAX(digits, 8);
45     return floor((x * fac[digits]) + 0.5) / fac[digits];
46 }
47 
48 
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
52 
hb_amount_to_euro(gdouble amount)53 gdouble hb_amount_to_euro(gdouble amount)
54 {
55 	return hb_amount_round((amount * PREFS->euro_value), PREFS->minor_cur.frac_digits);
56 }
57 
58 
59 /* new >5.1 currency fct
60 *
61  *	convert an amount in base currency
62  *
63  */
hb_amount_base(gdouble value,guint32 kcur)64 gdouble hb_amount_base(gdouble value, guint32 kcur)
65 {
66 gdouble newvalue;
67 Currency *cur;
68 
69 	if(kcur == GLOBALS->kcur)
70 		return value;
71 
72 	cur = da_cur_get(kcur);
73 	if(cur == NULL || cur->rate == 0.0)
74 		return 0;
75 
76 	newvalue = value / cur->rate;
77 	return hb_amount_round(newvalue, cur->frac_digits);
78 }
79 
80 
hb_amount_type_match(gdouble amount,gint type)81 gboolean hb_amount_type_match(gdouble amount, gint type)
82 {
83 gboolean retval = TRUE;
84 
85 	if( (type == TXN_TYPE_EXPENSE) && (amount > 0) )
86 		retval = FALSE;
87 	if( (type == TXN_TYPE_INCOME ) && (amount < 0) )
88 		retval = FALSE;
89 
90 	return retval;
91 }
92 
93 
hb_strfmon_check(gchar * outstr,guint32 kcur)94 static Currency *hb_strfmon_check(gchar *outstr, guint32 kcur)
95 {
96 Currency *cur = da_cur_get(kcur);
97 
98 	if(cur == NULL)
99 		g_stpcpy(outstr, "nan");
100 	return cur;
101 }
102 
103 
hb_rate(gdouble value,gdouble total)104 gdouble hb_rate(gdouble value, gdouble total)
105 {
106 	return (total) ? ABS((value * 100 / total)) : 0.0;
107 }
108 
109 
hb_str_rate(gchar * outstr,gint outlen,gdouble rate)110 gchar *hb_str_rate(gchar *outstr, gint outlen, gdouble rate)
111 {
112 gint count, i;
113 gchar *p;
114 
115 	count = g_snprintf(outstr, outlen, "%.6f", rate);
116 	//remove trailing 0 and decimal point
117 	p = &outstr[count-1];
118 	for(i=count;i>0;i--)
119 	{
120 		if(*p == '0')
121 			*p = '\0';
122 		else
123 			break;
124 		p--;
125 	}
126 	if(*p == '.' || *p == ',')
127 		*p = '\0';
128 
129 	return outstr;
130 }
131 
132 /* this function copy a number 99999.99 at s into d and count
133  * number of digits for integer part and decimal part
134  */
_strfnumcopycount(gchar * s,gchar * d,gchar * decchar,gint * plen,gint * pnbint,gint * pnbdec)135 static gchar * _strfnumcopycount(gchar *s, gchar *d, gchar *decchar, gint *plen, gint *pnbint, gint *pnbdec)
136 {
137 gint len=0, nbint=0, nbdec=0;
138 
139 	// sign part
140 	if(*s == '-') {
141 		*d++ = *s++;
142 		len++;
143 	}
144 	// integer part
145 	while(*s != 0 && *s != '.') {
146 		*d++ = *s++;
147 		nbint++;
148 		len++;
149 	}
150 	// decimal separator
151 	if(*s == '.') {
152 		d = g_stpcpy(d, decchar);
153 		len++;
154 		s++;
155 	}
156 	// decimal part
157 	while(*s != 0) {
158 		*d++ = *s++;
159 		nbdec++;
160 		len++;
161 	}
162 	// end string | fill external count
163 	*d = 0;
164 	*plen = len;
165 	*pnbint = nbint;
166 	*pnbdec = nbdec;
167 
168 	return d;
169 }
170 
171 //todo: used only in ui_prefs.c
hb_str_formatd(gchar * outstr,gint outlen,gchar * buf1,Currency * cur,gboolean showsymbol)172 gchar *hb_str_formatd(gchar *outstr, gint outlen, gchar *buf1, Currency *cur, gboolean showsymbol)
173 {
174 gint len, nbd, nbi;
175 gchar *s, *d, *tmp;
176 
177 	d = tmp = outstr;
178 	if(showsymbol && cur->sym_prefix)
179 	{
180 		d = g_stpcpy (d, cur->symbol);
181 		*d++ = ' ';
182 		tmp = d;
183 	}
184 
185 	d = _strfnumcopycount(buf1, d, cur->decimal_char, &len, &nbi, &nbd);
186 
187 	if( cur->grouping_char != NULL && strlen(cur->grouping_char) > 0 )
188 	{
189 	gint i, grpcnt;
190 
191 		s = buf1;
192 		d = tmp;
193 		if(*s == '-')
194 			*d++ = *s++;
195 
196 		grpcnt = 4 - nbi;
197 		for(i=0;i<nbi;i++)
198 		{
199 			*d++ = *s++;
200 			if( !(grpcnt % 3) && i<(nbi-1))
201 			{
202 				d = g_stpcpy(d, cur->grouping_char);
203 			}
204 			grpcnt++;
205 		}
206 
207 		if(nbd > 0)
208 		{
209 			d = g_stpcpy(d, cur->decimal_char);
210 			d = g_stpcpy(d, s+1);
211 		}
212 		*d = 0;
213 	}
214 
215 	if(showsymbol && !cur->sym_prefix)
216 	{
217 		*d++ = ' ';
218 		d = g_stpcpy (d, cur->symbol);
219 	}
220 
221 	*d = 0;
222 
223 	return d;
224 }
225 
226 
hb_strfmon(gchar * outstr,gint outlen,gdouble value,guint32 kcur,gboolean minor)227 void hb_strfmon(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor)
228 {
229 gchar formatd_buf[outlen];
230 Currency *cur;
231 gdouble monval;
232 
233 	if(minor == FALSE)
234 	{
235 		cur = hb_strfmon_check(outstr, kcur);
236 		if(cur != NULL)
237 		{
238 			monval = hb_amount_round(value, cur->frac_digits);
239 			g_ascii_formatd(formatd_buf, outlen, cur->format, monval);
240 			hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
241 		}
242 	}
243 	else
244 	{
245 		monval = hb_amount_base(value, kcur);
246 		monval = hb_amount_to_euro(monval);
247 		cur = &PREFS->minor_cur;
248 		g_ascii_formatd(formatd_buf, outlen, cur->format, monval);
249 		hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
250 	}
251 
252 }
253 
254 
255 // used only in gtk_chart.c
hb_strfmon_int(gchar * outstr,gint outlen,gdouble value,guint32 kcur,gboolean minor)256 void hb_strfmon_int(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor)
257 {
258 gchar formatd_buf[outlen];
259 Currency *cur;
260 gdouble monval;
261 
262 	if(minor == FALSE)
263 	{
264 		cur = hb_strfmon_check(outstr, kcur);
265 		if(cur != NULL)
266 		{
267 			monval = hb_amount_round(value, cur->frac_digits);
268 			g_ascii_formatd(formatd_buf, outlen, "%0.f", monval);
269 			hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
270 		}
271 	}
272 	else
273 	{
274 		monval = hb_amount_base(value, kcur);
275 		monval = hb_amount_to_euro(monval);
276 		cur = &PREFS->minor_cur;
277 		g_ascii_formatd(formatd_buf, outlen, cur->format, monval);
278 		hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
279 	}
280 
281 }
282 
283 
hb_strfnum(gchar * outstr,gint outlen,gdouble value,guint32 kcur,gboolean minor)284 void hb_strfnum(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor)
285 {
286 Currency *cur;
287 gdouble monval;
288 
289 	if(minor == FALSE)
290 	{
291 		cur = hb_strfmon_check(outstr, kcur);
292 		if(cur != NULL)
293 		{
294 			monval = hb_amount_round(value, cur->frac_digits);
295 			//#1868185 print raw number, not with monetary
296 			g_ascii_formatd(outstr, outlen, "%.2f", monval);
297 		}
298 	}
299 	else
300 	{
301 		cur = &PREFS->minor_cur;
302 		monval = hb_amount_to_euro(value);
303 		//#1868185 print raw number, not with monetary
304 		g_ascii_formatd(outstr, outlen, "%.2f", monval);
305 	}
306 }
307 
308 
get_normal_color_amount(gdouble value)309 gchar *get_normal_color_amount(gdouble value)
310 {
311 gchar *color = NULL;
312 
313 	//fix: 400483
314 	value = hb_amount_round(value, 2);
315 
316 	if(value != 0.0 && PREFS->custom_colors == TRUE)
317 	{
318 		color = (value > 0.0) ? PREFS->color_inc : PREFS->color_exp;
319 	}
320 	return color;
321 }
322 
323 
get_minimum_color_amount(gdouble value,gdouble minvalue)324 gchar *get_minimum_color_amount(gdouble value, gdouble minvalue)
325 {
326 gchar *color = NULL;
327 
328 	//fix: 400483
329 	value = hb_amount_round(value, 2);
330 	if(value != 0.0 && PREFS->custom_colors == TRUE)
331 	{
332 		color = (value > 0.0) ? PREFS->color_inc : PREFS->color_exp;
333 		if( value < minvalue)
334 			color = PREFS->color_warn;
335 	}
336 	return color;
337 }
338 
hb_label_set_amount(GtkLabel * label,gdouble value,guint32 kcur,gboolean minor)339 void hb_label_set_amount(GtkLabel *label, gdouble value, guint32 kcur, gboolean minor)
340 {
341 gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE];
342 
343 	hb_strfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, value, kcur, minor);
344 	gtk_label_set_text(GTK_LABEL(label), strbuffer);
345 
346 }
347 
348 
349 /*
350 ** format/color and set a label text with a amount value
351 */
hb_label_set_colvalue(GtkLabel * label,gdouble value,guint32 kcur,gboolean minor)352 void hb_label_set_colvalue(GtkLabel *label, gdouble value, guint32 kcur, gboolean minor)
353 {
354 gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE];
355 gchar *markuptxt;
356 gchar *color = NULL;
357 
358 	hb_strfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, value, kcur, minor);
359 
360 	if(value != 0.0 && PREFS->custom_colors == TRUE)
361 	{
362 		color = get_normal_color_amount(value);
363 
364 		//g_print("color: %s\n", color);
365 
366 		if(color)
367 		{
368 			markuptxt = g_strdup_printf("<span color='%s'>%s</span>", color, strbuffer);
369 			gtk_label_set_markup(GTK_LABEL(label), markuptxt);
370 			g_free(markuptxt);
371 			return;
372 		}
373 	}
374 
375 	gtk_label_set_text(GTK_LABEL(label), strbuffer);
376 
377 }
378 
379 
380 
381 
382 /*
383 ** String utility
384 */
385 
hb_string_ascii_compare(gchar * s1,gchar * s2)386 gint hb_string_ascii_compare(gchar *s1, gchar *s2)
387 {
388 	return g_ascii_strncasecmp(s1 == NULL ? "" : s1, s2 == NULL ? "" : s2, -1);
389 }
390 
391 
hb_string_compare(gchar * s1,gchar * s2)392 gint hb_string_compare(gchar *s1, gchar *s2)
393 {
394 gint retval = 0;
395 
396     if (s1 == NULL || s2 == NULL)
397     {
398 		if (s1 == NULL && s2 == NULL)
399 			goto end;
400 
401 		retval = (s1 == NULL) ? -1 : 1;
402     }
403     else
404     {
405         retval = strcasecmp(s1, s2);
406     }
407 end:
408 	return retval;
409 }
410 
411 
hb_string_utf8_strstr(gchar * haystack,gchar * needle,gboolean exact)412 gint hb_string_utf8_strstr(gchar *haystack, gchar *needle, gboolean exact)
413 {
414 gint retval = FALSE;
415 
416 	if( exact )
417 	{
418 		if( g_strstr_len(haystack, -1, needle) != NULL )
419 		{
420 			DB( g_print(" found case '%s'\n", needle) );
421 			retval = 1;
422 		}
423 	}
424 	else
425 	{
426 	gchar *nchaystack = g_utf8_casefold(haystack, -1);
427 	gchar *ncneedle   = g_utf8_casefold(needle, -1);
428 
429 		if( g_strrstr(nchaystack, ncneedle) != NULL )
430 		{
431 			DB( g_print(" found nocase '%s'\n", ncneedle) );
432 			retval = 1;
433 		}
434 
435 		g_free(nchaystack);
436 		g_free(ncneedle);
437 	}
438 	return retval;
439 }
440 
441 
442 
443 
444 
445 /*
446  * compare 2 utf8 string
447  */
hb_string_utf8_compare(gchar * s1,gchar * s2)448 gint hb_string_utf8_compare(gchar *s1, gchar *s2)
449 {
450 gint retval = 0;
451 gchar *ns1, *ns2;
452 
453     if (s1 == NULL || s2 == NULL)
454     {
455 		if (s1 == NULL && s2 == NULL)
456 			goto end;
457 
458 		retval = (s1 == NULL) ? -1 : 1;
459     }
460     else
461     {
462 		//#1325969
463 		//retval = g_utf8_collate(s1 != NULL ? s1 : "", s2 != NULL ? s2 : "");
464 		ns1 = g_utf8_normalize(s1, -1, G_NORMALIZE_DEFAULT);
465 		ns2 = g_utf8_normalize(s2, -1, G_NORMALIZE_DEFAULT);
466         retval = strcasecmp(ns1, ns2);
467 		g_free(ns2);
468 		g_free(ns1);
469     }
470 end:
471 	return retval;
472 }
473 
474 
hb_string_strip_utf8_bom(gchar * str)475 void hb_string_strip_utf8_bom(gchar *str)
476 {
477 	if( g_str_has_prefix(str, "\xEF\xBB\xBF") )
478 	{
479 	gint len;
480 
481 		DB( g_print("BOM is present into '%s'\n", str) );
482 		len = strlen(str);
483 		if(len>3)
484 		{
485 			memmove(str, str+3, len-3);
486 			str[len-3] = 0;
487 		}
488 	}
489 }
490 
491 
hb_string_strip_crlf(gchar * str)492 void hb_string_strip_crlf(gchar *str)
493 {
494 gchar *p = str;
495 
496 	if(str)
497 	{
498 		while( *p )
499 		{
500 			if( *p == '\n' || *p == '\r')
501 			{
502 				*p = '\0';
503 			}
504 			p++;
505 		}
506 	}
507 }
508 
509 
510 gboolean
hb_string_has_leading_trailing(gchar * str)511 hb_string_has_leading_trailing(gchar *str)
512 {
513 gsize str_len;
514 
515 	g_return_val_if_fail (str != NULL, FALSE);
516 
517 	str_len = strlen (str);
518 	if(*str == ' ' || str[str_len-1] == ' ')
519 		return TRUE;
520 
521 	return FALSE;
522 }
523 
524 
525 
526 //TODO: rename this it remove not replace
hb_string_replace_char(gchar c,gchar * str)527 void hb_string_replace_char(gchar c, gchar *str)
528 {
529 gchar *s = str;
530 gchar *d = str;
531 
532 	if(str)
533 	{
534 		while( *s )
535 		{
536 			if( *s != c )
537 			{
538 				*d++ = *s;
539 			}
540 			s++;
541 		}
542 		*d = 0;
543 	}
544 }
545 
546 
hb_string_copy_jsonpair(gchar * dst,gchar * str)547 gchar *hb_string_copy_jsonpair(gchar *dst, gchar *str)
548 {
549 
550 	while( *str!='\0' )
551 	{
552 		if( *str=='}' )
553 			break;
554 
555 		if( *str==',' )
556 		{
557 			*dst = '\0';
558 			return str + 1;
559 		}
560 
561 		if( *str!='{' && *str!='\"' )
562 		{
563 			*dst++ = *str;
564 		}
565 		str++;
566 	}
567 	*dst = '\0';
568 	return NULL;
569 }
570 
571 
hb_string_inline(gchar * str)572 void hb_string_inline(gchar *str)
573 {
574 gchar *s = str;
575 gchar *d = str;
576 
577 	if(str)
578 	{
579 		while( *s )
580 		{
581 			if( !(*s==' ' || *s=='\t' || *s=='\n' || *s=='\r') )
582 			{
583 				*d++ = *s;
584 			}
585 			s++;
586 		}
587 		*d = 0;
588 	}
589 }
590 
591 
592 /*void strip_extra_spaces(char* str) {
593   int i,x;
594   for(i=x=1; str[i]; ++i)
595     if(!isspace(str[i]) || (i>0 && !isspace(str[i-1])))
596       str[x++] = str[i];
597   str[x] = '\0';
598 }*/
599 
600 
601 
602 gchar*
hb_strdup_nobrackets(const gchar * str)603 hb_strdup_nobrackets (const gchar *str)
604 {
605   const gchar *s;
606   gchar *new_str, *d;
607   gsize length;
608 
609   if (str)
610     {
611       length = strlen (str) + 1;
612       new_str = g_new (char, length);
613       s = str;
614       d = new_str;
615       while(*s != '\0')
616       {
617 		if( *s != '[' && *s != ']' )
618 			*d++ = *s;
619       	s++;
620       }
621       *d = '\0';
622     }
623   else
624     new_str = NULL;
625 
626   return new_str;
627 }
628 
629 
630 /* if we found a . or , within last x digits it might be a dchar */
hb_string_raw_amount_guess_dchar(const gchar * s,gint len,gshort digit)631 static gchar hb_string_raw_amount_guess_dchar(const gchar *s, gint len, gshort digit)
632 {
633 gint nbc, nbd, i;
634 gchar gdc='.';
635 
636 	DB( g_print(" digit=%d maxidx=%d\n", digit, len-digit-1) );
637 
638 	nbc = nbd = 0;
639 	for(i=len-1;i>=0;i--)
640 	{
641 		DB( g_print(" [%2d] '%c' %d %d '%c'\n", i, s[i], nbc, nbd, gdc) );
642 		//store rightmost ,. within digit-1
643 		if( i>=(len-digit-1) )
644 		{
645 			if(s[i]=='.' || s[i]==',')
646 				gdc=s[i];
647 		}
648 		if(s[i]=='.') nbd++;
649 		else if(s[i]==',') nbc++;
650 	}
651 	if( gdc=='.' && nbd > 1) gdc='?';
652 	else if( gdc==',' && nbc > 1) gdc='?';
653 
654 	return gdc;
655 }
656 
657 
hb_string_dup_raw_amount_clean(const gchar * string,gint digits)658 gchar *hb_string_dup_raw_amount_clean(const gchar *string, gint digits)
659 {
660 gint l;
661 gchar *san_str, *new_str, *d;
662 gchar gdc;
663 const gchar *p;
664 
665 	//sanitize the string first: keep -,.0-9
666 	//#1876134 windows: pasted numbers from calculator loose dchar
667 	//https://github.com/microsoft/calculator/issues/504
668 	san_str = d = g_strdup(string);
669 	p = string;
670 	while(*p)
671 	{
672 		if( g_ascii_isdigit(*p) || *p=='-' || *p=='.' || *p==',' )
673 			*d++ = *p;
674 		p++;
675 	}
676 	*d++ = '\0';
677 
678 	l = strlen(san_str);
679 	gdc = hb_string_raw_amount_guess_dchar(san_str, l, digits);
680 
681 	new_str = d = g_malloc (l+1);
682 	p = san_str;
683 	while(*p)
684 	{
685 		if(*p=='-' || g_ascii_isdigit(*p) )
686 			*d++ = *p;
687 		else
688 			if( *p==gdc )
689 			{
690 				*d++ = '.';
691 			}
692 		p++;
693 	}
694 	*d++ = '\0';
695 
696 	g_free(san_str);
697 
698 	return new_str;
699 }
700 
701 
702 static gchar *
hb_date_add_separator(gchar * txt,gint dateorder)703 hb_date_add_separator(gchar *txt, gint dateorder)
704 {
705 gchar *newtxt, *d;
706 gint len;
707 
708 	len = strlen(txt);
709 	newtxt = g_new0(char, len+3);
710 	d = newtxt;
711 	if( (dateorder == PRF_DATEFMT_MDY) || (dateorder == PRF_DATEFMT_DMY) )
712 	{
713 		*d++ = *txt++;
714 		*d++ = *txt++;
715 		*d++ = '/';
716 		*d++ = *txt++;
717 		*d++ = *txt++;
718 		*d++ = '/';
719 		*d++ = *txt++;
720 		*d++ = *txt++;
721 		if( len == 8 )
722 		{
723 			*d++ = *txt++;
724 			*d++ = *txt++;
725 		}
726 	}
727 	else
728 	if( dateorder == PRF_DATEFMT_YMD )
729 	{
730 		*d++ = *txt++;
731 		*d++ = *txt++;
732 		if( len == 8 )
733 		{
734 			*d++ = *txt++;
735 			*d++ = *txt++;
736 		}
737 		*d++ = '/';
738 		*d++ = *txt++;
739 		*d++ = *txt++;
740 		*d++ = '/';
741 		*d++ = *txt++;
742 		*d++ = *txt++;
743 	}
744 
745 	*d++ = '\0';
746 
747 	return newtxt;
748 }
749 
750 
751 static gboolean
hb_date_parser_get_nums(gchar * string,gint * n1,gint * n2,gint * n3)752 hb_date_parser_get_nums(gchar *string, gint *n1, gint *n2, gint *n3)
753 {
754 gboolean retval;
755 gchar **str_array;
756 
757 	//DB( g_print("(qif) hb_qif_parser_get_dmy for '%s'\n", string) );
758 
759 	retval = FALSE;
760 	if( string )
761 	{
762 		str_array = g_strsplit (string, "/", 3);
763 		if( g_strv_length( str_array ) != 3 )
764 		{
765 			g_strfreev (str_array);
766 			str_array = g_strsplit (string, ".", 3);
767 			//#371381 add '-'
768 			if( g_strv_length( str_array ) != 3 )
769 			{
770 				g_strfreev (str_array);
771 				str_array = g_strsplit (string, "-", 3);
772 			}
773 		}
774 
775 		if( g_strv_length( str_array ) == 3 )
776 		{
777 			*n1 = atoi(str_array[0]);
778 			*n2 = atoi(str_array[1]);
779 			*n3 = atoi(str_array[2]);
780 			retval = TRUE;
781 		}
782 
783 		g_strfreev (str_array);
784 	}
785 	return retval;
786 }
787 
788 
hb_date_get_julian(gchar * string,gint dateorder)789 guint32 hb_date_get_julian(gchar *string, gint dateorder)
790 {
791 GDate *date;
792 gint n1, n2, n3, d, m, y;
793 guint32 julian = 0;
794 gboolean parsed;
795 gchar *datewithsep;
796 
797 	DB( g_print("\n[utils] hb_date_get_julian\n") );
798 
799 	//1st try with separator
800 	DB( g_print(" 1st pass str='%s'\n", string) );
801 	parsed = hb_date_parser_get_nums(string, &n1, &n2, &n3);
802 	if( parsed == FALSE )
803 	{
804 		//#1904569 give a try with no separator
805 		datewithsep = hb_date_add_separator(string, dateorder);
806 		DB( g_print(" 2nd pass str='%s'\n", datewithsep) );
807 		parsed = hb_date_parser_get_nums(datewithsep, &n1, &n2, &n3);
808 		g_free(datewithsep);
809 	}
810 
811 	if( parsed == TRUE )
812 	{
813 		DB( g_print(" num= '%d' '%d' '%d'\n", n1, n2, n3) );
814 
815 		switch(dateorder)
816 		{
817 			case PRF_DATEFMT_MDY:
818 				d = n2;
819 				m = n1;
820 				y = n3;
821 				break;
822 			case PRF_DATEFMT_DMY:
823 				d = n1;
824 				m = n2;
825 				y = n3;
826 				break;
827 			default:
828 			case PRF_DATEFMT_YMD:
829 				d = n3;
830 				m = n2;
831 				y = n1;
832 				break;
833 		}
834 
835 		//correct for 2 digits year
836 		if(y < 1970)
837 		{
838 			if(y < 60)
839 				y += 2000;
840 			else
841 				y += 1900;
842 		}
843 
844 		if(d <= 31 && m <= 12)
845 		{
846 			if( g_date_valid_dmy(d, m, y) )
847 			{
848 				DB( g_print(" ddmmyyyy = '%d' '%d' '%d'\n", d, m, y) );
849 				date = g_date_new_dmy(d, m, y);
850 				julian = g_date_get_julian (date);
851 				g_date_free(date);
852 			}
853 		}
854 	}
855 
856 	DB( g_print(" >%s :: julian=%d\n", parsed ? "OK":"--", julian) );
857 
858 	return julian;
859 }
860 
861 
862 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
863 
hb_filename_type_get_by_extension(gchar * filepath)864 gint hb_filename_type_get_by_extension(gchar *filepath)
865 {
866 gint retval = FILETYPE_UNKNOWN;
867 gint str_len;
868 
869 	g_return_val_if_fail(filepath != NULL, FILETYPE_UNKNOWN);
870 
871 	str_len = strlen(filepath);
872 	if( str_len >= 4 )
873 	{
874 		if( strcasecmp(filepath + str_len - 4, ".ofx") == 0)
875 			retval = FILETYPE_OFX;
876 		else
877 		if( strcasecmp(filepath + str_len - 4, ".qif") == 0)
878 			retval = FILETYPE_QIF;
879 		else
880 		if( strcasecmp(filepath + str_len - 4, ".qfx") == 0)
881 			retval = FILETYPE_OFX;
882 		else
883 		if( strcasecmp(filepath + str_len - 4, ".csv") == 0)
884 			retval = FILETYPE_CSV_HB;
885 		else
886 		if( strcasecmp(filepath + str_len - 4, ".xhb") == 0)
887 			retval = FILETYPE_HOMEBANK;
888 	}
889 	return retval;
890 }
891 
892 
hb_filename_new_without_extension(gchar * filename)893 static gchar *hb_filename_new_without_extension(gchar *filename)
894 {
895 gchar *lastdot;
896 
897 	lastdot = g_strrstr (filename, ".");
898 	if(lastdot != NULL)
899 	{
900 		return g_strndup(filename, strlen(filename) - strlen(lastdot));
901 	}
902 	return g_strdup(filename);
903 }
904 
905 
hb_filename_backup_list_sort_func(gchar ** a,gchar ** b)906 static gint hb_filename_backup_list_sort_func(gchar **a, gchar **b)
907 {
908 gint da = atoi( *a  + strlen(*a) - 12);
909 gint db = atoi( *b  + strlen(*b) - 12);
910 
911 	return db - da;
912 }
913 
914 
hb_filename_backup_list(gchar * filename)915 GPtrArray *hb_filename_backup_list(gchar *filename)
916 {
917 gchar *dirname, *basename;
918 gchar *rawfilename, *pattern;
919 GDir *dir;
920 const gchar *tmpname;
921 GPatternSpec *pspec;
922 GPtrArray *array;
923 
924 	DB( g_print("\n[util] filename backup list\n") );
925 
926 	dirname = g_path_get_dirname(filename);
927 	basename = g_path_get_basename(filename);
928 
929 	DB( g_print(" dir='%s' base='%s'\n", dirname, basename) );
930 
931 	rawfilename = hb_filename_new_without_extension(basename);
932 	pattern = g_strdup_printf("%s-????????.bak", rawfilename);
933 
934 	pspec = g_pattern_spec_new(pattern);
935 
936 
937 	DB( g_print(" pattern='%s'\n", pattern) );
938 
939 	array = g_ptr_array_new_with_free_func(g_free);
940 
941 	//dir = g_dir_open (PREFS->path_hbfile, 0, NULL);
942 	dir = g_dir_open (PREFS->path_hbbak, 0, NULL);
943 	if (dir)
944 	{
945 		while ((tmpname = g_dir_read_name (dir)) != NULL)
946 		{
947 		gboolean match;
948 
949 			match = g_pattern_match_string(pspec, tmpname);
950 			if( match )
951 			{
952 				DB( g_print(" %d => '%s'\n", match, tmpname) );
953 				g_ptr_array_add(array, g_strdup(tmpname));
954 			}
955 		}
956 	}
957 	g_free(pattern);
958 	g_dir_close (dir);
959 	g_pattern_spec_free(pspec);
960 	g_free(rawfilename);
961 
962 	g_free(basename);
963 	g_free(dirname);
964 
965 	g_ptr_array_sort(array, (GCompareFunc)hb_filename_backup_list_sort_func);
966 
967 	return array;
968 }
969 
970 
971 gchar *
hb_filename_backup_get_filtername(gchar * filename)972 hb_filename_backup_get_filtername(gchar *filename)
973 {
974 gchar *basename;
975 gchar *rawfilename, *pattern;
976 
977 	DB( g_print("\n[util] filename backup get filtername\n") );
978 
979 	basename = g_path_get_basename(filename);
980 
981 	rawfilename = hb_filename_new_without_extension(basename);
982 
983 	pattern = g_strdup_printf("%s*.[Bb][Aa][Kk]", rawfilename);
984 
985 	g_free(rawfilename);
986 	g_free(basename);
987 
988 	return pattern;
989 }
990 
991 
992 gchar *
hb_filename_new_for_backup(gchar * filename)993 hb_filename_new_for_backup(gchar *filename)
994 {
995 gchar *basename, *rawfilename, *newfilename, *newfilepath;
996 GDate date;
997 
998 	basename = g_path_get_basename(filename);
999 	rawfilename = hb_filename_new_without_extension(basename);
1000 
1001 	g_date_clear(&date, 1);
1002 	g_date_set_julian (&date, GLOBALS->today);
1003 
1004 	newfilename = g_strdup_printf("%s-%04d%02d%02d.bak",
1005 				rawfilename,
1006 				g_date_get_year(&date),
1007 				g_date_get_month(&date),
1008 				g_date_get_day(&date)
1009 				);
1010 
1011 	newfilepath = g_build_filename(PREFS->path_hbbak, newfilename, NULL);
1012 
1013 	g_free(newfilename);
1014 	g_free(rawfilename);
1015 	g_free(basename);
1016 
1017 	return newfilepath;
1018 }
1019 
1020 
hb_filename_new_with_extension(gchar * filename,const gchar * extension)1021 gchar *hb_filename_new_with_extension(gchar *filename, const gchar *extension)
1022 {
1023 gchar *rawfilename, *newfilename;
1024 
1025 	DB( g_print("\n[util] filename new with extension\n") );
1026 
1027 	rawfilename = hb_filename_new_without_extension(filename);
1028 	newfilename = g_strdup_printf("%s.%s", rawfilename, extension);
1029 	g_free(rawfilename);
1030 
1031 	DB( g_print(" - '%s' => '%s'\n", filename, newfilename) );
1032 
1033 	return newfilename;
1034 }
1035 
1036 
1037 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
1038 
1039 
hb_string_isdate(gchar * str)1040 gboolean hb_string_isdate(gchar *str)
1041 {
1042 gint d, m, y;
1043 
1044 	return(hb_date_parser_get_nums(str, &d, &m, &y));
1045 }
1046 
1047 
hb_string_isdigit(gchar * str)1048 gboolean hb_string_isdigit(gchar *str)
1049 {
1050 gboolean valid = TRUE;
1051 	while(*str && valid)
1052 		valid = g_ascii_isdigit(*str++);
1053 	return valid;
1054 }
1055 
1056 /*
1057 gboolean hb_string_isprint(gchar *str)
1058 {
1059 gboolean valid = TRUE;
1060 	while(*str && valid)
1061 		valid = g_ascii_isprint(*str++);
1062 	return valid;
1063 }
1064 */
1065 
1066 
1067 
hb_string_isprint(gchar * str)1068 gboolean hb_string_isprint(gchar *str)
1069 {
1070 gboolean valid = TRUE;
1071 gchar *p;
1072 gunichar c;
1073 
1074 	if(g_utf8_validate(str, -1, NULL))
1075 	{
1076 		p = str;
1077 		while(*p && valid)
1078 		{
1079 			c = g_utf8_get_char(p);
1080 			valid = g_unichar_isprint(c);
1081 			p = g_utf8_next_char(p);
1082 		}
1083 	}
1084 	return valid;
1085 }
1086 
1087 
hb_sprint_date(gchar * outstr,guint32 julian)1088 gchar *hb_sprint_date(gchar *outstr, guint32 julian)
1089 {
1090 GDate date;
1091 
1092 	g_date_clear(&date, 1);
1093 	g_date_set_julian (&date, julian);
1094 	switch(PREFS->dtex_datefmt)
1095 	{
1096 		case PRF_DATEFMT_MDY:
1097 		{
1098 			g_sprintf(outstr, "%02d/%02d/%04d",
1099 				g_date_get_month(&date),
1100 				g_date_get_day(&date),
1101 				g_date_get_year(&date)
1102 				);
1103 		}
1104 		break;
1105 		case PRF_DATEFMT_DMY:
1106 		{
1107 			g_sprintf(outstr, "%02d/%02d/%04d",
1108 				g_date_get_day(&date),
1109 				g_date_get_month(&date),
1110 				g_date_get_year(&date)
1111 				);
1112 		}
1113 		break;
1114 		default:
1115 			g_sprintf(outstr, "%04d/%02d/%02d",
1116 				g_date_get_year(&date),
1117 				g_date_get_month(&date),
1118 				g_date_get_day(&date)
1119 				);
1120 			break;
1121 	}
1122 	return outstr;
1123 }
1124 
1125 
1126 //used only in DB() macro !!
hb_print_date(guint32 jdate,gchar * label)1127 void hb_print_date(guint32 jdate, gchar *label)
1128 {
1129 gchar buffer1[128];
1130 GDate *date;
1131 
1132 	date = g_date_new_julian(jdate);
1133 	g_date_strftime (buffer1, 128-1, "%x", date);
1134 	g_date_free(date);
1135 	g_print(" - %s %s\n", label != NULL ? label:"date is", buffer1);
1136 }
1137 
1138 
1139 
1140 /*
1141 ** parse a string an retrieve an iso date (dd-mm-yy(yy) or dd/mm/yy(yy))
1142 **
1143 */
1144 /* obsolete 4.5
1145 guint32 hb_date_get_julian_parse(gchar *str)
1146 {
1147 gchar **str_array = NULL;
1148 GDate *date;
1149 guint d, m, y;
1150 guint32 julian = GLOBALS->today;
1151 
1152 	// try with - separator
1153 	if( g_strrstr(str, "-") != NULL )
1154 	{
1155 		str_array = g_strsplit (str, "-", 3);
1156 	}
1157 	else
1158 	{
1159 		if( g_strrstr(str, "/") != NULL )
1160 		{
1161 			str_array = g_strsplit (str, "/", 3);
1162 		}
1163 	}
1164 
1165 	if( g_strv_length( str_array ) == 3 )
1166 	{
1167 		d = atoi(str_array[0]);
1168 		m = atoi(str_array[1]);
1169 		y = atoi(str_array[2]);
1170 
1171 		//correct for 2 digits year
1172 		if(y < 1970)
1173 		{
1174 			if(y < 60)
1175 				y += 2000;
1176 			else
1177 				y += 1900;
1178 		}
1179 
1180 		//todo: here if month is > 12 then the format is probably mm/dd/yy(yy)
1181 		//or maybe check with g_date_valid_julian(julian)
1182 
1183 
1184 
1185 		date = g_date_new();
1186 		g_date_set_dmy(date, d, m, y);
1187 		julian = g_date_get_julian (date);
1188 		g_date_free(date);
1189 
1190 		DB( g_print("date: %s :: %d %d %d :: %d\n", str, d, m, y, julian ) );
1191 
1192 	}
1193 
1194 	g_strfreev (str_array);
1195 
1196 	return julian;
1197 }
1198 */
1199 
1200 /* -------------------- */
1201 
1202 #if MYDEBUG == 1
1203 
1204 /*
1205 ** hex memory dump
1206 */
1207 #define MAX_DUMP 16
hex_dump(guchar * ptr,guint length)1208 void hex_dump(guchar *ptr, guint length)
1209 {
1210 guchar ascii[MAX_DUMP+4];
1211 guint i,j;
1212 
1213 	g_print("**hex_dump - %d bytes\n", length);
1214 
1215 	for(i=0;i<length;)
1216 	{
1217 		g_print("%08x: ", (guint)ptr+i);
1218 
1219 		for(j=0;j<MAX_DUMP;j++)
1220 		{
1221 			if(i >= length) break;
1222 
1223 			//store ascii value
1224 			if(ptr[i] >= 32 && ptr[i] <= 126)
1225 				ascii[j] = ptr[i];
1226 			else
1227 				ascii[j] = '.';
1228 
1229 			g_print("%02x ", ptr[i]);
1230 			i++;
1231 		}
1232 		//newline
1233 		ascii[j] = 0;
1234 		g_print(" '%s'\n", ascii);
1235 	}
1236 }
1237 
1238 #endif
1239