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