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