1 /***************************************************************************
2 begin : Mon Apr 05 2004
3 copyright : (C) 2004 by Martin Preuss
4 email : martin@libchipcard.de
5
6 ***************************************************************************
7 * This file is part of the project "AqBanking". *
8 * Please see toplevel file COPYING of that project for license details. *
9 ***************************************************************************/
10
11 #ifdef HAVE_CONFIG_H
12 # include <config.h>
13 #endif
14
15 #include "value_p.h"
16
17 #include <gwenhywfar/misc.h>
18 #include <gwenhywfar/debug.h>
19 #include <gwenhywfar/text.h>
20 #include <gwenhywfar/buffer.h>
21
22 #include <assert.h>
23 #ifdef HAVE_LOCALE_H
24 # include <locale.h>
25 #endif
26
27 #include <ctype.h>
28
29
30 #define AB_VALUE_STRSIZE 256
31
32
33
GWEN_LIST_FUNCTIONS(AB_VALUE,AB_Value)34 GWEN_LIST_FUNCTIONS(AB_VALUE, AB_Value)
35
36
37
38
39 AB_VALUE *AB_Value_new(void)
40 {
41 AB_VALUE *v;
42
43 GWEN_NEW_OBJECT(AB_VALUE, v);
44 GWEN_LIST_INIT(AB_VALUE, v);
45 mpq_init(v->value);
46 return v;
47 }
48
49
50
AB_Value_free(AB_VALUE * v)51 void AB_Value_free(AB_VALUE *v)
52 {
53 if (v) {
54 mpq_clear(v->value);
55 free(v->currency);
56 GWEN_LIST_FINI(AB_VALUE, v);
57 GWEN_FREE_OBJECT(v);
58 }
59 }
60
61
62
AB_Value_dup(const AB_VALUE * ov)63 AB_VALUE *AB_Value_dup(const AB_VALUE *ov)
64 {
65 AB_VALUE *v;
66
67 assert(ov);
68 v=AB_Value_new();
69 mpq_set(v->value, ov->value);
70 if (ov->currency)
71 v->currency=strdup(ov->currency);
72
73 return v;
74 }
75
76
77
AB_Value_fromDouble(double i)78 AB_VALUE *AB_Value_fromDouble(double i)
79 {
80 GWEN_BUFFER *nbuf;
81 AB_VALUE *v;
82 int rv;
83
84 nbuf=GWEN_Buffer_new(0, 256, 0, 1);
85 rv=GWEN_Text_DoubleToBuffer(i, nbuf);
86 assert(rv==0);
87 v=AB_Value_fromString(GWEN_Buffer_GetStart(nbuf));
88 GWEN_Buffer_free(nbuf);
89 return v;
90 }
91
AB_Value_fromInt(long int num,long int denom)92 AB_VALUE *AB_Value_fromInt(long int num, long int denom)
93 {
94 AB_VALUE *v;
95
96 v=AB_Value_new();
97 mpq_set_si(v->value, num, denom);
98
99 return v;
100 }
101
102
AB_Value_determineDecimalComma(const char * s)103 static int AB_Value_determineDecimalComma(const char *s)
104 {
105 int len;
106 int i;
107
108 len=strlen(s);
109 for (i=len-1; i>=0; i--) {
110 if (s[i]==',' || s[i]=='.')
111 return (int)(s[i]);
112 }
113
114 return 0;
115 }
116
117
118
AB_Value_fromString(const char * s)119 AB_VALUE *AB_Value_fromString(const char *s)
120 {
121 AB_VALUE *v;
122 const char *currency=NULL;
123 int conversion_succeeded = 1; // assume conversion will succeed
124 char *tmpString=NULL;
125 char *p;
126 char *t;
127 char decimalComma;
128 int isNeg=0;
129
130 if (!s) {
131 DBG_ERROR(AQBANKING_LOGDOMAIN, "Attempt to convert a NULL value");
132 return NULL;
133 }
134
135 tmpString=strdup(s);
136 p=tmpString;
137
138 while (*p && *p<33)
139 p++;
140
141 if (*p=='-') {
142 isNeg=1;
143 p++;
144 }
145 else if (*p=='+') {
146 p++;
147 }
148
149 t=strchr(p, ':');
150 if (t) {
151 currency=t+1;
152 *t=0;
153 }
154
155 /* remove thousand's comma */
156 decimalComma=AB_Value_determineDecimalComma(p);
157 if (decimalComma) {
158 char *s1, *d1;
159
160 s1=p;
161 d1=p;
162 while (*s1) {
163 register char c;
164
165 c=*(s1++);
166 if (isdigit(c) || c=='/')
167 *(d1++)=c;
168 else if (c==decimalComma)
169 /* always use '.' as decimal comma */
170 *(d1++)='.';
171 }
172 *d1=0;
173 }
174
175 v=AB_Value_new();
176
177 t=strchr(p, '.');
178 if (t) {
179 // remove comma and calculate denominator
180 unsigned long denominator = 1;
181 char *next;
182 do {
183 next=t+1;
184 *t=*next;
185 if (*next != 0)
186 denominator *= 10;
187 t++;
188 }
189 while (*next);
190
191 // set denominator to the calculated value
192 mpz_set_ui(mpq_denref(v->value), denominator);
193
194 // set numerator to the resulting integer string without comma
195 if (mpz_set_str(mpq_numref(v->value), p, 10) == -1) {
196 conversion_succeeded = 0;
197 }
198 }
199 else {
200 /*DBG_ERROR(0, "Scanning this value: %s\n", p);*/
201 conversion_succeeded = (gmp_sscanf(p, "%Qu", v->value) == 1);
202 }
203
204 /* set currency (if any) */
205 if (currency)
206 v->currency=strdup(currency);
207
208 /* temporary string no longer needed */
209 free(tmpString);
210
211 if (!conversion_succeeded) {
212 DBG_ERROR(AQBANKING_LOGDOMAIN, "[%s] is not a valid value", s);
213 AB_Value_free(v);
214 return NULL;
215 }
216
217 if (isNeg)
218 mpq_neg(v->value, v->value);
219
220
221 return v;
222 }
223
224
225
AB_Value_GetCurrency(const AB_VALUE * v)226 const char *AB_Value_GetCurrency(const AB_VALUE *v)
227 {
228 assert(v);
229 return v->currency;
230 }
231
232
233
AB_Value_SetCurrency(AB_VALUE * v,const char * s)234 void AB_Value_SetCurrency(AB_VALUE *v, const char *s)
235 {
236 assert(v);
237 free(v->currency);
238 if (s)
239 v->currency=strdup(s);
240 else
241 v->currency=0;
242 }
243
244
245
AB_Value_fromDb(GWEN_DB_NODE * db)246 AB_VALUE *AB_Value_fromDb(GWEN_DB_NODE *db)
247 {
248 AB_VALUE *vc;
249 const char *p;
250
251 /* read and parse value */
252 p=GWEN_DB_GetCharValue(db, "value", 0, 0);
253 if (!p)
254 return NULL;
255 vc=AB_Value_fromString(p);
256 if (vc==NULL)
257 return NULL;
258
259 /* read currency (if any) */
260 p=GWEN_DB_GetCharValue(db, "currency", 0, "EUR");
261 if (p)
262 AB_Value_SetCurrency(vc, p);
263 return vc;
264 }
265
266
267
AB_Value_toDb(const AB_VALUE * v,GWEN_DB_NODE * db)268 int AB_Value_toDb(const AB_VALUE *v, GWEN_DB_NODE *db)
269 {
270 GWEN_BUFFER *buf;
271
272 buf=GWEN_Buffer_new(0, 128, 0, 1);
273 AB_Value__toString(v, buf);
274 GWEN_DB_SetCharValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS,
275 "value", GWEN_Buffer_GetStart(buf));
276 GWEN_Buffer_free(buf);
277 if (v->currency)
278 GWEN_DB_SetCharValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS,
279 "currency", v->currency);
280 return 0;
281 }
282
283
284
AB_Value_toDbFloat(const AB_VALUE * v,GWEN_DB_NODE * db)285 int AB_Value_toDbFloat(const AB_VALUE *v, GWEN_DB_NODE *db)
286 {
287 GWEN_BUFFER *buf;
288
289 buf=GWEN_Buffer_new(0, 128, 0, 1);
290 AB_Value_toHumanReadableString(v, buf, 2, 0);
291 GWEN_DB_SetCharValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS,
292 "value", GWEN_Buffer_GetStart(buf));
293 GWEN_Buffer_free(buf);
294 if (v->currency)
295 GWEN_DB_SetCharValue(db, GWEN_DB_FLAGS_OVERWRITE_VARS,
296 "currency", v->currency);
297 return 0;
298 }
299
300
301
AB_Value__toString(const AB_VALUE * v,GWEN_BUFFER * buf)302 void AB_Value__toString(const AB_VALUE *v, GWEN_BUFFER *buf)
303 {
304 int rv;
305 uint32_t size;
306 char *p;
307
308 assert(v);
309 GWEN_Buffer_AllocRoom(buf, AB_VALUE_STRSIZE);
310 p=GWEN_Buffer_GetPosPointer(buf);
311 size=GWEN_Buffer_GetMaxUnsegmentedWrite(buf);
312 rv=gmp_snprintf(p, size, "%Qi", v->value);
313 assert(rv<size);
314 GWEN_Buffer_IncrementPos(buf, rv);
315 GWEN_Buffer_AdjustUsedBytes(buf);
316 }
317
318
319
AB_Value_GetNumDenomString(const AB_VALUE * v,char * buffer,uint32_t buflen)320 int AB_Value_GetNumDenomString(const AB_VALUE *v,
321 char *buffer,
322 uint32_t buflen)
323 {
324 int rv;
325
326 assert(v);
327
328 rv=gmp_snprintf(buffer, buflen, "%Qu", v->value);
329 if (rv<0 || rv>=buflen) {
330 DBG_ERROR(AQBANKING_LOGDOMAIN, "Buffer too small");
331 return GWEN_ERROR_BUFFER_OVERFLOW;
332 }
333 return 0;
334 }
335
336
337
AB_Value_toString(const AB_VALUE * v,GWEN_BUFFER * buf)338 void AB_Value_toString(const AB_VALUE *v, GWEN_BUFFER *buf)
339 {
340 assert(v);
341 AB_Value__toString(v, buf);
342 if (v->currency) {
343 GWEN_Buffer_AppendString(buf, ":");
344 GWEN_Buffer_AppendString(buf, v->currency);
345 }
346 }
347
348
349
AB_Value_toHumanReadableString(const AB_VALUE * v,GWEN_BUFFER * buf,int prec,int withCurrency)350 void AB_Value_toHumanReadableString(const AB_VALUE *v,
351 GWEN_BUFFER *buf,
352 int prec,
353 int withCurrency)
354 {
355 char numbuf[128];
356 double num;
357 int rv;
358 #ifdef HAVE_SETLOCALE
359 const char *orig_locale = setlocale(LC_NUMERIC, NULL);
360 char *currentLocale = strdup(orig_locale ? orig_locale : "C");
361 setlocale(LC_NUMERIC, "C");
362 #endif
363
364 num=AB_Value_GetValueAsDouble(v);
365 rv=snprintf(numbuf, sizeof(numbuf), "%.*f",
366 prec, num);
367
368 #ifdef HAVE_SETLOCALE
369 setlocale(LC_NUMERIC, currentLocale);
370 free(currentLocale);
371 #endif
372
373 if (rv<1 || rv>=sizeof(numbuf)) {
374 assert(0);
375 }
376 GWEN_Buffer_AppendString(buf, numbuf);
377
378 if (v->currency && withCurrency) {
379 GWEN_Buffer_AppendString(buf, " ");
380 GWEN_Buffer_AppendString(buf, v->currency);
381 }
382 }
383
384
385
AB_Value_GetValueAsDouble(const AB_VALUE * v)386 double AB_Value_GetValueAsDouble(const AB_VALUE *v)
387 {
388 assert(v);
389 if (mpz_fits_slong_p(mpq_numref(v->value)) && mpz_fits_slong_p(mpq_denref(v->value))) {
390 return (double)(mpz_get_d(mpq_numref(v->value)) / mpz_get_d(mpq_denref(v->value)));
391 }
392 else {
393 return mpq_get_d(v->value);
394 }
395 }
396
397
398
AB_Value_SetValueFromDouble(AB_VALUE * v,double i)399 void AB_Value_SetValueFromDouble(AB_VALUE *v, double i)
400 {
401 assert(v);
402 mpq_set_d(v->value, i);
403 }
404
405
406
AB_Value_SetZero(AB_VALUE * v)407 void AB_Value_SetZero(AB_VALUE *v)
408 {
409 assert(v);
410 mpq_clear(v->value);
411 mpq_init(v->value);
412 }
413
414
415
AB_Value_IsZero(const AB_VALUE * v)416 int AB_Value_IsZero(const AB_VALUE *v)
417 {
418 assert(v);
419 return (mpq_sgn(v->value)==0);
420 }
421
422
423
AB_Value_IsNegative(const AB_VALUE * v)424 int AB_Value_IsNegative(const AB_VALUE *v)
425 {
426 assert(v);
427 return (mpq_sgn(v->value)<0);
428 }
429
430
431
AB_Value_IsPositive(const AB_VALUE * v)432 int AB_Value_IsPositive(const AB_VALUE *v)
433 {
434 assert(v);
435 return (mpq_sgn(v->value)>=0);
436 }
437
438
439
AB_Value_Compare(const AB_VALUE * v1,const AB_VALUE * v2)440 int AB_Value_Compare(const AB_VALUE *v1, const AB_VALUE *v2)
441 {
442 assert(v1);
443 assert(v2);
444
445 return mpq_cmp(v1->value, v2->value);
446 }
447
AB_Value_Equal(const AB_VALUE * v1,const AB_VALUE * v2)448 int AB_Value_Equal(const AB_VALUE *v1, const AB_VALUE *v2)
449 {
450 assert(v1);
451 assert(v2);
452
453 return mpq_equal(v1->value, v2->value);
454 }
455
456
457
AB_Value_AddValue(AB_VALUE * v1,const AB_VALUE * v2)458 int AB_Value_AddValue(AB_VALUE *v1, const AB_VALUE *v2)
459 {
460 assert(v1);
461 assert(v2);
462
463 mpq_add(v1->value, v1->value, v2->value);
464 return 0;
465 }
466
467
468
AB_Value_SubValue(AB_VALUE * v1,const AB_VALUE * v2)469 int AB_Value_SubValue(AB_VALUE *v1, const AB_VALUE *v2)
470 {
471 assert(v1);
472 assert(v2);
473 mpq_sub(v1->value, v1->value, v2->value);
474 return 0;
475 }
476
477
478
AB_Value_MultValue(AB_VALUE * v1,const AB_VALUE * v2)479 int AB_Value_MultValue(AB_VALUE *v1, const AB_VALUE *v2)
480 {
481 assert(v1);
482 assert(v2);
483
484 mpq_mul(v1->value, v1->value, v2->value);
485 return 0;
486 }
487
488
489
AB_Value_DivValue(AB_VALUE * v1,const AB_VALUE * v2)490 int AB_Value_DivValue(AB_VALUE *v1, const AB_VALUE *v2)
491 {
492 assert(v1);
493 assert(v2);
494
495 mpq_div(v1->value, v1->value, v2->value);
496 return 0;
497 }
498
499
500
AB_Value_Negate(AB_VALUE * v)501 int AB_Value_Negate(AB_VALUE *v)
502 {
503 assert(v);
504 mpq_neg(v->value, v->value);
505 return 0;
506 }
507
508
509
AB_Value_Dump(const AB_VALUE * v,FILE * f,unsigned int indent)510 void AB_Value_Dump(const AB_VALUE *v, FILE *f, unsigned int indent)
511 {
512 unsigned int i;
513
514 for (i=0; i<indent; i++)
515 fprintf(f, " ");
516 fprintf(f, "Value: ");
517 if (v) {
518 GWEN_BUFFER *nbuf;
519
520 nbuf=GWEN_Buffer_new(0, 128, 0, 1);
521 AB_Value_toHumanReadableString(v, nbuf, 2, 1);
522 gmp_fprintf(f, "%Qi (%s)\n", v->value, GWEN_Buffer_GetStart(nbuf));
523 GWEN_Buffer_free(nbuf);
524 }
525 else
526 fprintf(f, "[none]\n");
527 }
528
529
530
AB_Value_List_dup(const AB_VALUE_LIST * stl)531 AB_VALUE_LIST *AB_Value_List_dup(const AB_VALUE_LIST *stl)
532 {
533 if (stl) {
534 AB_VALUE_LIST *nl;
535 AB_VALUE *e;
536
537 nl=AB_Value_List_new();
538 e=AB_Value_List_First(stl);
539 while (e) {
540 AB_VALUE *ne;
541
542 ne=AB_Value_dup(e);
543 assert(ne);
544 AB_Value_List_Add(ne, nl);
545 e=AB_Value_List_Next(e);
546 } /* while (e) */
547 return nl;
548 }
549 else
550 return 0;
551 }
552
553
554
AB_Value_Num(const AB_VALUE * v)555 long int AB_Value_Num(const AB_VALUE *v)
556 {
557 assert(v);
558 return mpz_get_si(mpq_numref(v->value));
559 }
560
561
562
AB_Value_Denom(const AB_VALUE * v)563 long int AB_Value_Denom(const AB_VALUE *v)
564 {
565 assert(v);
566 return mpz_get_si(mpq_denref(v->value));
567 }
568
569
570
AB_Value_toHbciString(const AB_VALUE * v,GWEN_BUFFER * buf)571 void AB_Value_toHbciString(const AB_VALUE *v, GWEN_BUFFER *buf)
572 {
573 GWEN_BUFFER *tbuf;
574 char *p;
575 int l;
576
577 tbuf=GWEN_Buffer_new(0, 32, 0, 1);
578 AB_Value_toHumanReadableString(v, tbuf, 2, 0);
579
580 /* convert decimal komma */
581 p=GWEN_Buffer_GetStart(tbuf);
582 while (*p) {
583 if (*p=='.') {
584 *p=',';
585 break;
586 }
587 p++;
588 }
589
590 /* remove trailing zeroes */
591 p=GWEN_Buffer_GetStart(tbuf);
592 l=strlen(GWEN_Buffer_GetStart(tbuf));
593 if (l>0 && strchr(p, ',')!=NULL) {
594 l--;
595 while (l>0 && p[l]=='0') {
596 p[l]=0;
597 l--;
598 }
599 }
600
601 GWEN_Buffer_AppendString(buf, GWEN_Buffer_GetStart(tbuf));
602 GWEN_Buffer_free(tbuf);
603 }
604
605