]> sigrok.org Git - libsigrok.git/blame - src/strutil.c
rdtech-um: increase digits count for models with higher resolution
[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
30903c40
NT
148/**
149 * Convert a text to a number including support for non-decimal bases.
150 * Also optionally returns the position after the number, where callers
151 * can either error out, or support application specific suffixes.
152 *
153 * @param[in] str The input text to convert.
154 * @param[out] ret The conversion result.
155 * @param[out] end The position after the number.
156 * @param[in] base The number format's base, can be 0.
157 *
158 * @retval SR_OK Conversion successful.
159 * @retval SR_ERR Conversion failed.
160 *
161 * @private
162 *
163 * This routine is more general than @ref sr_atol(), which strictly
164 * expects the input text to contain just a decimal number, and nothing
165 * else in addition. The @ref sr_atoul_base() routine accepts trailing
166 * text after the number, and supports non-decimal numbers (bin, hex),
167 * including automatic detection from prefix text.
168 */
169SR_PRIV int sr_atoul_base(const char *str, unsigned long *ret, char **end, int base)
170{
171 unsigned long num;
172 char *endptr;
173
174 /* Add "0b" prefix support which strtol(3) may be missing. */
175 while (str && isspace(*str))
176 str++;
177 if (!base && strncmp(str, "0b", strlen("0b")) == 0) {
178 str += strlen("0b");
179 base = 2;
180 }
181
182 /* Run the number conversion. Quick bail out if that fails. */
183 errno = 0;
184 endptr = NULL;
185 num = strtoul(str, &endptr, base);
186 if (!endptr || errno) {
187 if (!errno)
188 errno = EINVAL;
189 return SR_ERR;
190 }
191 *ret = num;
192
193 /* Advance to optional non-space trailing suffix. */
194 while (endptr && isspace(*endptr))
195 endptr++;
196 if (end)
197 *end = endptr;
198
199 return SR_OK;
200}
201
9e4f8cf9 202/**
1ba4a1cf 203 * Convert a string representation of a numeric value (base 10) to an integer. The
9e4f8cf9
DJ
204 * conversion is strict and will fail if the complete string does not represent
205 * a valid integer. The function sets errno according to the details of the
206 * failure.
207 *
208 * @param str The string representation to convert.
209 * @param ret Pointer to int where the result of the conversion will be stored.
210 *
1ba4a1cf
MH
211 * @retval SR_OK Conversion successful.
212 * @retval SR_ERR Failure.
82b9f3d1
UH
213 *
214 * @private
9e4f8cf9 215 */
8d558c7a 216SR_PRIV int sr_atoi(const char *str, int *ret)
9e4f8cf9
DJ
217{
218 long tmp;
219
220 if (sr_atol(str, &tmp) != SR_OK)
221 return SR_ERR;
222
223 if ((int) tmp != tmp) {
224 errno = ERANGE;
225 return SR_ERR;
226 }
227
228 *ret = (int) tmp;
229 return SR_OK;
230}
231
232/**
233 * Convert a string representation of a numeric value to a double. The
234 * conversion is strict and will fail if the complete string does not represent
235 * a valid double. The function sets errno according to the details of the
236 * failure.
237 *
238 * @param str The string representation to convert.
239 * @param ret Pointer to double where the result of the conversion will be stored.
240 *
1ba4a1cf
MH
241 * @retval SR_OK Conversion successful.
242 * @retval SR_ERR Failure.
82b9f3d1
UH
243 *
244 * @private
9e4f8cf9 245 */
8d558c7a 246SR_PRIV int sr_atod(const char *str, double *ret)
9e4f8cf9
DJ
247{
248 double tmp;
249 char *endptr = NULL;
250
251 errno = 0;
252 tmp = strtof(str, &endptr);
253
b8278e09
GS
254 while (endptr && isspace(*endptr))
255 endptr++;
256
9e4f8cf9
DJ
257 if (!endptr || *endptr || errno) {
258 if (!errno)
259 errno = EINVAL;
260 return SR_ERR;
261 }
262
263 *ret = tmp;
264 return SR_OK;
265}
266
267/**
268 * Convert a string representation of a numeric value to a float. The
269 * conversion is strict and will fail if the complete string does not represent
270 * a valid float. The function sets errno according to the details of the
271 * failure.
272 *
273 * @param str The string representation to convert.
274 * @param ret Pointer to float where the result of the conversion will be stored.
275 *
1ba4a1cf
MH
276 * @retval SR_OK Conversion successful.
277 * @retval SR_ERR Failure.
82b9f3d1
UH
278 *
279 * @private
9e4f8cf9 280 */
8d558c7a 281SR_PRIV int sr_atof(const char *str, float *ret)
9e4f8cf9
DJ
282{
283 double tmp;
284
285 if (sr_atod(str, &tmp) != SR_OK)
286 return SR_ERR;
287
288 if ((float) tmp != tmp) {
289 errno = ERANGE;
290 return SR_ERR;
291 }
292
293 *ret = (float) tmp;
294 return SR_OK;
295}
296
4f0463a0 297/**
4f0463a0
FS
298 * Convert a string representation of a numeric value to a double. The
299 * conversion is strict and will fail if the complete string does not represent
300 * a valid double. The function sets errno according to the details of the
301 * failure. This version ignores the locale.
302 *
303 * @param str The string representation to convert.
304 * @param ret Pointer to double where the result of the conversion will be stored.
305 *
306 * @retval SR_OK Conversion successful.
307 * @retval SR_ERR Failure.
82b9f3d1
UH
308 *
309 * @private
4f0463a0
FS
310 */
311SR_PRIV int sr_atod_ascii(const char *str, double *ret)
312{
313 double tmp;
314 char *endptr = NULL;
315
316 errno = 0;
317 tmp = g_ascii_strtod(str, &endptr);
318
319 if (!endptr || *endptr || errno) {
320 if (!errno)
321 errno = EINVAL;
322 return SR_ERR;
323 }
324
325 *ret = tmp;
326 return SR_OK;
327}
328
91ab2f64
GS
329/**
330 * Convert text to a floating point value, and get its precision.
331 *
332 * @param[in] str The input text to convert.
333 * @param[out] ret The conversion result, a double precision float number.
334 * @param[out] digits The number of significant decimals.
335 *
336 * @returns SR_OK in case of successful text to number conversion.
337 * @returns SR_ERR when conversion fails.
338 *
339 * @since 0.6.0
340 */
341SR_PRIV int sr_atod_ascii_digits(const char *str, double *ret, int *digits)
342{
343 const char *p;
344 int *dig_ref, m_dig, exp;
345 char c;
346 double f;
347
348 /*
349 * Convert floating point text to the number value, _and_ get
350 * the value's precision in the process. Steps taken to do it:
351 * - Skip leading whitespace.
352 * - Count the number of decimals after the mantissa's period.
353 * - Get the exponent's signed value.
354 *
355 * This implementation still uses common code for the actual
356 * conversion, but "violates API layers" by duplicating the
357 * text scan, to get the number of significant digits.
358 */
359 p = str;
360 while (*p && isspace(*p))
361 p++;
362 if (*p == '-' || *p == '+')
363 p++;
364 m_dig = 0;
365 exp = 0;
366 dig_ref = NULL;
367 while (*p) {
368 c = *p++;
369 if (toupper(c) == 'E') {
370 exp = strtol(p, NULL, 10);
371 break;
372 }
373 if (c == '.') {
374 m_dig = 0;
375 dig_ref = &m_dig;
376 continue;
377 }
378 if (isdigit(c)) {
379 if (dig_ref)
380 (*dig_ref)++;
381 continue;
382 }
383 /* Need not warn, conversion will fail. */
384 break;
385 }
386 sr_spew("atod digits: txt \"%s\" -> m %d, e %d -> digits %d",
387 str, m_dig, exp, m_dig + -exp);
388 m_dig += -exp;
389
390 if (sr_atod_ascii(str, &f) != SR_OK)
391 return SR_ERR;
392 if (ret)
393 *ret = f;
394 if (digits)
395 *digits = m_dig;
396
397 return SR_OK;
398}
399
9806c2d5 400/**
9806c2d5
DJ
401 * Convert a string representation of a numeric value to a float. The
402 * conversion is strict and will fail if the complete string does not represent
403 * a valid float. The function sets errno according to the details of the
404 * failure. This version ignores the locale.
405 *
406 * @param str The string representation to convert.
407 * @param ret Pointer to float where the result of the conversion will be stored.
408 *
1ba4a1cf
MH
409 * @retval SR_OK Conversion successful.
410 * @retval SR_ERR Failure.
82b9f3d1
UH
411 *
412 * @private
9806c2d5
DJ
413 */
414SR_PRIV int sr_atof_ascii(const char *str, float *ret)
415{
416 double tmp;
417 char *endptr = NULL;
418
419 errno = 0;
420 tmp = g_ascii_strtod(str, &endptr);
421
422 if (!endptr || *endptr || errno) {
423 if (!errno)
424 errno = EINVAL;
425 return SR_ERR;
426 }
427
428 /* FIXME This fails unexpectedly. Some other method to safel downcast
429 * needs to be found. Checking against FLT_MAX doesn't work as well. */
430 /*
431 if ((float) tmp != tmp) {
432 errno = ERANGE;
433 sr_dbg("ERANGEEEE %e != %e", (float) tmp, tmp);
434 return SR_ERR;
435 }
436 */
437
438 *ret = (float) tmp;
439 return SR_OK;
440}
441
21ef355e
FS
442/**
443 * Compose a string with a format string in the buffer pointed to by buf.
444 *
445 * It is up to the caller to ensure that the allocated buffer is large enough
446 * to hold the formatted result.
447 *
448 * A terminating NUL character is automatically appended after the content
449 * written.
450 *
451 * After the format parameter, the function expects at least as many additional
452 * arguments as needed for format.
453 *
454 * This version ignores the current locale and uses the locale "C" for Linux,
455 * FreeBSD, OSX and Android.
456 *
457 * @param buf Pointer to a buffer where the resulting C string is stored.
458 * @param format C string that contains a format string (see printf).
459 * @param ... A sequence of additional arguments, each containing a value to be
460 * used to replace a format specifier in the format string.
461 *
462 * @return On success, the number of characters that would have been written,
463 * not counting the terminating NUL character.
464 *
465 * @since 0.6.0
466 */
467SR_API int sr_sprintf_ascii(char *buf, const char *format, ...)
468{
469 int ret;
470 va_list args;
471
472 va_start(args, format);
473 ret = sr_vsprintf_ascii(buf, format, args);
474 va_end(args);
475
476 return ret;
477}
478
479/**
480 * Compose a string with a format string in the buffer pointed to by buf.
481 *
482 * It is up to the caller to ensure that the allocated buffer is large enough
483 * to hold the formatted result.
484 *
485 * Internally, the function retrieves arguments from the list identified by
486 * args as if va_arg was used on it, and thus the state of args is likely to
487 * be altered by the call.
488 *
489 * In any case, args should have been initialized by va_start at some point
490 * before the call, and it is expected to be released by va_end at some point
491 * after the call.
492 *
493 * This version ignores the current locale and uses the locale "C" for Linux,
494 * FreeBSD, OSX and Android.
495 *
496 * @param buf Pointer to a buffer where the resulting C string is stored.
497 * @param format C string that contains a format string (see printf).
498 * @param args A value identifying a variable arguments list initialized with
499 * va_start.
500 *
501 * @return On success, the number of characters that would have been written,
502 * not counting the terminating NUL character.
503 *
504 * @since 0.6.0
505 */
506SR_API int sr_vsprintf_ascii(char *buf, const char *format, va_list args)
507{
508#if defined(_WIN32)
509 int ret;
510
511#if 0
512 /*
513 * TODO: This part compiles with mingw-w64 but doesn't run with Win7.
514 * Doesn't start because of "Procedure entry point _create_locale
515 * not found in msvcrt.dll".
516 * mingw-w64 should link to msvcr100.dll not msvcrt.dll!
517 * See: https://msdn.microsoft.com/en-us/en-en/library/1kt27hek.aspx
518 */
519 _locale_t locale;
520
521 locale = _create_locale(LC_NUMERIC, "C");
522 ret = _vsprintf_l(buf, format, locale, args);
523 _free_locale(locale);
524#endif
525
526 /* vsprintf() uses the current locale, may not work correctly for floats. */
527 ret = vsprintf(buf, format, args);
528
529 return ret;
530#elif defined(__APPLE__)
531 /*
532 * See:
533 * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/printf_l.3.html
534 * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/xlocale.3.html
535 */
536 int ret;
537 locale_t locale;
538
539 locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
540 ret = vsprintf_l(buf, locale, format, args);
541 freelocale(locale);
542
543 return ret;
544#elif defined(__FreeBSD__) && __FreeBSD_version >= 901000
545 /*
546 * See:
547 * https://www.freebsd.org/cgi/man.cgi?query=printf_l&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE
548 * https://www.freebsd.org/cgi/man.cgi?query=xlocale&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE
549 */
550 int ret;
551 locale_t locale;
552
553 locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
554 ret = vsprintf_l(buf, locale, format, args);
555 freelocale(locale);
556
557 return ret;
558#elif defined(__ANDROID__)
559 /*
560 * The Bionic libc only has two locales ("C" aka "POSIX" and "C.UTF-8"
561 * aka "en_US.UTF-8"). The decimal point is hard coded as "."
562 * See: https://android.googlesource.com/platform/bionic/+/master/libc/bionic/locale.cpp
563 */
564 int ret;
565
566 ret = vsprintf(buf, format, args);
567
568 return ret;
569#elif defined(__linux__)
570 int ret;
571 locale_t old_locale, temp_locale;
572
573 /* Switch to C locale for proper float/double conversion. */
574 temp_locale = newlocale(LC_NUMERIC, "C", NULL);
575 old_locale = uselocale(temp_locale);
576
577 ret = vsprintf(buf, format, args);
578
579 /* Switch back to original locale. */
580 uselocale(old_locale);
581 freelocale(temp_locale);
582
583 return ret;
584#elif defined(__unix__) || defined(__unix)
585 /*
586 * This is a fallback for all other BSDs, *nix and FreeBSD <= 9.0, by
587 * using the current locale for snprintf(). This may not work correctly
588 * for floats!
589 */
590 int ret;
591
592 ret = vsprintf(buf, format, args);
593
594 return ret;
595#else
596 /* No implementation for unknown systems! */
597 return -1;
598#endif
599}
600
79034d4f
FS
601/**
602 * Composes a string with a format string (like printf) in the buffer pointed
603 * by buf (taking buf_size as the maximum buffer capacity to fill).
604 * If the resulting string would be longer than n - 1 characters, the remaining
605 * characters are discarded and not stored, but counted for the value returned
606 * by the function.
607 * A terminating NUL character is automatically appended after the content
608 * written.
609 * After the format parameter, the function expects at least as many additional
610 * arguments as needed for format.
611 *
21ef355e 612 * This version ignores the current locale and uses the locale "C" for Linux,
79034d4f
FS
613 * FreeBSD, OSX and Android.
614 *
615 * @param buf Pointer to a buffer where the resulting C string is stored.
616 * @param buf_size Maximum number of bytes to be used in the buffer. The
617 * generated string has a length of at most buf_size - 1, leaving space
618 * for the additional terminating NUL character.
619 * @param format C string that contains a format string (see printf).
620 * @param ... A sequence of additional arguments, each containing a value to be
621 * used to replace a format specifier in the format string.
622 *
623 * @return On success, the number of characters that would have been written if
624 * buf_size had been sufficiently large, not counting the terminating
625 * NUL character. On failure, a negative number is returned.
626 * Notice that only when this returned value is non-negative and less
627 * than buf_size, the string has been completely written.
628 *
629 * @since 0.6.0
630 */
631SR_API int sr_snprintf_ascii(char *buf, size_t buf_size,
632 const char *format, ...)
633{
634 int ret;
635 va_list args;
636
637 va_start(args, format);
638 ret = sr_vsnprintf_ascii(buf, buf_size, format, args);
639 va_end(args);
640
641 return ret;
642}
643
644/**
645 * Composes a string with a format string (like printf) in the buffer pointed
646 * by buf (taking buf_size as the maximum buffer capacity to fill).
647 * If the resulting string would be longer than n - 1 characters, the remaining
648 * characters are discarded and not stored, but counted for the value returned
649 * by the function.
650 * A terminating NUL character is automatically appended after the content
651 * written.
652 * Internally, the function retrieves arguments from the list identified by
653 * args as if va_arg was used on it, and thus the state of args is likely to
654 * be altered by the call.
655 * In any case, arg should have been initialized by va_start at some point
656 * before the call, and it is expected to be released by va_end at some point
657 * after the call.
658 *
21ef355e 659 * This version ignores the current locale and uses the locale "C" for Linux,
79034d4f
FS
660 * FreeBSD, OSX and Android.
661 *
662 * @param buf Pointer to a buffer where the resulting C string is stored.
663 * @param buf_size Maximum number of bytes to be used in the buffer. The
664 * generated string has a length of at most buf_size - 1, leaving space
665 * for the additional terminating NUL character.
666 * @param format C string that contains a format string (see printf).
667 * @param args A value identifying a variable arguments list initialized with
668 * va_start.
669 *
670 * @return On success, the number of characters that would have been written if
671 * buf_size had been sufficiently large, not counting the terminating
672 * NUL character. On failure, a negative number is returned.
673 * Notice that only when this returned value is non-negative and less
674 * than buf_size, the string has been completely written.
675 *
676 * @since 0.6.0
677 */
678SR_API int sr_vsnprintf_ascii(char *buf, size_t buf_size,
679 const char *format, va_list args)
680{
681#if defined(_WIN32)
682 int ret;
683
684#if 0
685 /*
686 * TODO: This part compiles with mingw-w64 but doesn't run with Win7.
687 * Doesn't start because of "Procedure entry point _create_locale
688 * not found in msvcrt.dll".
689 * mingw-w64 should link to msvcr100.dll not msvcrt.dll!.
21ef355e 690 * See: https://msdn.microsoft.com/en-us/en-en/library/1kt27hek.aspx
79034d4f
FS
691 */
692 _locale_t locale;
693
694 locale = _create_locale(LC_NUMERIC, "C");
695 ret = _vsnprintf_l(buf, buf_size, format, locale, args);
696 _free_locale(locale);
697#endif
698
699 /* vsprintf uses the current locale, may cause issues for floats. */
700 ret = vsnprintf(buf, buf_size, format, args);
701
702 return ret;
703#elif defined(__APPLE__)
704 /*
705 * See:
706 * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/printf_l.3.html
707 * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/xlocale.3.html
708 */
709 int ret;
710 locale_t locale;
711
712 locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
713 ret = vsnprintf_l(buf, buf_size, locale, format, args);
714 freelocale(locale);
715
716 return ret;
717#elif defined(__FreeBSD__) && __FreeBSD_version >= 901000
718 /*
719 * See:
720 * https://www.freebsd.org/cgi/man.cgi?query=printf_l&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE
721 * https://www.freebsd.org/cgi/man.cgi?query=xlocale&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE
722 */
723 int ret;
724 locale_t locale;
725
726 locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
727 ret = vsnprintf_l(buf, buf_size, locale, format, args);
728 freelocale(locale);
729
730 return ret;
731#elif defined(__ANDROID__)
732 /*
733 * The Bionic libc only has two locales ("C" aka "POSIX" and "C.UTF-8"
734 * aka "en_US.UTF-8"). The decimal point is hard coded as ".".
735 * See: https://android.googlesource.com/platform/bionic/+/master/libc/bionic/locale.cpp
736 */
737 int ret;
738
739 ret = vsnprintf(buf, buf_size, format, args);
740
741 return ret;
742#elif defined(__linux__)
743 int ret;
744 locale_t old_locale, temp_locale;
745
746 /* Switch to C locale for proper float/double conversion. */
747 temp_locale = newlocale(LC_NUMERIC, "C", NULL);
748 old_locale = uselocale(temp_locale);
749
750 ret = vsnprintf(buf, buf_size, format, args);
751
752 /* Switch back to original locale. */
753 uselocale(old_locale);
754 freelocale(temp_locale);
755
756 return ret;
757#elif defined(__unix__) || defined(__unix)
758 /*
759 * This is a fallback for all other BSDs, *nix and FreeBSD <= 9.0, by
760 * using the current locale for snprintf(). This may not work correctly
761 * for floats!
762 */
763 int ret;
764
765 ret = vsnprintf(buf, buf_size, format, args);
766
767 return ret;
768#else
769 /* No implementation for unknown systems! */
770 return -1;
771#endif
772}
773
d8bc7ca3
GS
774/**
775 * Convert a sequence of bytes to its textual representation ("hex dump").
776 *
6762401d 777 * Callers should free the allocated GString. See sr_hexdump_free().
d8bc7ca3
GS
778 *
779 * @param[in] data Pointer to the byte sequence to print.
780 * @param[in] len Number of bytes to print.
781 *
6762401d 782 * @return NULL upon error, newly allocated GString pointer otherwise.
82b9f3d1
UH
783 *
784 * @private
d8bc7ca3
GS
785 */
786SR_PRIV GString *sr_hexdump_new(const uint8_t *data, const size_t len)
787{
788 GString *s;
789 size_t i;
790
791 s = g_string_sized_new(3 * len);
792 for (i = 0; i < len; i++) {
793 if (i)
794 g_string_append_c(s, ' ');
795 g_string_append_printf(s, "%02x", data[i]);
796 }
797
798 return s;
799}
800
801/**
6762401d 802 * Free a hex dump text that was created by sr_hexdump_new().
d8bc7ca3
GS
803 *
804 * @param[in] s Pointer to the GString to release.
82b9f3d1
UH
805 *
806 * @private
d8bc7ca3
GS
807 */
808SR_PRIV void sr_hexdump_free(GString *s)
809{
810 if (s)
811 g_string_free(s, TRUE);
812}
813
5ec172d7 814/**
5c436a3b
UH
815 * Convert a string representation of a numeric value to a sr_rational.
816 *
817 * The conversion is strict and will fail if the complete string does not
818 * represent a valid number. The function sets errno according to the details
819 * of the failure. This version ignores the locale.
5ec172d7
SB
820 *
821 * @param str The string representation to convert.
822 * @param ret Pointer to sr_rational where the result of the conversion will be stored.
823 *
824 * @retval SR_OK Conversion successful.
825 * @retval SR_ERR Failure.
826 *
827 * @since 0.5.0
828 */
829SR_API int sr_parse_rational(const char *str, struct sr_rational *ret)
830{
ca994998
GS
831 const char *readptr;
832 char *endptr;
833 gboolean is_negative, empty_integral, empty_fractional, exp_negative;
5ec172d7 834 int64_t integral;
ca994998
GS
835 int64_t fractional;
836 int64_t denominator;
837 uint32_t fractional_len;
838 int32_t exponent;
7f5bfd61 839
ca994998
GS
840 /*
841 * Implementor's note: This routine tries hard to avoid calling
842 * glib's or the platform's conversion routines with input that
843 * cannot get converted *at all* (see bug #1093). It also takes
844 * care to return with non-zero errno values for any failed
845 * conversion attempt. It's assumed that correctness and robustness
846 * are more important than performance, which is why code paths
847 * are not optimized at all. Maintainability took priority.
848 */
849
850 readptr = str;
5ec172d7 851
ca994998
GS
852 /* Skip leading whitespace. */
853 while (isspace(*readptr))
854 readptr++;
855
856 /* Determine the sign, default to non-negative. */
857 is_negative = FALSE;
858 if (*readptr == '-') {
0f5bba96 859 is_negative = TRUE;
ca994998
GS
860 readptr++;
861 } else if (*readptr == '+') {
862 is_negative = FALSE;
863 readptr++;
864 }
41c47f2c 865
ca994998
GS
866 /* Get the (optional) integral part. */
867 empty_integral = TRUE;
868 integral = 0;
869 endptr = (char *)readptr;
51bf39a1 870 errno = 0;
ca994998
GS
871 if (isdigit(*readptr)) {
872 empty_integral = FALSE;
873 integral = g_ascii_strtoll(readptr, &endptr, 10);
5ec172d7
SB
874 if (errno)
875 return SR_ERR;
ca994998
GS
876 if (endptr == str) {
877 errno = -EINVAL;
d8754963 878 return SR_ERR;
ca994998
GS
879 }
880 readptr = endptr;
5ec172d7
SB
881 }
882
ca994998
GS
883 /* Get the optional fractional part. */
884 empty_fractional = TRUE;
885 fractional = 0;
886 fractional_len = 0;
887 if (*readptr == '.') {
888 readptr++;
889 endptr++;
890 errno = 0;
891 if (isdigit(*readptr)) {
892 empty_fractional = FALSE;
893 fractional = g_ascii_strtoll(readptr, &endptr, 10);
894 if (errno)
895 return SR_ERR;
896 if (endptr == readptr) {
897 errno = -EINVAL;
898 return SR_ERR;
899 }
900 fractional_len = endptr - readptr;
901 readptr = endptr;
902 }
903 }
904
905 /* At least one of integral or fractional is required. */
906 if (empty_integral && empty_fractional) {
907 errno = -EINVAL;
908 return SR_ERR;
909 }
910
911 /* Get the (optional) exponent. */
912 exponent = 0;
913 if ((*readptr == 'E') || (*readptr == 'e')) {
914 readptr++;
915 endptr++;
916 exp_negative = FALSE;
917 if (*readptr == '+') {
918 exp_negative = FALSE;
919 readptr++;
920 endptr++;
921 } else if (*readptr == '-') {
922 exp_negative = TRUE;
923 readptr++;
924 endptr++;
925 }
926 if (!isdigit(*readptr)) {
927 errno = -EINVAL;
928 return SR_ERR;
929 }
930 errno = 0;
931 exponent = g_ascii_strtoll(readptr, &endptr, 10);
5ec172d7
SB
932 if (errno)
933 return SR_ERR;
ca994998
GS
934 if (endptr == readptr) {
935 errno = -EINVAL;
936 return SR_ERR;
937 }
938 readptr = endptr;
939 if (exp_negative)
940 exponent = -exponent;
5ec172d7
SB
941 }
942
ca994998
GS
943 /* Input must be exhausted. Unconverted remaining input is fatal. */
944 if (*endptr != '\0') {
945 errno = -EINVAL;
5ec172d7 946 return SR_ERR;
ca994998 947 }
5ec172d7 948
ca994998
GS
949 /*
950 * Apply the sign to the integral (and fractional) part(s).
951 * Adjust exponent (decimal position) such that the above integral
952 * and fractional parts both fit into the (new) integral part.
953 */
954 if (is_negative)
955 integral = -integral;
956 while (fractional_len-- > 0) {
5ec172d7 957 integral *= 10;
ca994998
GS
958 exponent--;
959 }
41c47f2c 960 if (!is_negative)
5ec172d7
SB
961 integral += fractional;
962 else
963 integral -= fractional;
5ec172d7
SB
964 while (exponent > 0) {
965 integral *= 10;
966 exponent--;
967 }
968
ca994998
GS
969 /*
970 * When significant digits remain after the decimal, scale up the
971 * denominator such that we end up with two integer p/q numbers.
972 */
973 denominator = 1;
5ec172d7
SB
974 while (exponent < 0) {
975 denominator *= 10;
976 exponent++;
977 }
978
979 ret->p = integral;
980 ret->q = denominator;
981
982 return SR_OK;
983}
984
25e7d9b1 985/**
b07b42f3
UH
986 * Convert a numeric value value to its "natural" string representation
987 * in SI units.
25e7d9b1 988 *
4cc9aea1
JH
989 * E.g. a value of 3000000, with units set to "W", would be converted
990 * to "3 MW", 20000 to "20 kW", 31500 would become "31.5 kW".
25e7d9b1 991 *
4cc9aea1
JH
992 * @param x The value to convert.
993 * @param unit The unit to append to the string, or NULL if the string
994 * has no units.
44dae539 995 *
91219afc 996 * @return A newly allocated string representation of the samplerate value,
133a37bf
UH
997 * or NULL upon errors. The caller is responsible to g_free() the
998 * memory.
47117241
UH
999 *
1000 * @since 0.2.0
25e7d9b1 1001 */
4cc9aea1 1002SR_API char *sr_si_string_u64(uint64_t x, const char *unit)
25e7d9b1 1003{
094e6b81
PS
1004 uint8_t i;
1005 uint64_t quot, divisor[] = {
b07b42f3
UH
1006 SR_HZ(1), SR_KHZ(1), SR_MHZ(1), SR_GHZ(1),
1007 SR_GHZ(1000), SR_GHZ(1000 * 1000), SR_GHZ(1000 * 1000 * 1000),
094e6b81
PS
1008 };
1009 const char *p, prefix[] = "\0kMGTPE";
69d83be9 1010 char fmt[16], fract[20] = "", *f;
094e6b81 1011
98fec29e 1012 if (!unit)
4cc9aea1 1013 unit = "";
25e7d9b1 1014
094e6b81
PS
1015 for (i = 0; (quot = x / divisor[i]) >= 1000; i++);
1016
1017 if (i) {
69d83be9 1018 sprintf(fmt, ".%%0%d"PRIu64, i * 3);
094e6b81
PS
1019 f = fract + sprintf(fract, fmt, x % divisor[i]) - 1;
1020
1021 while (f >= fract && strchr("0.", *f))
1022 *f-- = 0;
133a37bf 1023 }
25e7d9b1 1024
094e6b81
PS
1025 p = prefix + i;
1026
1027 return g_strdup_printf("%" PRIu64 "%s %.1s%s", quot, fract, p, unit);
4cc9aea1 1028}
25e7d9b1 1029
4cc9aea1
JH
1030/**
1031 * Convert a numeric samplerate value to its "natural" string representation.
1032 *
1033 * E.g. a value of 3000000 would be converted to "3 MHz", 20000 to "20 kHz",
1034 * 31500 would become "31.5 kHz".
1035 *
1036 * @param samplerate The samplerate in Hz.
1037 *
91219afc 1038 * @return A newly allocated string representation of the samplerate value,
4cc9aea1
JH
1039 * or NULL upon errors. The caller is responsible to g_free() the
1040 * memory.
47117241
UH
1041 *
1042 * @since 0.1.0
4cc9aea1
JH
1043 */
1044SR_API char *sr_samplerate_string(uint64_t samplerate)
1045{
1046 return sr_si_string_u64(samplerate, "Hz");
25e7d9b1 1047}
2a3f9541 1048
2a3f9541 1049/**
9a17c23b
UH
1050 * Convert a numeric period value to the "natural" string representation
1051 * of its period value.
2a3f9541 1052 *
9a17c23b 1053 * The period is specified as a rational number's numerator and denominator.
2a3f9541 1054 *
9a17c23b 1055 * E.g. a pair of (1, 5) would be converted to "200 ms", (10, 100) to "100 ms".
44dae539 1056 *
9a17c23b
UH
1057 * @param v_p The period numerator.
1058 * @param v_q The period denominator.
1059 *
1060 * @return A newly allocated string representation of the period value,
133a37bf
UH
1061 * or NULL upon errors. The caller is responsible to g_free() the
1062 * memory.
47117241 1063 *
9a17c23b 1064 * @since 0.5.0
2a3f9541 1065 */
6984cfb2 1066SR_API char *sr_period_string(uint64_t v_p, uint64_t v_q)
2a3f9541 1067{
6984cfb2 1068 double freq, v;
d2391b54 1069 int prec;
6984cfb2
SA
1070
1071 freq = 1 / ((double)v_p / v_q);
2a3f9541 1072
6984cfb2
SA
1073 if (freq > SR_GHZ(1)) {
1074 v = (double)v_p / v_q * 1000000000000.0;
1075 prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
d2391b54 1076 return g_strdup_printf("%.*f ps", prec, v);
6984cfb2
SA
1077 } else if (freq > SR_MHZ(1)) {
1078 v = (double)v_p / v_q * 1000000000.0;
1079 prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
d2391b54 1080 return g_strdup_printf("%.*f ns", prec, v);
6984cfb2
SA
1081 } else if (freq > SR_KHZ(1)) {
1082 v = (double)v_p / v_q * 1000000.0;
1083 prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
d2391b54 1084 return g_strdup_printf("%.*f us", prec, v);
6984cfb2
SA
1085 } else if (freq > 1) {
1086 v = (double)v_p / v_q * 1000.0;
1087 prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
d2391b54 1088 return g_strdup_printf("%.*f ms", prec, v);
6984cfb2
SA
1089 } else {
1090 v = (double)v_p / v_q;
1091 prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
d2391b54 1092 return g_strdup_printf("%.*f s", prec, v);
2a3f9541 1093 }
2a3f9541 1094}
40f5ddac 1095
79afc8ca 1096/**
e0e15067
BV
1097 * Convert a numeric voltage value to the "natural" string representation
1098 * of its voltage value. The voltage is specified as a rational number's
1099 * numerator and denominator.
79afc8ca
BV
1100 *
1101 * E.g. a value of 300000 would be converted to "300mV", 2 to "2V".
1102 *
e0e15067
BV
1103 * @param v_p The voltage numerator.
1104 * @param v_q The voltage denominator.
79afc8ca 1105 *
91219afc 1106 * @return A newly allocated string representation of the voltage value,
79afc8ca
BV
1107 * or NULL upon errors. The caller is responsible to g_free() the
1108 * memory.
47117241
UH
1109 *
1110 * @since 0.2.0
79afc8ca 1111 */
e0e15067 1112SR_API char *sr_voltage_string(uint64_t v_p, uint64_t v_q)
79afc8ca 1113{
e0e15067 1114 if (v_q == 1000)
b5df922e 1115 return g_strdup_printf("%" PRIu64 " mV", v_p);
e0e15067 1116 else if (v_q == 1)
b5df922e 1117 return g_strdup_printf("%" PRIu64 " V", v_p);
79afc8ca 1118 else
b5df922e 1119 return g_strdup_printf("%g V", (float)v_p / (float)v_q);
79afc8ca
BV
1120}
1121
dfcc0bf9
UH
1122/**
1123 * Convert a "natural" string representation of a size value to uint64_t.
1124 *
1125 * E.g. a value of "3k" or "3 K" would be converted to 3000, a value
1126 * of "15M" would be converted to 15000000.
1127 *
1128 * Value representations other than decimal (such as hex or octal) are not
1129 * supported. Only 'k' (kilo), 'm' (mega), 'g' (giga) suffixes are supported.
1130 * Spaces (but not other whitespace) between value and suffix are allowed.
1131 *
1132 * @param sizestring A string containing a (decimal) size value.
f64c1414 1133 * @param size Pointer to uint64_t which will contain the string's size value.
dfcc0bf9 1134 *
44dae539 1135 * @return SR_OK upon success, SR_ERR upon errors.
47117241
UH
1136 *
1137 * @since 0.1.0
dfcc0bf9 1138 */
1a081ca6 1139SR_API int sr_parse_sizestring(const char *sizestring, uint64_t *size)
40f5ddac 1140{
751ba4c8
GS
1141 uint64_t multiplier;
1142 int done;
580f3099 1143 double frac_part;
40f5ddac
BV
1144 char *s;
1145
f64c1414 1146 *size = strtoull(sizestring, &s, 10);
40f5ddac 1147 multiplier = 0;
580f3099 1148 frac_part = 0;
f64c1414
BV
1149 done = FALSE;
1150 while (s && *s && multiplier == 0 && !done) {
40f5ddac
BV
1151 switch (*s) {
1152 case ' ':
1153 break;
580f3099
DJ
1154 case '.':
1155 frac_part = g_ascii_strtod(s, &s);
1156 break;
40f5ddac
BV
1157 case 'k':
1158 case 'K':
59df0c77 1159 multiplier = SR_KHZ(1);
40f5ddac
BV
1160 break;
1161 case 'm':
1162 case 'M':
59df0c77 1163 multiplier = SR_MHZ(1);
40f5ddac
BV
1164 break;
1165 case 'g':
1166 case 'G':
59df0c77 1167 multiplier = SR_GHZ(1);
40f5ddac 1168 break;
751ba4c8
GS
1169 case 't':
1170 case 'T':
1171 multiplier = SR_GHZ(1000);
1172 break;
1173 case 'p':
1174 case 'P':
1175 multiplier = SR_GHZ(1000 * 1000);
1176 break;
1177 case 'e':
1178 case 'E':
1179 multiplier = SR_GHZ(1000 * 1000 * 1000);
1180 break;
40f5ddac 1181 default:
f64c1414
BV
1182 done = TRUE;
1183 s--;
40f5ddac
BV
1184 }
1185 s++;
1186 }
580f3099 1187 if (multiplier > 0) {
f64c1414 1188 *size *= multiplier;
580f3099 1189 *size += frac_part * multiplier;
751ba4c8 1190 } else {
580f3099 1191 *size += frac_part;
751ba4c8 1192 }
40f5ddac 1193
34577da6 1194 if (s && *s && g_ascii_strcasecmp(s, "Hz"))
f64c1414
BV
1195 return SR_ERR;
1196
1197 return SR_OK;
40f5ddac
BV
1198}
1199
dfcc0bf9
UH
1200/**
1201 * Convert a "natural" string representation of a time value to an
1202 * uint64_t value in milliseconds.
1203 *
1204 * E.g. a value of "3s" or "3 s" would be converted to 3000, a value
1205 * of "15ms" would be converted to 15.
1206 *
1207 * Value representations other than decimal (such as hex or octal) are not
1208 * supported. Only lower-case "s" and "ms" time suffixes are supported.
1209 * Spaces (but not other whitespace) between value and suffix are allowed.
1210 *
1211 * @param timestring A string containing a (decimal) time value.
1212 * @return The string's time value as uint64_t, in milliseconds.
1213 *
6b2d8d3e
UH
1214 * @todo Add support for "m" (minutes) and others.
1215 * @todo Add support for picoseconds?
1216 * @todo Allow both lower-case and upper-case? If no, document it.
47117241
UH
1217 *
1218 * @since 0.1.0
dfcc0bf9 1219 */
1a081ca6 1220SR_API uint64_t sr_parse_timestring(const char *timestring)
40f5ddac
BV
1221{
1222 uint64_t time_msec;
1223 char *s;
1224
6b2d8d3e
UH
1225 /* TODO: Error handling, logging. */
1226
40f5ddac
BV
1227 time_msec = strtoull(timestring, &s, 10);
1228 if (time_msec == 0 && s == timestring)
1229 return 0;
1230
1231 if (s && *s) {
1232 while (*s == ' ')
1233 s++;
1234 if (!strcmp(s, "s"))
1235 time_msec *= 1000;
1236 else if (!strcmp(s, "ms"))
1237 ; /* redundant */
1238 else
1239 return 0;
1240 }
1241
1242 return time_msec;
1243}
4d436e71 1244
47117241 1245/** @since 0.1.0 */
1a081ca6 1246SR_API gboolean sr_parse_boolstring(const char *boolstr)
4d436e71 1247{
ad466f86
GS
1248 /*
1249 * Complete absence of an input spec is assumed to mean TRUE,
1250 * as in command line option strings like this:
1251 * ...:samplerate=100k:header:numchannels=4:...
1252 */
1253 if (!boolstr || !*boolstr)
1254 return TRUE;
4d436e71 1255
993526f8
BV
1256 if (!g_ascii_strncasecmp(boolstr, "true", 4) ||
1257 !g_ascii_strncasecmp(boolstr, "yes", 3) ||
1258 !g_ascii_strncasecmp(boolstr, "on", 2) ||
1259 !g_ascii_strncasecmp(boolstr, "1", 1))
4d436e71
GM
1260 return TRUE;
1261
1262 return FALSE;
1263}
76f4c610 1264
47117241 1265/** @since 0.2.0 */
76e107d6 1266SR_API int sr_parse_period(const char *periodstr, uint64_t *p, uint64_t *q)
76f4c610
BV
1267{
1268 char *s;
1269
76e107d6
BV
1270 *p = strtoull(periodstr, &s, 10);
1271 if (*p == 0 && s == periodstr)
76f4c610
BV
1272 /* No digits found. */
1273 return SR_ERR_ARG;
1274
1275 if (s && *s) {
1276 while (*s == ' ')
1277 s++;
8c012adb 1278 if (!strcmp(s, "fs"))
d9b716fc 1279 *q = UINT64_C(1000000000000000);
8c012adb 1280 else if (!strcmp(s, "ps"))
d9b716fc 1281 *q = UINT64_C(1000000000000);
8c012adb 1282 else if (!strcmp(s, "ns"))
d9b716fc 1283 *q = UINT64_C(1000000000);
76f4c610 1284 else if (!strcmp(s, "us"))
76e107d6 1285 *q = 1000000;
76f4c610 1286 else if (!strcmp(s, "ms"))
76e107d6 1287 *q = 1000;
76f4c610 1288 else if (!strcmp(s, "s"))
76e107d6 1289 *q = 1;
76f4c610
BV
1290 else
1291 /* Must have a time suffix. */
1292 return SR_ERR_ARG;
1293 }
1294
1295 return SR_OK;
1296}
1297
47117241 1298/** @since 0.2.0 */
76e107d6 1299SR_API int sr_parse_voltage(const char *voltstr, uint64_t *p, uint64_t *q)
79afc8ca
BV
1300{
1301 char *s;
1302
76e107d6
BV
1303 *p = strtoull(voltstr, &s, 10);
1304 if (*p == 0 && s == voltstr)
79afc8ca
BV
1305 /* No digits found. */
1306 return SR_ERR_ARG;
1307
1308 if (s && *s) {
1309 while (*s == ' ')
1310 s++;
34577da6 1311 if (!g_ascii_strcasecmp(s, "mv"))
76e107d6 1312 *q = 1000L;
34577da6 1313 else if (!g_ascii_strcasecmp(s, "v"))
76e107d6 1314 *q = 1;
79afc8ca
BV
1315 else
1316 /* Must have a base suffix. */
1317 return SR_ERR_ARG;
1318 }
1319
1320 return SR_OK;
1321}
1322
ba42283a
GS
1323/**
1324 * Append another text item to a NULL terminated string vector.
1325 *
1326 * @param[in] table The previous string vector.
1327 * @param[in,out] sz The previous and the resulting vector size
1328 * (item count).
1329 * @param[in] text The text string to append to the vector.
1330 * Can be #NULL.
1331 *
1332 * @returns The new vector, its location can differ from 'table'.
1333 * Or #NULL in case of failure.
1334 *
1335 * This implementation happens to work for the first invocation when
1336 * 'table' is #NULL and 'sz' is 0, as well as subsequent append calls.
1337 * The 'text' can be #NULL or can be a non-empty string. When 'sz' is
1338 * not provided, then the 'table' must be a NULL terminated vector,
1339 * so that the routine can auto-determine the vector's current length.
1340 *
1341 * This routine re-allocates the vector as needed. Callers must not
1342 * rely on the memory address to remain the same across calls.
1343 */
1344static char **append_probe_name(char **table, size_t *sz, const char *text)
1345{
1346 size_t curr_size, alloc_size;
1347 char **new_table;
1348
1349 /* Get the table's previous size (item count). */
1350 if (sz)
1351 curr_size = *sz;
1352 else if (table)
1353 curr_size = g_strv_length(table);
1354 else
1355 curr_size = 0;
1356
1357 /* Extend storage to hold one more item, and the termination. */
1358 alloc_size = curr_size + (text ? 1 : 0) + 1;
1359 alloc_size *= sizeof(table[0]);
1360 new_table = g_realloc(table, alloc_size);
1361 if (!new_table) {
1362 g_strfreev(table);
1363 if (sz)
1364 *sz = 0;
1365 return NULL;
1366 }
1367
1368 /* Append the item, NULL terminate. */
1369 if (text) {
1370 new_table[curr_size] = g_strdup(text);
1371 if (!new_table[curr_size]) {
1372 g_strfreev(new_table);
1373 if (sz)
1374 *sz = 0;
1375 return NULL;
1376 }
1377 curr_size++;
1378 }
1379 if (sz)
1380 *sz = curr_size;
1381 new_table[curr_size] = NULL;
1382
1383 return new_table;
1384}
1385
1386static char **append_probe_names(char **table, size_t *sz,
1387 const char **names)
1388{
1389 if (!names)
1390 return table;
1391
1392 while (names[0]) {
1393 table = append_probe_name(table, sz, names[0]);
1394 names++;
1395 }
1396 return table;
1397}
1398
1399static const struct {
1400 const char *name;
1401 const char **expands;
1402} probe_name_aliases[] = {
1403 {
1404 "ac97", (const char *[]){
1405 "sync", "clk",
1406 "out", "in", "rst",
1407 NULL,
1408 },
1409 },
1410 {
1411 "i2c", (const char *[]){
1412 "scl", "sda", NULL,
1413 },
1414 },
1415 {
1416 "jtag", (const char *[]){
1417 "tdi", "tdo", "tck", "tms", NULL,
1418 },
1419 },
1420 {
1421 "jtag-opt", (const char *[]){
1422 "tdi", "tdo", "tck", "tms",
1423 "trst", "srst", "rtck", NULL,
1424 },
1425 },
1426 {
1427 "ieee488", (const char *[]){
1428 "dio1", "dio2", "dio3", "dio4",
1429 "dio5", "dio6", "dio7", "dio8",
1430 "eoi", "dav", "nrfd", "ndac",
1431 "ifc", "srq", "atn", "ren", NULL,
1432 },
1433 },
1434 {
1435 "lpc", (const char *[]){
1436 "lframe", "lclk",
1437 "lad0", "lad1", "lad2", "lad3",
1438 NULL,
1439 },
1440 },
1441 {
1442 "lpc-opt", (const char *[]){
1443 "lframe", "lclk",
1444 "lad0", "lad1", "lad2", "lad3",
1445 "lreset", "ldrq", "serirq", "clkrun",
1446 "lpme", "lpcpd", "lsmi",
1447 NULL,
1448 },
1449 },
1450 {
1451 "mcs48", (const char *[]){
1452 "ale", "psen",
1453 "d0", "d1", "d2", "d3",
1454 "d4", "d5", "d6", "d7",
1455 "a8", "a9", "a10", "a11",
1456 "a12", "a13",
1457 NULL,
1458 },
1459 },
1460 {
1461 "microwire", (const char *[]){
1462 "cs", "sk", "si", "so", NULL,
1463 },
1464 },
1465 {
1466 "sdcard_sd", (const char *[]){
1467 "cmd", "clk",
1468 "dat0", "dat1", "dat2", "dat3",
1469 NULL,
1470 },
1471 },
1472 {
1473 "seven_segment", (const char *[]){
1474 "a", "b", "c", "d", "e", "f", "g",
1475 "dp", NULL,
1476 },
1477 },
1478 {
1479 "spi", (const char *[]){
1480 "clk", "miso", "mosi", "cs", NULL,
1481 },
1482 },
1483 {
1484 "swd", (const char *[]){
1485 "swclk", "swdio", NULL,
1486 },
1487 },
1488 {
1489 "uart", (const char *[]){
1490 "rx", "tx", NULL,
1491 },
1492 },
1493 {
1494 "usb", (const char *[]){
1495 "dp", "dm", NULL,
1496 },
1497 },
1498 {
1499 "z80", (const char *[]){
1500 "d0", "d1", "d2", "d3",
1501 "d4", "d5", "d6", "d7",
1502 "m1", "rd", "wr",
1503 "mreq", "iorq",
1504 "a0", "a1", "a2", "a3",
1505 "a4", "a5", "a6", "a7",
1506 "a8", "a9", "a10", "a11",
1507 "a12", "a13", "a14", "a15",
1508 NULL,
1509 },
1510 },
1511};
1512
1513/* Case insensitive lookup of an alias name. */
1514static const char **lookup_probe_alias(const char *name)
1515{
1516 size_t idx;
1517
1518 for (idx = 0; idx < ARRAY_SIZE(probe_name_aliases); idx++) {
1519 if (g_ascii_strcasecmp(probe_name_aliases[idx].name, name) != 0)
1520 continue;
1521 return probe_name_aliases[idx].expands;
1522 }
1523 return NULL;
1524}
1525
1526/**
1527 * Parse a probe names specification, allocate a string vector.
1528 *
1529 * @param[in] spec The input spec, list of probes or aliases.
1530 * @param[in] dflt_names The default probe names, a string array.
1531 * @param[in] dflt_count The default probe names count. Either must
1532 * match the unterminated array size, or can be 0 when the
1533 * default names are NULL terminated.
1534 * @param[in] max_count Optional resulting vector size limit.
1535 * @param[out] ret_count Optional result vector size (return value).
1536 *
1537 * @returns A string vector with resulting probe names. Or #NULL
1538 * in case of failure.
1539 *
1540 * The input spec is a comma separated list of probe names. Items can
1541 * be aliases which expand to a corresponding set of signal names.
1542 * The resulting names list optionally gets padded from the caller's
1543 * builtin probe names, an empty input spec yields the original names
1544 * as provided by the caller. Padding is omitted when the spec starts
1545 * with '-', which may result in a device with fewer channels being
1546 * created, enough to cover the user's spec, but none extra to maybe
1547 * enable and use later on. An optional maximum length spec will trim
1548 * the result set to that size. The resulting vector length optionally
1549 * is returned to the caller, so that it need not re-get the length.
1550 *
1551 * Calling applications must release the allocated vector by means
1552 * of @ref sr_free_probe_names().
1553 *
1554 * @since 0.6.0
1555 */
1556SR_API char **sr_parse_probe_names(const char *spec,
1557 const char **dflt_names, size_t dflt_count,
1558 size_t max_count, size_t *ret_count)
1559{
1560 char **result_names;
1561 size_t result_count;
1562 gboolean pad_from_dflt;
1563 char **spec_names, *spec_name;
1564 size_t spec_idx;
1565 const char **alias_names;
1566
1567 if (!spec || !*spec)
1568 spec = NULL;
1569
1570 /*
1571 * Accept zero length spec for default input names. Determine
1572 * the name table's length here. Cannot re-use g_strv_length()
1573 * because of the 'const' decoration in application code.
1574 */
1575 if (!dflt_count) {
1576 while (dflt_names && dflt_names[dflt_count])
1577 dflt_count++;
1578 }
1579 if (!dflt_count)
1580 return NULL;
1581
1582 /*
1583 * Start with an empty resulting names table. Will grow
1584 * dynamically as more names get appended.
1585 */
1586 result_names = NULL;
1587 result_count = 0;
1588 pad_from_dflt = TRUE;
1589
1590 /*
1591 * When an input spec exists, use its content. Lookup alias
1592 * names, and append their corresponding signals. Or append
1593 * the verbatim input name if it is not an alias. Recursion
1594 * is not supported in this implementation.
1595 *
1596 * A leading '-' before the signal names list suppresses the
1597 * padding of the resulting list from the device's default
1598 * probe names.
1599 */
1600 spec_names = NULL;
1601 if (spec && *spec == '-') {
1602 spec++;
1603 pad_from_dflt = FALSE;
1604 }
1605 if (spec && *spec)
1606 spec_names = g_strsplit(spec, ",", 0);
1607 for (spec_idx = 0; spec_names && spec_names[spec_idx]; spec_idx++) {
1608 spec_name = spec_names[spec_idx];
1609 if (!*spec_name)
1610 continue;
1611 alias_names = lookup_probe_alias(spec_name);
1612 if (alias_names) {
1613 result_names = append_probe_names(result_names,
1614 &result_count, alias_names);
1615 } else {
1616 result_names = append_probe_name(result_names,
1617 &result_count, spec_name);
1618 }
1619 }
1620 g_strfreev(spec_names);
1621
1622 /*
1623 * By default pad the resulting names from the caller's
1624 * probe names. Don't pad if the input spec started with
1625 * '-', when the spec's exact length was requested.
1626 */
1627 if (pad_from_dflt) do {
1628 if (max_count && result_count >= max_count)
1629 break;
1630 if (result_count >= dflt_count)
1631 break;
1632 result_names = append_probe_name(result_names, &result_count,
1633 dflt_names[result_count]);
1634 } while (1);
1635
1636 /* Optionally trim the result to the caller's length limit. */
1637 if (max_count) {
1638 while (result_count > max_count) {
1639 --result_count;
1640 g_free(result_names[result_count]);
1641 result_names[result_count] = NULL;
1642 }
1643 }
1644
1645 if (ret_count)
1646 *ret_count = result_count;
1647
1648 return result_names;
1649}
1650
1651/**
1652 * Release previously allocated probe names (string vector).
1653 *
1654 * @param[in] names The previously allocated string vector.
1655 *
1656 * @since 0.6.0
1657 */
1658SR_API void sr_free_probe_names(char **names)
1659{
1660 g_strfreev(names);
1661}
1662
7b870c38 1663/** @} */