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