1 /* 2 * Tiny printf version for SPL 3 * 4 * Copied from: 5 * http://www.sparetimelabs.com/printfrevisited/printfrevisited.php 6 * 7 * Copyright (C) 2004,2008 Kustaa Nyholm 8 * 9 * SPDX-License-Identifier: LGPL-2.1+ 10 */ 11 12 #include <common.h> 13 #include <stdarg.h> 14 #include <serial.h> 15 16 struct printf_info { 17 char *bf; /* Digit buffer */ 18 char zs; /* non-zero if a digit has been written */ 19 char *outstr; /* Next output position for sprintf() */ 20 21 /* Output a character */ 22 void (*putc)(struct printf_info *info, char ch); 23 }; 24 25 static void putc_normal(struct printf_info *info, char ch) 26 { 27 putc(ch); 28 } 29 30 static void out(struct printf_info *info, char c) 31 { 32 *info->bf++ = c; 33 } 34 35 static void out_dgt(struct printf_info *info, char dgt) 36 { 37 out(info, dgt + (dgt < 10 ? '0' : 'a' - 10)); 38 info->zs = 1; 39 } 40 41 static void div_out(struct printf_info *info, unsigned long *num, 42 unsigned long div) 43 { 44 unsigned char dgt = 0; 45 46 while (*num >= div) { 47 *num -= div; 48 dgt++; 49 } 50 51 if (info->zs || dgt > 0) 52 out_dgt(info, dgt); 53 } 54 55 static int _vprintf(struct printf_info *info, const char *fmt, va_list va) 56 { 57 char ch; 58 char *p; 59 unsigned long num; 60 char buf[12]; 61 unsigned long div; 62 63 while ((ch = *(fmt++))) { 64 if (ch != '%') { 65 info->putc(info, ch); 66 } else { 67 bool lz = false; 68 int width = 0; 69 bool islong = false; 70 71 ch = *(fmt++); 72 if (ch == '-') 73 ch = *(fmt++); 74 75 if (ch == '0') { 76 ch = *(fmt++); 77 lz = 1; 78 } 79 80 if (ch >= '0' && ch <= '9') { 81 width = 0; 82 while (ch >= '0' && ch <= '9') { 83 width = (width * 10) + ch - '0'; 84 ch = *fmt++; 85 } 86 } 87 if (ch == 'l') { 88 ch = *(fmt++); 89 islong = true; 90 } 91 92 info->bf = buf; 93 p = info->bf; 94 info->zs = 0; 95 96 switch (ch) { 97 case '\0': 98 goto abort; 99 case 'u': 100 case 'd': 101 div = 1000000000; 102 if (islong) { 103 num = va_arg(va, unsigned long); 104 if (sizeof(long) > 4) 105 div *= div * 10; 106 } else { 107 num = va_arg(va, unsigned int); 108 } 109 110 if (ch == 'd') { 111 if (islong && (long)num < 0) { 112 num = -(long)num; 113 out(info, '-'); 114 } else if (!islong && (int)num < 0) { 115 num = -(int)num; 116 out(info, '-'); 117 } 118 } 119 if (!num) { 120 out_dgt(info, 0); 121 } else { 122 for (; div; div /= 10) 123 div_out(info, &num, div); 124 } 125 break; 126 case 'x': 127 if (islong) { 128 num = va_arg(va, unsigned long); 129 div = 1UL << (sizeof(long) * 8 - 4); 130 } else { 131 num = va_arg(va, unsigned int); 132 div = 0x10000000; 133 } 134 if (!num) { 135 out_dgt(info, 0); 136 } else { 137 for (; div; div /= 0x10) 138 div_out(info, &num, div); 139 } 140 break; 141 case 'c': 142 out(info, (char)(va_arg(va, int))); 143 break; 144 case 's': 145 p = va_arg(va, char*); 146 break; 147 case '%': 148 out(info, '%'); 149 default: 150 break; 151 } 152 153 *info->bf = 0; 154 info->bf = p; 155 while (*info->bf++ && width > 0) 156 width--; 157 while (width-- > 0) 158 info->putc(info, lz ? '0' : ' '); 159 if (p) { 160 while ((ch = *p++)) 161 info->putc(info, ch); 162 } 163 } 164 } 165 166 abort: 167 return 0; 168 } 169 170 int vprintf(const char *fmt, va_list va) 171 { 172 struct printf_info info; 173 174 info.putc = putc_normal; 175 return _vprintf(&info, fmt, va); 176 } 177 178 int printf(const char *fmt, ...) 179 { 180 struct printf_info info; 181 182 va_list va; 183 int ret; 184 185 info.putc = putc_normal; 186 va_start(va, fmt); 187 ret = _vprintf(&info, fmt, va); 188 va_end(va); 189 190 return ret; 191 } 192 193 static void putc_outstr(struct printf_info *info, char ch) 194 { 195 *info->outstr++ = ch; 196 } 197 198 int sprintf(char *buf, const char *fmt, ...) 199 { 200 struct printf_info info; 201 va_list va; 202 int ret; 203 204 va_start(va, fmt); 205 info.outstr = buf; 206 info.putc = putc_outstr; 207 ret = _vprintf(&info, fmt, va); 208 va_end(va); 209 *info.outstr = '\0'; 210 211 return ret; 212 } 213 214 /* Note that size is ignored */ 215 int snprintf(char *buf, size_t size, const char *fmt, ...) 216 { 217 struct printf_info info; 218 va_list va; 219 int ret; 220 221 va_start(va, fmt); 222 info.outstr = buf; 223 info.putc = putc_outstr; 224 ret = _vprintf(&info, fmt, va); 225 va_end(va); 226 *info.outstr = '\0'; 227 228 return ret; 229 } 230 231 void __assert_fail(const char *assertion, const char *file, unsigned line, 232 const char *function) 233 { 234 /* This will not return */ 235 printf("%s:%u: %s: Assertion `%s' failed.", file, line, function, 236 assertion); 237 hang(); 238 } 239