1 /* 2 * Copyright (C) Paul Mackerras 1997. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 7 * 2 of the License, or (at your option) any later version. 8 */ 9 #include <stdarg.h> 10 #include <stddef.h> 11 #include "string.h" 12 #include "stdio.h" 13 #include "prom.h" 14 15 size_t strnlen(const char * s, size_t count) 16 { 17 const char *sc; 18 19 for (sc = s; count-- && *sc != '\0'; ++sc) 20 /* nothing */; 21 return sc - s; 22 } 23 24 extern unsigned int __div64_32(unsigned long long *dividend, 25 unsigned int divisor); 26 27 /* The unnecessary pointer compare is there 28 * to check for type safety (n must be 64bit) 29 */ 30 # define do_div(n,base) ({ \ 31 unsigned int __base = (base); \ 32 unsigned int __rem; \ 33 (void)(((typeof((n)) *)0) == ((unsigned long long *)0)); \ 34 if (((n) >> 32) == 0) { \ 35 __rem = (unsigned int)(n) % __base; \ 36 (n) = (unsigned int)(n) / __base; \ 37 } else \ 38 __rem = __div64_32(&(n), __base); \ 39 __rem; \ 40 }) 41 42 static int skip_atoi(const char **s) 43 { 44 int i, c; 45 46 for (i = 0; '0' <= (c = **s) && c <= '9'; ++*s) 47 i = i*10 + c - '0'; 48 return i; 49 } 50 51 #define ZEROPAD 1 /* pad with zero */ 52 #define SIGN 2 /* unsigned/signed long */ 53 #define PLUS 4 /* show plus */ 54 #define SPACE 8 /* space if plus */ 55 #define LEFT 16 /* left justified */ 56 #define SPECIAL 32 /* 0x */ 57 #define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ 58 59 static char * number(char * str, unsigned long long num, int base, int size, int precision, int type) 60 { 61 char c,sign,tmp[66]; 62 const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; 63 int i; 64 65 if (type & LARGE) 66 digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 67 if (type & LEFT) 68 type &= ~ZEROPAD; 69 if (base < 2 || base > 36) 70 return 0; 71 c = (type & ZEROPAD) ? '0' : ' '; 72 sign = 0; 73 if (type & SIGN) { 74 if ((signed long long)num < 0) { 75 sign = '-'; 76 num = - (signed long long)num; 77 size--; 78 } else if (type & PLUS) { 79 sign = '+'; 80 size--; 81 } else if (type & SPACE) { 82 sign = ' '; 83 size--; 84 } 85 } 86 if (type & SPECIAL) { 87 if (base == 16) 88 size -= 2; 89 else if (base == 8) 90 size--; 91 } 92 i = 0; 93 if (num == 0) 94 tmp[i++]='0'; 95 else while (num != 0) { 96 tmp[i++] = digits[do_div(num, base)]; 97 } 98 if (i > precision) 99 precision = i; 100 size -= precision; 101 if (!(type&(ZEROPAD+LEFT))) 102 while(size-->0) 103 *str++ = ' '; 104 if (sign) 105 *str++ = sign; 106 if (type & SPECIAL) { 107 if (base==8) 108 *str++ = '0'; 109 else if (base==16) { 110 *str++ = '0'; 111 *str++ = digits[33]; 112 } 113 } 114 if (!(type & LEFT)) 115 while (size-- > 0) 116 *str++ = c; 117 while (i < precision--) 118 *str++ = '0'; 119 while (i-- > 0) 120 *str++ = tmp[i]; 121 while (size-- > 0) 122 *str++ = ' '; 123 return str; 124 } 125 126 int vsprintf(char *buf, const char *fmt, va_list args) 127 { 128 int len; 129 unsigned long long num; 130 int i, base; 131 char * str; 132 const char *s; 133 134 int flags; /* flags to number() */ 135 136 int field_width; /* width of output field */ 137 int precision; /* min. # of digits for integers; max 138 number of chars for from string */ 139 int qualifier; /* 'h', 'l', or 'L' for integer fields */ 140 /* 'z' support added 23/7/1999 S.H. */ 141 /* 'z' changed to 'Z' --davidm 1/25/99 */ 142 143 144 for (str=buf ; *fmt ; ++fmt) { 145 if (*fmt != '%') { 146 *str++ = *fmt; 147 continue; 148 } 149 150 /* process flags */ 151 flags = 0; 152 repeat: 153 ++fmt; /* this also skips first '%' */ 154 switch (*fmt) { 155 case '-': flags |= LEFT; goto repeat; 156 case '+': flags |= PLUS; goto repeat; 157 case ' ': flags |= SPACE; goto repeat; 158 case '#': flags |= SPECIAL; goto repeat; 159 case '0': flags |= ZEROPAD; goto repeat; 160 } 161 162 /* get field width */ 163 field_width = -1; 164 if ('0' <= *fmt && *fmt <= '9') 165 field_width = skip_atoi(&fmt); 166 else if (*fmt == '*') { 167 ++fmt; 168 /* it's the next argument */ 169 field_width = va_arg(args, int); 170 if (field_width < 0) { 171 field_width = -field_width; 172 flags |= LEFT; 173 } 174 } 175 176 /* get the precision */ 177 precision = -1; 178 if (*fmt == '.') { 179 ++fmt; 180 if ('0' <= *fmt && *fmt <= '9') 181 precision = skip_atoi(&fmt); 182 else if (*fmt == '*') { 183 ++fmt; 184 /* it's the next argument */ 185 precision = va_arg(args, int); 186 } 187 if (precision < 0) 188 precision = 0; 189 } 190 191 /* get the conversion qualifier */ 192 qualifier = -1; 193 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt =='Z') { 194 qualifier = *fmt; 195 ++fmt; 196 } 197 198 /* default base */ 199 base = 10; 200 201 switch (*fmt) { 202 case 'c': 203 if (!(flags & LEFT)) 204 while (--field_width > 0) 205 *str++ = ' '; 206 *str++ = (unsigned char) va_arg(args, int); 207 while (--field_width > 0) 208 *str++ = ' '; 209 continue; 210 211 case 's': 212 s = va_arg(args, char *); 213 if (!s) 214 s = "<NULL>"; 215 216 len = strnlen(s, precision); 217 218 if (!(flags & LEFT)) 219 while (len < field_width--) 220 *str++ = ' '; 221 for (i = 0; i < len; ++i) 222 *str++ = *s++; 223 while (len < field_width--) 224 *str++ = ' '; 225 continue; 226 227 case 'p': 228 if (field_width == -1) { 229 field_width = 2*sizeof(void *); 230 flags |= ZEROPAD; 231 } 232 str = number(str, 233 (unsigned long) va_arg(args, void *), 16, 234 field_width, precision, flags); 235 continue; 236 237 238 case 'n': 239 if (qualifier == 'l') { 240 long * ip = va_arg(args, long *); 241 *ip = (str - buf); 242 } else if (qualifier == 'Z') { 243 size_t * ip = va_arg(args, size_t *); 244 *ip = (str - buf); 245 } else { 246 int * ip = va_arg(args, int *); 247 *ip = (str - buf); 248 } 249 continue; 250 251 case '%': 252 *str++ = '%'; 253 continue; 254 255 /* integer number formats - set up the flags and "break" */ 256 case 'o': 257 base = 8; 258 break; 259 260 case 'X': 261 flags |= LARGE; 262 case 'x': 263 base = 16; 264 break; 265 266 case 'd': 267 case 'i': 268 flags |= SIGN; 269 case 'u': 270 break; 271 272 default: 273 *str++ = '%'; 274 if (*fmt) 275 *str++ = *fmt; 276 else 277 --fmt; 278 continue; 279 } 280 if (qualifier == 'l') { 281 num = va_arg(args, unsigned long); 282 if (flags & SIGN) 283 num = (signed long) num; 284 } else if (qualifier == 'Z') { 285 num = va_arg(args, size_t); 286 } else if (qualifier == 'h') { 287 num = (unsigned short) va_arg(args, int); 288 if (flags & SIGN) 289 num = (signed short) num; 290 } else { 291 num = va_arg(args, unsigned int); 292 if (flags & SIGN) 293 num = (signed int) num; 294 } 295 str = number(str, num, base, field_width, precision, flags); 296 } 297 *str = '\0'; 298 return str-buf; 299 } 300 301 int sprintf(char * buf, const char *fmt, ...) 302 { 303 va_list args; 304 int i; 305 306 va_start(args, fmt); 307 i=vsprintf(buf,fmt,args); 308 va_end(args); 309 return i; 310 } 311 312 static char sprint_buf[1024]; 313 314 int 315 printf(const char *fmt, ...) 316 { 317 va_list args; 318 int n; 319 320 va_start(args, fmt); 321 n = vsprintf(sprint_buf, fmt, args); 322 va_end(args); 323 write(stdout, sprint_buf, n); 324 return n; 325 } 326