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