1/* 2 * Copyright (c) 2008 Travis Geiselbrecht 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining 5 * a copy of this software and associated documentation files 6 * (the "Software"), to deal in the Software without restriction, 7 * including without limitation the rights to use, copy, modify, merge, 8 * publish, distribute, sublicense, and/or sell copies of the Software, 9 * and to permit persons to whom the Software is furnished to do so, 10 * subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be 13 * included in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 */ 23#include <aboot/aboot.h> 24#include <aboot/types.h> 25#include <stdarg.h> 26 27int printf(const char *fmt, ...) 28{ 29 char buf[128]; 30 int err; 31 32 va_list ap; 33 va_start(ap, fmt); 34 err = vsnprintf(buf, sizeof(buf), fmt, ap); 35 va_end(ap); 36 37 serial_puts(buf); 38 return err; 39} 40 41int snprintf(char *str, size_t len, const char *fmt, ...) 42{ 43 int err; 44 45 va_list ap; 46 va_start(ap, fmt); 47 err = vsnprintf(str, len, fmt, ap); 48 va_end(ap); 49 50 return err; 51} 52 53#define LONGFLAG 0x00000001 54#define LONGLONGFLAG 0x00000002 55#define HALFFLAG 0x00000004 56#define HALFHALFFLAG 0x00000008 57#define SIZETFLAG 0x00000010 58#define ALTFLAG 0x00000020 59#define CAPSFLAG 0x00000040 60#define SHOWSIGNFLAG 0x00000080 61#define SIGNEDFLAG 0x00000100 62#define LEFTFORMATFLAG 0x00000200 63#define LEADZEROFLAG 0x00000400 64 65static char *long_to_string(char *buf, unsigned long n, int len, uint flag) 66{ 67 int pos = len; 68 int negative = 0; 69 70 if((flag & SIGNEDFLAG) && (long long)n < 0) { 71 negative = 1; 72 n = -n; 73 } 74 75 buf[--pos] = 0; 76 77 /* only do the math if the number is >= 10 */ 78 while(n >= 10) { 79 int digit = n % 10; 80 81 n /= 10; 82 83 buf[--pos] = digit + '0'; 84 } 85 buf[--pos] = n + '0'; 86 87 if(negative) 88 buf[--pos] = '-'; 89 else if((flag & SHOWSIGNFLAG)) 90 buf[--pos] = '+'; 91 92 return &buf[pos]; 93} 94 95static char *long_to_hexstring(char *buf, unsigned long u, int len, uint flag) 96{ 97 int pos = len; 98 static const char hextable[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; 99 static const char hextable_caps[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 100 const char *table; 101 102 if((flag & CAPSFLAG)) 103 table = hextable_caps; 104 else 105 table = hextable; 106 107 buf[--pos] = 0; 108 do { 109 unsigned int digit = u % 16; 110 u /= 16; 111 112 buf[--pos] = table[digit]; 113 } while(u != 0); 114 115 return &buf[pos]; 116} 117 118int vsprintf(char *str, const char *fmt, va_list ap) 119{ 120 return vsnprintf(str, INT_MAX, fmt, ap); 121} 122 123int vsnprintf(char *str, size_t len, const char *fmt, va_list ap) 124{ 125 char c; 126 unsigned char uc; 127 const char *s; 128 unsigned long n; 129 void *ptr; 130 int flags; 131 unsigned int format_num; 132 size_t chars_written = 0; 133 char num_buffer[32]; 134 135#define OUTPUT_CHAR(c) do { (*str++ = c); chars_written++; if (chars_written + 1 == len) goto done; } while(0) 136#define OUTPUT_CHAR_NOLENCHECK(c) do { (*str++ = c); chars_written++; } while(0) 137 138 for(;;) { 139 /* handle regular chars that aren't format related */ 140 while((c = *fmt++) != 0) { 141 if(c == '%') 142 break; /* we saw a '%', break and start parsing format */ 143 OUTPUT_CHAR(c); 144 } 145 146 /* make sure we haven't just hit the end of the string */ 147 if(c == 0) 148 break; 149 150 /* reset the format state */ 151 flags = 0; 152 format_num = 0; 153 154next_format: 155 /* grab the next format character */ 156 c = *fmt++; 157 if(c == 0) 158 break; 159 160 switch(c) { 161 case '0'...'9': 162 if (c == '0' && format_num == 0) 163 flags |= LEADZEROFLAG; 164 format_num *= 10; 165 format_num += c - '0'; 166 goto next_format; 167 case '.': 168 /* XXX for now eat numeric formatting */ 169 goto next_format; 170 case '%': 171 OUTPUT_CHAR('%'); 172 break; 173 case 'c': 174 uc = va_arg(ap, unsigned int); 175 OUTPUT_CHAR(uc); 176 break; 177 case 's': 178 s = va_arg(ap, const char *); 179 if(s == 0) 180 s = "<null>"; 181 goto _output_string; 182 case '-': 183 flags |= LEFTFORMATFLAG; 184 goto next_format; 185 case '+': 186 flags |= SHOWSIGNFLAG; 187 goto next_format; 188 case '#': 189 flags |= ALTFLAG; 190 goto next_format; 191 case 'l': 192 flags |= LONGFLAG; 193 goto next_format; 194 case 'h': 195 if(flags & HALFFLAG) 196 flags |= HALFHALFFLAG; 197 flags |= HALFFLAG; 198 goto next_format; 199 case 'z': 200 flags |= SIZETFLAG; 201 goto next_format; 202 case 'D': 203 flags |= LONGFLAG; 204 /* fallthrough */ 205 case 'i': 206 case 'd': 207 n = (flags & LONGFLAG) ? va_arg(ap, long) : 208 (flags & HALFHALFFLAG) ? (signed char)va_arg(ap, int) : 209 (flags & HALFFLAG) ? (short)va_arg(ap, int) : 210 (flags & SIZETFLAG) ? va_arg(ap, ssize_t) : 211 va_arg(ap, int); 212 flags |= SIGNEDFLAG; 213 s = long_to_string(num_buffer, n, sizeof(num_buffer), flags); 214 goto _output_string; 215 case 'U': 216 flags |= LONGFLAG; 217 /* fallthrough */ 218 case 'u': 219 n = (flags & LONGFLAG) ? va_arg(ap, unsigned long) : 220 (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) : 221 (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) : 222 (flags & SIZETFLAG) ? va_arg(ap, size_t) : 223 va_arg(ap, unsigned int); 224 s = long_to_string(num_buffer, n, sizeof(num_buffer), flags); 225 goto _output_string; 226 case 'p': 227 flags |= LONGFLAG | ALTFLAG; 228 goto hex; 229 case 'X': 230 flags |= CAPSFLAG; 231 /* fallthrough */ 232 hex: 233 case 'x': 234 n = (flags & LONGFLAG) ? va_arg(ap, unsigned long) : 235 (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) : 236 (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) : 237 (flags & SIZETFLAG) ? va_arg(ap, size_t) : 238 va_arg(ap, unsigned int); 239 s = long_to_hexstring(num_buffer, n, sizeof(num_buffer), flags); 240 if(flags & ALTFLAG) { 241 OUTPUT_CHAR('0'); 242 OUTPUT_CHAR((flags & CAPSFLAG) ? 'X': 'x'); 243 } 244 goto _output_string; 245 case 'n': 246 ptr = va_arg(ap, void *); 247 if(flags & LONGLONGFLAG) 248 *(long long *)ptr = chars_written; 249 else if(flags & LONGFLAG) 250 *(long *)ptr = chars_written; 251 else if(flags & HALFHALFFLAG) 252 *(signed char *)ptr = chars_written; 253 else if(flags & HALFFLAG) 254 *(short *)ptr = chars_written; 255 else if(flags & SIZETFLAG) 256 *(size_t *)ptr = chars_written; 257 else 258 *(int *)ptr = chars_written; 259 break; 260 default: 261 OUTPUT_CHAR('%'); 262 OUTPUT_CHAR(c); 263 break; 264 } 265 266 /* move on to the next field */ 267 continue; 268 269 /* shared output code */ 270_output_string: 271 if (flags & LEFTFORMATFLAG) { 272 /* left justify the text */ 273 uint count = 0; 274 while(*s != 0) { 275 OUTPUT_CHAR(*s++); 276 count++; 277 } 278 279 /* pad to the right (if necessary) */ 280 for (; format_num > count; format_num--) 281 OUTPUT_CHAR(' '); 282 } else { 283 /* right justify the text (digits) */ 284 size_t string_len = strlen(s); 285 char outchar = (flags & LEADZEROFLAG) ? '0' : ' '; 286 for (; format_num > string_len; format_num--) 287 OUTPUT_CHAR(outchar); 288 289 /* output the string */ 290 while(*s != 0) 291 OUTPUT_CHAR(*s++); 292 } 293 continue; 294 } 295 296done: 297 /* null terminate */ 298 OUTPUT_CHAR_NOLENCHECK('\0'); 299 chars_written--; /* don't count the null */ 300 301#undef OUTPUT_CHAR 302#undef OUTPUT_CHAR_NOLENCHECK 303 304 return chars_written; 305} 306 307 308