]> Dogcows Code - chaz/homebank/blob - src/hb-misc.c
179e213ada4216026bbf1872be0a74a306a70fe0
[chaz/homebank] / src / hb-misc.c
1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2018 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[7] = { 1, 10, 100, 1000, 10000, 100000, 1000000 };
41
42 double hb_amount_round(const double x, unsigned int digits)
43 {
44 digits = MAX(digits, 6);
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
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 */
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
81 static Currency *hb_strfmon_check(gchar *outstr, guint32 kcur)
82 {
83 Currency *cur = da_cur_get(kcur);
84
85 if(cur == NULL)
86 g_stpcpy(outstr, "nan");
87 return cur;
88 }
89
90
91 gchar *hb_str_rate(gchar *outstr, gint outlen, gdouble rate)
92 {
93 gint count, i;
94 gchar *p;
95
96 count = g_snprintf(outstr, outlen, "%.6f", rate);
97 //remove trailing 0 and decimal point
98 p = &outstr[count-1];
99 for(i=count;i>0;i--)
100 {
101 if(*p == '0')
102 *p = '\0';
103 else
104 break;
105 p--;
106 }
107 if(*p == '.' || *p == ',')
108 *p = '\0';
109
110 return outstr;
111 }
112
113 /* this function copy a number 99999.99 at s into d and count
114 * number of digits for integer part and decimal part
115 */
116 static gchar * _strfnumcopycount(gchar *s, gchar *d, gchar *decchar, gint *plen, gint *pnbint, gint *pnbdec)
117 {
118 gint len=0, nbint=0, nbdec=0;
119
120 // sign part
121 if(*s == '-') {
122 *d++ = *s++;
123 len++;
124 }
125 // integer part
126 while(*s != 0 && *s != '.') {
127 *d++ = *s++;
128 nbint++;
129 len++;
130 }
131 // decimal separator
132 if(*s == '.') {
133 d = g_stpcpy(d, decchar);
134 len++;
135 s++;
136 }
137 // decimal part
138 while(*s != 0) {
139 *d++ = *s++;
140 nbdec++;
141 len++;
142 }
143 // end string | fill external count
144 *d = 0;
145 *plen = len;
146 *pnbint = nbint;
147 *pnbdec = nbdec;
148
149 return d;
150 }
151
152 //todo: used only in ui_prefs.c
153 gchar *hb_str_formatd(gchar *outstr, gint outlen, gchar *buf1, Currency *cur, gboolean showsymbol)
154 {
155 gint len, nbd, nbi;
156 gchar *s, *d, *tmp;
157
158 d = tmp = outstr;
159 if(showsymbol && cur->sym_prefix)
160 {
161 d = g_stpcpy (d, cur->symbol);
162 *d++ = ' ';
163 tmp = d;
164 }
165
166 d = _strfnumcopycount(buf1, d, cur->decimal_char, &len, &nbi, &nbd);
167
168 if( cur->grouping_char != NULL && strlen(cur->grouping_char) > 0 )
169 {
170 gint i, grpcnt;
171
172 s = buf1;
173 d = tmp;
174 if(*s == '-')
175 *d++ = *s++;
176
177 grpcnt = 4 - nbi;
178 for(i=0;i<nbi;i++)
179 {
180 *d++ = *s++;
181 if( !(grpcnt % 3) && i<(nbi-1))
182 {
183 d = g_stpcpy(d, cur->grouping_char);
184 }
185 grpcnt++;
186 }
187
188 if(nbd > 0)
189 {
190 d = g_stpcpy(d, cur->decimal_char);
191 d = g_stpcpy(d, s+1);
192 }
193 *d = 0;
194 }
195
196 if(showsymbol && !cur->sym_prefix)
197 {
198 *d++ = ' ';
199 d = g_stpcpy (d, cur->symbol);
200 }
201
202 *d = 0;
203
204 return d;
205 }
206
207
208 void hb_strfmon(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor)
209 {
210 gchar formatd_buf[outlen];
211 Currency *cur;
212 gdouble monval;
213
214 if(minor == FALSE)
215 {
216 cur = hb_strfmon_check(outstr, kcur);
217 if(cur != NULL)
218 {
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);
222 }
223 }
224 else
225 {
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);
231 }
232
233 }
234
235
236 void hb_strfmon_int(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor)
237 {
238 gchar formatd_buf[outlen];
239 Currency *cur;
240 gdouble monval;
241
242 if(minor == FALSE)
243 {
244 cur = hb_strfmon_check(outstr, kcur);
245 if(cur != NULL)
246 {
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);
250 }
251 }
252 else
253 {
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);
259 }
260
261 }
262
263
264 void hb_strfnum(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor)
265 {
266 gchar formatd_buf[outlen];
267 Currency *cur;
268 gdouble monval;
269
270 if(minor == FALSE)
271 {
272 cur = hb_strfmon_check(outstr, kcur);
273 if(cur != NULL)
274 {
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);
278 }
279 }
280 else
281 {
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);
286 }
287
288
289 }
290
291
292 gchar *get_normal_color_amount(gdouble value)
293 {
294 gchar *color = NULL;
295
296 //fix: 400483
297 value = hb_amount_round(value, 2);
298
299 if(value != 0.0 && PREFS->custom_colors == TRUE)
300 {
301 color = (value > 0.0) ? PREFS->color_inc : PREFS->color_exp;
302 }
303 return color;
304 }
305
306
307 gchar *get_minimum_color_amount(gdouble value, gdouble minvalue)
308 {
309 gchar *color = NULL;
310
311 //fix: 400483
312 value = hb_amount_round(value, 2);
313 if(value != 0.0 && PREFS->custom_colors == TRUE)
314 {
315 color = (value > 0.0) ? PREFS->color_inc : PREFS->color_exp;
316 if( value < minvalue)
317 color = PREFS->color_warn;
318 }
319 return color;
320 }
321
322 void hb_label_set_amount(GtkLabel *label, gdouble value, guint32 kcur, gboolean minor)
323 {
324 gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE];
325
326 hb_strfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, value, kcur, minor);
327 gtk_label_set_text(GTK_LABEL(label), strbuffer);
328
329 }
330
331
332 /*
333 ** format/color and set a label text with a amount value
334 */
335 void hb_label_set_colvalue(GtkLabel *label, gdouble value, guint32 kcur, gboolean minor)
336 {
337 gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE];
338 gchar *markuptxt;
339 gchar *color = NULL;
340
341 hb_strfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, value, kcur, minor);
342
343 if(value != 0.0 && PREFS->custom_colors == TRUE)
344 {
345 color = get_normal_color_amount(value);
346
347 //g_print("color: %s\n", color);
348
349 if(color)
350 {
351 markuptxt = g_strdup_printf("<span color='%s'>%s</span>", color, strbuffer);
352 gtk_label_set_markup(GTK_LABEL(label), markuptxt);
353 g_free(markuptxt);
354 return;
355 }
356 }
357
358 gtk_label_set_text(GTK_LABEL(label), strbuffer);
359
360 }
361
362
363
364
365 /*
366 ** String utility
367 */
368
369 gint hb_string_compare(gchar *s1, gchar *s2)
370 {
371 gint retval = 0;
372
373 if (s1 == NULL || s2 == NULL)
374 {
375 if (s1 == NULL && s2 == NULL)
376 goto end;
377
378 retval = (s1 == NULL) ? -1 : 1;
379 }
380 else
381 {
382 retval = strcasecmp(s1, s2);
383 }
384 end:
385 return retval;
386 }
387
388
389 /*
390 * compare 2 utf8 string
391 */
392 gint hb_string_utf8_compare(gchar *s1, gchar *s2)
393 {
394 gint retval = 0;
395 gchar *ns1, *ns2;
396
397 if (s1 == NULL || s2 == NULL)
398 {
399 if (s1 == NULL && s2 == NULL)
400 goto end;
401
402 retval = (s1 == NULL) ? -1 : 1;
403 }
404 else
405 {
406 //#1325969
407 //retval = g_utf8_collate(s1 != NULL ? s1 : "", s2 != NULL ? s2 : "");
408 ns1 = g_utf8_normalize(s1, -1, G_NORMALIZE_DEFAULT);
409 ns2 = g_utf8_normalize(s2, -1, G_NORMALIZE_DEFAULT);
410 retval = strcasecmp(ns1, ns2);
411 g_free(ns2);
412 g_free(ns1);
413 }
414 end:
415 return retval;
416 }
417
418
419 void hb_string_strip_crlf(gchar *str)
420 {
421 gchar *p = str;
422
423 if(str)
424 {
425 while( *p )
426 {
427 if( *p == '\n' || *p == '\r')
428 {
429 *p = '\0';
430 }
431 p++;
432 }
433 }
434 }
435
436
437 void hb_string_replace_char(gchar c, gchar *str)
438 {
439 gchar *s = str;
440 gchar *d = str;
441
442 if(str)
443 {
444 while( *s )
445 {
446 if( *s != c )
447 {
448 *d++ = *s;
449 }
450 s++;
451 }
452 *d = 0;
453 }
454 }
455
456
457 gchar *hb_string_copy_jsonpair(gchar *dst, gchar *str)
458 {
459
460 while( *str!='\0' )
461 {
462 if( *str=='}' )
463 break;
464
465 if( *str==',' )
466 {
467 *dst = '\0';
468 return str + 1;
469 }
470
471 if( *str!='{' && *str!='\"' )
472 {
473 *dst++ = *str;
474 }
475 str++;
476 }
477 *dst = '\0';
478 return NULL;
479 }
480
481
482 void hb_string_inline(gchar *str)
483 {
484 gchar *s = str;
485 gchar *d = str;
486
487 if(str)
488 {
489 while( *s )
490 {
491 if( !(*s==' ' || *s=='\t' || *s=='\n' || *s=='\r') )
492 {
493 *d++ = *s;
494 }
495 s++;
496 }
497 *d = 0;
498 }
499 }
500
501
502 /*void strip_extra_spaces(char* str) {
503 int i,x;
504 for(i=x=1; str[i]; ++i)
505 if(!isspace(str[i]) || (i>0 && !isspace(str[i-1])))
506 str[x++] = str[i];
507 str[x] = '\0';
508 }*/
509
510
511
512 gchar*
513 hb_strdup_nobrackets (const gchar *str)
514 {
515 const gchar *s;
516 gchar *new_str, *d;
517 gsize length;
518
519 if (str)
520 {
521 length = strlen (str) + 1;
522 new_str = g_new (char, length);
523 s = str;
524 d = new_str;
525 while(*s != '\0')
526 {
527 if( *s != '[' && *s != ']' )
528 *d++ = *s;
529 s++;
530 }
531 *d = '\0';
532 }
533 else
534 new_str = NULL;
535
536 return new_str;
537 }
538
539
540 static gboolean
541 hb_date_parser_get_nums(gchar *string, gint *n1, gint *n2, gint *n3)
542 {
543 gboolean retval;
544 gchar **str_array;
545
546 //DB( g_print("(qif) hb_qif_parser_get_dmy for '%s'\n", string) );
547
548 retval = FALSE;
549 str_array = g_strsplit (string, "/", 3);
550 if( g_strv_length( str_array ) != 3 )
551 {
552 g_strfreev (str_array);
553 str_array = g_strsplit (string, ".", 3);
554 // fix 371381
555 //todo test
556 if( g_strv_length( str_array ) != 3 )
557 {
558 g_strfreev (str_array);
559 str_array = g_strsplit (string, "-", 3);
560 }
561 }
562
563 if( g_strv_length( str_array ) == 3 )
564 {
565 *n1 = atoi(str_array[0]);
566 *n2 = atoi(str_array[1]);
567 *n3 = atoi(str_array[2]);
568 retval = TRUE;
569 }
570
571 g_strfreev (str_array);
572
573 return retval;
574 }
575
576
577 guint32 hb_date_get_julian(gchar *string, gint datefmt)
578 {
579 GDate *date;
580 gint n1, n2, n3, d, m, y;
581 guint32 julian = 0;
582
583 DB( g_print("\n[utils] hb_date_get_julian\n") );
584
585 DB( g_print(" - '%s' dateorder=%d\n", string, datefmt) );
586
587 if( hb_date_parser_get_nums(string, &n1, &n2, &n3) )
588 {
589 DB( g_print(" - '%d' '%d' '%d'\n", n1, n2, n3) );
590
591 switch(datefmt)
592 {
593 case PRF_DATEFMT_MDY:
594 d = n2;
595 m = n1;
596 y = n3;
597 break;
598 case PRF_DATEFMT_DMY:
599 d = n1;
600 m = n2;
601 y = n3;
602 break;
603 default:
604 case PRF_DATEFMT_YMD:
605 d = n3;
606 m = n2;
607 y = n1;
608 break;
609 }
610
611 //correct for 2 digits year
612 if(y < 1970)
613 {
614 if(y < 60)
615 y += 2000;
616 else
617 y += 1900;
618 }
619
620 if(d <= 31 && m <= 12)
621 {
622 if( g_date_valid_dmy(d, m, y) )
623 {
624 date = g_date_new_dmy(d, m, y);
625 julian = g_date_get_julian (date);
626 g_date_free(date);
627 }
628 }
629
630 DB( g_print(" > %d %d %d julian=%d\n", d, m, y, julian) );
631
632 }
633
634 return julian;
635 }
636
637
638 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
639
640
641 gchar *hb_filename_new_with_extension(gchar *filename, const gchar *extension)
642 {
643 gchar *lastdot, *fwe;
644 gchar *newfilename;
645
646 DB( g_print("\n[util] filename with extension\n") );
647
648 DB( g_print(" - orig: '%s' => '%s'\n", filename, extension) );
649
650 //duplicate without extensions
651 lastdot = g_strrstr(filename, ".");
652 if(lastdot != NULL)
653 {
654 fwe = g_strndup(filename, strlen(filename) - strlen(lastdot));
655 DB( g_print(" - fwe: '%s'\n", fwe) );
656 newfilename = g_strdup_printf("%s.%s", fwe, extension);
657 g_free(fwe);
658 }
659 else
660 {
661 newfilename = g_strdup_printf("%s.%s", filename, extension);
662 }
663
664 DB( g_print(" - new: '%s'\n", newfilename) );
665
666 return newfilename;
667 }
668
669
670 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
671
672
673 gboolean hb_string_isdate(gchar *str)
674 {
675 gint d, m, y;
676
677 return(hb_date_parser_get_nums(str, &d, &m, &y));
678 }
679
680
681 gboolean hb_string_isdigit(gchar *str)
682 {
683 gboolean valid = TRUE;
684 while(*str && valid)
685 valid = g_ascii_isdigit(*str++);
686 return valid;
687 }
688
689 /*
690 gboolean hb_string_isprint(gchar *str)
691 {
692 gboolean valid = TRUE;
693 while(*str && valid)
694 valid = g_ascii_isprint(*str++);
695 return valid;
696 }
697 */
698
699
700
701 gboolean hb_string_isprint(gchar *str)
702 {
703 gboolean valid = TRUE;
704 gchar *p;
705 gunichar c;
706
707 if(g_utf8_validate(str, -1, NULL))
708 {
709 p = str;
710 while(*p && valid)
711 {
712 c = g_utf8_get_char(p);
713 valid = g_unichar_isprint(c);
714 p = g_utf8_next_char(p);
715 }
716 }
717 return valid;
718 }
719
720
721 gchar *hb_sprint_date(gchar *outstr, guint32 julian)
722 {
723 GDate date;
724
725 g_date_clear(&date, 1);
726 g_date_set_julian (&date, julian);
727 switch(PREFS->dtex_datefmt)
728 {
729 case PRF_DATEFMT_MDY:
730 {
731 g_sprintf(outstr, "%02d/%02d/%04d",
732 g_date_get_month(&date),
733 g_date_get_day(&date),
734 g_date_get_year(&date)
735 );
736 }
737 break;
738 case PRF_DATEFMT_DMY:
739 {
740 g_sprintf(outstr, "%02d/%02d/%04d",
741 g_date_get_day(&date),
742 g_date_get_month(&date),
743 g_date_get_year(&date)
744 );
745 }
746 break;
747 default:
748 g_sprintf(outstr, "%04d/%02d/%02d",
749 g_date_get_year(&date),
750 g_date_get_month(&date),
751 g_date_get_day(&date)
752 );
753 break;
754 }
755 return outstr;
756 }
757
758
759 //used only in DB() macro !!
760 void hb_print_date(guint32 jdate, gchar *label)
761 {
762 gchar buffer1[128];
763 GDate *date;
764
765 date = g_date_new_julian(jdate);
766 g_date_strftime (buffer1, 128-1, "%x", date);
767 g_date_free(date);
768 g_print(" - %s %s\n", label != NULL ? label:"date is", buffer1);
769 }
770
771
772
773 /*
774 ** parse a string an retrieve an iso date (dd-mm-yy(yy) or dd/mm/yy(yy))
775 **
776 */
777 /* obsolete 4.5
778 guint32 hb_date_get_julian_parse(gchar *str)
779 {
780 gchar **str_array = NULL;
781 GDate *date;
782 guint d, m, y;
783 guint32 julian = GLOBALS->today;
784
785 // try with - separator
786 if( g_strrstr(str, "-") != NULL )
787 {
788 str_array = g_strsplit (str, "-", 3);
789 }
790 else
791 {
792 if( g_strrstr(str, "/") != NULL )
793 {
794 str_array = g_strsplit (str, "/", 3);
795 }
796 }
797
798 if( g_strv_length( str_array ) == 3 )
799 {
800 d = atoi(str_array[0]);
801 m = atoi(str_array[1]);
802 y = atoi(str_array[2]);
803
804 //correct for 2 digits year
805 if(y < 1970)
806 {
807 if(y < 60)
808 y += 2000;
809 else
810 y += 1900;
811 }
812
813 //todo: here if month is > 12 then the format is probably mm/dd/yy(yy)
814 //or maybe check with g_date_valid_julian(julian)
815
816
817
818 date = g_date_new();
819 g_date_set_dmy(date, d, m, y);
820 julian = g_date_get_julian (date);
821 g_date_free(date);
822
823 DB( g_print("date: %s :: %d %d %d :: %d\n", str, d, m, y, julian ) );
824
825 }
826
827 g_strfreev (str_array);
828
829 return julian;
830 }
831 */
832
833 /* -------------------- */
834
835 #if MYDEBUG == 1
836
837 /*
838 ** hex memory dump
839 */
840 #define MAX_DUMP 16
841 void hex_dump(guchar *ptr, guint length)
842 {
843 guchar ascii[MAX_DUMP+4];
844 guint i,j;
845
846 g_print("**hex_dump - %d bytes\n", length);
847
848 for(i=0;i<length;)
849 {
850 g_print("%08x: ", (guint)ptr+i);
851
852 for(j=0;j<MAX_DUMP;j++)
853 {
854 if(i >= length) break;
855
856 //store ascii value
857 if(ptr[i] >= 32 && ptr[i] <= 126)
858 ascii[j] = ptr[i];
859 else
860 ascii[j] = '.';
861
862 g_print("%02x ", ptr[i]);
863 i++;
864 }
865 //newline
866 ascii[j] = 0;
867 g_print(" '%s'\n", ascii);
868 }
869 }
870
871 #endif
This page took 0.067399 seconds and 4 git commands to generate.