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