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