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