162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) Paul Mackerras 1997.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <stdarg.h>
662306a36Sopenharmony_ci#include <stddef.h>
762306a36Sopenharmony_ci#include "string.h"
862306a36Sopenharmony_ci#include "stdio.h"
962306a36Sopenharmony_ci#include "ops.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cisize_t strnlen(const char * s, size_t count)
1262306a36Sopenharmony_ci{
1362306a36Sopenharmony_ci	const char *sc;
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci	for (sc = s; count-- && *sc != '\0'; ++sc)
1662306a36Sopenharmony_ci		/* nothing */;
1762306a36Sopenharmony_ci	return sc - s;
1862306a36Sopenharmony_ci}
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cichar *strrchr(const char *s, int c)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	const char *last = NULL;
2362306a36Sopenharmony_ci	do {
2462306a36Sopenharmony_ci		if (*s == (char)c)
2562306a36Sopenharmony_ci			last = s;
2662306a36Sopenharmony_ci	} while (*s++);
2762306a36Sopenharmony_ci	return (char *)last;
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#ifdef __powerpc64__
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci# define do_div(n, base) ({						\
3362306a36Sopenharmony_ci	unsigned int __base = (base);					\
3462306a36Sopenharmony_ci	unsigned int __rem;						\
3562306a36Sopenharmony_ci	__rem = ((unsigned long long)(n)) % __base;			\
3662306a36Sopenharmony_ci	(n) = ((unsigned long long)(n)) / __base;			\
3762306a36Sopenharmony_ci	__rem;								\
3862306a36Sopenharmony_ci})
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#else
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ciextern unsigned int __div64_32(unsigned long long *dividend,
4362306a36Sopenharmony_ci			       unsigned int divisor);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* The unnecessary pointer compare is there
4662306a36Sopenharmony_ci * to check for type safety (n must be 64bit)
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_ci# define do_div(n,base) ({						\
4962306a36Sopenharmony_ci	unsigned int __base = (base);					\
5062306a36Sopenharmony_ci	unsigned int __rem;						\
5162306a36Sopenharmony_ci	(void)(((typeof((n)) *)0) == ((unsigned long long *)0));	\
5262306a36Sopenharmony_ci	if (((n) >> 32) == 0) {						\
5362306a36Sopenharmony_ci		__rem = (unsigned int)(n) % __base;			\
5462306a36Sopenharmony_ci		(n) = (unsigned int)(n) / __base;			\
5562306a36Sopenharmony_ci	} else								\
5662306a36Sopenharmony_ci		__rem = __div64_32(&(n), __base);			\
5762306a36Sopenharmony_ci	__rem;								\
5862306a36Sopenharmony_ci })
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#endif /* __powerpc64__ */
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic int skip_atoi(const char **s)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	int i, c;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	for (i = 0; '0' <= (c = **s) && c <= '9'; ++*s)
6762306a36Sopenharmony_ci		i = i*10 + c - '0';
6862306a36Sopenharmony_ci	return i;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#define ZEROPAD	1		/* pad with zero */
7262306a36Sopenharmony_ci#define SIGN	2		/* unsigned/signed long */
7362306a36Sopenharmony_ci#define PLUS	4		/* show plus */
7462306a36Sopenharmony_ci#define SPACE	8		/* space if plus */
7562306a36Sopenharmony_ci#define LEFT	16		/* left justified */
7662306a36Sopenharmony_ci#define SPECIAL	32		/* 0x */
7762306a36Sopenharmony_ci#define LARGE	64		/* use 'ABCDEF' instead of 'abcdef' */
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic char * number(char * str, unsigned long long num, int base, int size, int precision, int type)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	char c,sign,tmp[66];
8262306a36Sopenharmony_ci	const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
8362306a36Sopenharmony_ci	int i;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (type & LARGE)
8662306a36Sopenharmony_ci		digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
8762306a36Sopenharmony_ci	if (type & LEFT)
8862306a36Sopenharmony_ci		type &= ~ZEROPAD;
8962306a36Sopenharmony_ci	if (base < 2 || base > 36)
9062306a36Sopenharmony_ci		return 0;
9162306a36Sopenharmony_ci	c = (type & ZEROPAD) ? '0' : ' ';
9262306a36Sopenharmony_ci	sign = 0;
9362306a36Sopenharmony_ci	if (type & SIGN) {
9462306a36Sopenharmony_ci		if ((signed long long)num < 0) {
9562306a36Sopenharmony_ci			sign = '-';
9662306a36Sopenharmony_ci			num = - (signed long long)num;
9762306a36Sopenharmony_ci			size--;
9862306a36Sopenharmony_ci		} else if (type & PLUS) {
9962306a36Sopenharmony_ci			sign = '+';
10062306a36Sopenharmony_ci			size--;
10162306a36Sopenharmony_ci		} else if (type & SPACE) {
10262306a36Sopenharmony_ci			sign = ' ';
10362306a36Sopenharmony_ci			size--;
10462306a36Sopenharmony_ci		}
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci	if (type & SPECIAL) {
10762306a36Sopenharmony_ci		if (base == 16)
10862306a36Sopenharmony_ci			size -= 2;
10962306a36Sopenharmony_ci		else if (base == 8)
11062306a36Sopenharmony_ci			size--;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci	i = 0;
11362306a36Sopenharmony_ci	if (num == 0)
11462306a36Sopenharmony_ci		tmp[i++]='0';
11562306a36Sopenharmony_ci	else while (num != 0) {
11662306a36Sopenharmony_ci		tmp[i++] = digits[do_div(num, base)];
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci	if (i > precision)
11962306a36Sopenharmony_ci		precision = i;
12062306a36Sopenharmony_ci	size -= precision;
12162306a36Sopenharmony_ci	if (!(type&(ZEROPAD+LEFT)))
12262306a36Sopenharmony_ci		while(size-->0)
12362306a36Sopenharmony_ci			*str++ = ' ';
12462306a36Sopenharmony_ci	if (sign)
12562306a36Sopenharmony_ci		*str++ = sign;
12662306a36Sopenharmony_ci	if (type & SPECIAL) {
12762306a36Sopenharmony_ci		if (base==8)
12862306a36Sopenharmony_ci			*str++ = '0';
12962306a36Sopenharmony_ci		else if (base==16) {
13062306a36Sopenharmony_ci			*str++ = '0';
13162306a36Sopenharmony_ci			*str++ = digits[33];
13262306a36Sopenharmony_ci		}
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci	if (!(type & LEFT))
13562306a36Sopenharmony_ci		while (size-- > 0)
13662306a36Sopenharmony_ci			*str++ = c;
13762306a36Sopenharmony_ci	while (i < precision--)
13862306a36Sopenharmony_ci		*str++ = '0';
13962306a36Sopenharmony_ci	while (i-- > 0)
14062306a36Sopenharmony_ci		*str++ = tmp[i];
14162306a36Sopenharmony_ci	while (size-- > 0)
14262306a36Sopenharmony_ci		*str++ = ' ';
14362306a36Sopenharmony_ci	return str;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ciint vsprintf(char *buf, const char *fmt, va_list args)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	int len;
14962306a36Sopenharmony_ci	unsigned long long num;
15062306a36Sopenharmony_ci	int i, base;
15162306a36Sopenharmony_ci	char * str;
15262306a36Sopenharmony_ci	const char *s;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	int flags;		/* flags to number() */
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	int field_width;	/* width of output field */
15762306a36Sopenharmony_ci	int precision;		/* min. # of digits for integers; max
15862306a36Sopenharmony_ci				   number of chars for from string */
15962306a36Sopenharmony_ci	int qualifier;		/* 'h', 'l', or 'L' for integer fields */
16062306a36Sopenharmony_ci	                        /* 'z' support added 23/7/1999 S.H.    */
16162306a36Sopenharmony_ci				/* 'z' changed to 'Z' --davidm 1/25/99 */
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	for (str=buf ; *fmt ; ++fmt) {
16562306a36Sopenharmony_ci		if (*fmt != '%') {
16662306a36Sopenharmony_ci			*str++ = *fmt;
16762306a36Sopenharmony_ci			continue;
16862306a36Sopenharmony_ci		}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci		/* process flags */
17162306a36Sopenharmony_ci		flags = 0;
17262306a36Sopenharmony_ci		repeat:
17362306a36Sopenharmony_ci			++fmt;		/* this also skips first '%' */
17462306a36Sopenharmony_ci			switch (*fmt) {
17562306a36Sopenharmony_ci				case '-': flags |= LEFT; goto repeat;
17662306a36Sopenharmony_ci				case '+': flags |= PLUS; goto repeat;
17762306a36Sopenharmony_ci				case ' ': flags |= SPACE; goto repeat;
17862306a36Sopenharmony_ci				case '#': flags |= SPECIAL; goto repeat;
17962306a36Sopenharmony_ci				case '0': flags |= ZEROPAD; goto repeat;
18062306a36Sopenharmony_ci				}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci		/* get field width */
18362306a36Sopenharmony_ci		field_width = -1;
18462306a36Sopenharmony_ci		if ('0' <= *fmt && *fmt <= '9')
18562306a36Sopenharmony_ci			field_width = skip_atoi(&fmt);
18662306a36Sopenharmony_ci		else if (*fmt == '*') {
18762306a36Sopenharmony_ci			++fmt;
18862306a36Sopenharmony_ci			/* it's the next argument */
18962306a36Sopenharmony_ci			field_width = va_arg(args, int);
19062306a36Sopenharmony_ci			if (field_width < 0) {
19162306a36Sopenharmony_ci				field_width = -field_width;
19262306a36Sopenharmony_ci				flags |= LEFT;
19362306a36Sopenharmony_ci			}
19462306a36Sopenharmony_ci		}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci		/* get the precision */
19762306a36Sopenharmony_ci		precision = -1;
19862306a36Sopenharmony_ci		if (*fmt == '.') {
19962306a36Sopenharmony_ci			++fmt;
20062306a36Sopenharmony_ci			if ('0' <= *fmt && *fmt <= '9')
20162306a36Sopenharmony_ci				precision = skip_atoi(&fmt);
20262306a36Sopenharmony_ci			else if (*fmt == '*') {
20362306a36Sopenharmony_ci				++fmt;
20462306a36Sopenharmony_ci				/* it's the next argument */
20562306a36Sopenharmony_ci				precision = va_arg(args, int);
20662306a36Sopenharmony_ci			}
20762306a36Sopenharmony_ci			if (precision < 0)
20862306a36Sopenharmony_ci				precision = 0;
20962306a36Sopenharmony_ci		}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci		/* get the conversion qualifier */
21262306a36Sopenharmony_ci		qualifier = -1;
21362306a36Sopenharmony_ci		if (*fmt == 'l' && *(fmt + 1) == 'l') {
21462306a36Sopenharmony_ci			qualifier = 'q';
21562306a36Sopenharmony_ci			fmt += 2;
21662306a36Sopenharmony_ci		} else if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L'
21762306a36Sopenharmony_ci			|| *fmt == 'Z') {
21862306a36Sopenharmony_ci			qualifier = *fmt;
21962306a36Sopenharmony_ci			++fmt;
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		/* default base */
22362306a36Sopenharmony_ci		base = 10;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		switch (*fmt) {
22662306a36Sopenharmony_ci		case 'c':
22762306a36Sopenharmony_ci			if (!(flags & LEFT))
22862306a36Sopenharmony_ci				while (--field_width > 0)
22962306a36Sopenharmony_ci					*str++ = ' ';
23062306a36Sopenharmony_ci			*str++ = (unsigned char) va_arg(args, int);
23162306a36Sopenharmony_ci			while (--field_width > 0)
23262306a36Sopenharmony_ci				*str++ = ' ';
23362306a36Sopenharmony_ci			continue;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci		case 's':
23662306a36Sopenharmony_ci			s = va_arg(args, char *);
23762306a36Sopenharmony_ci			if (!s)
23862306a36Sopenharmony_ci				s = "<NULL>";
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci			len = strnlen(s, precision);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci			if (!(flags & LEFT))
24362306a36Sopenharmony_ci				while (len < field_width--)
24462306a36Sopenharmony_ci					*str++ = ' ';
24562306a36Sopenharmony_ci			for (i = 0; i < len; ++i)
24662306a36Sopenharmony_ci				*str++ = *s++;
24762306a36Sopenharmony_ci			while (len < field_width--)
24862306a36Sopenharmony_ci				*str++ = ' ';
24962306a36Sopenharmony_ci			continue;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci		case 'p':
25262306a36Sopenharmony_ci			if (field_width == -1) {
25362306a36Sopenharmony_ci				field_width = 2*sizeof(void *);
25462306a36Sopenharmony_ci				flags |= ZEROPAD;
25562306a36Sopenharmony_ci			}
25662306a36Sopenharmony_ci			str = number(str,
25762306a36Sopenharmony_ci				(unsigned long) va_arg(args, void *), 16,
25862306a36Sopenharmony_ci				field_width, precision, flags);
25962306a36Sopenharmony_ci			continue;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		case 'n':
26362306a36Sopenharmony_ci			if (qualifier == 'l') {
26462306a36Sopenharmony_ci				long * ip = va_arg(args, long *);
26562306a36Sopenharmony_ci				*ip = (str - buf);
26662306a36Sopenharmony_ci			} else if (qualifier == 'Z') {
26762306a36Sopenharmony_ci				size_t * ip = va_arg(args, size_t *);
26862306a36Sopenharmony_ci				*ip = (str - buf);
26962306a36Sopenharmony_ci			} else {
27062306a36Sopenharmony_ci				int * ip = va_arg(args, int *);
27162306a36Sopenharmony_ci				*ip = (str - buf);
27262306a36Sopenharmony_ci			}
27362306a36Sopenharmony_ci			continue;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci		case '%':
27662306a36Sopenharmony_ci			*str++ = '%';
27762306a36Sopenharmony_ci			continue;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci		/* integer number formats - set up the flags and "break" */
28062306a36Sopenharmony_ci		case 'o':
28162306a36Sopenharmony_ci			base = 8;
28262306a36Sopenharmony_ci			break;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci		case 'X':
28562306a36Sopenharmony_ci			flags |= LARGE;
28662306a36Sopenharmony_ci		case 'x':
28762306a36Sopenharmony_ci			base = 16;
28862306a36Sopenharmony_ci			break;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		case 'd':
29162306a36Sopenharmony_ci		case 'i':
29262306a36Sopenharmony_ci			flags |= SIGN;
29362306a36Sopenharmony_ci		case 'u':
29462306a36Sopenharmony_ci			break;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci		default:
29762306a36Sopenharmony_ci			*str++ = '%';
29862306a36Sopenharmony_ci			if (*fmt)
29962306a36Sopenharmony_ci				*str++ = *fmt;
30062306a36Sopenharmony_ci			else
30162306a36Sopenharmony_ci				--fmt;
30262306a36Sopenharmony_ci			continue;
30362306a36Sopenharmony_ci		}
30462306a36Sopenharmony_ci		if (qualifier == 'l') {
30562306a36Sopenharmony_ci			num = va_arg(args, unsigned long);
30662306a36Sopenharmony_ci			if (flags & SIGN)
30762306a36Sopenharmony_ci				num = (signed long) num;
30862306a36Sopenharmony_ci		} else if (qualifier == 'q') {
30962306a36Sopenharmony_ci			num = va_arg(args, unsigned long long);
31062306a36Sopenharmony_ci			if (flags & SIGN)
31162306a36Sopenharmony_ci				num = (signed long long) num;
31262306a36Sopenharmony_ci		} else if (qualifier == 'Z') {
31362306a36Sopenharmony_ci			num = va_arg(args, size_t);
31462306a36Sopenharmony_ci		} else if (qualifier == 'h') {
31562306a36Sopenharmony_ci			num = (unsigned short) va_arg(args, int);
31662306a36Sopenharmony_ci			if (flags & SIGN)
31762306a36Sopenharmony_ci				num = (signed short) num;
31862306a36Sopenharmony_ci		} else {
31962306a36Sopenharmony_ci			num = va_arg(args, unsigned int);
32062306a36Sopenharmony_ci			if (flags & SIGN)
32162306a36Sopenharmony_ci				num = (signed int) num;
32262306a36Sopenharmony_ci		}
32362306a36Sopenharmony_ci		str = number(str, num, base, field_width, precision, flags);
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci	*str = '\0';
32662306a36Sopenharmony_ci	return str-buf;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ciint sprintf(char * buf, const char *fmt, ...)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	va_list args;
33262306a36Sopenharmony_ci	int i;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	va_start(args, fmt);
33562306a36Sopenharmony_ci	i=vsprintf(buf,fmt,args);
33662306a36Sopenharmony_ci	va_end(args);
33762306a36Sopenharmony_ci	return i;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic char sprint_buf[1024];
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ciint
34362306a36Sopenharmony_ciprintf(const char *fmt, ...)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	va_list args;
34662306a36Sopenharmony_ci	int n;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	va_start(args, fmt);
34962306a36Sopenharmony_ci	n = vsprintf(sprint_buf, fmt, args);
35062306a36Sopenharmony_ci	va_end(args);
35162306a36Sopenharmony_ci	if (console_ops.write)
35262306a36Sopenharmony_ci		console_ops.write(sprint_buf, n);
35362306a36Sopenharmony_ci	return n;
35462306a36Sopenharmony_ci}
355