strutil.c 15.4 KB
Newer Older
1
/*
2
 * This file is part of the libsigrok project.
3 4 5 6 7 8 9 10 11 12 13 14 15 16
 *
 * Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * 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
#include <config.h>
21 22 23
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
24
#include <strings.h>
25
#include <errno.h>
26
#include <stdbool.h>
27
#include <libsigrok/libsigrok.h>
28
#include "libsigrok-internal.h"
29

30
/** @cond PRIVATE */
31
#define LOG_PREFIX "strutil"
32
/** @endcond */
33

34 35 36 37 38 39
/**
 * @file
 *
 * Helper functions for handling or converting libsigrok-related strings.
 */

40 41 42 43 44 45 46 47
/**
 * @defgroup grp_strutil String utilities
 *
 * Helper functions for handling or converting libsigrok-related strings.
 *
 * @{
 */

48
/**
49 50
 * @private
 *
51
 * Convert a string representation of a numeric value (base 10) to a long integer. The
52 53 54 55 56 57 58
 * conversion is strict and will fail if the complete string does not represent
 * a valid long integer. The function sets errno according to the details of the
 * failure.
 *
 * @param str The string representation to convert.
 * @param ret Pointer to long where the result of the conversion will be stored.
 *
59 60
 * @retval SR_OK Conversion successful.
 * @retval SR_ERR Failure.
61
 */
62
SR_PRIV int sr_atol(const char *str, long *ret)
63 64 65 66 67
{
	long tmp;
	char *endptr = NULL;

	errno = 0;
68
	tmp = strtol(str, &endptr, 10);
69 70 71 72 73 74 75 76 77 78 79 80

	if (!endptr || *endptr || errno) {
		if (!errno)
			errno = EINVAL;
		return SR_ERR;
	}

	*ret = tmp;
	return SR_OK;
}

/**
81 82
 * @private
 *
83
 * Convert a string representation of a numeric value (base 10) to an integer. The
84 85 86 87 88 89 90
 * conversion is strict and will fail if the complete string does not represent
 * a valid integer. The function sets errno according to the details of the
 * failure.
 *
 * @param str The string representation to convert.
 * @param ret Pointer to int where the result of the conversion will be stored.
 *
91 92
 * @retval SR_OK Conversion successful.
 * @retval SR_ERR Failure.
93
 */
94
SR_PRIV int sr_atoi(const char *str, int *ret)
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
{
	long tmp;

	if (sr_atol(str, &tmp) != SR_OK)
		return SR_ERR;

	if ((int) tmp != tmp) {
		errno = ERANGE;
		return SR_ERR;
	}

	*ret = (int) tmp;
	return SR_OK;
}

/**
111 112
 * @private
 *
113 114 115 116 117 118 119 120
 * Convert a string representation of a numeric value to a double. The
 * conversion is strict and will fail if the complete string does not represent
 * a valid double. The function sets errno according to the details of the
 * failure.
 *
 * @param str The string representation to convert.
 * @param ret Pointer to double where the result of the conversion will be stored.
 *
121 122
 * @retval SR_OK Conversion successful.
 * @retval SR_ERR Failure.
123
 */
124
SR_PRIV int sr_atod(const char *str, double *ret)
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
{
	double tmp;
	char *endptr = NULL;

	errno = 0;
	tmp = strtof(str, &endptr);

	if (!endptr || *endptr || errno) {
		if (!errno)
			errno = EINVAL;
		return SR_ERR;
	}

	*ret = tmp;
	return SR_OK;
}

/**
143 144
 * @private
 *
145 146 147 148 149 150 151 152
 * Convert a string representation of a numeric value to a float. The
 * conversion is strict and will fail if the complete string does not represent
 * a valid float. The function sets errno according to the details of the
 * failure.
 *
 * @param str The string representation to convert.
 * @param ret Pointer to float where the result of the conversion will be stored.
 *
153 154
 * @retval SR_OK Conversion successful.
 * @retval SR_ERR Failure.
155
 */
156
SR_PRIV int sr_atof(const char *str, float *ret)
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
{
	double tmp;

	if (sr_atod(str, &tmp) != SR_OK)
		return SR_ERR;

	if ((float) tmp != tmp) {
		errno = ERANGE;
		return SR_ERR;
	}

	*ret = (float) tmp;
	return SR_OK;
}

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
/**
 * @private
 *
 * Convert a string representation of a numeric value to a double. The
 * conversion is strict and will fail if the complete string does not represent
 * a valid double. 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 double where the result of the conversion will be stored.
 *
 * @retval SR_OK Conversion successful.
 * @retval SR_ERR Failure.
 */
SR_PRIV int sr_atod_ascii(const char *str, double *ret)
{
	double tmp;
	char *endptr = NULL;

	errno = 0;
	tmp = g_ascii_strtod(str, &endptr);

	if (!endptr || *endptr || errno) {
		if (!errno)
			errno = EINVAL;
		return SR_ERR;
	}

	*ret = tmp;
	return SR_OK;
}

204 205 206 207 208 209 210 211 212 213 214
/**
 * @private
 *
 * Convert a string representation of a numeric value to a float. The
 * conversion is strict and will fail if the complete string does not represent
 * a valid float. 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 float where the result of the conversion will be stored.
 *
215 216
 * @retval SR_OK Conversion successful.
 * @retval SR_ERR Failure.
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
 */
SR_PRIV int sr_atof_ascii(const char *str, float *ret)
{
	double tmp;
	char *endptr = NULL;

	errno = 0;
	tmp = g_ascii_strtod(str, &endptr);

	if (!endptr || *endptr || errno) {
		if (!errno)
			errno = EINVAL;
		return SR_ERR;
	}

	/* FIXME This fails unexpectedly. Some other method to safel downcast
	 * needs to be found. Checking against FLT_MAX doesn't work as well. */
	/*
	if ((float) tmp != tmp) {
		errno = ERANGE;
		sr_dbg("ERANGEEEE %e != %e", (float) tmp, tmp);
		return SR_ERR;
	}
	*/

	*ret = (float) tmp;
	return SR_OK;
}

246
/**
247 248 249 250 251
 * 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.
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
 *
 * @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;
269
	bool is_negative = false;
270 271 272 273

	errno = 0;
	integral = g_ascii_strtoll(str, &endptr, 10);

274 275 276
	if (str == endptr && (str[0] == '-' || str[0] == '+') && str[1] == '.')
		endptr += 1;
	else if (errno)
277 278
		return SR_ERR;

279
	if (integral < 0 || str[0] == '-')
280
		is_negative = true;
281

282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
	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;

303
	if (!is_negative)
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
		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;
}

324
/**
Uwe Hermann's avatar
Uwe Hermann committed
325 326
 * Convert a numeric value value to its "natural" string representation
 * in SI units.
327
 *
328 329
 * E.g. a value of 3000000, with units set to "W", would be converted
 * to "3 MW", 20000 to "20 kW", 31500 would become "31.5 kW".
330
 *
331 332 333
 * @param x The value to convert.
 * @param unit The unit to append to the string, or NULL if the string
 *             has no units.
334
 *
335
 * @return A newly allocated string representation of the samplerate value,
336 337
 *         or NULL upon errors. The caller is responsible to g_free() the
 *         memory.
338 339
 *
 * @since 0.2.0
340
 */
341
SR_API char *sr_si_string_u64(uint64_t x, const char *unit)
342
{
343 344
	uint8_t i;
	uint64_t quot, divisor[] = {
Uwe Hermann's avatar
Uwe Hermann committed
345 346
		SR_HZ(1), SR_KHZ(1), SR_MHZ(1), SR_GHZ(1),
		SR_GHZ(1000), SR_GHZ(1000 * 1000), SR_GHZ(1000 * 1000 * 1000),
347 348
	};
	const char *p, prefix[] = "\0kMGTPE";
349
	char fmt[16], fract[20] = "", *f;
350

351
	if (!unit)
352
		unit = "";
353

354 355 356
	for (i = 0; (quot = x / divisor[i]) >= 1000; i++);

	if (i) {
357
		sprintf(fmt, ".%%0%d"PRIu64, i * 3);
358 359 360 361
		f = fract + sprintf(fract, fmt, x % divisor[i]) - 1;

		while (f >= fract && strchr("0.", *f))
			*f-- = 0;
362
	}
363

364 365 366
	p = prefix + i;

	return g_strdup_printf("%" PRIu64 "%s %.1s%s", quot, fract, p, unit);
367
}
368

369 370 371 372 373 374 375 376
/**
 * Convert a numeric samplerate value to its "natural" string representation.
 *
 * E.g. a value of 3000000 would be converted to "3 MHz", 20000 to "20 kHz",
 * 31500 would become "31.5 kHz".
 *
 * @param samplerate The samplerate in Hz.
 *
377
 * @return A newly allocated string representation of the samplerate value,
378 379
 *         or NULL upon errors. The caller is responsible to g_free() the
 *         memory.
380 381
 *
 * @since 0.1.0
382 383 384 385
 */
SR_API char *sr_samplerate_string(uint64_t samplerate)
{
	return sr_si_string_u64(samplerate, "Hz");
386
}
387 388

/**
389 390
 * Convert a numeric period value to the "natural" string representation
 * of its period value.
391
 *
392
 * The period is specified as a rational number's numerator and denominator.
393
 *
394
 * E.g. a pair of (1, 5) would be converted to "200 ms", (10, 100) to "100 ms".
395
 *
396 397 398 399
 * @param v_p The period numerator.
 * @param v_q The period denominator.
 *
 * @return A newly allocated string representation of the period value,
400 401
 *         or NULL upon errors. The caller is responsible to g_free() the
 *         memory.
402
 *
403
 * @since 0.5.0
404
 */
Soeren Apel's avatar
Soeren Apel committed
405
SR_API char *sr_period_string(uint64_t v_p, uint64_t v_q)
406
{
Soeren Apel's avatar
Soeren Apel committed
407
	double freq, v;
408
	int prec;
Soeren Apel's avatar
Soeren Apel committed
409 410

	freq = 1 / ((double)v_p / v_q);
411

Soeren Apel's avatar
Soeren Apel committed
412 413 414
	if (freq > SR_GHZ(1)) {
		v = (double)v_p / v_q * 1000000000000.0;
		prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
415
		return g_strdup_printf("%.*f ps", prec, v);
Soeren Apel's avatar
Soeren Apel committed
416 417 418
	} else if (freq > SR_MHZ(1)) {
		v = (double)v_p / v_q * 1000000000.0;
		prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
419
		return g_strdup_printf("%.*f ns", prec, v);
Soeren Apel's avatar
Soeren Apel committed
420 421 422
	} else if (freq > SR_KHZ(1)) {
		v = (double)v_p / v_q * 1000000.0;
		prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
423
		return g_strdup_printf("%.*f us", prec, v);
Soeren Apel's avatar
Soeren Apel committed
424 425 426
	} else if (freq > 1) {
		v = (double)v_p / v_q * 1000.0;
		prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
427
		return g_strdup_printf("%.*f ms", prec, v);
Soeren Apel's avatar
Soeren Apel committed
428 429 430
	} else {
		v = (double)v_p / v_q;
		prec = ((v - (uint64_t)v) < FLT_MIN) ? 0 : 3;
431
		return g_strdup_printf("%.*f s", prec, v);
432 433
	}
}
434

435
/**
436 437 438
 * Convert a numeric voltage value to the "natural" string representation
 * of its voltage value. The voltage is specified as a rational number's
 * numerator and denominator.
439 440 441
 *
 * E.g. a value of 300000 would be converted to "300mV", 2 to "2V".
 *
442 443
 * @param v_p The voltage numerator.
 * @param v_q The voltage denominator.
444
 *
445
 * @return A newly allocated string representation of the voltage value,
446 447
 *         or NULL upon errors. The caller is responsible to g_free() the
 *         memory.
448 449
 *
 * @since 0.2.0
450
 */
451
SR_API char *sr_voltage_string(uint64_t v_p, uint64_t v_q)
452
{
453
	if (v_q == 1000)
454
		return g_strdup_printf("%" PRIu64 " mV", v_p);
455
	else if (v_q == 1)
456
		return g_strdup_printf("%" PRIu64 " V", v_p);
457
	else
458
		return g_strdup_printf("%g V", (float)v_p / (float)v_q);
459 460
}

461 462 463 464 465 466 467 468 469 470 471
/**
 * Convert a "natural" string representation of a size value to uint64_t.
 *
 * E.g. a value of "3k" or "3 K" would be converted to 3000, a value
 * of "15M" would be converted to 15000000.
 *
 * Value representations other than decimal (such as hex or octal) are not
 * supported. Only 'k' (kilo), 'm' (mega), 'g' (giga) suffixes are supported.
 * Spaces (but not other whitespace) between value and suffix are allowed.
 *
 * @param sizestring A string containing a (decimal) size value.
472
 * @param size Pointer to uint64_t which will contain the string's size value.
473
 *
474
 * @return SR_OK upon success, SR_ERR upon errors.
475 476
 *
 * @since 0.1.0
477
 */
478
SR_API int sr_parse_sizestring(const char *sizestring, uint64_t *size)
479
{
480
	int multiplier, done;
481
	double frac_part;
482 483
	char *s;

484
	*size = strtoull(sizestring, &s, 10);
485
	multiplier = 0;
486
	frac_part = 0;
487 488
	done = FALSE;
	while (s && *s && multiplier == 0 && !done) {
489 490 491
		switch (*s) {
		case ' ':
			break;
492 493 494
		case '.':
			frac_part = g_ascii_strtod(s, &s);
			break;
495 496
		case 'k':
		case 'K':
497
			multiplier = SR_KHZ(1);
498 499 500
			break;
		case 'm':
		case 'M':
501
			multiplier = SR_MHZ(1);
502 503 504
			break;
		case 'g':
		case 'G':
505
			multiplier = SR_GHZ(1);
506 507
			break;
		default:
508 509
			done = TRUE;
			s--;
510 511 512
		}
		s++;
	}
513
	if (multiplier > 0) {
514
		*size *= multiplier;
515 516 517
		*size += frac_part * multiplier;
	} else
		*size += frac_part;
518

519
	if (s && *s && g_ascii_strcasecmp(s, "Hz"))
520 521 522
		return SR_ERR;

	return SR_OK;
523 524
}

525 526 527 528 529 530 531 532 533 534 535 536 537 538
/**
 * Convert a "natural" string representation of a time value to an
 * uint64_t value in milliseconds.
 *
 * E.g. a value of "3s" or "3 s" would be converted to 3000, a value
 * of "15ms" would be converted to 15.
 *
 * Value representations other than decimal (such as hex or octal) are not
 * supported. Only lower-case "s" and "ms" time suffixes are supported.
 * Spaces (but not other whitespace) between value and suffix are allowed.
 *
 * @param timestring A string containing a (decimal) time value.
 * @return The string's time value as uint64_t, in milliseconds.
 *
539 540 541
 * @todo Add support for "m" (minutes) and others.
 * @todo Add support for picoseconds?
 * @todo Allow both lower-case and upper-case? If no, document it.
542 543
 *
 * @since 0.1.0
544
 */
545
SR_API uint64_t sr_parse_timestring(const char *timestring)
546 547 548 549
{
	uint64_t time_msec;
	char *s;

550 551
	/* TODO: Error handling, logging. */

552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
	time_msec = strtoull(timestring, &s, 10);
	if (time_msec == 0 && s == timestring)
		return 0;

	if (s && *s) {
		while (*s == ' ')
			s++;
		if (!strcmp(s, "s"))
			time_msec *= 1000;
		else if (!strcmp(s, "ms"))
			; /* redundant */
		else
			return 0;
	}

	return time_msec;
}
569

570
/** @since 0.1.0 */
571
SR_API gboolean sr_parse_boolstring(const char *boolstr)
572
{
573 574 575 576 577 578 579
	/*
	 * 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;
580

581 582 583 584
	if (!g_ascii_strncasecmp(boolstr, "true", 4) ||
	    !g_ascii_strncasecmp(boolstr, "yes", 3) ||
	    !g_ascii_strncasecmp(boolstr, "on", 2) ||
	    !g_ascii_strncasecmp(boolstr, "1", 1))
585 586 587 588
		return TRUE;

	return FALSE;
}
Bert Vermeulen's avatar
Bert Vermeulen committed
589

590
/** @since 0.2.0 */
591
SR_API int sr_parse_period(const char *periodstr, uint64_t *p, uint64_t *q)
Bert Vermeulen's avatar
Bert Vermeulen committed
592 593 594
{
	char *s;

595 596
	*p = strtoull(periodstr, &s, 10);
	if (*p == 0 && s == periodstr)
Bert Vermeulen's avatar
Bert Vermeulen committed
597 598 599 600 601 602
		/* No digits found. */
		return SR_ERR_ARG;

	if (s && *s) {
		while (*s == ' ')
			s++;
603
		if (!strcmp(s, "fs"))
604
			*q = 1000000000000000ULL;
605
		else if (!strcmp(s, "ps"))
606
			*q = 1000000000000ULL;
607
		else if (!strcmp(s, "ns"))
608
			*q = 1000000000ULL;
Bert Vermeulen's avatar
Bert Vermeulen committed
609
		else if (!strcmp(s, "us"))
610
			*q = 1000000;
Bert Vermeulen's avatar
Bert Vermeulen committed
611
		else if (!strcmp(s, "ms"))
612
			*q = 1000;
Bert Vermeulen's avatar
Bert Vermeulen committed
613
		else if (!strcmp(s, "s"))
614
			*q = 1;
Bert Vermeulen's avatar
Bert Vermeulen committed
615 616 617 618 619 620 621 622
		else
			/* Must have a time suffix. */
			return SR_ERR_ARG;
	}

	return SR_OK;
}

623
/** @since 0.2.0 */
624
SR_API int sr_parse_voltage(const char *voltstr, uint64_t *p, uint64_t *q)
625 626 627
{
	char *s;

628 629
	*p = strtoull(voltstr, &s, 10);
	if (*p == 0 && s == voltstr)
630 631 632 633 634 635
		/* No digits found. */
		return SR_ERR_ARG;

	if (s && *s) {
		while (*s == ' ')
			s++;
636
		if (!g_ascii_strcasecmp(s, "mv"))
637
			*q = 1000L;
638
		else if (!g_ascii_strcasecmp(s, "v"))
639
			*q = 1;
640 641 642 643 644 645 646 647
		else
			/* Must have a base suffix. */
			return SR_ERR_ARG;
	}

	return SR_OK;
}

648
/** @} */