Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2025 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Lesser General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file value_kinds.c
18 : * @brief Parsing quantities and other decimal fractions
19 : * @author Christian Grothoff
20 : * @author Bohdan
21 : */
22 : #include "platform.h"
23 : #include <gnunet/gnunet_util_lib.h>
24 : #include <gnunet/gnunet_db_lib.h>
25 : #include <taler/taler_json_lib.h>
26 : #include "taler_merchant_util.h"
27 :
28 :
29 : enum GNUNET_GenericReturnValue
30 63 : TALER_MERCHANT_vk_parse_fractional_string (
31 : const char *value,
32 : int64_t *integer_part,
33 : uint32_t *fractional_part)
34 : {
35 : const char *ptr;
36 63 : uint64_t integer = 0;
37 63 : uint32_t frac = 0;
38 63 : unsigned int digits = 0;
39 :
40 63 : GNUNET_assert (NULL != integer_part);
41 63 : GNUNET_assert (NULL != fractional_part);
42 :
43 63 : if (NULL == value)
44 : {
45 0 : GNUNET_break_op (0);
46 0 : return GNUNET_SYSERR;
47 : }
48 63 : ptr = value;
49 63 : if ('\0' == *ptr)
50 : {
51 0 : GNUNET_break_op (0);
52 0 : return GNUNET_SYSERR;
53 : }
54 63 : if ('-' == *ptr)
55 : {
56 0 : GNUNET_break_op (0);
57 0 : return GNUNET_SYSERR;
58 : }
59 63 : if (! isdigit ((unsigned char) *ptr))
60 : {
61 0 : GNUNET_break_op (0);
62 0 : return GNUNET_SYSERR;
63 : }
64 135 : while (isdigit ((unsigned char) *ptr))
65 : {
66 72 : unsigned int digit = (unsigned int) (*ptr - '0');
67 :
68 72 : if (integer > (UINT64_MAX - digit) / 10)
69 : {
70 0 : GNUNET_break_op (0);
71 0 : return GNUNET_SYSERR;
72 : }
73 72 : integer = integer * 10 + digit;
74 72 : ptr++;
75 : }
76 63 : if ('.' == *ptr)
77 : {
78 12 : ptr++;
79 12 : if ('\0' == *ptr)
80 : {
81 0 : GNUNET_break_op (0);
82 0 : return GNUNET_SYSERR;
83 : }
84 28 : while (isdigit ((unsigned char) *ptr))
85 : {
86 16 : unsigned int digit = (unsigned int) (*ptr - '0');
87 :
88 16 : if (digits >= TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS)
89 : {
90 0 : GNUNET_break_op (0);
91 0 : return GNUNET_SYSERR;
92 : }
93 16 : frac = (uint32_t) (frac * 10 + digit);
94 16 : digits++;
95 16 : ptr++;
96 : }
97 68 : while (digits < TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS)
98 : {
99 56 : frac *= 10;
100 56 : digits++;
101 : }
102 : }
103 63 : if ('\0' != *ptr)
104 : {
105 0 : GNUNET_break_op (0);
106 0 : return GNUNET_SYSERR;
107 : }
108 63 : if (integer > (uint64_t) INT64_MAX)
109 : {
110 0 : GNUNET_break_op (0);
111 0 : return GNUNET_SYSERR;
112 : }
113 63 : *integer_part = integer;
114 63 : *fractional_part = frac;
115 63 : return GNUNET_OK;
116 : }
117 :
118 :
119 : enum GNUNET_GenericReturnValue
120 65 : TALER_MERCHANT_vk_process_quantity_inputs (enum TALER_MERCHANT_ValueKind kind,
121 : bool allow_fractional,
122 : bool int_missing,
123 : int64_t int_raw,
124 : bool str_missing,
125 : const char *str_raw,
126 : uint64_t *int_out,
127 : uint32_t *frac_out,
128 : const char **error_param)
129 : {
130 : static char errbuf[128];
131 65 : int64_t parsed_int = 0;
132 65 : uint32_t parsed_frac = 0;
133 65 : const char *int_field = (TALER_MERCHANT_VK_STOCK == kind)
134 : ? "total_stock"
135 65 : : "quantity";
136 65 : const char *str_field = (TALER_MERCHANT_VK_STOCK == kind)
137 : ? "unit_total_stock"
138 65 : : "unit_quantity";
139 :
140 65 : *error_param = NULL;
141 :
142 65 : if (int_missing && str_missing)
143 : {
144 0 : GNUNET_snprintf (errbuf,
145 : sizeof (errbuf),
146 : "missing %s and %s",
147 : int_field,
148 : str_field);
149 0 : *error_param = errbuf;
150 0 : GNUNET_break_op (0);
151 0 : return GNUNET_SYSERR;
152 : }
153 :
154 65 : if (! str_missing)
155 : {
156 65 : if ( (TALER_MERCHANT_VK_STOCK == kind) &&
157 36 : (0 == strcmp ("-1",
158 : str_raw)) )
159 : {
160 2 : parsed_int = -1;
161 2 : parsed_frac = 0;
162 : }
163 : else
164 : {
165 63 : if (GNUNET_OK !=
166 63 : TALER_MERCHANT_vk_parse_fractional_string (str_raw,
167 : &parsed_int,
168 : &parsed_frac))
169 : {
170 0 : GNUNET_snprintf (errbuf,
171 : sizeof (errbuf),
172 : "malformed %s",
173 : str_field);
174 0 : *error_param = errbuf;
175 0 : GNUNET_break_op (0);
176 0 : return GNUNET_SYSERR;
177 : }
178 : }
179 : }
180 :
181 65 : if ( (! int_missing) && (! str_missing) )
182 : {
183 1 : if ( (parsed_int != int_raw) || (0 != parsed_frac) )
184 : {
185 0 : GNUNET_snprintf (errbuf,
186 : sizeof (errbuf),
187 : "%s/%s mismatch",
188 : int_field,
189 : str_field);
190 0 : *error_param = errbuf;
191 0 : GNUNET_break_op (0);
192 0 : return GNUNET_SYSERR;
193 : }
194 : }
195 64 : else if (int_missing)
196 : {
197 64 : int_raw = parsed_int;
198 : }
199 :
200 65 : if ( (TALER_MERCHANT_VK_STOCK == kind) && (-1 == int_raw) )
201 : {
202 2 : if ( (! str_missing) && (0 != parsed_frac) )
203 : {
204 0 : GNUNET_snprintf (errbuf,
205 : sizeof (errbuf),
206 : "fractional part forbidden with %s='-1'",
207 : str_field);
208 0 : *error_param = errbuf;
209 0 : GNUNET_break_op (0);
210 0 : return GNUNET_SYSERR;
211 : }
212 2 : *int_out = INT64_MAX;
213 2 : *frac_out = INT32_MAX;
214 2 : return GNUNET_OK;
215 : }
216 :
217 63 : if (int_raw < 0)
218 : {
219 0 : GNUNET_snprintf (errbuf,
220 : sizeof (errbuf),
221 : "%s must be non-negative",
222 : int_field);
223 0 : *error_param = errbuf;
224 0 : GNUNET_break_op (0);
225 0 : return GNUNET_SYSERR;
226 : }
227 :
228 63 : if (! allow_fractional)
229 : {
230 51 : if ( (! str_missing) && (0 != parsed_frac) )
231 : {
232 4 : GNUNET_snprintf (errbuf,
233 : sizeof (errbuf),
234 : "fractional part not allowed for %s",
235 : str_field);
236 4 : *error_param = errbuf;
237 4 : GNUNET_break_op (0);
238 4 : return GNUNET_SYSERR;
239 : }
240 47 : parsed_frac = 0;
241 : }
242 12 : else if (! str_missing)
243 : {
244 12 : if (parsed_frac >= TALER_MERCHANT_UNIT_FRAC_BASE)
245 : {
246 0 : GNUNET_snprintf (errbuf,
247 : sizeof (errbuf),
248 : "%s fractional part exceeds base %u",
249 : str_field,
250 : TALER_MERCHANT_UNIT_FRAC_BASE);
251 0 : *error_param = errbuf;
252 0 : GNUNET_break_op (0);
253 0 : return GNUNET_SYSERR;
254 : }
255 : }
256 :
257 59 : *int_out = (uint64_t) int_raw;
258 59 : *frac_out = parsed_frac;
259 59 : return GNUNET_OK;
260 : }
261 :
262 :
263 : void
264 54 : TALER_MERCHANT_vk_format_fractional_string (
265 : enum TALER_MERCHANT_ValueKind kind,
266 : uint64_t integer,
267 : uint32_t fractional,
268 : size_t buffer_length,
269 : char buffer[static buffer_length])
270 54 : {
271 54 : GNUNET_assert (0 < buffer_length);
272 :
273 54 : if ( (TALER_MERCHANT_VK_STOCK == kind) &&
274 1 : (INT64_MAX == (int64_t) integer) &&
275 : (INT32_MAX == (int32_t) fractional) )
276 : {
277 1 : GNUNET_snprintf (buffer,
278 : buffer_length,
279 : "-1");
280 1 : return;
281 : }
282 :
283 53 : GNUNET_assert ( (TALER_MERCHANT_VK_QUANTITY != kind) ||
284 : ((INT64_MAX != (int64_t) integer) &&
285 : (INT32_MAX != (int32_t) fractional)) );
286 53 : GNUNET_assert (fractional < TALER_MERCHANT_UNIT_FRAC_BASE);
287 :
288 53 : if (0 == fractional)
289 : {
290 43 : GNUNET_snprintf (buffer,
291 : buffer_length,
292 : "%lu",
293 : integer);
294 43 : return;
295 : }
296 : {
297 : char frac_buf[TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS + 1];
298 : size_t idx;
299 :
300 10 : GNUNET_snprintf (frac_buf,
301 : sizeof (frac_buf),
302 : "%0*u",
303 : TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS,
304 : (unsigned int) fractional);
305 56 : for (idx = strlen (frac_buf); idx > 0; idx--)
306 : {
307 56 : if ('0' != frac_buf[idx - 1])
308 10 : break;
309 46 : frac_buf[idx - 1] = '\0';
310 : }
311 10 : GNUNET_snprintf (buffer,
312 : buffer_length,
313 : "%lu.%s",
314 : integer,
315 : frac_buf);
316 : }
317 : }
|