1 /***********************************************************************************************************************************
2 Convert C Types
3 ***********************************************************************************************************************************/
4 #include "build.auto.h"
5
6 #include <ctype.h>
7 #include <inttypes.h>
8 #include <limits.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <time.h>
13
14 #include "common/debug.h"
15 #include "common/type/convert.h"
16
17 /***********************************************************************************************************************************
18 Check results of strto*() function for:
19 * leading/trailing spaces
20 * invalid characters
21 * blank string
22 * error in errno
23 ***********************************************************************************************************************************/
24 static void
cvtZToIntValid(int errNo,int base,const char * value,const char * endPtr,const char * type)25 cvtZToIntValid(int errNo, int base, const char *value, const char *endPtr, const char *type)
26 {
27 FUNCTION_TEST_BEGIN();
28 FUNCTION_TEST_PARAM(INT, errNo);
29 FUNCTION_TEST_PARAM(STRINGZ, value);
30 FUNCTION_TEST_PARAM(STRINGZ, endPtr);
31 FUNCTION_TEST_PARAM(STRINGZ, type);
32 FUNCTION_TEST_END();
33
34 ASSERT(value != NULL);
35 ASSERT(endPtr != NULL);
36
37 if (errNo != 0 || *value == '\0' || isspace(*value) || *endPtr != '\0')
38 THROW_FMT(FormatError, "unable to convert base %d string '%s' to %s", base, value, type);
39
40 FUNCTION_TEST_RETURN_VOID();
41 }
42
43 /***********************************************************************************************************************************
44 Convert zero-terminated string to int64 and validate result
45 ***********************************************************************************************************************************/
46 static int64_t
cvtZToInt64Internal(const char * value,const char * type,int base)47 cvtZToInt64Internal(const char *value, const char *type, int base)
48 {
49 FUNCTION_TEST_BEGIN();
50 FUNCTION_TEST_PARAM(STRINGZ, value);
51 FUNCTION_TEST_PARAM(STRINGZ, type);
52 FUNCTION_TEST_END();
53
54 ASSERT(value != NULL);
55 ASSERT(type != NULL);
56
57 // Convert from string
58 errno = 0;
59 char *endPtr = NULL;
60 int64_t result = strtoll(value, &endPtr, base);
61
62 // Validate the result
63 cvtZToIntValid(errno, base, value, endPtr, type);
64
65 FUNCTION_TEST_RETURN(result);
66 }
67
68 /***********************************************************************************************************************************
69 Convert zero-terminated string to uint64 and validate result
70 ***********************************************************************************************************************************/
71 static uint64_t
cvtZToUInt64Internal(const char * value,const char * type,int base)72 cvtZToUInt64Internal(const char *value, const char *type, int base)
73 {
74 FUNCTION_TEST_BEGIN();
75 FUNCTION_TEST_PARAM(STRINGZ, value);
76 FUNCTION_TEST_PARAM(STRINGZ, type);
77 FUNCTION_TEST_END();
78
79 ASSERT(value != NULL);
80 ASSERT(type != NULL);
81
82 // Convert from string
83 errno = 0;
84 char *endPtr = NULL;
85 uint64_t result = strtoull(value, &endPtr, base);
86
87 // Validate the result
88 cvtZToIntValid(errno, base, value, endPtr, type);
89
90 FUNCTION_TEST_RETURN(result);
91 }
92
93 /**********************************************************************************************************************************/
94 size_t
cvtBoolToZ(bool value,char * buffer,size_t bufferSize)95 cvtBoolToZ(bool value, char *buffer, size_t bufferSize)
96 {
97 FUNCTION_TEST_BEGIN();
98 FUNCTION_TEST_PARAM(BOOL, value);
99 FUNCTION_TEST_PARAM_P(CHARDATA, buffer);
100 FUNCTION_TEST_PARAM(SIZE, bufferSize);
101 FUNCTION_TEST_END();
102
103 ASSERT(buffer != NULL);
104
105 size_t result = (size_t)snprintf(buffer, bufferSize, "%s", cvtBoolToConstZ(value));
106
107 if (result >= bufferSize)
108 THROW(AssertError, "buffer overflow");
109
110 FUNCTION_TEST_RETURN(result);
111 }
112
113 const char *
cvtBoolToConstZ(bool value)114 cvtBoolToConstZ(bool value)
115 {
116 return value ? TRUE_Z : FALSE_Z;
117 }
118
119 /**********************************************************************************************************************************/
120 size_t
cvtCharToZ(char value,char * buffer,size_t bufferSize)121 cvtCharToZ(char value, char *buffer, size_t bufferSize)
122 {
123 FUNCTION_TEST_BEGIN();
124 FUNCTION_TEST_PARAM(BOOL, value);
125 FUNCTION_TEST_PARAM_P(CHARDATA, buffer);
126 FUNCTION_TEST_PARAM(SIZE, bufferSize);
127 FUNCTION_TEST_END();
128
129 ASSERT(buffer != NULL);
130
131 size_t result = (size_t)snprintf(buffer, bufferSize, "%c", value);
132
133 if (result >= bufferSize)
134 THROW(AssertError, "buffer overflow");
135
136 FUNCTION_TEST_RETURN(result);
137 }
138
139 /**********************************************************************************************************************************/
140 size_t
cvtDoubleToZ(double value,char * buffer,size_t bufferSize)141 cvtDoubleToZ(double value, char *buffer, size_t bufferSize)
142 {
143 FUNCTION_TEST_BEGIN();
144 FUNCTION_TEST_PARAM(DOUBLE, value);
145 FUNCTION_TEST_PARAM_P(CHARDATA, buffer);
146 FUNCTION_TEST_PARAM(SIZE, bufferSize);
147 FUNCTION_TEST_END();
148
149 ASSERT(buffer != NULL);
150
151 // Convert to a string
152 size_t result = (size_t)snprintf(buffer, bufferSize, "%lf", value);
153
154 if (result >= bufferSize)
155 THROW(AssertError, "buffer overflow");
156
157 // Any formatted double should be at least 8 characters, i.e. 0.000000
158 ASSERT(strlen(buffer) >= 8);
159 // Any formatted double should have a decimal point
160 ASSERT(strchr(buffer, '.') != NULL);
161
162 // Strip off any final 0s and the decimal point if there are no non-zero digits after it
163 char *end = buffer + strlen(buffer) - 1;
164
165 while (*end == '0' || *end == '.')
166 {
167 // It should not be possible to go past the beginning because format "%lf" will always write a decimal point
168 ASSERT(end > buffer);
169
170 end--;
171
172 if (*(end + 1) == '.')
173 break;
174 }
175
176 // Zero terminate the string
177 end[1] = 0;
178
179 // Return string length
180 FUNCTION_TEST_RETURN((size_t)(end - buffer + 1));
181 }
182
183 double
cvtZToDouble(const char * value)184 cvtZToDouble(const char *value)
185 {
186 FUNCTION_TEST_BEGIN();
187 FUNCTION_TEST_PARAM(STRINGZ, value);
188 FUNCTION_TEST_END();
189
190 ASSERT(value != NULL);
191
192 double result = 0;
193 sscanf(value, "%lf", &result);
194
195 if (result == 0 && strcmp(value, "0") != 0)
196 THROW_FMT(FormatError, "unable to convert string '%s' to double", value);
197
198 FUNCTION_TEST_RETURN(result);
199 }
200
201 /**********************************************************************************************************************************/
202 size_t
cvtIntToZ(int value,char * buffer,size_t bufferSize)203 cvtIntToZ(int value, char *buffer, size_t bufferSize)
204 {
205 FUNCTION_TEST_BEGIN();
206 FUNCTION_TEST_PARAM(INT, value);
207 FUNCTION_TEST_PARAM_P(CHARDATA, buffer);
208 FUNCTION_TEST_PARAM(SIZE, bufferSize);
209 FUNCTION_TEST_END();
210
211 ASSERT(buffer != NULL);
212
213 size_t result = (size_t)snprintf(buffer, bufferSize, "%d", value);
214
215 if (result >= bufferSize)
216 THROW(AssertError, "buffer overflow");
217
218 FUNCTION_TEST_RETURN(result);
219 }
220
221 int
cvtZToIntBase(const char * value,int base)222 cvtZToIntBase(const char *value, int base)
223 {
224 FUNCTION_TEST_BEGIN();
225 FUNCTION_TEST_PARAM(STRINGZ, value);
226 FUNCTION_TEST_END();
227
228 ASSERT(value != NULL);
229
230 int64_t result = cvtZToInt64Internal(value, "int", base);
231
232 if (result > INT_MAX || result < INT_MIN)
233 THROW_FMT(FormatError, "unable to convert base %d string '%s' to int", base, value);
234
235 FUNCTION_TEST_RETURN((int)result);
236 }
237
238 int
cvtZToInt(const char * value)239 cvtZToInt(const char *value)
240 {
241 FUNCTION_TEST_BEGIN();
242 FUNCTION_TEST_PARAM(STRINGZ, value);
243 FUNCTION_TEST_END();
244
245 ASSERT(value != NULL);
246
247 FUNCTION_TEST_RETURN(cvtZToIntBase(value, 10));
248 }
249
250 /**********************************************************************************************************************************/
251 size_t
cvtInt64ToZ(int64_t value,char * buffer,size_t bufferSize)252 cvtInt64ToZ(int64_t value, char *buffer, size_t bufferSize)
253 {
254 FUNCTION_TEST_BEGIN();
255 FUNCTION_TEST_PARAM(INT64, value);
256 FUNCTION_TEST_PARAM_P(CHARDATA, buffer);
257 FUNCTION_TEST_PARAM(SIZE, bufferSize);
258 FUNCTION_TEST_END();
259
260 ASSERT(buffer != NULL);
261
262 size_t result = (size_t)snprintf(buffer, bufferSize, "%" PRId64, value);
263
264 if (result >= bufferSize)
265 THROW(AssertError, "buffer overflow");
266
267 FUNCTION_TEST_RETURN(result);
268 }
269
270 int64_t
cvtZToInt64Base(const char * value,int base)271 cvtZToInt64Base(const char *value, int base)
272 {
273 FUNCTION_TEST_BEGIN();
274 FUNCTION_TEST_PARAM(STRINGZ, value);
275 FUNCTION_TEST_END();
276
277 ASSERT(value != NULL);
278
279 FUNCTION_TEST_RETURN(cvtZToInt64Internal(value, "int64", base));
280 }
281
282 int64_t
cvtZToInt64(const char * value)283 cvtZToInt64(const char *value)
284 {
285 FUNCTION_TEST_BEGIN();
286 FUNCTION_TEST_PARAM(STRINGZ, value);
287 FUNCTION_TEST_END();
288
289 ASSERT(value != NULL);
290
291 FUNCTION_TEST_RETURN(cvtZToInt64Base(value, 10));
292 }
293
294 /**********************************************************************************************************************************/
295 size_t
cvtModeToZ(mode_t value,char * buffer,size_t bufferSize)296 cvtModeToZ(mode_t value, char *buffer, size_t bufferSize)
297 {
298 FUNCTION_TEST_BEGIN();
299 FUNCTION_TEST_PARAM(MODE, value);
300 FUNCTION_TEST_PARAM_P(CHARDATA, buffer);
301 FUNCTION_TEST_PARAM(SIZE, bufferSize);
302 FUNCTION_TEST_END();
303
304 ASSERT(buffer != NULL);
305
306 size_t result = (size_t)snprintf(buffer, bufferSize, "%04o", value);
307
308 if (result >= bufferSize)
309 THROW(AssertError, "buffer overflow");
310
311 FUNCTION_TEST_RETURN(result);
312 }
313
314 mode_t
cvtZToMode(const char * value)315 cvtZToMode(const char *value)
316 {
317 FUNCTION_TEST_BEGIN();
318 FUNCTION_TEST_PARAM(STRINGZ, value);
319 FUNCTION_TEST_END();
320
321 ASSERT(value != NULL);
322
323 FUNCTION_TEST_RETURN((mode_t)cvtZToUIntBase(value, 8));
324 }
325
326 /**********************************************************************************************************************************/
327 size_t
cvtSizeToZ(size_t value,char * buffer,size_t bufferSize)328 cvtSizeToZ(size_t value, char *buffer, size_t bufferSize)
329 {
330 FUNCTION_TEST_BEGIN();
331 FUNCTION_TEST_PARAM(SIZE, value);
332 FUNCTION_TEST_PARAM_P(CHARDATA, buffer);
333 FUNCTION_TEST_PARAM(SIZE, bufferSize);
334 FUNCTION_TEST_END();
335
336 ASSERT(buffer != NULL);
337
338 size_t result = (size_t)snprintf(buffer, bufferSize, "%zu", value);
339
340 if (result >= bufferSize)
341 THROW(AssertError, "buffer overflow");
342
343 FUNCTION_TEST_RETURN(result);
344 }
345
346 size_t
cvtSSizeToZ(ssize_t value,char * buffer,size_t bufferSize)347 cvtSSizeToZ(ssize_t value, char *buffer, size_t bufferSize)
348 {
349 FUNCTION_TEST_BEGIN();
350 FUNCTION_TEST_PARAM(SSIZE, value);
351 FUNCTION_TEST_PARAM_P(CHARDATA, buffer);
352 FUNCTION_TEST_PARAM(SIZE, bufferSize);
353 FUNCTION_TEST_END();
354
355 ASSERT(buffer != NULL);
356
357 size_t result = (size_t)snprintf(buffer, bufferSize, "%zd", value);
358
359 if (result >= bufferSize)
360 THROW(AssertError, "buffer overflow");
361
362 FUNCTION_TEST_RETURN(result);
363 }
364
365 /**********************************************************************************************************************************/
366 size_t
cvtTimeToZ(time_t value,char * buffer,size_t bufferSize)367 cvtTimeToZ(time_t value, char *buffer, size_t bufferSize)
368 {
369 FUNCTION_TEST_BEGIN();
370 FUNCTION_TEST_PARAM(TIME, value);
371 FUNCTION_TEST_PARAM_P(CHARDATA, buffer);
372 FUNCTION_TEST_PARAM(SIZE, bufferSize);
373 FUNCTION_TEST_END();
374
375 ASSERT(buffer != NULL);
376
377 struct tm timePart;
378 size_t result = strftime(buffer, bufferSize, "%s", localtime_r(&value, &timePart));
379
380 if (result == 0)
381 THROW(AssertError, "buffer overflow");
382
383 FUNCTION_TEST_RETURN(result);
384 }
385
386 /**********************************************************************************************************************************/
387 size_t
cvtUIntToZ(unsigned int value,char * buffer,size_t bufferSize)388 cvtUIntToZ(unsigned int value, char *buffer, size_t bufferSize)
389 {
390 FUNCTION_TEST_BEGIN();
391 FUNCTION_TEST_PARAM(UINT, value);
392 FUNCTION_TEST_PARAM_P(CHARDATA, buffer);
393 FUNCTION_TEST_PARAM(SIZE, bufferSize);
394 FUNCTION_TEST_END();
395
396 ASSERT(buffer != NULL);
397
398 size_t result = (size_t)snprintf(buffer, bufferSize, "%u", value);
399
400 if (result >= bufferSize)
401 THROW(AssertError, "buffer overflow");
402
403 FUNCTION_TEST_RETURN(result);
404 }
405
406 unsigned int
cvtZToUIntBase(const char * value,int base)407 cvtZToUIntBase(const char *value, int base)
408 {
409 FUNCTION_TEST_BEGIN();
410 FUNCTION_TEST_PARAM(STRINGZ, value);
411 FUNCTION_TEST_END();
412
413 ASSERT(value != NULL);
414
415 uint64_t result = cvtZToUInt64Internal(value, "unsigned int", base);
416
417 // Don't allow negative numbers even though strtoull() does and check max value
418 if (*value == '-' || result > UINT_MAX)
419 THROW_FMT(FormatError, "unable to convert base %d string '%s' to unsigned int", base, value);
420
421 FUNCTION_TEST_RETURN((unsigned int)result);
422 }
423
424 unsigned int
cvtZToUInt(const char * value)425 cvtZToUInt(const char *value)
426 {
427 FUNCTION_TEST_BEGIN();
428 FUNCTION_TEST_PARAM(STRINGZ, value);
429 FUNCTION_TEST_END();
430
431 ASSERT(value != NULL);
432
433 FUNCTION_TEST_RETURN(cvtZToUIntBase(value, 10));
434 }
435
436 /**********************************************************************************************************************************/
437 size_t
cvtUInt64ToZ(uint64_t value,char * buffer,size_t bufferSize)438 cvtUInt64ToZ(uint64_t value, char *buffer, size_t bufferSize)
439 {
440 FUNCTION_TEST_BEGIN();
441 FUNCTION_TEST_PARAM(UINT64, value);
442 FUNCTION_TEST_PARAM_P(CHARDATA, buffer);
443 FUNCTION_TEST_PARAM(SIZE, bufferSize);
444 FUNCTION_TEST_END();
445
446 ASSERT(buffer != NULL);
447
448 size_t result = (size_t)snprintf(buffer, bufferSize, "%" PRIu64, value);
449
450 if (result >= bufferSize)
451 THROW(AssertError, "buffer overflow");
452
453 FUNCTION_TEST_RETURN(result);
454 }
455
456 uint64_t
cvtZToUInt64Base(const char * value,int base)457 cvtZToUInt64Base(const char *value, int base)
458 {
459 FUNCTION_TEST_BEGIN();
460 FUNCTION_TEST_PARAM(STRINGZ, value);
461 FUNCTION_TEST_END();
462
463 ASSERT(value != NULL);
464
465 uint64_t result = cvtZToUInt64Internal(value, "uint64", base);
466
467 // Don't allow negative numbers even though strtoull() does
468 if (*value == '-')
469 THROW_FMT(FormatError, "unable to convert base %d string '%s' to uint64", base, value);
470
471 FUNCTION_TEST_RETURN(result);
472 }
473
474 uint64_t
cvtZToUInt64(const char * value)475 cvtZToUInt64(const char *value)
476 {
477 FUNCTION_TEST_BEGIN();
478 FUNCTION_TEST_PARAM(STRINGZ, value);
479 FUNCTION_TEST_END();
480
481 ASSERT(value != NULL);
482
483 FUNCTION_TEST_RETURN(cvtZToUInt64Base(value, 10));
484 }
485