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