1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <stdio.h>
18 #include <printf.h>
19 #include <cpu/cpuMath.h>
20
21 #define FLAG_ALT (1 << 0)
22 #define FLAG_ZERO_EXTEND (1 << 1)
23 #define FLAG_IS_SIGNED (1 << 2)
24 #define FLAG_NEG_PAD (1 << 3)
25 #define FLAG_CAPS (1 << 4)
26
27 struct PrintfData
28 {
29 uint64_t number;
30 void *userData;
31 uint32_t fieldWidth;
32 uint32_t precision;
33 uint32_t flags;
34 uint8_t posChar;
35 uint8_t base;
36 };
37
StrPrvPrintfEx_number(printf_write_c putc_,struct PrintfData * data,bool * bail)38 static uint32_t StrPrvPrintfEx_number(printf_write_c putc_, struct PrintfData *data, bool *bail)
39 {
40 char buf[64];
41 uint32_t idx = sizeof(buf) - 1;
42 uint32_t chr, i;
43 uint32_t numPrinted = 0;
44
45 *bail = false;
46
47 #ifdef USE_PRINTF_FLAG_CHARS
48 if (data->fieldWidth > sizeof(buf) - 1)
49 data->fieldWidth = sizeof(buf) - 1;
50
51 if (data->precision > sizeof(buf) - 1)
52 data->precision = sizeof(buf) - 1;
53 #endif
54
55 buf[idx--] = 0; //terminate
56
57 if (data->flags & FLAG_IS_SIGNED) {
58
59 if (((int64_t)data->number) < 0) {
60
61 data->posChar = '-';
62 data->number = -data->number;
63 }
64 }
65
66 do {
67 if (data->base == 8) {
68
69 chr = (data->number & 0x07) + '0';
70 data->number >>= 3;
71 }
72 else if (data->base == 10) {
73
74 uint64_t t = U64_DIV_BY_CONST_U16(data->number, 10);
75 chr = (data->number - t * 10) + '0';
76 data->number = t;
77 }
78 else {
79
80 chr = data->number & 0x0F;
81 data->number >>= 4;
82 chr = (chr >= 10) ? (chr + (data->flags & FLAG_CAPS ? 'A' : 'a') - 10) : (chr + '0');
83 }
84
85 buf[idx--] = chr;
86
87 numPrinted++;
88
89 } while (data->number);
90
91 #ifdef USE_PRINTF_FLAG_CHARS
92 while (data->precision > numPrinted) {
93
94 buf[idx--] = '0';
95 numPrinted++;
96 }
97
98 if (data->flags & FLAG_ALT) {
99
100 if (data->base == 8) {
101
102 if (buf[idx+1] != '0') {
103 buf[idx--] = '0';
104 numPrinted++;
105 }
106 }
107 else if (data->base == 16) {
108
109 buf[idx--] = data->flags & FLAG_CAPS ? 'X' : 'x';
110 numPrinted++;
111 buf[idx--] = '0';
112 numPrinted++;
113 }
114 }
115
116
117 if (!(data->flags & FLAG_NEG_PAD)) {
118
119 if (data->fieldWidth > 0 && data->posChar != '\0')
120 data->fieldWidth--;
121
122 while (data->fieldWidth > numPrinted) {
123
124 buf[idx--] = data->flags & FLAG_ZERO_EXTEND ? '0' : ' ';
125 numPrinted++;
126 }
127 }
128 #endif
129
130 if (data->posChar != '\0') {
131
132 buf[idx--] = data->posChar;
133 numPrinted++;
134 }
135
136 idx++;
137
138 for(i = 0; i < numPrinted; i++) {
139
140 if (!putc_(data->userData,(buf + idx)[i])) {
141
142 *bail = true;
143 break;
144 }
145 }
146
147 #ifdef USE_PRINTF_FLAG_CHARS
148 if (!*bail && data->flags & FLAG_NEG_PAD) {
149
150 for(i = numPrinted; i < data->fieldWidth; i++) {
151
152 if (!putc_(data->userData, ' ')) {
153
154 *bail = true;
155 break;
156 }
157 }
158 }
159 #endif
160
161 return i;
162 }
163
StrVPrintf_StrLen_withMax(const char * s,uint32_t max)164 static uint32_t StrVPrintf_StrLen_withMax(const char* s, uint32_t max)
165 {
166 uint32_t len = 0;
167
168 while ((*s++) && (len < max)) len++;
169
170 return len;
171 }
172
StrVPrintf_StrLen(const char * s)173 static uint32_t StrVPrintf_StrLen(const char* s)
174 {
175 uint32_t len = 0;
176
177 while (*s++) len++;
178
179 return len;
180 }
181
prvGetChar(const char ** fmtP)182 static inline char prvGetChar(const char** fmtP)
183 {
184
185 return *(*fmtP)++;
186 }
187
cvprintf(printf_write_c putc_f,uint32_t flags,void * userData,const char * fmtStr,va_list vl)188 uint32_t cvprintf(printf_write_c putc_f, uint32_t flags, void* userData, const char* fmtStr, va_list vl)
189 {
190
191 char c, t;
192 uint32_t numPrinted = 0;
193 double dbl;
194 long double ldbl;
195 struct PrintfData data;
196
197 data.userData = userData;
198
199 #define putc_(_ud,_c) \
200 do { \
201 if (!putc_f(_ud,_c)) \
202 goto out; \
203 } while(0)
204
205 while ((c = prvGetChar(&fmtStr)) != 0) {
206
207 if (c == '\n') {
208
209 putc_(userData,c);
210 numPrinted++;
211 }
212 else if (c == '%') {
213 uint32_t len, i;
214 const char* str;
215 bool useChar = false, useShort = false, useLong = false, useLongLong = false, useLongDouble =false, useSizeT = false, usePtrdiffT = false;
216 bool havePrecision = false, bail = false;
217
218 data.fieldWidth = 0;
219 data.precision = 0;
220 data.flags = 0;
221 data.posChar = 0;
222
223 more_fmt:
224
225 c = prvGetChar(&fmtStr);
226
227 switch(c) {
228
229 case '%':
230
231 putc_(userData,c);
232 numPrinted++;
233 break;
234
235 case 'c':
236
237 t = va_arg(vl,unsigned int);
238 putc_(userData,t);
239 numPrinted++;
240 break;
241
242 case 's':
243
244 str = va_arg(vl,char*);
245 if (!str) str = "(null)";
246
247 if (data.precision)
248 len = StrVPrintf_StrLen_withMax(str,data.precision);
249 else
250 len = StrVPrintf_StrLen(str);
251
252 #ifdef USE_PRINTF_FLAG_CHARS
253 if (!(data.flags & FLAG_NEG_PAD)) {
254 for(i = len; i < data.fieldWidth; i++) {
255 putc_(userData, ' ');
256 numPrinted++;
257 }
258 }
259 #endif
260
261 for(i = 0; i < len; i++) {
262 putc_(userData,*str++);
263 numPrinted++;
264 }
265
266 #ifdef USE_PRINTF_FLAG_CHARS
267 if (data.flags & FLAG_NEG_PAD) {
268 for(i = len; i < data.fieldWidth; i++) {
269 putc_(userData, ' ');
270 numPrinted++;
271 }
272 }
273 #endif
274
275 break;
276
277 case '.':
278
279 havePrecision = true;
280 goto more_fmt;
281
282 case '0':
283
284 if (!(data.flags & FLAG_ZERO_EXTEND) && !data.fieldWidth && !havePrecision) {
285
286 data.flags |= FLAG_ZERO_EXTEND;
287 goto more_fmt;
288 }
289
290 case '1':
291 case '2':
292 case '3':
293 case '4':
294 case '5':
295 case '6':
296 case '7':
297 case '8':
298 case '9':
299
300 if (havePrecision)
301 data.precision = (data.precision * 10) + c - '0';
302 else
303 data.fieldWidth = (data.fieldWidth * 10) + c - '0';
304 goto more_fmt;
305
306 case '#':
307
308 data.flags |= FLAG_ALT;
309 goto more_fmt;
310
311 case '-':
312
313 data.flags |= FLAG_NEG_PAD;
314 goto more_fmt;
315
316 case '+':
317
318 data.posChar = '+';
319 goto more_fmt;
320
321 case ' ':
322
323 if (data.posChar != '+')
324 data.posChar = ' ';
325 goto more_fmt;
326
327 #define GET_UVAL64() \
328 useSizeT ? va_arg(vl, size_t) : \
329 usePtrdiffT ? va_arg(vl, ptrdiff_t) : \
330 useLongLong ? va_arg(vl, unsigned long long) : \
331 useLong ? va_arg(vl, unsigned long) : \
332 useChar ? (unsigned char)va_arg(vl, unsigned int) : \
333 useShort ? (unsigned short)va_arg(vl, unsigned int) : \
334 va_arg(vl, unsigned int)
335
336 #define GET_SVAL64() \
337 useSizeT ? va_arg(vl, size_t) : \
338 usePtrdiffT ? va_arg(vl, ptrdiff_t) : \
339 useLongLong ? va_arg(vl, signed long long) : \
340 useLong ? va_arg(vl, signed long) : \
341 useChar ? (signed char)va_arg(vl, signed int) : \
342 useShort ? (signed short)va_arg(vl, signed int) : \
343 va_arg(vl, signed int)
344
345 case 'u':
346
347 data.number = GET_UVAL64();
348 data.base = 10;
349 data.flags &= ~(FLAG_ALT | FLAG_CAPS);
350 numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail);
351 if (bail)
352 goto out;
353 break;
354
355 case 'd':
356 case 'i':
357
358 data.number = GET_SVAL64();
359 data.base = 10;
360 data.flags &= ~(FLAG_ALT | FLAG_CAPS);
361 data.flags |= FLAG_IS_SIGNED;
362 numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail);
363 if (bail)
364 goto out;
365 break;
366
367 case 'o':
368
369 data.number = GET_UVAL64();
370 data.base = 8;
371 data.flags &= ~FLAG_CAPS;
372 data.posChar = '\0';
373 numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail);
374 if (bail)
375 goto out;
376 break;
377
378 case 'X':
379
380 data.flags |= FLAG_CAPS;
381
382 case 'x':
383
384 data.number = GET_UVAL64();
385 data.base = 16;
386 data.posChar = '\0';
387 numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail);
388 if (bail)
389 goto out;
390 break;
391
392 case 'p':
393
394 data.number = (uintptr_t)va_arg(vl, const void*);
395 data.base = 16;
396 data.flags &= ~FLAG_CAPS;
397 data.flags |= FLAG_ALT;
398 data.posChar = '\0';
399 numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail);
400 if (bail)
401 goto out;
402 break;
403
404 #undef GET_UVAL64
405 #undef GET_SVAL64
406
407 case 'F':
408
409 data.flags |= FLAG_CAPS;
410
411 case 'f':
412
413 if (flags & PRINTF_FLAG_CHRE) {
414 if (flags & PRINTF_FLAG_SHORT_DOUBLE) {
415 if (useLongDouble) {
416 dbl = va_arg(vl, double);
417 data.number = *(uint64_t *)(&dbl);
418 } else {
419 // just grab the 32-bits
420 data.number = va_arg(vl, uint32_t);
421 }
422 } else {
423 if (useLongDouble) {
424 ldbl = va_arg(vl, long double);
425 data.number = *(uint64_t *)(&ldbl);
426 } else {
427 dbl = va_arg(vl, double);
428 data.number = *(uint64_t *)(&dbl);
429 }
430 }
431 data.base = 16;
432 data.flags |= FLAG_ALT;
433 data.posChar = '\0';
434 numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail);
435 } else {
436 bail = true;
437 }
438 if (bail)
439 goto out;
440 break;
441
442 case 'h':
443
444 if (useShort)
445 useChar = true;
446 useShort = true;
447 goto more_fmt;
448
449 case 'L':
450
451 useLongDouble = true;
452 goto more_fmt;
453
454 case 'l':
455
456 if (useLong)
457 useLongLong = true;
458 useLong = true;
459 goto more_fmt;
460
461 case 'z':
462
463 useSizeT = true;
464 goto more_fmt;
465
466 case 't':
467
468 usePtrdiffT = true;
469 goto more_fmt;
470
471 default:
472
473 putc_(userData,c);
474 numPrinted++;
475 break;
476
477 }
478 }
479 else {
480
481 putc_(userData,c);
482 numPrinted++;
483 }
484 }
485
486 out:
487
488 return numPrinted;
489 }
490