]> sigrok.org Git - libsigrok.git/blob - src/strutil.c
Use UINT64_C instead of "ULL" number suffix.
[libsigrok.git] / src / strutil.c
1 /*
2  * This file is part of the libsigrok project.
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
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /* Needed for POSIX.1-2008 locale functions */
21 #define _XOPEN_SOURCE 700
22 #include <config.h>
23 #include <ctype.h>
24 #include <locale.h>
25 #if defined(__FreeBSD__) || defined(__APPLE__)
26 #include <xlocale.h>
27 #endif
28 #if defined(__FreeBSD__)
29 #include <sys/param.h>
30 #endif
31 #include <stdint.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <errno.h>
36 #include <stdbool.h>
37 #include <libsigrok/libsigrok.h>
38 #include "libsigrok-internal.h"
39
40 /** @cond PRIVATE */
41 #define LOG_PREFIX "strutil"
42 /** @endcond */
43
44 /**
45  * @file
46  *
47  * Helper functions for handling or converting libsigrok-related strings.
48  */
49
50 /**
51  * @defgroup grp_strutil String utilities
52  *
53  * Helper functions for handling or converting libsigrok-related strings.
54  *
55  * @{
56  */
57
58 /**
59  * @private
60  *
61  * Convert a string representation of a numeric value (base 10) to a long integer. The
62  * conversion is strict and will fail if the complete string does not represent
63  * a valid long integer. The function sets errno according to the details of the
64  * failure.
65  *
66  * @param str The string representation to convert.
67  * @param ret Pointer to long where the result of the conversion will be stored.
68  *
69  * @retval SR_OK Conversion successful.
70  * @retval SR_ERR Failure.
71  */
72 SR_PRIV int sr_atol(const char *str, long *ret)
73 {
74         long tmp;
75         char *endptr = NULL;
76
77         errno = 0;
78         tmp = strtol(str, &endptr, 10);
79
80         while (endptr && isspace(*endptr))
81                 endptr++;
82
83         if (!endptr || *endptr || errno) {
84                 if (!errno)
85                         errno = EINVAL;
86                 return SR_ERR;
87         }
88
89         *ret = tmp;
90         return SR_OK;
91 }
92
93 /**
94  * @private
95  *
96  * Convert a string representation of a numeric value (base 10) to an integer. The
97  * conversion is strict and will fail if the complete string does not represent
98  * a valid integer. The function sets errno according to the details of the
99  * failure.
100  *
101  * @param str The string representation to convert.
102  * @param ret Pointer to int where the result of the conversion will be stored.
103  *
104  * @retval SR_OK Conversion successful.
105  * @retval SR_ERR Failure.
106  */
107 SR_PRIV int sr_atoi(const char *str, int *ret)
108 {
109         long tmp;
110
111         if (sr_atol(str, &tmp) != SR_OK)
112                 return SR_ERR;
113
114         if ((int) tmp != tmp) {
115                 errno = ERANGE;
116                 return SR_ERR;
117         }
118
119         *ret = (int) tmp;
120         return SR_OK;
121 }
122
123 /**
124  * @private
125  *
126  * Convert a string representation of a numeric value to a double. The
127  * conversion is strict and will fail if the complete string does not represent
128  * a valid double. The function sets errno according to the details of the
129  * failure.
130  *
131  * @param str The string representation to convert.
132  * @param ret Pointer to double where the result of the conversion will be stored.
133  *
134  * @retval SR_OK Conversion successful.
135  * @retval SR_ERR Failure.
136  */
137 SR_PRIV int sr_atod(const char *str, double *ret)
138 {
139         double tmp;
140         char *endptr = NULL;
141
142         errno = 0;
143         tmp = strtof(str, &endptr);
144
145         while (endptr && isspace(*endptr))
146                 endptr++;
147
148         if (!endptr || *endptr || errno) {
149                 if (!errno)
150                         errno = EINVAL;
151                 return SR_ERR;
152         }
153
154         *ret = tmp;
155         return SR_OK;
156 }
157
158 /**
159  * @private
160  *
161  * Convert a string representation of a numeric value to a float. The
162  * conversion is strict and will fail if the complete string does not represent
163  * a valid float. The function sets errno according to the details of the
164  * failure.
165  *
166  * @param str The string representation to convert.
167  * @param ret Pointer to float where the result of the conversion will be stored.
168  *
169  * @retval SR_OK Conversion successful.
170  * @retval SR_ERR Failure.
171  */
172 SR_PRIV int sr_atof(const char *str, float *ret)
173 {
174         double tmp;
175
176         if (sr_atod(str, &tmp) != SR_OK)
177                 return SR_ERR;
178
179         if ((float) tmp != tmp) {
180                 errno = ERANGE;
181                 return SR_ERR;
182         }
183
184         *ret = (float) tmp;
185         return SR_OK;
186 }
187
188 /**
189  * @private
190  *
191  * Convert a string representation of a numeric value to a double. The
192  * conversion is strict and will fail if the complete string does not represent
193  * a valid double. The function sets errno according to the details of the
194  * failure. This version ignores the locale.
195  *
196  * @param str The string representation to convert.
197  * @param ret Pointer to double where the result of the conversion will be stored.
198  *
199  * @retval SR_OK Conversion successful.
200  * @retval SR_ERR Failure.
201  */
202 SR_PRIV int sr_atod_ascii(const char *str, double *ret)
203 {
204         double tmp;
205         char *endptr = NULL;
206
207         errno = 0;
208         tmp = g_ascii_strtod(str, &endptr);
209
210         if (!endptr || *endptr || errno) {
211                 if (!errno)
212                         errno = EINVAL;
213                 return SR_ERR;
214         }
215
216         *ret = tmp;
217         return SR_OK;
218 }
219
220 /**
221  * @private
222  *
223  * Convert a string representation of a numeric value to a float. The
224  * conversion is strict and will fail if the complete string does not represent
225  * a valid float. The function sets errno according to the details of the
226  * failure. This version ignores the locale.
227  *
228  * @param str The string representation to convert.
229  * @param ret Pointer to float where the result of the conversion will be stored.
230  *
231  * @retval SR_OK Conversion successful.
232  * @retval SR_ERR Failure.
233  */
234 SR_PRIV int sr_atof_ascii(const char *str, float *ret)
235 {
236         double tmp;
237         char *endptr = NULL;
238
239         errno = 0;
240         tmp = g_ascii_strtod(str, &endptr);
241
242         if (!endptr || *endptr || errno) {
243                 if (!errno)
244                         errno = EINVAL;
245                 return SR_ERR;
246         }
247
248         /* FIXME This fails unexpectedly. Some other method to safel downcast
249          * needs to be found. Checking against FLT_MAX doesn't work as well. */
250         /*
251         if ((float) tmp != tmp) {
252                 errno = ERANGE;
253                 sr_dbg("ERANGEEEE %e != %e", (float) tmp, tmp);
254                 return SR_ERR;
255         }
256         */
257
258         *ret = (float) tmp;
259         return SR_OK;
260 }
261
262 /**
263  * Composes a string with a format string (like printf) in the buffer pointed
264  * by buf (taking buf_size as the maximum buffer capacity to fill).
265  * If the resulting string would be longer than n - 1 characters, the remaining
266  * characters are discarded and not stored, but counted for the value returned
267  * by the function.
268  * A terminating NUL character is automatically appended after the content
269  * written.
270  * After the format parameter, the function expects at least as many additional
271  * arguments as needed for format.
272  *
273  * This version ignores the actual locale and uses the locale "C" for Linux,
274  * FreeBSD, OSX and Android.
275  *
276  * @param buf Pointer to a buffer where the resulting C string is stored.
277  * @param buf_size Maximum number of bytes to be used in the buffer. The
278  *        generated string has a length of at most buf_size - 1, leaving space
279  *        for the additional terminating NUL character.
280  * @param format C string that contains a format string (see printf).
281  * @param ... A sequence of additional arguments, each containing a value to be
282  *        used to replace a format specifier in the format string.
283  *
284  * @return On success, the number of characters that would have been written if
285  *         buf_size had been sufficiently large, not counting the terminating
286  *         NUL character. On failure, a negative number is returned.
287  *         Notice that only when this returned value is non-negative and less
288  *         than buf_size, the string has been completely written.
289  *
290  * @since 0.6.0
291  */
292 SR_API int sr_snprintf_ascii(char *buf, size_t buf_size,
293         const char *format, ...)
294 {
295         int ret;
296         va_list args;
297
298         va_start(args, format);
299         ret = sr_vsnprintf_ascii(buf, buf_size, format, args);
300         va_end(args);
301
302         return ret;
303 }
304
305 /**
306  * Composes a string with a format string (like printf) in the buffer pointed
307  * by buf (taking buf_size as the maximum buffer capacity to fill).
308  * If the resulting string would be longer than n - 1 characters, the remaining
309  * characters are discarded and not stored, but counted for the value returned
310  * by the function.
311  * A terminating NUL character is automatically appended after the content
312  * written.
313  * Internally, the function retrieves arguments from the list identified by
314  * args as if va_arg was used on it, and thus the state of args is likely to
315  * be altered by the call.
316  * In any case, arg should have been initialized by va_start at some point
317  * before the call, and it is expected to be released by va_end at some point
318  * after the call.
319  *
320  * This version ignores the actual locale and uses the locale "C" for Linux,
321  * FreeBSD, OSX and Android.
322  *
323  * @param buf Pointer to a buffer where the resulting C string is stored.
324  * @param buf_size Maximum number of bytes to be used in the buffer. The
325  *        generated string has a length of at most buf_size - 1, leaving space
326  *        for the additional terminating NUL character.
327  * @param format C string that contains a format string (see printf).
328  * @param args A value identifying a variable arguments list initialized with
329  *        va_start.
330  *
331  * @return On success, the number of characters that would have been written if
332  *         buf_size had been sufficiently large, not counting the terminating
333  *         NUL character. On failure, a negative number is returned.
334  *         Notice that only when this returned value is non-negative and less
335  *         than buf_size, the string has been completely written.
336  *
337  * @since 0.6.0
338  */
339 SR_API int sr_vsnprintf_ascii(char *buf, size_t buf_size,
340         const char *format, va_list args)
341 {
342 #if defined(_WIN32)
343         int ret;
344
345 #if 0
346         /*
347          * TODO: This part compiles with mingw-w64 but doesn't run with Win7.
348          *       Doesn't start because of "Procedure entry point _create_locale
349          *       not found in msvcrt.dll".
350          *       mingw-w64 should link to msvcr100.dll not msvcrt.dll!.
351          */
352         _locale_t locale;
353
354         locale = _create_locale(LC_NUMERIC, "C");
355         ret = _vsnprintf_l(buf, buf_size, format, locale, args);
356         _free_locale(locale);
357 #endif
358
359         /* vsprintf uses the current locale, may cause issues for floats. */
360         ret = vsnprintf(buf, buf_size, format, args);
361
362         return ret;
363 #elif defined(__APPLE__)
364         /*
365          * See:
366          * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/printf_l.3.html
367          * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/xlocale.3.html
368          */
369         int ret;
370         locale_t locale;
371
372         locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
373         ret = vsnprintf_l(buf, buf_size, locale, format, args);
374         freelocale(locale);
375
376         return ret;
377 #elif defined(__FreeBSD__) && __FreeBSD_version >= 901000
378         /*
379          * See:
380          * https://www.freebsd.org/cgi/man.cgi?query=printf_l&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE
381          * https://www.freebsd.org/cgi/man.cgi?query=xlocale&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE
382          */
383         int ret;
384         locale_t locale;
385
386         locale = newlocale(LC_NUMERIC_MASK, "C", NULL);
387         ret = vsnprintf_l(buf, buf_size, locale, format, args);
388         freelocale(locale);
389
390         return ret;
391 #elif defined(__ANDROID__)
392         /*
393          * The Bionic libc only has two locales ("C" aka "POSIX" and "C.UTF-8"
394          * aka "en_US.UTF-8"). The decimal point is hard coded as ".".
395          * See: https://android.googlesource.com/platform/bionic/+/master/libc/bionic/locale.cpp
396          */
397         int ret;
398
399         ret = vsnprintf(buf, buf_size, format, args);
400
401         return ret;
402 #elif defined(__linux__)
403         int ret;
404         locale_t old_locale, temp_locale;
405
406         /* Switch to C locale for proper float/double conversion. */
407         temp_locale = newlocale(LC_NUMERIC, "C", NULL);
408         old_locale = uselocale(temp_locale);
409
410         ret = vsnprintf(buf, buf_size, format, args);
411
412         /* Switch back to original locale. */
413         uselocale(old_locale);
414         freelocale(temp_locale);
415
416         return ret;
417 #elif defined(__unix__) || defined(__unix)
418         /*
419          * This is a fallback for all other BSDs, *nix and FreeBSD <= 9.0, by
420          * using the current locale for snprintf(). This may not work correctly
421          * for floats!
422          */
423         int ret;
424
425         ret = vsnprintf(buf, buf_size, format, args);
426
427         return ret;
428 #else
429         /* No implementation for unknown systems! */
430         return -1;
431 #endif
432 }
433
434 /**
435  * Convert a string representation of a numeric value to a sr_rational.
436  *
437  * The conversion is strict and will fail if the complete string does not
438  * represent a valid number. The function sets errno according to the details
439  * of the failure. This version ignores the locale.
440  *
441  * @param str The string representation to convert.
442  * @param ret Pointer to sr_rational where the result of the conversion will be stored.
443  *
444  * @retval SR_OK Conversion successful.
445  * @retval SR_ERR Failure.
446  *
447  * @since 0.5.0
448  */
449 SR_API int sr_parse_rational(const char *str, struct sr_rational *ret)
450 {
451         char *endptr = NULL;
452         int64_t integral;
453         int64_t fractional = 0;
454         int64_t denominator = 1;
455         int32_t fractional_len = 0;
456         int32_t exponent = 0;
457         bool is_negative = false;
458
459         errno = 0;
460         integral = g_ascii_strtoll(str, &endptr, 10);
461
462         if (str == endptr && (str[0] == '-' || str[0] == '+') && str[1] == '.')
463                 endptr += 1;
464         else if (errno)
465                 return SR_ERR;
466
467         if (integral < 0 || str[0] == '-')
468                 is_negative = true;
469
470         if (*endptr == '.') {
471                 const char* start = endptr + 1;
472                 fractional = g_ascii_strtoll(start, &endptr, 10);
473                 if (errno)
474                         return SR_ERR;
475                 fractional_len = endptr - start;
476         }
477
478         if ((*endptr == 'E') || (*endptr == 'e')) {
479                 exponent = g_ascii_strtoll(endptr + 1, &endptr, 10);
480                 if (errno)
481                         return SR_ERR;
482         }
483
484         if (*endptr != '\0')
485                 return SR_ERR;
486
487         for (int i = 0; i < fractional_len; i++)
488                 integral *= 10;
489         exponent -= fractional_len;
490
491         if (!is_negative)
492                 integral += fractional;
493         else
494                 integral -= fractional;
495
496         while (exponent > 0) {
497                 integral *= 10;
498                 exponent--;
499         }
500
501         while (exponent < 0) {
502                 denominator *= 10;
503                 exponent++;
504         }
505
506         ret->p = integral;
507         ret->q = denominator;
508
509         return SR_OK;
510 }
511
512 /**
513  * Convert a numeric value value to its "natural" string representation
514  * in SI units.
515  *
516  * E.g. a value of 3000000, with units set to "W", would be converted
517  * to "3 MW", 20000 to "20 kW", 31500 would become "31.5 kW".
518  *
519  * @param x The value to convert.
520  * @param unit The unit to append to the string, or NULL if the string
521  *             has no units.
522  *
523  * @return A newly allocated string representation of the samplerate value,
524  *         or NULL upon errors. The caller is responsible to g_free() the
525  *         memory.
526  *
527  * @since 0.2.0
528  */
529 SR_API char *sr_si_string_u64(uint64_t x, const char *unit)
530 {
531         uint8_t i;
532         uint64_t quot, divisor[] = {
533                 SR_HZ(1), SR_KHZ(1), SR_MHZ(1), SR_GHZ(1),
534                 SR_GHZ(1000), SR_GHZ(1000 * 1000), SR_GHZ(1000 * 1000 * 1000),
535         };
536         const char *p, prefix[] = "\0kMGTPE";
537         char fmt[16], fract[20] = "", *f;
538
539         if (!unit)
540                 unit = "";
541
542         for (i = 0; (quot = x / divisor[i]) >= 1000; i++);
543
544         if (i) {
545                 sprintf(fmt, ".%%0%d"PRIu64, i * 3);
546                 f = fract + sprintf(fract, fmt, x % divisor[i]) - 1;
547
548                 while (f >= fract && strchr("0.", *f))
549                         *f-- = 0;
550         }
551
552         p = prefix + i;
553
554         return g_strdup_printf("%" PRIu64 "%s %.1s%s", quot, fract, p, unit);
555 }
556
557 /**
558  * Convert a numeric samplerate value to its "natural" string representation.
559  *
560  * E.g. a value of 3000000 would be converted to "3 MHz", 20000 to "20 kHz",
561  * 31500 would become "31.5 kHz".
562  *
563  * @param samplerate The samplerate in Hz.
564  *
565  * @return A newly allocated string representation of the samplerate value,
566  *         or NULL upon errors. The caller is responsible to g_free() the
567  *         memory.
568  *
569  * @since 0.1.0
570  */
571 SR_API char *sr_samplerate_string(uint64_t samplerate)
572 {
573         return sr_si_string_u64(samplerate, "Hz");
574 }
575
576 /**
577  * Convert a numeric period value to the "natural" string representation
578  * of its period value.
579  *
580  * The period is specified as a rational number's numerator and denominator.
581  *
582  * E.g. a pair of (1, 5) would be converted to "200 ms", (10, 100) to "100 ms".
583  *
584  * @param v_p The period numerator.
585  * @param v_q The period denominator.
586  *
587  * @return A newly allocated string representation of the period value,
588  *         or NULL upon errors. The caller is responsible to g_free() the
589  *         memory.
590  *
591  * @since 0.5.0
592  */
593 SR_API char *sr_period_string(uint64_t v_p, uint64_t v_q)
594 {
595         double freq, v;
596         int prec;
597
598         freq = 1 / ((double)v_p / v_q);
599
600         if (freq > SR_GHZ(1)) {
601                 v = (double)v_p / v_q * 1000000000000.0;
602                 prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
603                 return g_strdup_printf("%.*f ps", prec, v);
604         } else if (freq > SR_MHZ(1)) {
605                 v = (double)v_p / v_q * 1000000000.0;
606                 prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
607                 return g_strdup_printf("%.*f ns", prec, v);
608         } else if (freq > SR_KHZ(1)) {
609                 v = (double)v_p / v_q * 1000000.0;
610                 prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
611                 return g_strdup_printf("%.*f us", prec, v);
612         } else if (freq > 1) {
613                 v = (double)v_p / v_q * 1000.0;
614                 prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
615                 return g_strdup_printf("%.*f ms", prec, v);
616         } else {
617                 v = (double)v_p / v_q;
618                 prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
619                 return g_strdup_printf("%.*f s", prec, v);
620         }
621 }
622
623 /**
624  * Convert a numeric voltage value to the "natural" string representation
625  * of its voltage value. The voltage is specified as a rational number's
626  * numerator and denominator.
627  *
628  * E.g. a value of 300000 would be converted to "300mV", 2 to "2V".
629  *
630  * @param v_p The voltage numerator.
631  * @param v_q The voltage denominator.
632  *
633  * @return A newly allocated string representation of the voltage value,
634  *         or NULL upon errors. The caller is responsible to g_free() the
635  *         memory.
636  *
637  * @since 0.2.0
638  */
639 SR_API char *sr_voltage_string(uint64_t v_p, uint64_t v_q)
640 {
641         if (v_q == 1000)
642                 return g_strdup_printf("%" PRIu64 " mV", v_p);
643         else if (v_q == 1)
644                 return g_strdup_printf("%" PRIu64 " V", v_p);
645         else
646                 return g_strdup_printf("%g V", (float)v_p / (float)v_q);
647 }
648
649 /**
650  * Convert a "natural" string representation of a size value to uint64_t.
651  *
652  * E.g. a value of "3k" or "3 K" would be converted to 3000, a value
653  * of "15M" would be converted to 15000000.
654  *
655  * Value representations other than decimal (such as hex or octal) are not
656  * supported. Only 'k' (kilo), 'm' (mega), 'g' (giga) suffixes are supported.
657  * Spaces (but not other whitespace) between value and suffix are allowed.
658  *
659  * @param sizestring A string containing a (decimal) size value.
660  * @param size Pointer to uint64_t which will contain the string's size value.
661  *
662  * @return SR_OK upon success, SR_ERR upon errors.
663  *
664  * @since 0.1.0
665  */
666 SR_API int sr_parse_sizestring(const char *sizestring, uint64_t *size)
667 {
668         uint64_t multiplier;
669         int done;
670         double frac_part;
671         char *s;
672
673         *size = strtoull(sizestring, &s, 10);
674         multiplier = 0;
675         frac_part = 0;
676         done = FALSE;
677         while (s && *s && multiplier == 0 && !done) {
678                 switch (*s) {
679                 case ' ':
680                         break;
681                 case '.':
682                         frac_part = g_ascii_strtod(s, &s);
683                         break;
684                 case 'k':
685                 case 'K':
686                         multiplier = SR_KHZ(1);
687                         break;
688                 case 'm':
689                 case 'M':
690                         multiplier = SR_MHZ(1);
691                         break;
692                 case 'g':
693                 case 'G':
694                         multiplier = SR_GHZ(1);
695                         break;
696                 case 't':
697                 case 'T':
698                         multiplier = SR_GHZ(1000);
699                         break;
700                 case 'p':
701                 case 'P':
702                         multiplier = SR_GHZ(1000 * 1000);
703                         break;
704                 case 'e':
705                 case 'E':
706                         multiplier = SR_GHZ(1000 * 1000 * 1000);
707                         break;
708                 default:
709                         done = TRUE;
710                         s--;
711                 }
712                 s++;
713         }
714         if (multiplier > 0) {
715                 *size *= multiplier;
716                 *size += frac_part * multiplier;
717         } else {
718                 *size += frac_part;
719         }
720
721         if (s && *s && g_ascii_strcasecmp(s, "Hz"))
722                 return SR_ERR;
723
724         return SR_OK;
725 }
726
727 /**
728  * Convert a "natural" string representation of a time value to an
729  * uint64_t value in milliseconds.
730  *
731  * E.g. a value of "3s" or "3 s" would be converted to 3000, a value
732  * of "15ms" would be converted to 15.
733  *
734  * Value representations other than decimal (such as hex or octal) are not
735  * supported. Only lower-case "s" and "ms" time suffixes are supported.
736  * Spaces (but not other whitespace) between value and suffix are allowed.
737  *
738  * @param timestring A string containing a (decimal) time value.
739  * @return The string's time value as uint64_t, in milliseconds.
740  *
741  * @todo Add support for "m" (minutes) and others.
742  * @todo Add support for picoseconds?
743  * @todo Allow both lower-case and upper-case? If no, document it.
744  *
745  * @since 0.1.0
746  */
747 SR_API uint64_t sr_parse_timestring(const char *timestring)
748 {
749         uint64_t time_msec;
750         char *s;
751
752         /* TODO: Error handling, logging. */
753
754         time_msec = strtoull(timestring, &s, 10);
755         if (time_msec == 0 && s == timestring)
756                 return 0;
757
758         if (s && *s) {
759                 while (*s == ' ')
760                         s++;
761                 if (!strcmp(s, "s"))
762                         time_msec *= 1000;
763                 else if (!strcmp(s, "ms"))
764                         ; /* redundant */
765                 else
766                         return 0;
767         }
768
769         return time_msec;
770 }
771
772 /** @since 0.1.0 */
773 SR_API gboolean sr_parse_boolstring(const char *boolstr)
774 {
775         /*
776          * Complete absence of an input spec is assumed to mean TRUE,
777          * as in command line option strings like this:
778          *   ...:samplerate=100k:header:numchannels=4:...
779          */
780         if (!boolstr || !*boolstr)
781                 return TRUE;
782
783         if (!g_ascii_strncasecmp(boolstr, "true", 4) ||
784             !g_ascii_strncasecmp(boolstr, "yes", 3) ||
785             !g_ascii_strncasecmp(boolstr, "on", 2) ||
786             !g_ascii_strncasecmp(boolstr, "1", 1))
787                 return TRUE;
788
789         return FALSE;
790 }
791
792 /** @since 0.2.0 */
793 SR_API int sr_parse_period(const char *periodstr, uint64_t *p, uint64_t *q)
794 {
795         char *s;
796
797         *p = strtoull(periodstr, &s, 10);
798         if (*p == 0 && s == periodstr)
799                 /* No digits found. */
800                 return SR_ERR_ARG;
801
802         if (s && *s) {
803                 while (*s == ' ')
804                         s++;
805                 if (!strcmp(s, "fs"))
806                         *q = UINT64_C(1000000000000000);
807                 else if (!strcmp(s, "ps"))
808                         *q = UINT64_C(1000000000000);
809                 else if (!strcmp(s, "ns"))
810                         *q = UINT64_C(1000000000);
811                 else if (!strcmp(s, "us"))
812                         *q = 1000000;
813                 else if (!strcmp(s, "ms"))
814                         *q = 1000;
815                 else if (!strcmp(s, "s"))
816                         *q = 1;
817                 else
818                         /* Must have a time suffix. */
819                         return SR_ERR_ARG;
820         }
821
822         return SR_OK;
823 }
824
825 /** @since 0.2.0 */
826 SR_API int sr_parse_voltage(const char *voltstr, uint64_t *p, uint64_t *q)
827 {
828         char *s;
829
830         *p = strtoull(voltstr, &s, 10);
831         if (*p == 0 && s == voltstr)
832                 /* No digits found. */
833                 return SR_ERR_ARG;
834
835         if (s && *s) {
836                 while (*s == ' ')
837                         s++;
838                 if (!g_ascii_strcasecmp(s, "mv"))
839                         *q = 1000L;
840                 else if (!g_ascii_strcasecmp(s, "v"))
841                         *q = 1;
842                 else
843                         /* Must have a base suffix. */
844                         return SR_ERR_ARG;
845         }
846
847         return SR_OK;
848 }
849
850 /** @} */