]> sigrok.org Git - libsigrok.git/blobdiff - src/strutil.c
drivers: Add and use STD_CONFIG_LIST().
[libsigrok.git] / src / strutil.c
index 54dc1f39df50123ac985a158bc3d9ca425f9f5a6..382708936244ba9a3f0b2aa0972c66fab12177c2 100644 (file)
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <strings.h>
 #include <errno.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 /** @cond PRIVATE */
@@ -56,8 +57,6 @@
  *
  * @retval SR_OK Conversion successful.
  * @retval SR_ERR Failure.
- *
- * @since 0.3.0
  */
 SR_PRIV int sr_atol(const char *str, long *ret)
 {
@@ -90,8 +89,6 @@ SR_PRIV int sr_atol(const char *str, long *ret)
  *
  * @retval SR_OK Conversion successful.
  * @retval SR_ERR Failure.
- *
- * @since 0.3.0
  */
 SR_PRIV int sr_atoi(const char *str, int *ret)
 {
@@ -122,8 +119,6 @@ SR_PRIV int sr_atoi(const char *str, int *ret)
  *
  * @retval SR_OK Conversion successful.
  * @retval SR_ERR Failure.
- *
- * @since 0.3.0
  */
 SR_PRIV int sr_atod(const char *str, double *ret)
 {
@@ -156,8 +151,6 @@ SR_PRIV int sr_atod(const char *str, double *ret)
  *
  * @retval SR_OK Conversion successful.
  * @retval SR_ERR Failure.
- *
- * @since 0.3.0
  */
 SR_PRIV int sr_atof(const char *str, float *ret)
 {
@@ -188,8 +181,6 @@ SR_PRIV int sr_atof(const char *str, float *ret)
  *
  * @retval SR_OK Conversion successful.
  * @retval SR_ERR Failure.
- *
- * @since 0.3.0
  */
 SR_PRIV int sr_atof_ascii(const char *str, float *ret)
 {
@@ -219,6 +210,78 @@ SR_PRIV int sr_atof_ascii(const char *str, float *ret)
        return SR_OK;
 }
 
+/**
+ * Convert a string representation of a numeric value to a sr_rational.
+ *
+ * The conversion is strict and will fail if the complete string does not
+ * represent a valid number. The function sets errno according to the details
+ * of the failure. This version ignores the locale.
+ *
+ * @param str The string representation to convert.
+ * @param ret Pointer to sr_rational where the result of the conversion will be stored.
+ *
+ * @retval SR_OK Conversion successful.
+ * @retval SR_ERR Failure.
+ *
+ * @since 0.5.0
+ */
+SR_API int sr_parse_rational(const char *str, struct sr_rational *ret)
+{
+       char *endptr = NULL;
+       int64_t integral;
+       int64_t fractional = 0;
+       int64_t denominator = 1;
+       int32_t fractional_len = 0;
+       int32_t exponent = 0;
+
+       errno = 0;
+       integral = g_ascii_strtoll(str, &endptr, 10);
+
+       if (errno)
+               return SR_ERR;
+
+       if (*endptr == '.') {
+               const char* start = endptr + 1;
+               fractional = g_ascii_strtoll(start, &endptr, 10);
+               if (errno)
+                       return SR_ERR;
+               fractional_len = endptr - start;
+       }
+
+       if ((*endptr == 'E') || (*endptr == 'e')) {
+               exponent = g_ascii_strtoll(endptr + 1, &endptr, 10);
+               if (errno)
+                       return SR_ERR;
+       }
+
+       if (*endptr != '\0')
+               return SR_ERR;
+
+       for (int i = 0; i < fractional_len; i++)
+               integral *= 10;
+       exponent -= fractional_len;
+
+       if (integral >= 0)
+               integral += fractional;
+       else
+               integral -= fractional;
+
+       while (exponent > 0) {
+               integral *= 10;
+               exponent--;
+       }
+
+       while (exponent < 0) {
+               denominator *= 10;
+               exponent++;
+       }
+
+       ret->p = integral;
+       ret->q = denominator;
+
+       return SR_OK;
+}
+
 /**
  * Convert a numeric value value to its "natural" string representation
  * in SI units.
@@ -230,7 +293,7 @@ SR_PRIV int sr_atof_ascii(const char *str, float *ret)
  * @param unit The unit to append to the string, or NULL if the string
  *             has no units.
  *
- * @return A g_try_malloc()ed string representation of the samplerate value,
+ * @return A newly allocated string representation of the samplerate value,
  *         or NULL upon errors. The caller is responsible to g_free() the
  *         memory.
  *
@@ -246,7 +309,7 @@ SR_API char *sr_si_string_u64(uint64_t x, const char *unit)
        const char *p, prefix[] = "\0kMGTPE";
        char fmt[16], fract[20] = "", *f;
 
-       if (unit == NULL)
+       if (!unit)
                unit = "";
 
        for (i = 0; (quot = x / divisor[i]) >= 1000; i++);
@@ -272,7 +335,7 @@ SR_API char *sr_si_string_u64(uint64_t x, const char *unit)
  *
  * @param samplerate The samplerate in Hz.
  *
- * @return A g_try_malloc()ed string representation of the samplerate value,
+ * @return A newly allocated string representation of the samplerate value,
  *         or NULL upon errors. The caller is responsible to g_free() the
  *         memory.
  *
@@ -284,39 +347,54 @@ SR_API char *sr_samplerate_string(uint64_t samplerate)
 }
 
 /**
- * Convert a numeric frequency value to the "natural" string representation
- * of its period.
+ * Convert a numeric period value to the "natural" string representation
+ * of its period value.
+ *
+ * The period is specified as a rational number's numerator and denominator.
  *
- * E.g. a value of 3000000 would be converted to "3 us", 20000 to "50 ms".
+ * E.g. a pair of (1, 5) would be converted to "200 ms", (10, 100) to "100 ms".
  *
- * @param frequency The frequency in Hz.
+ * @param v_p The period numerator.
+ * @param v_q The period denominator.
  *
- * @return A g_try_malloc()ed string representation of the frequency value,
+ * @return A newly allocated string representation of the period value,
  *         or NULL upon errors. The caller is responsible to g_free() the
  *         memory.
  *
- * @since 0.1.0
+ * @since 0.5.0
  */
-SR_API char *sr_period_string(uint64_t frequency)
+SR_API char *sr_period_string(uint64_t v_p, uint64_t v_q)
 {
+       double freq, v;
        char *o;
-       int r;
-
-       /* Allocate enough for a uint64_t as string + " ms". */
-       if (!(o = g_try_malloc0(30 + 1))) {
-               sr_err("%s: o malloc failed", __func__);
-               return NULL;
+       int prec, r;
+
+       freq = 1 / ((double)v_p / v_q);
+
+       o = g_malloc0(30 + 1);
+
+       if (freq > SR_GHZ(1)) {
+               v = (double)v_p / v_q * 1000000000000.0;
+               prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
+               r = snprintf(o, 30, "%.*f ps", prec, v);
+       } else if (freq > SR_MHZ(1)) {
+               v = (double)v_p / v_q * 1000000000.0;
+               prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
+               r = snprintf(o, 30, "%.*f ns", prec, v);
+       } else if (freq > SR_KHZ(1)) {
+               v = (double)v_p / v_q * 1000000.0;
+               prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
+               r = snprintf(o, 30, "%.*f us", prec, v);
+       } else if (freq > 1) {
+               v = (double)v_p / v_q * 1000.0;
+               prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
+               r = snprintf(o, 30, "%.*f ms", prec, v);
+       } else {
+               v = (double)v_p / v_q;
+               prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
+               r = snprintf(o, 30, "%.*f s", prec, v);
        }
 
-       if (frequency >= SR_GHZ(1))
-               r = snprintf(o, 30, "%" PRIu64 " ns", frequency / 1000000000);
-       else if (frequency >= SR_MHZ(1))
-               r = snprintf(o, 30, "%" PRIu64 " us", frequency / 1000000);
-       else if (frequency >= SR_KHZ(1))
-               r = snprintf(o, 30, "%" PRIu64 " ms", frequency / 1000);
-       else
-               r = snprintf(o, 30, "%" PRIu64 " s", frequency);
-
        if (r < 0) {
                /* Something went wrong... */
                g_free(o);
@@ -336,7 +414,7 @@ SR_API char *sr_period_string(uint64_t frequency)
  * @param v_p The voltage numerator.
  * @param v_q The voltage denominator.
  *
- * @return A g_try_malloc()ed string representation of the voltage value,
+ * @return A newly allocated string representation of the voltage value,
  *         or NULL upon errors. The caller is responsible to g_free() the
  *         memory.
  *
@@ -347,10 +425,7 @@ SR_API char *sr_voltage_string(uint64_t v_p, uint64_t v_q)
        int r;
        char *o;
 
-       if (!(o = g_try_malloc0(30 + 1))) {
-               sr_err("%s: o malloc failed", __func__);
-               return NULL;
-       }
+       o = g_malloc0(30 + 1);
 
        if (v_q == 1000)
                r = snprintf(o, 30, "%" PRIu64 "mV", v_p);
@@ -426,7 +501,7 @@ SR_API int sr_parse_sizestring(const char *sizestring, uint64_t *size)
        } else
                *size += frac_part;
 
-       if (*s && strcasecmp(s, "Hz"))
+       if (s && *s && g_ascii_strcasecmp(s, "Hz"))
                return SR_ERR;
 
        return SR_OK;
@@ -480,8 +555,13 @@ SR_API uint64_t sr_parse_timestring(const char *timestring)
 /** @since 0.1.0 */
 SR_API gboolean sr_parse_boolstring(const char *boolstr)
 {
-       if (!boolstr)
-               return FALSE;
+       /*
+        * Complete absence of an input spec is assumed to mean TRUE,
+        * as in command line option strings like this:
+        *   ...:samplerate=100k:header:numchannels=4:...
+        */
+       if (!boolstr || !*boolstr)
+               return TRUE;
 
        if (!g_ascii_strncasecmp(boolstr, "true", 4) ||
            !g_ascii_strncasecmp(boolstr, "yes", 3) ||
@@ -538,9 +618,9 @@ SR_API int sr_parse_voltage(const char *voltstr, uint64_t *p, uint64_t *q)
        if (s && *s) {
                while (*s == ' ')
                        s++;
-               if (!strcasecmp(s, "mv"))
+               if (!g_ascii_strcasecmp(s, "mv"))
                        *q = 1000L;
-               else if (!strcasecmp(s, "v"))
+               else if (!g_ascii_strcasecmp(s, "v"))
                        *q = 1;
                else
                        /* Must have a base suffix. */