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