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