value_kinds.c (8242B)
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 "taler/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/taler_merchant_util.h" 27 28 29 enum GNUNET_GenericReturnValue 30 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 uint64_t integer = 0; 37 uint32_t frac = 0; 38 unsigned int digits = 0; 39 40 GNUNET_assert (NULL != integer_part); 41 GNUNET_assert (NULL != fractional_part); 42 43 if (NULL == value) 44 { 45 GNUNET_break_op (0); 46 return GNUNET_SYSERR; 47 } 48 ptr = value; 49 if ('\0' == *ptr) 50 { 51 GNUNET_break_op (0); 52 return GNUNET_SYSERR; 53 } 54 if ('-' == *ptr) 55 { 56 GNUNET_break_op (0); 57 return GNUNET_SYSERR; 58 } 59 if (! isdigit ((unsigned char) *ptr)) 60 { 61 GNUNET_break_op (0); 62 return GNUNET_SYSERR; 63 } 64 while (isdigit ((unsigned char) *ptr)) 65 { 66 unsigned int digit = (unsigned int) (*ptr - '0'); 67 68 if (integer > (UINT64_MAX - digit) / 10) 69 { 70 GNUNET_break_op (0); 71 return GNUNET_SYSERR; 72 } 73 integer = integer * 10 + digit; 74 ptr++; 75 } 76 if ('.' == *ptr) 77 { 78 ptr++; 79 if ('\0' == *ptr) 80 { 81 GNUNET_break_op (0); 82 return GNUNET_SYSERR; 83 } 84 while (isdigit ((unsigned char) *ptr)) 85 { 86 unsigned int digit = (unsigned int) (*ptr - '0'); 87 88 if (digits >= TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS) 89 { 90 GNUNET_break_op (0); 91 return GNUNET_SYSERR; 92 } 93 frac = (uint32_t) (frac * 10 + digit); 94 digits++; 95 ptr++; 96 } 97 while (digits < TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS) 98 { 99 frac *= 10; 100 digits++; 101 } 102 } 103 if ('\0' != *ptr) 104 { 105 GNUNET_break_op (0); 106 return GNUNET_SYSERR; 107 } 108 if (integer > (uint64_t) INT64_MAX) 109 { 110 GNUNET_break_op (0); 111 return GNUNET_SYSERR; 112 } 113 *integer_part = integer; 114 *fractional_part = frac; 115 return GNUNET_OK; 116 } 117 118 119 enum GNUNET_GenericReturnValue 120 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 int64_t parsed_int = 0; 132 uint32_t parsed_frac = 0; 133 const char *int_field = (TALER_MERCHANT_VK_STOCK == kind) 134 ? "total_stock" 135 : "quantity"; 136 const char *str_field = (TALER_MERCHANT_VK_STOCK == kind) 137 ? "unit_total_stock" 138 : "unit_quantity"; 139 140 *error_param = NULL; 141 142 if (int_missing && str_missing) 143 { 144 GNUNET_snprintf (errbuf, 145 sizeof (errbuf), 146 "missing %s and %s", 147 int_field, 148 str_field); 149 *error_param = errbuf; 150 GNUNET_break_op (0); 151 return GNUNET_SYSERR; 152 } 153 154 if (! str_missing) 155 { 156 if ( (TALER_MERCHANT_VK_STOCK == kind) && 157 (0 == strcmp ("-1", 158 str_raw)) ) 159 { 160 parsed_int = -1; 161 parsed_frac = 0; 162 } 163 else 164 { 165 if (GNUNET_OK != 166 TALER_MERCHANT_vk_parse_fractional_string (str_raw, 167 &parsed_int, 168 &parsed_frac)) 169 { 170 GNUNET_snprintf (errbuf, 171 sizeof (errbuf), 172 "malformed %s", 173 str_field); 174 *error_param = errbuf; 175 GNUNET_break_op (0); 176 return GNUNET_SYSERR; 177 } 178 } 179 } 180 181 if ( (! int_missing) && (! str_missing) ) 182 { 183 if ( (parsed_int != int_raw) || (0 != parsed_frac) ) 184 { 185 GNUNET_snprintf (errbuf, 186 sizeof (errbuf), 187 "%s/%s mismatch", 188 int_field, 189 str_field); 190 *error_param = errbuf; 191 GNUNET_break_op (0); 192 return GNUNET_SYSERR; 193 } 194 } 195 else if (int_missing) 196 { 197 int_raw = parsed_int; 198 } 199 200 if ( (TALER_MERCHANT_VK_STOCK == kind) && (-1 == int_raw) ) 201 { 202 if ( (! str_missing) && (0 != parsed_frac) ) 203 { 204 GNUNET_snprintf (errbuf, 205 sizeof (errbuf), 206 "fractional part forbidden with %s='-1'", 207 str_field); 208 *error_param = errbuf; 209 GNUNET_break_op (0); 210 return GNUNET_SYSERR; 211 } 212 *int_out = INT64_MAX; 213 *frac_out = INT32_MAX; 214 return GNUNET_OK; 215 } 216 217 if (int_raw < 0) 218 { 219 GNUNET_snprintf (errbuf, 220 sizeof (errbuf), 221 "%s must be non-negative", 222 int_field); 223 *error_param = errbuf; 224 GNUNET_break_op (0); 225 return GNUNET_SYSERR; 226 } 227 228 if (! allow_fractional) 229 { 230 if ( (! str_missing) && (0 != parsed_frac) ) 231 { 232 GNUNET_snprintf (errbuf, 233 sizeof (errbuf), 234 "fractional part not allowed for %s", 235 str_field); 236 *error_param = errbuf; 237 GNUNET_break_op (0); 238 return GNUNET_SYSERR; 239 } 240 parsed_frac = 0; 241 } 242 else if (! str_missing) 243 { 244 if (parsed_frac >= TALER_MERCHANT_UNIT_FRAC_BASE) 245 { 246 GNUNET_snprintf (errbuf, 247 sizeof (errbuf), 248 "%s fractional part exceeds base %u", 249 str_field, 250 TALER_MERCHANT_UNIT_FRAC_BASE); 251 *error_param = errbuf; 252 GNUNET_break_op (0); 253 return GNUNET_SYSERR; 254 } 255 } 256 257 *int_out = (uint64_t) int_raw; 258 *frac_out = parsed_frac; 259 return GNUNET_OK; 260 } 261 262 263 void 264 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 { 271 GNUNET_assert (0 < buffer_length); 272 273 if ( (TALER_MERCHANT_VK_STOCK == kind) && 274 (INT64_MAX == (int64_t) integer) && 275 (INT32_MAX == (int32_t) fractional) ) 276 { 277 GNUNET_snprintf (buffer, 278 buffer_length, 279 "-1"); 280 return; 281 } 282 283 GNUNET_assert ( (TALER_MERCHANT_VK_QUANTITY != kind) || 284 ((INT64_MAX != (int64_t) integer) && 285 (INT32_MAX != (int32_t) fractional)) ); 286 GNUNET_assert (fractional < TALER_MERCHANT_UNIT_FRAC_BASE); 287 288 if (0 == fractional) 289 { 290 GNUNET_snprintf (buffer, 291 buffer_length, 292 "%lu", 293 integer); 294 return; 295 } 296 { 297 char frac_buf[TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS + 1]; 298 size_t idx; 299 300 GNUNET_snprintf (frac_buf, 301 sizeof (frac_buf), 302 "%0*u", 303 TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS, 304 (unsigned int) fractional); 305 for (idx = strlen (frac_buf); idx > 0; idx--) 306 { 307 if ('0' != frac_buf[idx - 1]) 308 break; 309 frac_buf[idx - 1] = '\0'; 310 } 311 GNUNET_snprintf (buffer, 312 buffer_length, 313 "%lu.%s", 314 integer, 315 frac_buf); 316 } 317 }