]> Dogcows Code - chaz/homebank/blob - src/hb-misc.c
import homebank-5.1.3
[chaz/homebank] / src / hb-misc.c
1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2017 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 cur = &PREFS->minor_cur;
227 monval = hb_amount_to_euro(value);
228 g_ascii_formatd(formatd_buf, outlen, cur->format, monval);
229 hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
230 }
231
232 }
233
234 void hb_strfmon_int(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor)
235 {
236 gchar formatd_buf[outlen];
237 Currency *cur;
238 gdouble monval;
239
240 if(minor == FALSE)
241 {
242 cur = hb_strfmon_check(outstr, kcur);
243 if(cur != NULL)
244 {
245 monval = hb_amount_round(value, cur->frac_digits);
246 g_ascii_formatd(formatd_buf, outlen, "%0.f", monval);
247 hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
248 }
249 }
250 else
251 {
252 cur = &PREFS->minor_cur;
253 monval = hb_amount_to_euro(value);
254 g_ascii_formatd(formatd_buf, outlen, cur->format, monval);
255 hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
256 }
257
258 }
259
260
261 void hb_strfnum(gchar *outstr, gint outlen, gdouble value, guint32 kcur, gboolean minor)
262 {
263 gchar formatd_buf[outlen];
264 Currency *cur;
265 gdouble monval;
266
267 if(minor == FALSE)
268 {
269 cur = hb_strfmon_check(outstr, kcur);
270 if(cur != NULL)
271 {
272 monval = hb_amount_round(value, cur->frac_digits);
273 g_ascii_formatd(formatd_buf, outlen, "%.2f", monval);
274 hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
275 }
276 }
277 else
278 {
279 cur = &PREFS->minor_cur;
280 monval = hb_amount_to_euro(value);
281 g_ascii_formatd(formatd_buf, outlen, "%.2f", monval);
282 hb_str_formatd(outstr, outlen, formatd_buf, cur, TRUE);
283 }
284
285
286 }
287
288
289
290 gchar *get_normal_color_amount(gdouble value)
291 {
292 gchar *color = NULL;
293
294 //fix: 400483
295 value = hb_amount_round(value, 2);
296
297 if(value != 0.0 && PREFS->custom_colors == TRUE)
298 {
299 color = (value > 0.0) ? PREFS->color_inc : PREFS->color_exp;
300 }
301 return color;
302 }
303
304
305 gchar *get_minimum_color_amount(gdouble value, gdouble minvalue)
306 {
307 gchar *color = NULL;
308
309 //fix: 400483
310 value = hb_amount_round(value, 2);
311 if(value != 0.0 && PREFS->custom_colors == TRUE)
312 {
313 color = (value > 0.0) ? PREFS->color_inc : PREFS->color_exp;
314 if( value < minvalue)
315 color = PREFS->color_warn;
316 }
317 return color;
318 }
319
320 void hb_label_set_amount(GtkLabel *label, gdouble value, guint32 kcur, gboolean minor)
321 {
322 gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE];
323
324 hb_strfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, value, kcur, minor);
325 gtk_label_set_text(GTK_LABEL(label), strbuffer);
326
327 }
328
329
330 /*
331 ** format/color and set a label text with a amount value
332 */
333 void hb_label_set_colvalue(GtkLabel *label, gdouble value, guint32 kcur, gboolean minor)
334 {
335 gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE];
336 gchar *markuptxt;
337 gchar *color = NULL;
338
339 hb_strfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, value, kcur, minor);
340
341 if(value != 0.0 && PREFS->custom_colors == TRUE)
342 {
343 color = get_normal_color_amount(value);
344
345 //g_print("color: %s\n", color);
346
347 if(color)
348 {
349 markuptxt = g_strdup_printf("<span color='%s'>%s</span>", color, strbuffer);
350 gtk_label_set_markup(GTK_LABEL(label), markuptxt);
351 g_free(markuptxt);
352 return;
353 }
354 }
355
356 gtk_label_set_text(GTK_LABEL(label), strbuffer);
357
358 }
359
360
361
362
363 /*
364 ** String utility
365 */
366
367 gint hb_string_compare(gchar *s1, gchar *s2)
368 {
369 gint retval = 0;
370
371 if (s1 == NULL || s2 == NULL)
372 {
373 if (s1 == NULL && s2 == NULL)
374 goto end;
375
376 retval = (s1 == NULL) ? -1 : 1;
377 }
378 else
379 {
380 retval = strcasecmp(s1, s2);
381 }
382 end:
383 return retval;
384 }
385
386
387 /*
388 * compare 2 utf8 string
389 */
390 gint hb_string_utf8_compare(gchar *s1, gchar *s2)
391 {
392 gint retval = 0;
393 gchar *ns1, *ns2;
394
395 if (s1 == NULL || s2 == NULL)
396 {
397 if (s1 == NULL && s2 == NULL)
398 goto end;
399
400 retval = (s1 == NULL) ? -1 : 1;
401 }
402 else
403 {
404 //#1325969
405 //retval = g_utf8_collate(s1 != NULL ? s1 : "", s2 != NULL ? s2 : "");
406 ns1 = g_utf8_normalize(s1, -1, G_NORMALIZE_DEFAULT);
407 ns2 = g_utf8_normalize(s2, -1, G_NORMALIZE_DEFAULT);
408 retval = strcasecmp(ns1, ns2);
409 g_free(ns2);
410 g_free(ns1);
411 }
412 end:
413 return retval;
414 }
415
416
417 void hb_string_strip_crlf(gchar *str)
418 {
419 gchar *p = str;
420
421 if(str)
422 {
423 while( *p )
424 {
425 if( *p == '\n' || *p == '\r')
426 {
427 *p = '\0';
428 }
429 p++;
430 }
431 }
432 }
433
434
435 void hb_string_replace_char(gchar c, gchar *str)
436 {
437 gchar *s = str;
438 gchar *d = str;
439
440 if(str)
441 {
442 while( *s )
443 {
444 if( *s != c )
445 {
446 *d++ = *s;
447 }
448 s++;
449 }
450 *d = 0;
451 }
452 }
453
454
455 /*static void strip_extra_spaces(char* str) {
456 int i,x;
457 for(i=x=1; str[i]; ++i)
458 if(!isspace(str[i]) || (i>0 && !isspace(str[i-1])))
459 str[x++] = str[i];
460 str[x] = '\0';
461 }*/
462
463
464
465 gchar*
466 hb_strdup_nobrackets (const gchar *str)
467 {
468 const gchar *s;
469 gchar *new_str, *d;
470 gsize length;
471
472 if (str)
473 {
474 length = strlen (str) + 1;
475 new_str = g_new (char, length);
476 s = str;
477 d = new_str;
478 while(*s != '\0')
479 {
480 if( *s != '[' && *s != ']' )
481 *d++ = *s;
482 s++;
483 }
484 *d = '\0';
485 }
486 else
487 new_str = NULL;
488
489 return new_str;
490 }
491
492
493 static gboolean
494 hb_date_parser_get_nums(gchar *string, gint *n1, gint *n2, gint *n3)
495 {
496 gboolean retval;
497 gchar **str_array;
498
499 //DB( g_print("(qif) hb_qif_parser_get_dmy for '%s'\n", string) );
500
501 retval = FALSE;
502 str_array = g_strsplit (string, "/", 3);
503 if( g_strv_length( str_array ) != 3 )
504 {
505 g_strfreev (str_array);
506 str_array = g_strsplit (string, ".", 3);
507 // fix 371381
508 //todo test
509 if( g_strv_length( str_array ) != 3 )
510 {
511 g_strfreev (str_array);
512 str_array = g_strsplit (string, "-", 3);
513 }
514 }
515
516 if( g_strv_length( str_array ) == 3 )
517 {
518 *n1 = atoi(str_array[0]);
519 *n2 = atoi(str_array[1]);
520 *n3 = atoi(str_array[2]);
521 retval = TRUE;
522 }
523
524 g_strfreev (str_array);
525
526 return retval;
527 }
528
529
530 guint32 hb_date_get_julian(gchar *string, gint datefmt)
531 {
532 GDate *date;
533 gint n1, n2, n3, d, m, y;
534 guint32 julian = 0;
535
536 DB( g_print("\n[utils] hb_date_get_julian\n") );
537
538 DB( g_print(" - '%s' dateorder=%d\n", string, datefmt) );
539
540 if( hb_date_parser_get_nums(string, &n1, &n2, &n3) )
541 {
542 DB( g_print(" - '%d' '%d' '%d'\n", n1, n2, n3) );
543
544 switch(datefmt)
545 {
546 case PRF_DATEFMT_MDY:
547 d = n2;
548 m = n1;
549 y = n3;
550 break;
551 case PRF_DATEFMT_DMY:
552 d = n1;
553 m = n2;
554 y = n3;
555 break;
556 default:
557 case PRF_DATEFMT_YMD:
558 d = n3;
559 m = n2;
560 y = n1;
561 break;
562 }
563
564 //correct for 2 digits year
565 if(y < 1970)
566 {
567 if(y < 60)
568 y += 2000;
569 else
570 y += 1900;
571 }
572
573 if(d <= 31 && m <= 12)
574 {
575 if( g_date_valid_dmy(d, m, y) )
576 {
577 date = g_date_new_dmy(d, m, y);
578 julian = g_date_get_julian (date);
579 g_date_free(date);
580 }
581 }
582
583 DB( g_print(" > %d %d %d julian=%d\n", d, m, y, julian) );
584
585 }
586
587 return julian;
588 }
589
590
591 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
592
593
594 gchar *hb_util_filename_new_with_extension(gchar *filename, const gchar *extension)
595 {
596 gchar *lastdot, *fwe;
597 gchar *newfilename;
598
599 DB( g_print("\n[util] filename with extension\n") );
600
601 DB( g_print(" - orig: '%s' => '%s'\n", filename, extension) );
602
603 //duplicate without extensions
604 lastdot = g_strrstr(filename, ".");
605 if(lastdot != NULL)
606 {
607 fwe = g_strndup(filename, strlen(filename) - strlen(lastdot));
608 DB( g_print(" - fwe: '%s'\n", fwe) );
609 newfilename = g_strdup_printf("%s.%s", fwe, extension);
610 g_free(fwe);
611 }
612 else
613 {
614 newfilename = g_strdup_printf("%s.%s", filename, extension);
615 }
616
617 DB( g_print(" - new: '%s'\n", newfilename) );
618
619 return newfilename;
620 }
621
622
623 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
624
625
626 gboolean hb_string_isdate(gchar *str)
627 {
628 gint d, m, y;
629
630 return(hb_date_parser_get_nums(str, &d, &m, &y));
631 }
632
633
634 gboolean hb_string_isdigit(gchar *str)
635 {
636 gboolean valid = TRUE;
637 while(*str && valid)
638 valid = g_ascii_isdigit(*str++);
639 return valid;
640 }
641
642 /*
643 gboolean hb_string_isprint(gchar *str)
644 {
645 gboolean valid = TRUE;
646 while(*str && valid)
647 valid = g_ascii_isprint(*str++);
648 return valid;
649 }
650 */
651
652
653
654 gboolean hb_string_isprint(gchar *str)
655 {
656 gboolean valid = TRUE;
657 gchar *p;
658 gunichar c;
659
660 if(g_utf8_validate(str, -1, NULL))
661 {
662 p = str;
663 while(*p && valid)
664 {
665 c = g_utf8_get_char(p);
666 valid = g_unichar_isprint(c);
667 p = g_utf8_next_char(p);
668 }
669 }
670 return valid;
671 }
672
673
674 gchar *hb_sprint_date(gchar *outstr, guint32 julian)
675 {
676 GDate date;
677
678 g_date_clear(&date, 1);
679 g_date_set_julian (&date, julian);
680 switch(PREFS->dtex_datefmt)
681 {
682 case PRF_DATEFMT_MDY:
683 {
684 g_sprintf(outstr, "%02d/%02d/%04d",
685 g_date_get_month(&date),
686 g_date_get_day(&date),
687 g_date_get_year(&date)
688 );
689 }
690 break;
691 case PRF_DATEFMT_DMY:
692 {
693 g_sprintf(outstr, "%02d/%02d/%04d",
694 g_date_get_day(&date),
695 g_date_get_month(&date),
696 g_date_get_year(&date)
697 );
698 }
699 default:
700 g_sprintf(outstr, "%04d/%02d/%02d",
701 g_date_get_year(&date),
702 g_date_get_month(&date),
703 g_date_get_day(&date)
704 );
705 break;
706 }
707 return outstr;
708 }
709
710
711 //used only in DB() macro !!
712 void hb_print_date(guint32 jdate, gchar *label)
713 {
714 gchar buffer1[128];
715 GDate *date;
716
717 date = g_date_new_julian(jdate);
718 g_date_strftime (buffer1, 128-1, "%x", date);
719 g_date_free(date);
720 g_print(" - %s %s\n", label != NULL ? label:"date is", buffer1);
721 }
722
723
724
725 /*
726 ** parse a string an retrieve an iso date (dd-mm-yy(yy) or dd/mm/yy(yy))
727 **
728 */
729 /* obsolete 4.5
730 guint32 hb_date_get_julian_parse(gchar *str)
731 {
732 gchar **str_array = NULL;
733 GDate *date;
734 guint d, m, y;
735 guint32 julian = GLOBALS->today;
736
737 // try with - separator
738 if( g_strrstr(str, "-") != NULL )
739 {
740 str_array = g_strsplit (str, "-", 3);
741 }
742 else
743 {
744 if( g_strrstr(str, "/") != NULL )
745 {
746 str_array = g_strsplit (str, "/", 3);
747 }
748 }
749
750 if( g_strv_length( str_array ) == 3 )
751 {
752 d = atoi(str_array[0]);
753 m = atoi(str_array[1]);
754 y = atoi(str_array[2]);
755
756 //correct for 2 digits year
757 if(y < 1970)
758 {
759 if(y < 60)
760 y += 2000;
761 else
762 y += 1900;
763 }
764
765 //todo: here if month is > 12 then the format is probably mm/dd/yy(yy)
766 //or maybe check with g_date_valid_julian(julian)
767
768
769
770 date = g_date_new();
771 g_date_set_dmy(date, d, m, y);
772 julian = g_date_get_julian (date);
773 g_date_free(date);
774
775 DB( g_print("date: %s :: %d %d %d :: %d\n", str, d, m, y, julian ) );
776
777 }
778
779 g_strfreev (str_array);
780
781 return julian;
782 }
783 */
784
785 /* -------------------- */
786
787 #if MYDEBUG == 1
788
789 /*
790 ** hex memory dump
791 */
792 #define MAX_DUMP 16
793 void hex_dump(guchar *ptr, guint length)
794 {
795 guchar ascii[MAX_DUMP+4];
796 guint i,j;
797
798 g_print("**hex_dump - %d bytes\n", length);
799
800 for(i=0;i<length;)
801 {
802 g_print("%08x: ", (guint)ptr+i);
803
804 for(j=0;j<MAX_DUMP;j++)
805 {
806 if(i >= length) break;
807
808 //store ascii value
809 if(ptr[i] >= 32 && ptr[i] <= 126)
810 ascii[j] = ptr[i];
811 else
812 ascii[j] = '.';
813
814 g_print("%02x ", ptr[i]);
815 i++;
816 }
817 //newline
818 ascii[j] = 0;
819 g_print(" '%s'\n", ascii);
820 }
821 }
822
823 #endif
This page took 0.066172 seconds and 4 git commands to generate.