]> sigrok.org Git - libsigrok.git/blame - src/strutil.c
Doxygen: Properly mark a few symbols as private.
[libsigrok.git] / src / strutil.c
CommitLineData
25e7d9b1 1/*
50985c20 2 * This file is part of the libsigrok project.
25e7d9b1
UH
3 *
4 * Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
2ea1fdf1 17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
25e7d9b1
UH
18 */
19
79034d4f 20/* Needed for POSIX.1-2008 locale functions */
82b9f3d1 21/** @cond PRIVATE */
79034d4f 22#define _XOPEN_SOURCE 700
82b9f3d1 23/** @endcond */
6ec6c43b 24#include <config.h>
b8278e09 25#include <ctype.h>
79034d4f
FS
26#include <locale.h>
27#if defined(__FreeBSD__) || defined(__APPLE__)
28#include <xlocale.h>
29#endif
30#if defined(__FreeBSD__)
31#include <sys/param.h>
32#endif
25e7d9b1
UH
33#include <stdint.h>
34#include <stdlib.h>
35#include <string.h>
ba464a12 36#include <strings.h>
9e4f8cf9 37#include <errno.h>
c1aae900 38#include <libsigrok/libsigrok.h>
45c59c8b 39#include "libsigrok-internal.h"
25e7d9b1 40
2ad1deb8 41/** @cond PRIVATE */
3544f848 42#define LOG_PREFIX "strutil"
2ad1deb8 43/** @endcond */
a885ce3e 44
393fb9cb
UH
45/**
46 * @file
47 *
48 * Helper functions for handling or converting libsigrok-related strings.
49 */
50
7b870c38
UH
51/**
52 * @defgroup grp_strutil String utilities
53 *
54 * Helper functions for handling or converting libsigrok-related strings.
55 *
56 * @{
57 */
58
9e4f8cf9 59/**
1ba4a1cf 60 * Convert a string representation of a numeric value (base 10) to a long integer. The
9e4f8cf9
DJ
61 * conversion is strict and will fail if the complete string does not represent
62 * a valid long integer. The function sets errno according to the details of the
63 * failure.
64 *
65 * @param str The string representation to convert.
66 * @param ret Pointer to long where the result of the conversion will be stored.
67 *
1ba4a1cf
MH
68 * @retval SR_OK Conversion successful.
69 * @retval SR_ERR Failure.
82b9f3d1
UH
70 *
71 * @private
9e4f8cf9 72 */
8d558c7a 73SR_PRIV int sr_atol(const char *str, long *ret)
9e4f8cf9
DJ
74{
75 long tmp;
76 char *endptr = NULL;
77
78 errno = 0;
1ba4a1cf 79 tmp = strtol(str, &endptr, 10);
9e4f8cf9 80
b8278e09
GS
81 while (endptr && isspace(*endptr))
82 endptr++;
83
9e4f8cf9
DJ
84 if (!endptr || *endptr || errno) {
85 if (!errno)
86 errno = EINVAL;
87 return SR_ERR;
88 }
89
90 *ret = tmp;
91 return SR_OK;
92}
93
94/**
1ba4a1cf 95 * Convert a string representation of a numeric value (base 10) to an integer. The
9e4f8cf9
DJ
96 * conversion is strict and will fail if the complete string does not represent
97 * a valid integer. The function sets errno according to the details of the
98 * failure.
99 *
100 * @param str The string representation to convert.
101 * @param ret Pointer to int where the result of the conversion will be stored.
102 *
1ba4a1cf
MH
103 * @retval SR_OK Conversion successful.
104 * @retval SR_ERR Failure.
82b9f3d1
UH
105 *
106 * @private
9e4f8cf9 107 */
8d558c7a 108SR_PRIV int sr_atoi(const char *str, int *ret)
9e4f8cf9
DJ
109{
110 long tmp;
111
112 if (sr_atol(str, &tmp) != SR_OK)
113 return SR_ERR;
114
115 if ((int) tmp != tmp) {
116 errno = ERANGE;
117 return SR_ERR;
118 }
119
120 *ret = (int) tmp;
121 return SR_OK;
122}
123
124/**
125 * Convert a string representation of a numeric value to a double. The
126 * conversion is strict and will fail if the complete string does not represent
127 * a valid double. The function sets errno according to the details of the
128 * failure.
129 *
130 * @param str The string representation to convert.
131 * @param ret Pointer to double where the result of the conversion will be stored.
132 *
1ba4a1cf
MH
133 * @retval SR_OK Conversion successful.
134 * @retval SR_ERR Failure.
82b9f3d1
UH
135 *
136 * @private
9e4f8cf9 137 */
8d558c7a 138SR_PRIV int sr_atod(const char *str, double *ret)
9e4f8cf9
DJ
139{
140 double tmp;
141 char *endptr = NULL;
142
143 errno = 0;
144 tmp = strtof(str, &endptr);
145
b8278e09
GS
146 while (endptr && isspace(*endptr))
147 endptr++;
148
9e4f8cf9
DJ
149 if (!endptr || *endptr || errno) {
150 if (!errno)
151 errno = EINVAL;
152 return SR_ERR;
153 }
154
155 *ret = tmp;
156 return SR_OK;
157}
158
159/**
160 * Convert a string representation of a numeric value to a float. The
161 * conversion is strict and will fail if the complete string does not represent
162 * a valid float. The function sets errno according to the details of the
163 * failure.
164 *
165 * @param str The string representation to convert.
166 * @param ret Pointer to float where the result of the conversion will be stored.
167 *
1ba4a1cf
MH
168 * @retval SR_OK Conversion successful.
169 * @retval SR_ERR Failure.
82b9f3d1
UH
170 *
171 * @private
9e4f8cf9 172 */
8d558c7a 173SR_PRIV int sr_atof(const char *str, float *ret)
9e4f8cf9
DJ
174{
175 double tmp;
176
177 if (sr_atod(str, &tmp) != SR_OK)
178 return SR_ERR;
179
180 if ((float) tmp != tmp) {
181 errno = ERANGE;
182 return SR_ERR;
183 }
184
185 *ret = (float) tmp;
186 return SR_OK;
187}
188
4f0463a0 189/**
4f0463a0
FS
190 * Convert a string representation of a numeric value to a double. The
191 * conversion is strict and will fail if the complete string does not represent
192 * a valid double. The function sets errno according to the details of the
193 * failure. This version ignores the locale.
194 *
195 * @param str The string representation to convert.
196 * @param ret Pointer to double where the result of the conversion will be stored.
197 *
198 * @retval SR_OK Conversion successful.
199 * @retval SR_ERR Failure.
82b9f3d1
UH
200 *
201 * @private
4f0463a0
FS
202 */
203SR_PRIV int sr_atod_ascii(const char *str, double *ret)
204{
205 double tmp;
206 char *endptr = NULL;
207
208 errno = 0;
209 tmp = g_ascii_strtod(str, &endptr);
210
211 if (!endptr || *endptr || errno) {
212 if (!errno)
213 errno = EINVAL;
214 return SR_ERR;
215 }
216
217 *ret = tmp;
218 return SR_OK;
219}
220
9806c2d5 221/**
9806c2d5
DJ
222 * Convert a string representation of a numeric value to a float. The
223 * conversion is strict and will fail if the complete string does not represent
224 * a valid float. The function sets errno according to the details of the
225 * failure. This version ignores the locale.
226 *
227 * @param str The string representation to convert.
228 * @param ret Pointer to float where the result of the conversion will be stored.
229 *
1ba4a1cf
MH
230 * @retval SR_OK Conversion successful.
231 * @retval SR_ERR Failure.
82b9f3d1
UH
232 *
233 * @private
9806c2d5
DJ
234 */
235SR_PRIV int sr_atof_ascii(const char *str, float *ret)
236{
237 double tmp;
238 char *endptr = NULL;
239
240 errno = 0;
241 tmp = g_ascii_strtod(str, &endptr);
242
243 if (!endptr || *endptr || errno) {
244 if (!errno)
245 errno = EINVAL;
246 return SR_ERR;
247 }
248
249 /* FIXME This fails unexpectedly. Some other method to safel downcast
250 * needs to be found. Checking against FLT_MAX doesn't work as well. */
251 /*
252 if ((float) tmp != tmp) {
253 errno = ERANGE;
254 sr_dbg("ERANGEEEE %e != %e", (float) tmp, tmp);
255 return SR_ERR;
256 }
257 */
258
259 *ret = (float) tmp;
260 return SR_OK;
261}
262
21ef355e
FS
263/**
264 * Compose a string with a format string in the buffer pointed to by buf.
265 *
266 * It is up to the caller to ensure that the allocated buffer is large enough
267 * to hold the formatted result.
268 *
269 * A terminating NUL character is automatically appended after the content
270 * written.
271 *
272 * After the format parameter, the function expects at least as many additional
273 * arguments as needed for format.
274 *
275 * This version ignores the current locale and uses the locale "C" for Linux,
276 * FreeBSD, OSX and Android.
277 *
278 * @param buf Pointer to a buffer where the resulting C string is stored.
279 * @param format C string that contains a format string (see printf).
280 * @param ... A sequence of additional arguments, each containing a value to be
281 * used to replace a format specifier in the format string.
282 *
283 * @return On success, the number of characters that would have been written,
284 * not counting the terminating NUL character.
285 *
286 * @since 0.6.0
287 */
288SR_API int sr_sprintf_ascii(char *buf, const char *format, ...)
289{
290 int ret;
291 va_list args;
292
293 va_start(args, format);
294 ret = sr_vsprintf_ascii(buf, format, args);
295 va_end(args);
296
297 return ret;
298}
299
300/**
301 * Compose a string with a format string in the buffer pointed to by buf.
302 *
303 * It is up to the caller to ensure that the allocated buffer is large enough
304 * to hold the formatted result.
305 *
306 * Internally, the function retrieves arguments from the list identified by
307 * args as if va_arg was used on it, and thus the state of args is likely to
308 * be altered by the call.
309 *
310 * In any case, args should have been initialized by va_start at some point
311 * before the call, and it is expected to be released by va_end at some point
312 * after the call.
313 *
314 * This version ignores the current locale and uses the locale "C" for Linux,
315 * FreeBSD, OSX and Android.
316 *
317 * @param buf Pointer to a buffer where the resulting C string is stored.
318 * @param format C string that contains a format string (see printf).
319 * @param args A value identifying a variable arguments list initialized with
320 * va_start.
321 *
322 * @return On success, the number of characters that would have been written,
323 * not counting the terminating NUL character.
324 *
325 * @since 0.6.0
326 */
327SR_API int sr_vsprintf_ascii(char *buf, const char *format, va_list args)
328{
329#if defined(_WIN32)
330 int ret;
331
332#if 0
333 /*
334 * TODO: This part compiles with mingw-w64 but doesn't run with Win7.
335 * Doesn't start because of "Procedure entry point _create_locale
336 * not found in msvcrt.dll".
337 * mingw-w64 should link to msvcr100.dll not msvcrt.dll!
338 * See: https://msdn.microsoft.com/en-us/en-en/library/1kt27hek.aspx
339 */
340 _locale_t locale;
341
342 locale = _create_locale(LC_NUMERIC, "C");
343 ret = _vsprintf_l(buf, format, locale, args);
344 _free_locale(locale);
345#endif
346
347 /* vsprintf() uses the current locale, may not work correctly for floats. */
348 ret = vsprintf(buf, format, args);
349
350 return ret;
351#elif defined(__APPLE__)
352 /*
353 * See:
354 * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/printf_l.3.html
355 * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/xlocale.3.html
356 */
357 int ret;
358 locale_t locale;
359
360 locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
361 ret = vsprintf_l(buf, locale, format, args);
362 freelocale(locale);
363
364 return ret;
365#elif defined(__FreeBSD__) && __FreeBSD_version >= 901000
366 /*
367 * See:
368 * https://www.freebsd.org/cgi/man.cgi?query=printf_l&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE
369 * https://www.freebsd.org/cgi/man.cgi?query=xlocale&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE
370 */
371 int ret;
372 locale_t locale;
373
374 locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
375 ret = vsprintf_l(buf, locale, format, args);
376 freelocale(locale);
377
378 return ret;
379#elif defined(__ANDROID__)
380 /*
381 * The Bionic libc only has two locales ("C" aka "POSIX" and "C.UTF-8"
382 * aka "en_US.UTF-8"). The decimal point is hard coded as "."
383 * See: https://android.googlesource.com/platform/bionic/+/master/libc/bionic/locale.cpp
384 */
385 int ret;
386
387 ret = vsprintf(buf, format, args);
388
389 return ret;
390#elif defined(__linux__)
391 int ret;
392 locale_t old_locale, temp_locale;
393
394 /* Switch to C locale for proper float/double conversion. */
395 temp_locale = newlocale(LC_NUMERIC, "C", NULL);
396 old_locale = uselocale(temp_locale);
397
398 ret = vsprintf(buf, format, args);
399
400 /* Switch back to original locale. */
401 uselocale(old_locale);
402 freelocale(temp_locale);
403
404 return ret;
405#elif defined(__unix__) || defined(__unix)
406 /*
407 * This is a fallback for all other BSDs, *nix and FreeBSD <= 9.0, by
408 * using the current locale for snprintf(). This may not work correctly
409 * for floats!
410 */
411 int ret;
412
413 ret = vsprintf(buf, format, args);
414
415 return ret;
416#else
417 /* No implementation for unknown systems! */
418 return -1;
419#endif
420}
421
79034d4f
FS
422/**
423 * Composes a string with a format string (like printf) in the buffer pointed
424 * by buf (taking buf_size as the maximum buffer capacity to fill).
425 * If the resulting string would be longer than n - 1 characters, the remaining
426 * characters are discarded and not stored, but counted for the value returned
427 * by the function.
428 * A terminating NUL character is automatically appended after the content
429 * written.
430 * After the format parameter, the function expects at least as many additional
431 * arguments as needed for format.
432 *
21ef355e 433 * This version ignores the current locale and uses the locale "C" for Linux,
79034d4f
FS
434 * FreeBSD, OSX and Android.
435 *
436 * @param buf Pointer to a buffer where the resulting C string is stored.
437 * @param buf_size Maximum number of bytes to be used in the buffer. The
438 * generated string has a length of at most buf_size - 1, leaving space
439 * for the additional terminating NUL character.
440 * @param format C string that contains a format string (see printf).
441 * @param ... A sequence of additional arguments, each containing a value to be
442 * used to replace a format specifier in the format string.
443 *
444 * @return On success, the number of characters that would have been written if
445 * buf_size had been sufficiently large, not counting the terminating
446 * NUL character. On failure, a negative number is returned.
447 * Notice that only when this returned value is non-negative and less
448 * than buf_size, the string has been completely written.
449 *
450 * @since 0.6.0
451 */
452SR_API int sr_snprintf_ascii(char *buf, size_t buf_size,
453 const char *format, ...)
454{
455 int ret;
456 va_list args;
457
458 va_start(args, format);
459 ret = sr_vsnprintf_ascii(buf, buf_size, format, args);
460 va_end(args);
461
462 return ret;
463}
464
465/**
466 * Composes a string with a format string (like printf) in the buffer pointed
467 * by buf (taking buf_size as the maximum buffer capacity to fill).
468 * If the resulting string would be longer than n - 1 characters, the remaining
469 * characters are discarded and not stored, but counted for the value returned
470 * by the function.
471 * A terminating NUL character is automatically appended after the content
472 * written.
473 * Internally, the function retrieves arguments from the list identified by
474 * args as if va_arg was used on it, and thus the state of args is likely to
475 * be altered by the call.
476 * In any case, arg should have been initialized by va_start at some point
477 * before the call, and it is expected to be released by va_end at some point
478 * after the call.
479 *
21ef355e 480 * This version ignores the current locale and uses the locale "C" for Linux,
79034d4f
FS
481 * FreeBSD, OSX and Android.
482 *
483 * @param buf Pointer to a buffer where the resulting C string is stored.
484 * @param buf_size Maximum number of bytes to be used in the buffer. The
485 * generated string has a length of at most buf_size - 1, leaving space
486 * for the additional terminating NUL character.
487 * @param format C string that contains a format string (see printf).
488 * @param args A value identifying a variable arguments list initialized with
489 * va_start.
490 *
491 * @return On success, the number of characters that would have been written if
492 * buf_size had been sufficiently large, not counting the terminating
493 * NUL character. On failure, a negative number is returned.
494 * Notice that only when this returned value is non-negative and less
495 * than buf_size, the string has been completely written.
496 *
497 * @since 0.6.0
498 */
499SR_API int sr_vsnprintf_ascii(char *buf, size_t buf_size,
500 const char *format, va_list args)
501{
502#if defined(_WIN32)
503 int ret;
504
505#if 0
506 /*
507 * TODO: This part compiles with mingw-w64 but doesn't run with Win7.
508 * Doesn't start because of "Procedure entry point _create_locale
509 * not found in msvcrt.dll".
510 * mingw-w64 should link to msvcr100.dll not msvcrt.dll!.
21ef355e 511 * See: https://msdn.microsoft.com/en-us/en-en/library/1kt27hek.aspx
79034d4f
FS
512 */
513 _locale_t locale;
514
515 locale = _create_locale(LC_NUMERIC, "C");
516 ret = _vsnprintf_l(buf, buf_size, format, locale, args);
517 _free_locale(locale);
518#endif
519
520 /* vsprintf uses the current locale, may cause issues for floats. */
521 ret = vsnprintf(buf, buf_size, format, args);
522
523 return ret;
524#elif defined(__APPLE__)
525 /*
526 * See:
527 * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/printf_l.3.html
528 * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/xlocale.3.html
529 */
530 int ret;
531 locale_t locale;
532
533 locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
534 ret = vsnprintf_l(buf, buf_size, locale, format, args);
535 freelocale(locale);
536
537 return ret;
538#elif defined(__FreeBSD__) && __FreeBSD_version >= 901000
539 /*
540 * See:
541 * https://www.freebsd.org/cgi/man.cgi?query=printf_l&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE
542 * https://www.freebsd.org/cgi/man.cgi?query=xlocale&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE
543 */
544 int ret;
545 locale_t locale;
546
547 locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
548 ret = vsnprintf_l(buf, buf_size, locale, format, args);
549 freelocale(locale);
550
551 return ret;
552#elif defined(__ANDROID__)
553 /*
554 * The Bionic libc only has two locales ("C" aka "POSIX" and "C.UTF-8"
555 * aka "en_US.UTF-8"). The decimal point is hard coded as ".".
556 * See: https://android.googlesource.com/platform/bionic/+/master/libc/bionic/locale.cpp
557 */
558 int ret;
559
560 ret = vsnprintf(buf, buf_size, format, args);
561
562 return ret;
563#elif defined(__linux__)
564 int ret;
565 locale_t old_locale, temp_locale;
566
567 /* Switch to C locale for proper float/double conversion. */
568 temp_locale = newlocale(LC_NUMERIC, "C", NULL);
569 old_locale = uselocale(temp_locale);
570
571 ret = vsnprintf(buf, buf_size, format, args);
572
573 /* Switch back to original locale. */
574 uselocale(old_locale);
575 freelocale(temp_locale);
576
577 return ret;
578#elif defined(__unix__) || defined(__unix)
579 /*
580 * This is a fallback for all other BSDs, *nix and FreeBSD <= 9.0, by
581 * using the current locale for snprintf(). This may not work correctly
582 * for floats!
583 */
584 int ret;
585
586 ret = vsnprintf(buf, buf_size, format, args);
587
588 return ret;
589#else
590 /* No implementation for unknown systems! */
591 return -1;
592#endif
593}
594
d8bc7ca3
GS
595/**
596 * Convert a sequence of bytes to its textual representation ("hex dump").
597 *
598 * Callers should free the allocated GString. See @ref sr_hexdump_free().
599 *
600 * @param[in] data Pointer to the byte sequence to print.
601 * @param[in] len Number of bytes to print.
602 *
603 * @return #NULL upon error, newly allocated GString pointer otherwise.
82b9f3d1
UH
604 *
605 * @private
d8bc7ca3
GS
606 */
607SR_PRIV GString *sr_hexdump_new(const uint8_t *data, const size_t len)
608{
609 GString *s;
610 size_t i;
611
612 s = g_string_sized_new(3 * len);
613 for (i = 0; i < len; i++) {
614 if (i)
615 g_string_append_c(s, ' ');
616 g_string_append_printf(s, "%02x", data[i]);
617 }
618
619 return s;
620}
621
622/**
623 * Free a hex dump text that was created by @ref sr_hexdump_new().
624 *
625 * @param[in] s Pointer to the GString to release.
82b9f3d1
UH
626 *
627 * @private
d8bc7ca3
GS
628 */
629SR_PRIV void sr_hexdump_free(GString *s)
630{
631 if (s)
632 g_string_free(s, TRUE);
633}
634
5ec172d7 635/**
5c436a3b
UH
636 * Convert a string representation of a numeric value to a sr_rational.
637 *
638 * The conversion is strict and will fail if the complete string does not
639 * represent a valid number. The function sets errno according to the details
640 * of the failure. This version ignores the locale.
5ec172d7
SB
641 *
642 * @param str The string representation to convert.
643 * @param ret Pointer to sr_rational where the result of the conversion will be stored.
644 *
645 * @retval SR_OK Conversion successful.
646 * @retval SR_ERR Failure.
647 *
648 * @since 0.5.0
649 */
650SR_API int sr_parse_rational(const char *str, struct sr_rational *ret)
651{
652 char *endptr = NULL;
653 int64_t integral;
654 int64_t fractional = 0;
655 int64_t denominator = 1;
656 int32_t fractional_len = 0;
657 int32_t exponent = 0;
0f5bba96
UH
658 gboolean is_negative = FALSE;
659 gboolean no_integer, no_fractional;
5ec172d7 660
7f5bfd61
GS
661 while (isspace(*str))
662 str++;
663
5ec172d7
SB
664 errno = 0;
665 integral = g_ascii_strtoll(str, &endptr, 10);
666
d8754963 667 if (str == endptr && (str[0] == '-' || str[0] == '+') && str[1] == '.') {
41c47f2c 668 endptr += 1;
0f5bba96 669 no_integer = TRUE;
d8754963 670 } else if (str == endptr && str[0] == '.') {
0f5bba96 671 no_integer = TRUE;
d8754963 672 } else if (errno) {
5ec172d7 673 return SR_ERR;
d8754963 674 } else {
0f5bba96 675 no_integer = FALSE;
d8754963 676 }
5ec172d7 677
41c47f2c 678 if (integral < 0 || str[0] == '-')
0f5bba96 679 is_negative = TRUE;
41c47f2c 680
51bf39a1 681 errno = 0;
5ec172d7 682 if (*endptr == '.') {
0f5bba96 683 gboolean is_exp, is_eos;
dd3202fe 684 const char *start = endptr + 1;
5ec172d7 685 fractional = g_ascii_strtoll(start, &endptr, 10);
42408643
GS
686 is_exp = *endptr == 'E' || *endptr == 'e';
687 is_eos = *endptr == '\0';
688 if (endptr == start && (is_exp || is_eos)) {
689 fractional = 0;
690 errno = 0;
691 }
5ec172d7
SB
692 if (errno)
693 return SR_ERR;
d8754963
GS
694 no_fractional = endptr == start;
695 if (no_integer && no_fractional)
696 return SR_ERR;
5ec172d7
SB
697 fractional_len = endptr - start;
698 }
699
51bf39a1 700 errno = 0;
5ec172d7
SB
701 if ((*endptr == 'E') || (*endptr == 'e')) {
702 exponent = g_ascii_strtoll(endptr + 1, &endptr, 10);
703 if (errno)
704 return SR_ERR;
705 }
706
707 if (*endptr != '\0')
708 return SR_ERR;
709
710 for (int i = 0; i < fractional_len; i++)
711 integral *= 10;
712 exponent -= fractional_len;
713
41c47f2c 714 if (!is_negative)
5ec172d7
SB
715 integral += fractional;
716 else
717 integral -= fractional;
718
719 while (exponent > 0) {
720 integral *= 10;
721 exponent--;
722 }
723
724 while (exponent < 0) {
725 denominator *= 10;
726 exponent++;
727 }
728
729 ret->p = integral;
730 ret->q = denominator;
731
732 return SR_OK;
733}
734
25e7d9b1 735/**
b07b42f3
UH
736 * Convert a numeric value value to its "natural" string representation
737 * in SI units.
25e7d9b1 738 *
4cc9aea1
JH
739 * E.g. a value of 3000000, with units set to "W", would be converted
740 * to "3 MW", 20000 to "20 kW", 31500 would become "31.5 kW".
25e7d9b1 741 *
4cc9aea1
JH
742 * @param x The value to convert.
743 * @param unit The unit to append to the string, or NULL if the string
744 * has no units.
44dae539 745 *
91219afc 746 * @return A newly allocated string representation of the samplerate value,
133a37bf
UH
747 * or NULL upon errors. The caller is responsible to g_free() the
748 * memory.
47117241
UH
749 *
750 * @since 0.2.0
25e7d9b1 751 */
4cc9aea1 752SR_API char *sr_si_string_u64(uint64_t x, const char *unit)
25e7d9b1 753{
094e6b81
PS
754 uint8_t i;
755 uint64_t quot, divisor[] = {
b07b42f3
UH
756 SR_HZ(1), SR_KHZ(1), SR_MHZ(1), SR_GHZ(1),
757 SR_GHZ(1000), SR_GHZ(1000 * 1000), SR_GHZ(1000 * 1000 * 1000),
094e6b81
PS
758 };
759 const char *p, prefix[] = "\0kMGTPE";
69d83be9 760 char fmt[16], fract[20] = "", *f;
094e6b81 761
98fec29e 762 if (!unit)
4cc9aea1 763 unit = "";
25e7d9b1 764
094e6b81
PS
765 for (i = 0; (quot = x / divisor[i]) >= 1000; i++);
766
767 if (i) {
69d83be9 768 sprintf(fmt, ".%%0%d"PRIu64, i * 3);
094e6b81
PS
769 f = fract + sprintf(fract, fmt, x % divisor[i]) - 1;
770
771 while (f >= fract && strchr("0.", *f))
772 *f-- = 0;
133a37bf 773 }
25e7d9b1 774
094e6b81
PS
775 p = prefix + i;
776
777 return g_strdup_printf("%" PRIu64 "%s %.1s%s", quot, fract, p, unit);
4cc9aea1 778}
25e7d9b1 779
4cc9aea1
JH
780/**
781 * Convert a numeric samplerate value to its "natural" string representation.
782 *
783 * E.g. a value of 3000000 would be converted to "3 MHz", 20000 to "20 kHz",
784 * 31500 would become "31.5 kHz".
785 *
786 * @param samplerate The samplerate in Hz.
787 *
91219afc 788 * @return A newly allocated string representation of the samplerate value,
4cc9aea1
JH
789 * or NULL upon errors. The caller is responsible to g_free() the
790 * memory.
47117241
UH
791 *
792 * @since 0.1.0
4cc9aea1
JH
793 */
794SR_API char *sr_samplerate_string(uint64_t samplerate)
795{
796 return sr_si_string_u64(samplerate, "Hz");
25e7d9b1 797}
2a3f9541 798
2a3f9541 799/**
9a17c23b
UH
800 * Convert a numeric period value to the "natural" string representation
801 * of its period value.
2a3f9541 802 *
9a17c23b 803 * The period is specified as a rational number's numerator and denominator.
2a3f9541 804 *
9a17c23b 805 * E.g. a pair of (1, 5) would be converted to "200 ms", (10, 100) to "100 ms".
44dae539 806 *
9a17c23b
UH
807 * @param v_p The period numerator.
808 * @param v_q The period denominator.
809 *
810 * @return A newly allocated string representation of the period value,
133a37bf
UH
811 * or NULL upon errors. The caller is responsible to g_free() the
812 * memory.
47117241 813 *
9a17c23b 814 * @since 0.5.0
2a3f9541 815 */
6984cfb2 816SR_API char *sr_period_string(uint64_t v_p, uint64_t v_q)
2a3f9541 817{
6984cfb2 818 double freq, v;
d2391b54 819 int prec;
6984cfb2
SA
820
821 freq = 1 / ((double)v_p / v_q);
2a3f9541 822
6984cfb2
SA
823 if (freq > SR_GHZ(1)) {
824 v = (double)v_p / v_q * 1000000000000.0;
825 prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
d2391b54 826 return g_strdup_printf("%.*f ps", prec, v);
6984cfb2
SA
827 } else if (freq > SR_MHZ(1)) {
828 v = (double)v_p / v_q * 1000000000.0;
829 prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
d2391b54 830 return g_strdup_printf("%.*f ns", prec, v);
6984cfb2
SA
831 } else if (freq > SR_KHZ(1)) {
832 v = (double)v_p / v_q * 1000000.0;
833 prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
d2391b54 834 return g_strdup_printf("%.*f us", prec, v);
6984cfb2
SA
835 } else if (freq > 1) {
836 v = (double)v_p / v_q * 1000.0;
837 prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
d2391b54 838 return g_strdup_printf("%.*f ms", prec, v);
6984cfb2
SA
839 } else {
840 v = (double)v_p / v_q;
841 prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
d2391b54 842 return g_strdup_printf("%.*f s", prec, v);
2a3f9541 843 }
2a3f9541 844}
40f5ddac 845
79afc8ca 846/**
e0e15067
BV
847 * Convert a numeric voltage value to the "natural" string representation
848 * of its voltage value. The voltage is specified as a rational number's
849 * numerator and denominator.
79afc8ca
BV
850 *
851 * E.g. a value of 300000 would be converted to "300mV", 2 to "2V".
852 *
e0e15067
BV
853 * @param v_p The voltage numerator.
854 * @param v_q The voltage denominator.
79afc8ca 855 *
91219afc 856 * @return A newly allocated string representation of the voltage value,
79afc8ca
BV
857 * or NULL upon errors. The caller is responsible to g_free() the
858 * memory.
47117241
UH
859 *
860 * @since 0.2.0
79afc8ca 861 */
e0e15067 862SR_API char *sr_voltage_string(uint64_t v_p, uint64_t v_q)
79afc8ca 863{
e0e15067 864 if (v_q == 1000)
b5df922e 865 return g_strdup_printf("%" PRIu64 " mV", v_p);
e0e15067 866 else if (v_q == 1)
b5df922e 867 return g_strdup_printf("%" PRIu64 " V", v_p);
79afc8ca 868 else
b5df922e 869 return g_strdup_printf("%g V", (float)v_p / (float)v_q);
79afc8ca
BV
870}
871
dfcc0bf9
UH
872/**
873 * Convert a "natural" string representation of a size value to uint64_t.
874 *
875 * E.g. a value of "3k" or "3 K" would be converted to 3000, a value
876 * of "15M" would be converted to 15000000.
877 *
878 * Value representations other than decimal (such as hex or octal) are not
879 * supported. Only 'k' (kilo), 'm' (mega), 'g' (giga) suffixes are supported.
880 * Spaces (but not other whitespace) between value and suffix are allowed.
881 *
882 * @param sizestring A string containing a (decimal) size value.
f64c1414 883 * @param size Pointer to uint64_t which will contain the string's size value.
dfcc0bf9 884 *
44dae539 885 * @return SR_OK upon success, SR_ERR upon errors.
47117241
UH
886 *
887 * @since 0.1.0
dfcc0bf9 888 */
1a081ca6 889SR_API int sr_parse_sizestring(const char *sizestring, uint64_t *size)
40f5ddac 890{
751ba4c8
GS
891 uint64_t multiplier;
892 int done;
580f3099 893 double frac_part;
40f5ddac
BV
894 char *s;
895
f64c1414 896 *size = strtoull(sizestring, &s, 10);
40f5ddac 897 multiplier = 0;
580f3099 898 frac_part = 0;
f64c1414
BV
899 done = FALSE;
900 while (s && *s && multiplier == 0 && !done) {
40f5ddac
BV
901 switch (*s) {
902 case ' ':
903 break;
580f3099
DJ
904 case '.':
905 frac_part = g_ascii_strtod(s, &s);
906 break;
40f5ddac
BV
907 case 'k':
908 case 'K':
59df0c77 909 multiplier = SR_KHZ(1);
40f5ddac
BV
910 break;
911 case 'm':
912 case 'M':
59df0c77 913 multiplier = SR_MHZ(1);
40f5ddac
BV
914 break;
915 case 'g':
916 case 'G':
59df0c77 917 multiplier = SR_GHZ(1);
40f5ddac 918 break;
751ba4c8
GS
919 case 't':
920 case 'T':
921 multiplier = SR_GHZ(1000);
922 break;
923 case 'p':
924 case 'P':
925 multiplier = SR_GHZ(1000 * 1000);
926 break;
927 case 'e':
928 case 'E':
929 multiplier = SR_GHZ(1000 * 1000 * 1000);
930 break;
40f5ddac 931 default:
f64c1414
BV
932 done = TRUE;
933 s--;
40f5ddac
BV
934 }
935 s++;
936 }
580f3099 937 if (multiplier > 0) {
f64c1414 938 *size *= multiplier;
580f3099 939 *size += frac_part * multiplier;
751ba4c8 940 } else {
580f3099 941 *size += frac_part;
751ba4c8 942 }
40f5ddac 943
34577da6 944 if (s && *s && g_ascii_strcasecmp(s, "Hz"))
f64c1414
BV
945 return SR_ERR;
946
947 return SR_OK;
40f5ddac
BV
948}
949
dfcc0bf9
UH
950/**
951 * Convert a "natural" string representation of a time value to an
952 * uint64_t value in milliseconds.
953 *
954 * E.g. a value of "3s" or "3 s" would be converted to 3000, a value
955 * of "15ms" would be converted to 15.
956 *
957 * Value representations other than decimal (such as hex or octal) are not
958 * supported. Only lower-case "s" and "ms" time suffixes are supported.
959 * Spaces (but not other whitespace) between value and suffix are allowed.
960 *
961 * @param timestring A string containing a (decimal) time value.
962 * @return The string's time value as uint64_t, in milliseconds.
963 *
6b2d8d3e
UH
964 * @todo Add support for "m" (minutes) and others.
965 * @todo Add support for picoseconds?
966 * @todo Allow both lower-case and upper-case? If no, document it.
47117241
UH
967 *
968 * @since 0.1.0
dfcc0bf9 969 */
1a081ca6 970SR_API uint64_t sr_parse_timestring(const char *timestring)
40f5ddac
BV
971{
972 uint64_t time_msec;
973 char *s;
974
6b2d8d3e
UH
975 /* TODO: Error handling, logging. */
976
40f5ddac
BV
977 time_msec = strtoull(timestring, &s, 10);
978 if (time_msec == 0 && s == timestring)
979 return 0;
980
981 if (s && *s) {
982 while (*s == ' ')
983 s++;
984 if (!strcmp(s, "s"))
985 time_msec *= 1000;
986 else if (!strcmp(s, "ms"))
987 ; /* redundant */
988 else
989 return 0;
990 }
991
992 return time_msec;
993}
4d436e71 994
47117241 995/** @since 0.1.0 */
1a081ca6 996SR_API gboolean sr_parse_boolstring(const char *boolstr)
4d436e71 997{
ad466f86
GS
998 /*
999 * Complete absence of an input spec is assumed to mean TRUE,
1000 * as in command line option strings like this:
1001 * ...:samplerate=100k:header:numchannels=4:...
1002 */
1003 if (!boolstr || !*boolstr)
1004 return TRUE;
4d436e71 1005
993526f8
BV
1006 if (!g_ascii_strncasecmp(boolstr, "true", 4) ||
1007 !g_ascii_strncasecmp(boolstr, "yes", 3) ||
1008 !g_ascii_strncasecmp(boolstr, "on", 2) ||
1009 !g_ascii_strncasecmp(boolstr, "1", 1))
4d436e71
GM
1010 return TRUE;
1011
1012 return FALSE;
1013}
76f4c610 1014
47117241 1015/** @since 0.2.0 */
76e107d6 1016SR_API int sr_parse_period(const char *periodstr, uint64_t *p, uint64_t *q)
76f4c610
BV
1017{
1018 char *s;
1019
76e107d6
BV
1020 *p = strtoull(periodstr, &s, 10);
1021 if (*p == 0 && s == periodstr)
76f4c610
BV
1022 /* No digits found. */
1023 return SR_ERR_ARG;
1024
1025 if (s && *s) {
1026 while (*s == ' ')
1027 s++;
8c012adb 1028 if (!strcmp(s, "fs"))
d9b716fc 1029 *q = UINT64_C(1000000000000000);
8c012adb 1030 else if (!strcmp(s, "ps"))
d9b716fc 1031 *q = UINT64_C(1000000000000);
8c012adb 1032 else if (!strcmp(s, "ns"))
d9b716fc 1033 *q = UINT64_C(1000000000);
76f4c610 1034 else if (!strcmp(s, "us"))
76e107d6 1035 *q = 1000000;
76f4c610 1036 else if (!strcmp(s, "ms"))
76e107d6 1037 *q = 1000;
76f4c610 1038 else if (!strcmp(s, "s"))
76e107d6 1039 *q = 1;
76f4c610
BV
1040 else
1041 /* Must have a time suffix. */
1042 return SR_ERR_ARG;
1043 }
1044
1045 return SR_OK;
1046}
1047
47117241 1048/** @since 0.2.0 */
76e107d6 1049SR_API int sr_parse_voltage(const char *voltstr, uint64_t *p, uint64_t *q)
79afc8ca
BV
1050{
1051 char *s;
1052
76e107d6
BV
1053 *p = strtoull(voltstr, &s, 10);
1054 if (*p == 0 && s == voltstr)
79afc8ca
BV
1055 /* No digits found. */
1056 return SR_ERR_ARG;
1057
1058 if (s && *s) {
1059 while (*s == ' ')
1060 s++;
34577da6 1061 if (!g_ascii_strcasecmp(s, "mv"))
76e107d6 1062 *q = 1000L;
34577da6 1063 else if (!g_ascii_strcasecmp(s, "v"))
76e107d6 1064 *q = 1;
79afc8ca
BV
1065 else
1066 /* Must have a base suffix. */
1067 return SR_ERR_ARG;
1068 }
1069
1070 return SR_OK;
1071}
1072
7b870c38 1073/** @} */