All of lore.kernel.org
 help / color / mirror / Atom feed
From: Anastasiia Lukianenko <vicooodin@gmail.com>
To: u-boot@lists.denx.de
Subject: [RESEND PATCH v2 09/18] lib: sscanf: add sscanf implementation
Date: Thu,  6 Aug 2020 12:42:52 +0300	[thread overview]
Message-ID: <20200806094301.4999-10-vicooodin@gmail.com> (raw)
In-Reply-To: <20200806094301.4999-1-vicooodin@gmail.com>

From: Andrii Anisov <andrii_anisov@epam.com>

Port sscanf implementation from mini-os and introduce new
Kconfig option to enable it: CONFIG_SSCANF. Disable by default.

Signed-off-by: Andrii Anisov <andrii_anisov@epam.com>
Signed-off-by: Anastasiia Lukianenko <anastasiia_lukianenko@epam.com>
Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
---

Changes since v1:
 - provide a test in /tests/lib
 - reduce the code size
 - add comments
 - correct code style

 include/vsprintf.h |   8 +
 lib/Kconfig        |   4 +
 lib/Makefile       |   1 +
 lib/sscanf.c       | 823 +++++++++++++++++++++++++++++++++++++++++++++
 test/lib/Makefile  |   1 +
 test/lib/sscanf.c  | 174 ++++++++++
 6 files changed, 1011 insertions(+)
 create mode 100644 lib/sscanf.c
 create mode 100644 test/lib/sscanf.c

diff --git a/include/vsprintf.h b/include/vsprintf.h
index d9fb68add0..2290083eba 100644
--- a/include/vsprintf.h
+++ b/include/vsprintf.h
@@ -234,4 +234,12 @@ char *strmhz(char *buf, unsigned long hz);
  */
 void str_to_upper(const char *in, char *out, size_t len);
 
+/**
+ * sscanf - Unformat a buffer into a list of arguments
+ * @buf:	input buffer
+ * @fmt:	formatting of buffer
+ * @...:	resulting arguments
+ */
+int sscanf(const char *buf, const char *fmt, ...);
+
 #endif
diff --git a/lib/Kconfig b/lib/Kconfig
index 2142bd06e6..efd383ea88 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -80,6 +80,10 @@ config SPL_SPRINTF
 config TPL_SPRINTF
 	bool
 
+config SSCANF
+	bool
+	default n
+
 config STRTO
 	bool
 	default y
diff --git a/lib/Makefile b/lib/Makefile
index 1dc06c57d5..0cd7bea282 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -123,6 +123,7 @@ else
 # Main U-Boot always uses the full printf support
 obj-y += vsprintf.o strto.o
 obj-$(CONFIG_OID_REGISTRY) += oid_registry.o
+obj-$(CONFIG_SSCANF) += sscanf.o
 endif
 
 obj-y += date.o
diff --git a/lib/sscanf.c b/lib/sscanf.c
new file mode 100644
index 0000000000..d1e2dc272c
--- /dev/null
+++ b/lib/sscanf.c
@@ -0,0 +1,823 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Copyright (c) 2011 The FreeBSD Foundation
+ * All rights reserved.
+ * Portions of this software were developed by David Chisnall
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Author: Juergen Gross <jgross@suse.com>
+ * Date: Jun 2016
+ */
+
+#if !defined HAVE_LIBC
+
+#include <os.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <vsprintf.h>
+#include <linux/string.h>
+#include <malloc.h>
+#define __DECONST(type, var)    ((type)(uintptr_t)(const void *)(var))
+
+/**
+ * struct str_info - Input string parameters
+ * @neg: negative number or not
+ *	 0 - not negative
+ *	 1 - negative
+ * @any: set any if any `digits' consumed; make it negative to indicate
+ *	 overflow
+ * @acc: accumulated value
+ */
+struct str_info {
+	int neg, any;
+	u64 acc;
+};
+
+/**
+ * str_to_int_convert() - Write string data to structure
+ * @nptr: pointer to string
+ * @base: number's base
+ * @unsign: describes what integer is expected
+ *	    0 - not unsigned
+ *	    1 - unsigned
+ *
+ * Ignores `locale' stuff.  Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ *
+ * Return: struct str_info *, which contains string data to future process
+ */
+static struct str_info *
+str_to_int_convert(const char **nptr, int base, unsigned int unsign)
+{
+	const char *s = *nptr;
+	u64 acc;
+	unsigned char c;
+	u64 cutoff;
+	int neg, any, cutlim;
+	u64 qbase;
+	struct str_info *info;
+
+	/*
+	 * Skip white space and pick up leading +/- sign if any.
+	 * If base is 0, allow 0x for hex and 0 for octal, else
+	 * assume decimal; if base is already 16, allow 0x.
+	 */
+	info = (struct str_info *)malloc(sizeof(struct str_info));
+	if (!info)
+		return NULL;
+
+	do {
+		c = *s++;
+	} while (isspace(c));
+	if (c == '-') {
+		neg = 1;
+		c = *s++;
+	} else {
+		neg = 0;
+		if (c == '+')
+			c = *s++;
+	}
+	if ((base == 0 || base == 16) &&
+	    c == '0' && (*s == 'x' || *s == 'X')) {
+		c = s[1];
+		s += 2;
+		base = 16;
+	}
+	if (base == 0)
+		base = c == '0' ? 8 : 10;
+
+	/*
+	 * Compute the cutoff value between legal numbers and illegal
+	 * numbers.  That is the largest legal value, divided by the
+	 * base.  An input number that is greater than this value, if
+	 * followed by a legal input character, is too big.  One that
+	 * is equal to this value may be valid or not; the limit
+	 * between valid and invalid numbers is then based on the last
+	 * digit.  For instance, if the range for quads is
+	 * [-9223372036854775808..9223372036854775807] and the input base
+	 * is 10, cutoff will be set to 922337203685477580 and cutlim to
+	 * either 7 (neg==0) or 8 (neg==1), meaning that if we have
+	 * accumulated a value > 922337203685477580, or equal but the
+	 * next digit is > 7 (or 8), the number is too big, and we will
+	 * return a range error.
+	 *
+	 * Set any if any `digits' consumed; make it negative to indicate
+	 * overflow.
+	 */
+	qbase = (unsigned int)base;
+
+	if (!unsign) {
+		cutoff = neg ? (u64)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX : LLONG_MAX;
+		cutlim = cutoff % qbase;
+		cutoff /= qbase;
+	} else {
+		cutoff = (u64)ULLONG_MAX / qbase;
+		cutlim = (u64)ULLONG_MAX % qbase;
+	}
+
+	for (acc = 0, any = 0;; c = *s++) {
+		if (!isascii(c))
+			break;
+		if (isdigit(c))
+			c -= '0';
+		else if (isalpha(c))
+			c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+		else
+			break;
+		if (c >= base)
+			break;
+		if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) {
+			any = -1;
+		} else {
+			any = 1;
+			acc *= qbase;
+			acc += c;
+		}
+	}
+
+	info->any = any;
+	info->neg = neg;
+	info->acc = acc;
+
+	*nptr = s;
+
+	return info;
+}
+
+/**
+ * strtoq() - Convert a string to a quad integer
+ * @nptr: pointer to string
+ * @endptr: pointer to number's end in the string
+ * @base: number's base
+ *
+ * Return: s64 quad integer number converted from input string
+ */
+static s64
+strtoq(const char *nptr, char **endptr, int base)
+{
+	const char *s = nptr;
+	u64 acc;
+	int unsign = 0;
+	struct str_info *info;
+
+	info = str_to_int_convert(&s, base, unsign);
+	if (!info)
+		return -1;
+
+	acc = info->acc;
+
+	if (info->any < 0)
+		acc = info->neg ? LLONG_MIN : LLONG_MAX;
+	else if (info->neg)
+		acc = -acc;
+	if (endptr != 0)
+		*endptr = __DECONST(char *, info->any ? s - 1 : nptr);
+
+	free(info);
+
+	return acc;
+}
+
+/**
+ * strtouq() - Convert a string to an unsigned quad integer
+ * @nptr: pointer to string
+ * @endptr: pointer to number's end in the string
+ * @base: number's base
+ *
+ * Return: s64 unsigned quad integer number converted from
+ *         input string
+ */
+u64
+strtouq(const char *nptr, char **endptr, int base)
+{
+		const char *s = nptr;
+	u64 acc;
+	int unsign = 1;
+	struct str_info *info;
+
+	info = str_to_int_convert(&s, base, unsign);
+	if (!info)
+		return -1;
+
+	acc = info->acc;
+
+	if (info->any < 0)
+		acc = ULLONG_MAX;
+	else if (info->neg)
+		acc = -acc;
+	if (endptr != 0)
+		*endptr = __DECONST(char *, info->any ? s - 1 : nptr);
+
+	free(info);
+
+	return acc;
+}
+
+/**
+ * __sccl() - Fill in the given table from the scanset at the given format
+ * (just after `[')
+ * @tab: table to fill in
+ * @fmt: format of buffer
+ *
+ * The table has a 1 wherever characters should be considered part of the
+ * scanset.
+ *
+ * Return: pointer to the character past the closing `]'
+ */
+static const u_char *
+__sccl(char *tab, const u_char *fmt)
+{
+	int c, n, v;
+
+	/* first `clear' the whole table */
+	c = *fmt++;             /* first char hat => negated scanset */
+	if (c == '^') {
+		v = 1;          /* default => accept */
+		c = *fmt++;     /* get new first char */
+	} else {
+		v = 0;          /* default => reject */
+	}
+
+	/* XXX: Will not work if sizeof(tab*) > sizeof(char) */
+	for (n = 0; n < 256; n++)
+		tab[n] = v;        /* memset(tab, v, 256) */
+
+	if (c == 0)
+		return (fmt - 1);/* format ended before closing ] */
+
+	/*
+	 * Now set the entries corresponding to the actual scanset
+	 * to the opposite of the above.
+	 *
+	 * The first character may be ']' (or '-') without being special;
+	 * the last character may be '-'.
+	 */
+	v = 1 - v;
+	for (;;) {
+		tab[c] = v;             /* take character c */
+doswitch:
+		n = *fmt++;             /* and examine the next */
+		switch (n) {
+		case 0:                 /* format ended too soon */
+			return (fmt - 1);
+
+		case '-':
+			/*
+			 * A scanset of the form
+			 *      [01+-]
+			 * is defined as `the digit 0, the digit 1,
+			 * the character +, the character -', but
+			 * the effect of a scanset such as
+			 *      [a-zA-Z0-9]
+			 * is implementation defined.  The V7 Unix
+			 * scanf treats `a-z' as `the letters a through
+			 * z', but treats `a-a' as `the letter a, the
+			 * character -, and the letter a'.
+			 *
+			 * For compatibility, the `-' is not considerd
+			 * to define a range if the character following
+			 * it is either a close bracket (required by ANSI)
+			 * or is not numerically greater than the character
+			 * we just stored in the table (c).
+			 */
+			n = *fmt;
+			if (n == ']' || n < c) {
+				c = '-';
+				break;  /* resume the for(;;) */
+			}
+			fmt++;
+			/* fill in the range */
+			do {
+				tab[++c] = v;
+			} while (c < n);
+			c = n;
+			/*
+			 * Alas, the V7 Unix scanf also treats formats
+			 * such as [a-c-e] as `the letters a through e'.
+			 * This too is permitted by the standard....
+			 */
+			goto doswitch;
+			break;
+
+		case ']':               /* end of scanset */
+			return (fmt);
+
+		default:                /* just another character */
+			c = n;
+			break;
+		}
+	}
+	/* NOTREACHED */
+}
+
+/**
+ * vsscanf - Unformat a buffer into a list of arguments
+ * @buf:	input buffer
+ * @fmt:	format of buffer
+ * @args:	arguments
+ */
+#define BUF             32      /* Maximum length of numeric string. */
+
+/*
+ * Flags used during conversion.
+ */
+#define LONG            0x01    /* l: long or double */
+#define SHORT           0x04    /* h: short */
+#define SUPPRESS        0x08    /* suppress assignment */
+#define POINTER         0x10    /* weird %p pointer (`fake hex') */
+#define NOSKIP          0x20    /* do not skip blanks */
+#define QUAD            0x400
+#define SHORTSHORT      0x4000  /** hh: char */
+
+/*
+ * The following are used in numeric conversions only:
+ * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point;
+ * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral.
+ */
+#define SIGNOK          0x40    /* +/- is (still) legal */
+#define NDIGITS         0x80    /* no digits detected */
+
+#define DPTOK           0x100   /* (float) decimal point is still legal */
+#define EXPOK           0x200   /* (float) exponent (e+3, etc) still legal */
+
+#define PFXOK           0x100   /* 0x prefix is (still) legal */
+#define NZDIGITS        0x200   /* no zero digits detected */
+
+/*
+ * Conversion types.
+ */
+#define CT_CHAR         0       /* %c conversion */
+#define CT_CCL          1       /* %[...] conversion */
+#define CT_STRING       2       /* %s conversion */
+#define CT_INT          3       /* integer, i.e., strtoq or strtouq */
+typedef u64 (*ccfntype)(const char *, char **, int);
+
+int
+vsscanf(const char *inp, char const *fmt0, va_list ap)
+{
+	int inr;
+	const u_char *fmt = (const u_char *)fmt0;
+	int c;                  /* character from format, or conversion */
+	size_t width;           /* field width, or 0 */
+	char *p;                /* points into all kinds of strings */
+	int n;                  /* handy integer */
+	int flags;              /* flags as defined above */
+	char *p0;               /* saves original value of p when necessary */
+	int nassigned;          /* number of fields assigned */
+	int nconversions;       /* number of conversions */
+	int nread;              /* number of characters consumed from fp */
+	int base;               /* base argument to strtoq/strtouq */
+	ccfntype ccfn;          /* conversion function (strtoq/strtouq) */
+	char ccltab[256];       /* character class table for %[...] */
+	char buf[BUF];          /* buffer for numeric conversions */
+
+	/* `basefix' is used to avoid `if' tests in the integer scanner */
+	static short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+				     12, 13, 14, 15, 16 };
+
+	inr = strlen(inp);
+
+	nassigned = 0;
+	nconversions = 0;
+	nread = 0;
+	base = 0;               /* XXX just to keep gcc happy */
+	ccfn = NULL;            /* XXX just to keep gcc happy */
+	for (;;) {
+		c = *fmt++;
+		if (c == 0)
+			return (nassigned);
+		if (isspace(c)) {
+			while (inr > 0 && isspace(*inp))
+				nread++, inr--, inp++;
+			continue;
+		}
+		if (c != '%')
+			goto literal;
+		width = 0;
+		flags = 0;
+		/*
+		 * switch on the format.  continue if done;
+		 * break once format type is derived.
+		 */
+again:          c = *fmt++;
+		switch (c) {
+		case '%':
+literal:
+			if (inr <= 0)
+				goto input_failure;
+			if (*inp != c)
+				goto match_failure;
+			inr--, inp++;
+			nread++;
+			continue;
+
+		case '*':
+			flags |= SUPPRESS;
+			goto again;
+		case 'l':
+			if (flags & LONG) {
+				flags &= ~LONG;
+				flags |= QUAD;
+			} else {
+				flags |= LONG;
+			}
+			goto again;
+		case 'q':
+			flags |= QUAD;
+			goto again;
+		case 'h':
+			if (flags & SHORT) {
+				flags &= ~SHORT;
+				flags |= SHORTSHORT;
+			} else {
+				flags |= SHORT;
+			}
+			goto again;
+
+		case '0': case '1': case '2': case '3': case '4':
+		case '5': case '6': case '7': case '8': case '9':
+			width = width * 10 + c - '0';
+			goto again;
+
+		/*
+		 * Conversions.
+		 *
+		 */
+		case 'd':
+			c = CT_INT;
+			ccfn = (ccfntype)strtoq;
+			base = 10;
+			break;
+
+		case 'i':
+			c = CT_INT;
+			ccfn = (ccfntype)strtoq;
+			base = 0;
+			break;
+
+		case 'o':
+			c = CT_INT;
+			ccfn = strtouq;
+			base = 8;
+			break;
+
+		case 'u':
+			c = CT_INT;
+			ccfn = strtouq;
+			base = 10;
+			break;
+
+		case 'x':
+			flags |= PFXOK; /* enable 0x prefixing */
+			c = CT_INT;
+			ccfn = strtouq;
+			base = 16;
+			break;
+
+		case 's':
+			c = CT_STRING;
+			break;
+
+		case '[':
+			fmt = __sccl(ccltab, fmt);
+			flags |= NOSKIP;
+			c = CT_CCL;
+			break;
+
+		case 'c':
+			flags |= NOSKIP;
+			c = CT_CHAR;
+			break;
+
+		case 'p':       /* pointer format is like hex */
+			flags |= POINTER | PFXOK;
+			c = CT_INT;
+			ccfn = strtouq;
+			base = 16;
+			break;
+
+		case 'n':
+			nconversions++;
+			if (flags & SUPPRESS)   /* ??? */
+				continue;
+			if (flags & SHORTSHORT)
+				*va_arg(ap, char *) = nread;
+			else if (flags & SHORT)
+				*va_arg(ap, short *) = nread;
+			else if (flags & LONG)
+				*va_arg(ap, long *) = nread;
+			else if (flags & QUAD)
+				*va_arg(ap, s64 *) = nread;
+			else
+				*va_arg(ap, int *) = nread;
+			continue;
+		}
+
+		/*
+		 * We have a conversion that requires input.
+		 */
+		if (inr <= 0)
+			goto input_failure;
+
+		/*
+		 * Consume leading white space, except for formats
+		 * that suppress this.
+		 */
+		if ((flags & NOSKIP) == 0) {
+			while (isspace(*inp)) {
+				nread++;
+				if (--inr > 0)
+					inp++;
+				else
+					goto input_failure;
+			}
+			/*
+			 * Note that there is at least one character in
+			 * the buffer, so conversions that do not set NOSKIP
+			 * can no longer result in an input failure.
+			 */
+		}
+
+		/*
+		 * Do the conversion.
+		 */
+		switch (c) {
+		case CT_CHAR:
+			/* scan arbitrary characters (sets NOSKIP) */
+			if (width == 0)
+				width = 1;
+			if (flags & SUPPRESS) {
+				size_t sum = 0;
+
+				if ((n = inr) < width) {
+					sum += n;
+					width -= n;
+					inp += n;
+					if (sum == 0)
+						goto input_failure;
+				} else {
+					sum += width;
+					inr -= width;
+					inp += width;
+				}
+				nread += sum;
+			} else {
+				memcpy(va_arg(ap, char *), inp, width);
+				inr -= width;
+				inp += width;
+				nread += width;
+				nassigned++;
+			}
+			nconversions++;
+			break;
+
+		case CT_CCL:
+			/* scan a (nonempty) character class (sets NOSKIP) */
+			if (width == 0)
+				width = (size_t)~0;     /* `infinity' */
+			/* take only those things in the class */
+			if (flags & SUPPRESS) {
+				n = 0;
+				while (ccltab[(unsigned char)*inp]) {
+					n++, inr--, inp++;
+					if (--width == 0)
+						break;
+					if (inr <= 0) {
+						if (n == 0)
+							goto input_failure;
+						break;
+					}
+				}
+				if (n == 0)
+					goto match_failure;
+			} else {
+				p = va_arg(ap, char *);
+				p0 = p;
+				while (ccltab[(unsigned char)*inp]) {
+					inr--;
+					*p++ = *inp++;
+					if (--width == 0)
+						break;
+					if (inr <= 0) {
+						if (p == p0)
+							goto input_failure;
+						break;
+					}
+				}
+				n = p - p0;
+				if (n == 0)
+					goto match_failure;
+				*p = 0;
+				nassigned++;
+			}
+			nread += n;
+			nconversions++;
+			break;
+
+		case CT_STRING:
+			/* like CCL, but zero-length string OK, & no NOSKIP */
+			if (width == 0)
+				width = (size_t)~0;
+			if (flags & SUPPRESS) {
+				n = 0;
+				while (!isspace(*inp)) {
+					n++, inr--, inp++;
+					if (--width == 0)
+						break;
+					if (inr <= 0)
+						break;
+				}
+				nread += n;
+			} else {
+				p = va_arg(ap, char *);
+				p0 = p;
+				while (!isspace(*inp)) {
+					inr--;
+					*p++ = *inp++;
+					if (--width == 0)
+						break;
+					if (inr <= 0)
+						break;
+				}
+				*p = 0;
+				nread += p - p0;
+				nassigned++;
+			}
+			nconversions++;
+			continue;
+
+		case CT_INT:
+			/* scan an integer as if by strtoq/strtouq */
+#ifdef hardway
+			if (width == 0 || width > sizeof(buf) - 1)
+				width = sizeof(buf) - 1;
+#else
+			/* size_t is unsigned, hence this optimisation */
+			if (--width > sizeof(buf) - 2)
+				width = sizeof(buf) - 2;
+			width++;
+#endif
+			flags |= SIGNOK | NDIGITS | NZDIGITS;
+			for (p = buf; width; width--) {
+				c = *inp;
+				/*
+				 * Switch on the character; `goto ok'
+				 * if we accept it as a part of number.
+				 */
+				switch (c) {
+				/*
+				 * The digit 0 is always legal, but is
+				 * special.  For %i conversions, if no
+				 * digits (zero or nonzero) have been
+				 * scanned (only signs), we will have
+				 * base==0.  In that case, we should set
+				 * it to 8 and enable 0x prefixing.
+				 * Also, if we have not scanned zero digits
+				 * before this, do not turn off prefixing
+				 * (someone else will turn it off if we
+				 * have scanned any nonzero digits).
+				 */
+				case '0':
+					if (base == 0) {
+						base = 8;
+						flags |= PFXOK;
+					}
+					if (flags & NZDIGITS)
+						flags &= ~(SIGNOK | NZDIGITS | NDIGITS);
+					else
+						flags &= ~(SIGNOK | PFXOK | NDIGITS);
+					goto ok;
+
+				/* 1 through 7 always legal */
+				case '1': case '2': case '3':
+				case '4': case '5': case '6': case '7':
+					base = basefix[base];
+					flags &= ~(SIGNOK | PFXOK | NDIGITS);
+					goto ok;
+
+				/* digits 8 and 9 ok iff decimal or hex */
+				case '8': case '9':
+					base = basefix[base];
+					if (base <= 8)
+						break;  /* not legal here */
+					flags &= ~(SIGNOK | PFXOK | NDIGITS);
+					goto ok;
+
+				/* letters ok iff hex */
+				case 'A': case 'B': case 'C':
+				case 'D': case 'E': case 'F':
+				case 'a': case 'b': case 'c':
+				case 'd': case 'e': case 'f':
+					/* no need to fix base here */
+					if (base <= 10)
+						break;  /* not legal here */
+					flags &= ~(SIGNOK | PFXOK | NDIGITS);
+					goto ok;
+
+				/* sign ok only as first character */
+				case '+': case '-':
+					if (flags & SIGNOK) {
+						flags &= ~SIGNOK;
+						goto ok;
+						}
+					break;
+
+				/* x ok iff flag still set & 2nd char */
+				case 'x': case 'X':
+					if (flags & PFXOK && p == buf + 1) {
+						base = 16;      /* if %i */
+						flags &= ~PFXOK;
+						goto ok;
+					}
+					break;
+				}
+
+				/*
+				 * If we got here, c is not a legal character
+				 * for a number.  Stop accumulating digits.
+				 */
+				break;
+ok:
+				/*
+				 * c is legal: store it and look@the next.
+				 */
+				*p++ = c;
+				if (--inr > 0)
+					inp++;
+				else
+					break;          /* end of input */
+			}
+			/*
+			 * If we had only a sign, it is no good; push
+			 * back the sign.  If the number ends in `x',
+			 * it was [sign] '' 'x', so push back the x
+			 * and treat it as [sign] ''.
+			 */
+			if (flags & NDIGITS) {
+				if (p > buf) {
+					inp--;
+					inr++;
+				}
+				goto match_failure;
+			}
+			c = ((u_char *)p)[-1];
+			if (c == 'x' || c == 'X') {
+				--p;
+				inp--;
+				inr++;
+			}
+			if ((flags & SUPPRESS) == 0) {
+				u64 res;
+
+				*p = 0;
+				res = (*ccfn)(buf, (char **)NULL, base);
+				if (flags & POINTER)
+					*va_arg(ap, void **) =
+					(void *)(uintptr_t)res;
+				else if (flags & SHORTSHORT)
+					*va_arg(ap, char *) = res;
+				else if (flags & SHORT)
+					*va_arg(ap, short *) = res;
+				else if (flags & LONG)
+					*va_arg(ap, long *) = res;
+				else if (flags & QUAD)
+					*va_arg(ap, s64 *) = res;
+				else
+					*va_arg(ap, int *) = res;
+				nassigned++;
+			}
+			nread += p - buf;
+			nconversions++;
+			break;
+		}
+	}
+input_failure:
+		return (nconversions != 0 ? nassigned : -1);
+match_failure:
+		return (nassigned);
+}
+
+/**
+ * sscanf - Unformat a buffer into a list of arguments
+ * @buf:	input buffer
+ * @fmt:	formatting of buffer
+ * @...:	resulting arguments
+ */
+int sscanf(const char *buf, const char *fmt, ...)
+{
+	va_list args;
+	int i;
+
+	va_start(args, fmt);
+	i = vsscanf(buf, fmt, args);
+	va_end(args);
+	return i;
+}
+
+#endif
diff --git a/test/lib/Makefile b/test/lib/Makefile
index 6ccc2c41bc..b6a0a208c5 100644
--- a/test/lib/Makefile
+++ b/test/lib/Makefile
@@ -6,6 +6,7 @@ obj-y += cmd_ut_lib.o
 obj-$(CONFIG_EFI_SECURE_BOOT) += efi_image_region.o
 obj-y += hexdump.o
 obj-y += lmb.o
+obj-y += sscanf.o
 obj-y += string.o
 obj-$(CONFIG_ERRNO_STR) += test_errno_str.o
 obj-$(CONFIG_UT_LIB_ASN1) += asn1.o
diff --git a/test/lib/sscanf.c b/test/lib/sscanf.c
new file mode 100644
index 0000000000..772e4b9204
--- /dev/null
+++ b/test/lib/sscanf.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2002, Uwe Bonnes
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2016, The Tor Project, Inc.
+ * Copyright (c) 2020, EPAM Systems Inc.
+ *
+ * Unit tests for sscanf() function
+ */
+
+#include <common.h>
+#include <command.h>
+#include <log.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#define EOF -1
+
+/**
+ * lib_sscanf() - unit test for sscanf()
+ * @uts: unit test state
+ *
+ * Test sscanf() with varied parameters in different combinations passed
+ * as arguments.
+ *
+ * Return: 0 - success
+ *	   1 - failure
+ */
+static int lib_sscanf(struct unit_test_state *uts)
+{
+	char buffer[100], buffer1[100];
+	int result, ret;
+	static const char pname[] = " Hello World!\n";
+	int hour = 21, min = 59, sec = 20;
+	int number, number_so_far;
+	unsigned int u1, u2, u3;
+	char s1[20], s2[10], s3[10], ch;
+	int r, int1, int2;
+	long lng1;
+
+	/* check EOF */
+	strcpy(buffer, "");
+	ret = sscanf(buffer, "%d", &result);
+	ut_asserteq(ret, EOF);
+
+	/* check %x */
+	strcpy(buffer, "0x519");
+	ut_asserteq(sscanf(buffer, "%x", &result), 1);
+	ut_asserteq(result, 0x519);
+
+	strcpy(buffer, "0x51a");
+	ut_asserteq(sscanf(buffer, "%x", &result), 1);
+	ut_asserteq(result, 0x51a);
+
+	strcpy(buffer, "0x51g");
+	ut_asserteq(sscanf(buffer, "%x", &result), 1);
+	ut_asserteq(result, 0x51);
+
+	/* check strings */
+	ret = sprintf(buffer, " %s", pname);
+	ret = sscanf(buffer, "%*c%[^\n]", buffer1);
+	ut_asserteq(ret, 1);
+	ut_asserteq(strncmp(pname, buffer1, strlen(buffer1)), 0);
+
+	/* check digits */
+	ret = sprintf(buffer, "%d:%d:%d", hour, min, sec);
+	ret = sscanf(buffer, "%d%n", &number, &number_so_far);
+	ut_asserteq(ret, 1);
+	ut_asserteq(number, hour);
+	ut_asserteq(number_so_far, 2);
+
+	ret = sscanf(buffer + 2, "%*c%n", &number_so_far);
+	ut_asserteq(ret, 0);
+	ut_asserteq(number_so_far, 1);
+
+	/* Check %i */
+	strcpy(buffer, "123");
+	ret = sscanf(buffer, "%i", &result);
+	ut_asserteq(ret, 1);
+	ut_asserteq(result, 123);
+	ret = sscanf(buffer, "%d", &result);
+	ut_asserteq(ret, 1);
+	ut_asserteq(result, 123);
+
+	ut_asserteq(0, sscanf("hello world", "hello world"));
+	ut_asserteq(0, sscanf("hello world", "good bye"));
+	/* Excess data */
+	ut_asserteq(0, sscanf("hello 3", "%u", &u1));  /* have to match the start */
+	ut_asserteq(1, sscanf("3 hello", "%u", &u1));  /* but trailing is alright */
+
+	/* Numbers (ie. %u) */
+	ut_asserteq(0, sscanf("hello world 3", "hello worlb %u", &u1)); /* d vs b */
+	ut_asserteq(1, sscanf("12345", "%u", &u1));
+	ut_asserteq(12345u, u1);
+	ut_asserteq(1, sscanf("0", "%u", &u1));
+	ut_asserteq(0u, u1);
+	ut_asserteq(1, sscanf("0000", "%u", &u2));
+	ut_asserteq(0u, u2);
+	ut_asserteq(0, sscanf("A", "%u", &u1)); /* bogus number */
+
+	/* Numbers with size (eg. %2u) */
+	ut_asserteq(2, sscanf("123456", "%2u%u", &u1, &u2));
+	ut_asserteq(12u, u1);
+	ut_asserteq(3456u, u2);
+	ut_asserteq(1, sscanf("123456", "%8u", &u1));
+	ut_asserteq(123456u, u1);
+	ut_asserteq(1, sscanf("123457  ", "%8u", &u1));
+	ut_asserteq(123457u, u1);
+	ut_asserteq(3, sscanf("!12:3:456", "!%2u:%2u:%3u", &u1, &u2, &u3));
+	ut_asserteq(12u, u1);
+	ut_asserteq(3u, u2);
+	ut_asserteq(456u, u3);
+	ut_asserteq(3, sscanf("67:8:099", "%2u:%2u:%3u", &u1, &u2, &u3)); /* 0s */
+	ut_asserteq(67u, u1);
+	ut_asserteq(8u, u2);
+	ut_asserteq(99u, u3);
+	/* Arbitrary amounts of 0-padding are okay */
+	ut_asserteq(3, sscanf("12:03:000000000000000099", "%2u:%2u:%u", &u1, &u2, &u3));
+	ut_asserteq(12u, u1);
+	ut_asserteq(3u, u2);
+	ut_asserteq(99u, u3);
+
+	/* Hex (ie. %x) */
+	ut_asserteq(3, sscanf("1234 02aBcdEf ff", "%x %x %x", &u1, &u2, &u3));
+	ut_asserteq(0x1234, u1);
+	ut_asserteq(0x2ABCDEF, u2);
+	ut_asserteq(0xFF, u3);
+	/* Width works on %x */
+	ut_asserteq(3, sscanf("f00dcafe444", "%4x%4x%u", &u1, &u2, &u3));
+	ut_asserteq(0xf00d, u1);
+	ut_asserteq(0xcafe, u2);
+	ut_asserteq(444, u3);
+
+	/* Literal '%' (ie. '%%') */
+	ut_asserteq(1, sscanf("99% fresh", "%3u%% fresh", &u1));
+	ut_asserteq(99, u1);
+	ut_asserteq(0, sscanf("99 fresh", "%% %3u %s", &u1, s1));
+	ut_asserteq(1, sscanf("99 fresh", "%3u%% %s", &u1, s1));
+	ut_asserteq(2, sscanf("99 fresh", "%3u %5s %%", &u1, s1));
+	ut_asserteq(99, u1);
+	ut_asserteq_str(s1, "fresh");
+	ut_asserteq(1, sscanf("% boo", "%% %3s", s1));
+	ut_asserteq_str("boo", s1);
+
+	/* Strings (ie. %s) */
+	ut_asserteq(2, sscanf("hello", "%3s%7s", s1, s2));
+	ut_asserteq_str(s1, "hel");
+	ut_asserteq_str(s2, "lo");
+	ut_asserteq(2, sscanf("WD40", "%2s%u", s3, &u1)); /* %s%u */
+	ut_asserteq_str(s3, "WD");
+	ut_asserteq(40, u1);
+	ut_asserteq(2, sscanf("WD40", "%3s%u", s3, &u1)); /* %s%u */
+	ut_asserteq_str(s3, "WD4");
+	ut_asserteq(0, u1);
+	ut_asserteq(2, sscanf("76trombones", "%6u%9s", &u1, s1)); /* %u%s */
+	ut_asserteq(76, u1);
+	ut_asserteq_str(s1, "trombones");
+
+	ut_asserteq(3, sscanf("1.2.3", "%u.%u.%u%c", &u1, &u2, &u3, &ch));
+	ut_asserteq(4, sscanf("1.2.3 foobar", "%u.%u.%u%c", &u1, &u2, &u3, &ch));
+	ut_asserteq(' ', ch);
+
+	r = sscanf("12345 -67890 -1", "%d %ld %d", &int1, &lng1, &int2);
+	ut_asserteq(r, 3);
+	ut_asserteq(int1, 12345);
+	ut_asserteq(lng1, -67890);
+	ut_asserteq(int2, -1);
+
+	return 0;
+}
+
+LIB_TEST(lib_sscanf, 0);
-- 
2.17.1

  parent reply	other threads:[~2020-08-06  9:42 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-08-06  9:42 [RESEND PATCH v2 00/18] Add new board: Xen guest for ARM64 Anastasiia Lukianenko
2020-08-06  9:42 ` [RESEND PATCH v2 01/18] Add MIT License Anastasiia Lukianenko
2020-08-14 19:49   ` Tom Rini
2020-08-06  9:42 ` [RESEND PATCH v2 02/18] Kconfig: Introduce CONFIG_XEN Anastasiia Lukianenko
2020-08-14 19:50   ` Tom Rini
2020-08-06  9:42 ` [RESEND PATCH v2 03/18] xen: Add essential and required interface headers Anastasiia Lukianenko
2020-08-14 19:50   ` Tom Rini
2020-08-06  9:42 ` [RESEND PATCH v2 04/18] board: Introduce xenguest_arm64 board Anastasiia Lukianenko
2020-08-14 19:50   ` Tom Rini
2020-08-06  9:42 ` [RESEND PATCH v2 05/18] xen: Port Xen hypervisor related code from mini-os Anastasiia Lukianenko
2020-08-14 19:50   ` Tom Rini
2020-08-06  9:42 ` [RESEND PATCH v2 06/18] xen: Port Xen event channel driver " Anastasiia Lukianenko
2020-08-14 19:50   ` Tom Rini
2020-08-06  9:42 ` [RESEND PATCH v2 07/18] serial: serial_xen: Add Xen PV serial driver Anastasiia Lukianenko
2020-08-14 19:50   ` Tom Rini
2020-08-06  9:42 ` [RESEND PATCH v2 08/18] linux/compat.h: Add wait_event_timeout macro Anastasiia Lukianenko
2020-08-14 19:50   ` Tom Rini
2020-08-06  9:42 ` Anastasiia Lukianenko [this message]
2020-08-14 19:50   ` [RESEND PATCH v2 09/18] lib: sscanf: add sscanf implementation Tom Rini
2020-08-16  8:23   ` Andy Shevchenko
     [not found]     ` <999cf014-ac1b-51b6-0522-f75e5ac3a3ed@epam.com>
2020-08-17 12:12       ` Artem Mygaiev
2020-08-17 14:19         ` Tom Rini
2020-08-06  9:42 ` [RESEND PATCH v2 10/18] xen: Port Xen bus driver from mini-os Anastasiia Lukianenko
2020-08-14 19:50   ` Tom Rini
2020-08-06  9:42 ` [RESEND PATCH v2 11/18] xen: Port Xen grant table " Anastasiia Lukianenko
2020-08-14 19:50   ` Tom Rini
2020-08-06  9:42 ` [RESEND PATCH v2 12/18] xen: pvblock: Add initial support for para-virtualized block driver Anastasiia Lukianenko
2020-08-14 19:50   ` Tom Rini
2020-08-06  9:42 ` [RESEND PATCH v2 13/18] xen: pvblock: Enumerate virtual block devices Anastasiia Lukianenko
2020-08-14 19:51   ` Tom Rini
2020-08-06  9:42 ` [RESEND PATCH v2 14/18] xen: pvblock: Read XenStore configuration and initialize Anastasiia Lukianenko
2020-08-14 19:51   ` Tom Rini
2020-08-06  9:42 ` [RESEND PATCH v2 15/18] xen: pvblock: Implement front-back protocol and do IO Anastasiia Lukianenko
2020-08-14 19:51   ` Tom Rini
2020-08-06  9:42 ` [RESEND PATCH v2 16/18] xen: pvblock: Print found devices indices Anastasiia Lukianenko
2020-08-14 19:51   ` Tom Rini
2020-08-06  9:43 ` [RESEND PATCH v2 17/18] board: xen: De-initialize before jumping to Linux Anastasiia Lukianenko
2020-08-14 19:51   ` Tom Rini
2020-08-06  9:43 ` [RESEND PATCH v2 18/18] doc: xen: Add Xen guest ARM64 board documentation Anastasiia Lukianenko
2020-08-14 19:51   ` Tom Rini
2020-08-13  7:57 ` [RESEND PATCH v2 00/18] Add new board: Xen guest for ARM64 Oleksandr Andrushchenko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200806094301.4999-10-vicooodin@gmail.com \
    --to=vicooodin@gmail.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.