1// SPDX-License-Identifier: BSD-3-Clause
2/*
3 * Copyright (c) 1990, 1993
4 * The Regents of the University of California. All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * Chris Torek.
8 *
9 * Copyright (c) 2011 The FreeBSD Foundation
10 * All rights reserved.
11 * Portions of this software were developed by David Chisnall
12 * under sponsorship from the FreeBSD Foundation.
13 *
14 * Author: Juergen Gross <jgross@suse.com>
15 * Date: Jun 2016
16 */
17
18#if !defined HAVE_LIBC
19
20#include <os.h>
21#include <linux/kernel.h>
22#include <linux/ctype.h>
23#include <vsprintf.h>
24#include <linux/string.h>
25#include <malloc.h>
26#define __DECONST(type, var)    ((type)(uintptr_t)(const void *)(var))
27
28/**
29 * struct str_info - Input string parameters
30 * @neg: negative number or not
31 *	 0 - not negative
32 *	 1 - negative
33 * @any: set any if any `digits' consumed; make it negative to indicate
34 *	 overflow
35 * @acc: accumulated value
36 */
37struct str_info {
38	int neg, any;
39	u64 acc;
40};
41
42/**
43 * str_to_int_convert() - Write string data to structure
44 * @nptr: pointer to string
45 * @base: number's base
46 * @unsign: describes what integer is expected
47 *	    0 - not unsigned
48 *	    1 - unsigned
49 *
50 * Ignores `locale' stuff.  Assumes that the upper and lower case
51 * alphabets and digits are each contiguous.
52 *
53 * Return: struct str_info *, which contains string data to future process
54 */
55static struct str_info *
56str_to_int_convert(const char **nptr, int base, unsigned int unsign)
57{
58	const char *s = *nptr;
59	u64 acc;
60	unsigned char c;
61	u64 cutoff;
62	int neg, any, cutlim;
63	u64 qbase;
64	struct str_info *info;
65
66	/*
67	 * Skip white space and pick up leading +/- sign if any.
68	 * If base is 0, allow 0x for hex and 0 for octal, else
69	 * assume decimal; if base is already 16, allow 0x.
70	 */
71	info = (struct str_info *)malloc(sizeof(struct str_info));
72	if (!info)
73		return NULL;
74
75	do {
76		c = *s++;
77	} while (isspace(c));
78	if (c == '-') {
79		neg = 1;
80		c = *s++;
81	} else {
82		neg = 0;
83		if (c == '+')
84			c = *s++;
85	}
86	if ((base == 0 || base == 16) &&
87	    c == '0' && (*s == 'x' || *s == 'X')) {
88		c = s[1];
89		s += 2;
90		base = 16;
91	}
92	if (base == 0)
93		base = c == '0' ? 8 : 10;
94
95	/*
96	 * Compute the cutoff value between legal numbers and illegal
97	 * numbers.  That is the largest legal value, divided by the
98	 * base.  An input number that is greater than this value, if
99	 * followed by a legal input character, is too big.  One that
100	 * is equal to this value may be valid or not; the limit
101	 * between valid and invalid numbers is then based on the last
102	 * digit.  For instance, if the range for quads is
103	 * [-9223372036854775808..9223372036854775807] and the input base
104	 * is 10, cutoff will be set to 922337203685477580 and cutlim to
105	 * either 7 (neg==0) or 8 (neg==1), meaning that if we have
106	 * accumulated a value > 922337203685477580, or equal but the
107	 * next digit is > 7 (or 8), the number is too big, and we will
108	 * return a range error.
109	 *
110	 * Set any if any `digits' consumed; make it negative to indicate
111	 * overflow.
112	 */
113	qbase = (unsigned int)base;
114
115	if (!unsign) {
116		cutoff = neg ? (u64)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX : LLONG_MAX;
117		cutlim = cutoff % qbase;
118		cutoff /= qbase;
119	} else {
120		cutoff = (u64)ULLONG_MAX / qbase;
121		cutlim = (u64)ULLONG_MAX % qbase;
122	}
123
124	for (acc = 0, any = 0;; c = *s++) {
125		if (!isascii(c))
126			break;
127		if (isdigit(c))
128			c -= '0';
129		else if (isalpha(c))
130			c -= isupper(c) ? 'A' - 10 : 'a' - 10;
131		else
132			break;
133		if (c >= base)
134			break;
135		if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) {
136			any = -1;
137		} else {
138			any = 1;
139			acc *= qbase;
140			acc += c;
141		}
142	}
143
144	info->any = any;
145	info->neg = neg;
146	info->acc = acc;
147
148	*nptr = s;
149
150	return info;
151}
152
153/**
154 * strtoq() - Convert a string to a quad integer
155 * @nptr: pointer to string
156 * @endptr: pointer to number's end in the string
157 * @base: number's base
158 *
159 * Return: s64 quad integer number converted from input string
160 */
161static s64
162strtoq(const char *nptr, char **endptr, int base)
163{
164	const char *s = nptr;
165	u64 acc;
166	int unsign = 0;
167	struct str_info *info;
168
169	info = str_to_int_convert(&s, base, unsign);
170	if (!info)
171		return -1;
172
173	acc = info->acc;
174
175	if (info->any < 0)
176		acc = info->neg ? LLONG_MIN : LLONG_MAX;
177	else if (info->neg)
178		acc = -acc;
179	if (endptr != 0)
180		*endptr = __DECONST(char *, info->any ? s - 1 : nptr);
181
182	free(info);
183
184	return acc;
185}
186
187/**
188 * strtouq() - Convert a string to an unsigned quad integer
189 * @nptr: pointer to string
190 * @endptr: pointer to number's end in the string
191 * @base: number's base
192 *
193 * Return: s64 unsigned quad integer number converted from
194 *         input string
195 */
196u64
197strtouq(const char *nptr, char **endptr, int base)
198{
199		const char *s = nptr;
200	u64 acc;
201	int unsign = 1;
202	struct str_info *info;
203
204	info = str_to_int_convert(&s, base, unsign);
205	if (!info)
206		return -1;
207
208	acc = info->acc;
209
210	if (info->any < 0)
211		acc = ULLONG_MAX;
212	else if (info->neg)
213		acc = -acc;
214	if (endptr != 0)
215		*endptr = __DECONST(char *, info->any ? s - 1 : nptr);
216
217	free(info);
218
219	return acc;
220}
221
222/**
223 * __sccl() - Fill in the given table from the scanset at the given format
224 * (just after `[')
225 * @tab: table to fill in
226 * @fmt: format of buffer
227 *
228 * The table has a 1 wherever characters should be considered part of the
229 * scanset.
230 *
231 * Return: pointer to the character past the closing `]'
232 */
233static const u_char *
234__sccl(char *tab, const u_char *fmt)
235{
236	int c, n, v;
237
238	/* first `clear' the whole table */
239	c = *fmt++;             /* first char hat => negated scanset */
240	if (c == '^') {
241		v = 1;          /* default => accept */
242		c = *fmt++;     /* get new first char */
243	} else {
244		v = 0;          /* default => reject */
245	}
246
247	/* XXX: Will not work if sizeof(tab*) > sizeof(char) */
248	for (n = 0; n < 256; n++)
249		tab[n] = v;        /* memset(tab, v, 256) */
250
251	if (c == 0)
252		return (fmt - 1);/* format ended before closing ] */
253
254	/*
255	 * Now set the entries corresponding to the actual scanset
256	 * to the opposite of the above.
257	 *
258	 * The first character may be ']' (or '-') without being special;
259	 * the last character may be '-'.
260	 */
261	v = 1 - v;
262	for (;;) {
263		tab[c] = v;             /* take character c */
264doswitch:
265		n = *fmt++;             /* and examine the next */
266		switch (n) {
267		case 0:                 /* format ended too soon */
268			return (fmt - 1);
269
270		case '-':
271			/*
272			 * A scanset of the form
273			 *      [01+-]
274			 * is defined as `the digit 0, the digit 1,
275			 * the character +, the character -', but
276			 * the effect of a scanset such as
277			 *      [a-zA-Z0-9]
278			 * is implementation defined.  The V7 Unix
279			 * scanf treats `a-z' as `the letters a through
280			 * z', but treats `a-a' as `the letter a, the
281			 * character -, and the letter a'.
282			 *
283			 * For compatibility, the `-' is not considerd
284			 * to define a range if the character following
285			 * it is either a close bracket (required by ANSI)
286			 * or is not numerically greater than the character
287			 * we just stored in the table (c).
288			 */
289			n = *fmt;
290			if (n == ']' || n < c) {
291				c = '-';
292				break;  /* resume the for(;;) */
293			}
294			fmt++;
295			/* fill in the range */
296			do {
297				tab[++c] = v;
298			} while (c < n);
299			c = n;
300			/*
301			 * Alas, the V7 Unix scanf also treats formats
302			 * such as [a-c-e] as `the letters a through e'.
303			 * This too is permitted by the standard....
304			 */
305			goto doswitch;
306			break;
307
308		case ']':               /* end of scanset */
309			return (fmt);
310
311		default:                /* just another character */
312			c = n;
313			break;
314		}
315	}
316	/* NOTREACHED */
317}
318
319/**
320 * vsscanf - Unformat a buffer into a list of arguments
321 * @buf:	input buffer
322 * @fmt:	format of buffer
323 * @args:	arguments
324 */
325#define BUF             32      /* Maximum length of numeric string. */
326
327/*
328 * Flags used during conversion.
329 */
330#define LONG            0x01    /* l: long or double */
331#define SHORT           0x04    /* h: short */
332#define SUPPRESS        0x08    /* suppress assignment */
333#define POINTER         0x10    /* weird %p pointer (`fake hex') */
334#define NOSKIP          0x20    /* do not skip blanks */
335#define QUAD            0x400
336#define SHORTSHORT      0x4000  /** hh: char */
337
338/*
339 * The following are used in numeric conversions only:
340 * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point;
341 * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral.
342 */
343#define SIGNOK          0x40    /* +/- is (still) legal */
344#define NDIGITS         0x80    /* no digits detected */
345
346#define DPTOK           0x100   /* (float) decimal point is still legal */
347#define EXPOK           0x200   /* (float) exponent (e+3, etc) still legal */
348
349#define PFXOK           0x100   /* 0x prefix is (still) legal */
350#define NZDIGITS        0x200   /* no zero digits detected */
351
352/*
353 * Conversion types.
354 */
355#define CT_CHAR         0       /* %c conversion */
356#define CT_CCL          1       /* %[...] conversion */
357#define CT_STRING       2       /* %s conversion */
358#define CT_INT          3       /* integer, i.e., strtoq or strtouq */
359typedef u64 (*ccfntype)(const char *, char **, int);
360
361int
362vsscanf(const char *inp, char const *fmt0, va_list ap)
363{
364	int inr;
365	const u_char *fmt = (const u_char *)fmt0;
366	int c;                  /* character from format, or conversion */
367	size_t width;           /* field width, or 0 */
368	char *p;                /* points into all kinds of strings */
369	int n;                  /* handy integer */
370	int flags;              /* flags as defined above */
371	char *p0;               /* saves original value of p when necessary */
372	int nassigned;          /* number of fields assigned */
373	int nconversions;       /* number of conversions */
374	int nread;              /* number of characters consumed from fp */
375	int base;               /* base argument to strtoq/strtouq */
376	ccfntype ccfn;          /* conversion function (strtoq/strtouq) */
377	char ccltab[256];       /* character class table for %[...] */
378	char buf[BUF];          /* buffer for numeric conversions */
379
380	/* `basefix' is used to avoid `if' tests in the integer scanner */
381	static short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
382				     12, 13, 14, 15, 16 };
383
384	inr = strlen(inp);
385
386	nassigned = 0;
387	nconversions = 0;
388	nread = 0;
389	base = 0;               /* XXX just to keep gcc happy */
390	ccfn = NULL;            /* XXX just to keep gcc happy */
391	for (;;) {
392		c = *fmt++;
393		if (c == 0)
394			return (nassigned);
395		if (isspace(c)) {
396			while (inr > 0 && isspace(*inp))
397				nread++, inr--, inp++;
398			continue;
399		}
400		if (c != '%')
401			goto literal;
402		width = 0;
403		flags = 0;
404		/*
405		 * switch on the format.  continue if done;
406		 * break once format type is derived.
407		 */
408again:          c = *fmt++;
409		switch (c) {
410		case '%':
411literal:
412			if (inr <= 0)
413				goto input_failure;
414			if (*inp != c)
415				goto match_failure;
416			inr--, inp++;
417			nread++;
418			continue;
419
420		case '*':
421			flags |= SUPPRESS;
422			goto again;
423		case 'l':
424			if (flags & LONG) {
425				flags &= ~LONG;
426				flags |= QUAD;
427			} else {
428				flags |= LONG;
429			}
430			goto again;
431		case 'q':
432			flags |= QUAD;
433			goto again;
434		case 'h':
435			if (flags & SHORT) {
436				flags &= ~SHORT;
437				flags |= SHORTSHORT;
438			} else {
439				flags |= SHORT;
440			}
441			goto again;
442
443		case '0': case '1': case '2': case '3': case '4':
444		case '5': case '6': case '7': case '8': case '9':
445			width = width * 10 + c - '0';
446			goto again;
447
448		/*
449		 * Conversions.
450		 *
451		 */
452		case 'd':
453			c = CT_INT;
454			ccfn = (ccfntype)strtoq;
455			base = 10;
456			break;
457
458		case 'i':
459			c = CT_INT;
460			ccfn = (ccfntype)strtoq;
461			base = 0;
462			break;
463
464		case 'o':
465			c = CT_INT;
466			ccfn = strtouq;
467			base = 8;
468			break;
469
470		case 'u':
471			c = CT_INT;
472			ccfn = strtouq;
473			base = 10;
474			break;
475
476		case 'x':
477			flags |= PFXOK; /* enable 0x prefixing */
478			c = CT_INT;
479			ccfn = strtouq;
480			base = 16;
481			break;
482
483		case 's':
484			c = CT_STRING;
485			break;
486
487		case '[':
488			fmt = __sccl(ccltab, fmt);
489			flags |= NOSKIP;
490			c = CT_CCL;
491			break;
492
493		case 'c':
494			flags |= NOSKIP;
495			c = CT_CHAR;
496			break;
497
498		case 'p':       /* pointer format is like hex */
499			flags |= POINTER | PFXOK;
500			c = CT_INT;
501			ccfn = strtouq;
502			base = 16;
503			break;
504
505		case 'n':
506			nconversions++;
507			if (flags & SUPPRESS)   /* ??? */
508				continue;
509			if (flags & SHORTSHORT)
510				*va_arg(ap, char *) = nread;
511			else if (flags & SHORT)
512				*va_arg(ap, short *) = nread;
513			else if (flags & LONG)
514				*va_arg(ap, long *) = nread;
515			else if (flags & QUAD)
516				*va_arg(ap, s64 *) = nread;
517			else
518				*va_arg(ap, int *) = nread;
519			continue;
520		}
521
522		/*
523		 * We have a conversion that requires input.
524		 */
525		if (inr <= 0)
526			goto input_failure;
527
528		/*
529		 * Consume leading white space, except for formats
530		 * that suppress this.
531		 */
532		if ((flags & NOSKIP) == 0) {
533			while (isspace(*inp)) {
534				nread++;
535				if (--inr > 0)
536					inp++;
537				else
538					goto input_failure;
539			}
540			/*
541			 * Note that there is at least one character in
542			 * the buffer, so conversions that do not set NOSKIP
543			 * can no longer result in an input failure.
544			 */
545		}
546
547		/*
548		 * Do the conversion.
549		 */
550		switch (c) {
551		case CT_CHAR:
552			/* scan arbitrary characters (sets NOSKIP) */
553			if (width == 0)
554				width = 1;
555			if (flags & SUPPRESS) {
556				size_t sum = 0;
557
558				n = inr;
559				if (n < width) {
560					sum += n;
561					width -= n;
562					inp += n;
563					if (sum == 0)
564						goto input_failure;
565				} else {
566					sum += width;
567					inr -= width;
568					inp += width;
569				}
570				nread += sum;
571			} else {
572				memcpy(va_arg(ap, char *), inp, width);
573				inr -= width;
574				inp += width;
575				nread += width;
576				nassigned++;
577			}
578			nconversions++;
579			break;
580
581		case CT_CCL:
582			/* scan a (nonempty) character class (sets NOSKIP) */
583			if (width == 0)
584				width = (size_t)~0;     /* `infinity' */
585			/* take only those things in the class */
586			if (flags & SUPPRESS) {
587				n = 0;
588				while (ccltab[(unsigned char)*inp]) {
589					n++, inr--, inp++;
590					if (--width == 0)
591						break;
592					if (inr <= 0) {
593						if (n == 0)
594							goto input_failure;
595						break;
596					}
597				}
598				if (n == 0)
599					goto match_failure;
600			} else {
601				p = va_arg(ap, char *);
602				p0 = p;
603				while (ccltab[(unsigned char)*inp]) {
604					inr--;
605					*p++ = *inp++;
606					if (--width == 0)
607						break;
608					if (inr <= 0) {
609						if (p == p0)
610							goto input_failure;
611						break;
612					}
613				}
614				n = p - p0;
615				if (n == 0)
616					goto match_failure;
617				*p = 0;
618				nassigned++;
619			}
620			nread += n;
621			nconversions++;
622			break;
623
624		case CT_STRING:
625			/* like CCL, but zero-length string OK, & no NOSKIP */
626			if (width == 0)
627				width = (size_t)~0;
628			if (flags & SUPPRESS) {
629				n = 0;
630				while (!isspace(*inp)) {
631					n++, inr--, inp++;
632					if (--width == 0)
633						break;
634					if (inr <= 0)
635						break;
636				}
637				nread += n;
638			} else {
639				p = va_arg(ap, char *);
640				p0 = p;
641				while (!isspace(*inp)) {
642					inr--;
643					*p++ = *inp++;
644					if (--width == 0)
645						break;
646					if (inr <= 0)
647						break;
648				}
649				*p = 0;
650				nread += p - p0;
651				nassigned++;
652			}
653			nconversions++;
654			continue;
655
656		case CT_INT:
657			/* scan an integer as if by strtoq/strtouq */
658#ifdef hardway
659			if (width == 0 || width > sizeof(buf) - 1)
660				width = sizeof(buf) - 1;
661#else
662			/* size_t is unsigned, hence this optimisation */
663			if (--width > sizeof(buf) - 2)
664				width = sizeof(buf) - 2;
665			width++;
666#endif
667			flags |= SIGNOK | NDIGITS | NZDIGITS;
668			for (p = buf; width; width--) {
669				c = *inp;
670				/*
671				 * Switch on the character; `goto ok'
672				 * if we accept it as a part of number.
673				 */
674				switch (c) {
675				/*
676				 * The digit 0 is always legal, but is
677				 * special.  For %i conversions, if no
678				 * digits (zero or nonzero) have been
679				 * scanned (only signs), we will have
680				 * base==0.  In that case, we should set
681				 * it to 8 and enable 0x prefixing.
682				 * Also, if we have not scanned zero digits
683				 * before this, do not turn off prefixing
684				 * (someone else will turn it off if we
685				 * have scanned any nonzero digits).
686				 */
687				case '0':
688					if (base == 0) {
689						base = 8;
690						flags |= PFXOK;
691					}
692					if (flags & NZDIGITS)
693						flags &= ~(SIGNOK | NZDIGITS | NDIGITS);
694					else
695						flags &= ~(SIGNOK | PFXOK | NDIGITS);
696					goto ok;
697
698				/* 1 through 7 always legal */
699				case '1': case '2': case '3':
700				case '4': case '5': case '6': case '7':
701					base = basefix[base];
702					flags &= ~(SIGNOK | PFXOK | NDIGITS);
703					goto ok;
704
705				/* digits 8 and 9 ok iff decimal or hex */
706				case '8': case '9':
707					base = basefix[base];
708					if (base <= 8)
709						break;  /* not legal here */
710					flags &= ~(SIGNOK | PFXOK | NDIGITS);
711					goto ok;
712
713				/* letters ok iff hex */
714				case 'A': case 'B': case 'C':
715				case 'D': case 'E': case 'F':
716				case 'a': case 'b': case 'c':
717				case 'd': case 'e': case 'f':
718					/* no need to fix base here */
719					if (base <= 10)
720						break;  /* not legal here */
721					flags &= ~(SIGNOK | PFXOK | NDIGITS);
722					goto ok;
723
724				/* sign ok only as first character */
725				case '+': case '-':
726					if (flags & SIGNOK) {
727						flags &= ~SIGNOK;
728						goto ok;
729						}
730					break;
731
732				/* x ok iff flag still set & 2nd char */
733				case 'x': case 'X':
734					if (flags & PFXOK && p == buf + 1) {
735						base = 16;      /* if %i */
736						flags &= ~PFXOK;
737						goto ok;
738					}
739					break;
740				}
741
742				/*
743				 * If we got here, c is not a legal character
744				 * for a number.  Stop accumulating digits.
745				 */
746				break;
747ok:
748				/*
749				 * c is legal: store it and look at the next.
750				 */
751				*p++ = c;
752				if (--inr > 0)
753					inp++;
754				else
755					break;          /* end of input */
756			}
757			/*
758			 * If we had only a sign, it is no good; push
759			 * back the sign.  If the number ends in `x',
760			 * it was [sign] '' 'x', so push back the x
761			 * and treat it as [sign] ''.
762			 */
763			if (flags & NDIGITS) {
764				if (p > buf) {
765					inp--;
766					inr++;
767				}
768				goto match_failure;
769			}
770			c = ((u_char *)p)[-1];
771			if (c == 'x' || c == 'X') {
772				--p;
773				inp--;
774				inr++;
775			}
776			if ((flags & SUPPRESS) == 0) {
777				u64 res;
778
779				*p = 0;
780				res = (*ccfn)(buf, (char **)NULL, base);
781				if (flags & POINTER)
782					*va_arg(ap, void **) =
783					(void *)(uintptr_t)res;
784				else if (flags & SHORTSHORT)
785					*va_arg(ap, char *) = res;
786				else if (flags & SHORT)
787					*va_arg(ap, short *) = res;
788				else if (flags & LONG)
789					*va_arg(ap, long *) = res;
790				else if (flags & QUAD)
791					*va_arg(ap, s64 *) = res;
792				else
793					*va_arg(ap, int *) = res;
794				nassigned++;
795			}
796			nread += p - buf;
797			nconversions++;
798			break;
799		}
800	}
801input_failure:
802		return (nconversions != 0 ? nassigned : -1);
803match_failure:
804		return (nassigned);
805}
806
807/**
808 * sscanf - Unformat a buffer into a list of arguments
809 * @buf:	input buffer
810 * @fmt:	formatting of buffer
811 * @...:	resulting arguments
812 */
813int sscanf(const char *buf, const char *fmt, ...)
814{
815	va_list args;
816	int i;
817
818	va_start(args, fmt);
819	i = vsscanf(buf, fmt, args);
820	va_end(args);
821	return i;
822}
823
824#endif
825