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