1 /*
2  * Copyright (c) 2020 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <stdarg.h>
7 #include <stdbool.h>
8 #include <stddef.h>
9 
10 /* Tiny, but not-as-primitive-as-it-looks implementation of something
11  * like s/n/printf().  Handles %d, %x, %p, %c and %s only, allows a
12  * "l" qualifier on %d and %x (and silently ignores one %s/%c/%p).
13  * Accepts, but ignores, field width and precision values that match:
14  * the regex: [0-9]*\.?[0-9]*
15  */
16 
17 struct _pfr {
18 	char *buf;
19 	int len;
20 	int idx;
21 };
22 
23 /* Set this function pointer to something that generates output */
24 static void (*z_putchar)(int c);
25 
pc(struct _pfr * r,int c)26 static void pc(struct _pfr *r, int c)
27 {
28 	if (r->buf != NULL) {
29 		if (r->idx <= r->len) {
30 			r->buf[r->idx] = c;
31 		}
32 	} else {
33 		z_putchar(c);
34 	}
35 	r->idx++;
36 }
37 
prdec(struct _pfr * r,long v)38 static void prdec(struct _pfr *r, long v)
39 {
40 	if (v < 0) {
41 		pc(r, '-');
42 		v = -v;
43 	}
44 
45 	char digs[11 * sizeof(long)/4];
46 	int i = sizeof(digs) - 1;
47 
48 	digs[i] = 0;
49 	--i;
50 	while (v || i == 9) {
51 		digs[i] = '0' + (v % 10);
52 		--i;
53 		v /= 10;
54 	}
55 
56 	++i;
57 	while (digs[i] != '\0') {
58 		pc(r, digs[i]);
59 		++i;
60 	}
61 }
62 
endrec(struct _pfr * r)63 static void endrec(struct _pfr *r)
64 {
65 	if (r->buf && r->idx < r->len) {
66 		r->buf[r->idx] = 0;
67 	}
68 }
69 
vpf(struct _pfr * r,const char * f,va_list ap)70 static int vpf(struct _pfr *r, const char *f, va_list ap)
71 {
72 	for (/**/; *f != '\0'; f++) {
73 		bool islong = false;
74 
75 		if (*f != '%') {
76 			pc(r, *f);
77 			continue;
78 		}
79 
80 		if (f[1] == 'l') {
81 			islong = sizeof(long) > 4;
82 			f++;
83 		}
84 
85 		/* Ignore (but accept) field width and precision values */
86 		while (f[1] >= '0' && f[1] <= '9') {
87 			f++;
88 		}
89 		if (f[1] == '.') {
90 			f++;
91 		}
92 		while (f[1] >= '0' && f[1] <= '9') {
93 			f++;
94 		}
95 
96 		switch (*(++f)) {
97 		case 0:
98 			return r->idx;
99 		case '%':
100 			pc(r, '%');
101 			break;
102 		case 'c':
103 			pc(r, va_arg(ap, int));
104 			break;
105 		case 's': {
106 			char *s = va_arg(ap, char *);
107 
108 			while (*s != '\0') {
109 				pc(r, *s);
110 				++s;
111 			}
112 			break;
113 		}
114 		case 'p':
115 			pc(r, '0');
116 			pc(r, 'x'); /* fall through... */
117 			islong = sizeof(long) > 4;
118 		case 'x': {
119 			int sig = 0;
120 			unsigned long v = islong ? va_arg(ap, unsigned long)
121 				: va_arg(ap, unsigned int);
122 			for (int i = 2*sizeof(long) - 1; i >= 0; i--) {
123 				int d = (v >> (i*4)) & 0xf;
124 
125 				sig += !!d;
126 				if (sig || i == 0) {
127 					pc(r, "0123456789abcdef"[d]);
128 				}
129 			}
130 			break;
131 		}
132 		case 'd':
133 			prdec(r, va_arg(ap, int));
134 			break;
135 		default:
136 			pc(r, '%');
137 			pc(r, *f);
138 		}
139 	}
140 	endrec(r);
141 	return r->idx;
142 }
143 
144 #define CALL_VPF(rec)				\
145 	va_list ap;				\
146 	va_start(ap, f);			\
147 	ret = vpf(&r, f, ap);			\
148 	va_end(ap);
149 
snprintf(char * buf,unsigned long len,const char * f,...)150 static inline int snprintf(char *buf, unsigned long len, const char *f, ...)
151 {
152 	int ret;
153 	struct _pfr r = { .buf = buf, .len = len };
154 
155 	CALL_VPF(&r);
156 	return ret;
157 }
158 
sprintf(char * buf,const char * f,...)159 static inline int sprintf(char *buf, const char *f, ...)
160 {
161 	int ret;
162 	struct _pfr r = { .buf = buf, .len = 0x7fffffff };
163 
164 	CALL_VPF(&r);
165 	return ret;
166 }
167 
printf(const char * f,...)168 static inline int printf(const char *f, ...)
169 {
170 	int ret;
171 	struct _pfr r = {0};
172 
173 	CALL_VPF(&r);
174 	return ret;
175 }
176