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