vfprintf.c (6337B)
1 #include <ctype.h> 2 #include <limits.h> 3 #include <stdarg.h> 4 #include <stdint.h> 5 #include <stdio.h> 6 #include <string.h> 7 #include <wchar.h> 8 #undef vfprintf 9 10 enum { 11 LONG = 1 << 0, 12 LLONG = 1 << 1, 13 SHORT = 1 << 2, 14 CHAR = 1 << 3, 15 SIZET = 1 << 4, 16 PTRDIFF = 1 << 5, 17 INTMAX = 1 << 6, 18 VOIDPTR = 1 << 7, 19 UNSIGNED = 1 << 8, 20 ALTFORM = 1 << 9, 21 }; 22 23 #define MAXPREC 50 24 25 struct conv { 26 int sign; 27 int prec; 28 char *digs; 29 int base; 30 }; 31 32 static uintmax_t 33 getnum(va_list *va, int flags, int *sign) 34 { 35 uintmax_t uval; 36 intmax_t val; 37 38 if (flags & CHAR) { 39 val = va_arg(*va, int); 40 uval = (unsigned char) val; 41 } else if (flags & SHORT) { 42 val = va_arg(*va, int); 43 uval = (unsigned short) val; 44 } else if (flags & LONG) { 45 val = va_arg(*va, long); 46 uval = (unsigned long) val; 47 } else if (flags & LLONG) { 48 val = va_arg(*va, long long); 49 uval = (unsigned long long) val; 50 } else if (flags & SIZET) { 51 uval = va_arg(*va, size_t); 52 } else if (flags & INTMAX) { 53 val = va_arg(*va, intmax_t); 54 uval = (uintmax_t) val; 55 } else if (flags & VOIDPTR) { 56 uval = (uintmax_t) va_arg(*va, void *); 57 } else { 58 val = va_arg(*va, int); 59 uval = (unsigned) val; 60 } 61 62 if ((flags & UNSIGNED) == 0 && val < 0) { 63 *sign = '-'; 64 uval = -uval; 65 } 66 return uval; 67 } 68 69 static char * 70 numtostr(uintmax_t val, int flags, struct conv *conv, char *buf) 71 { 72 char *buf0 = buf; 73 int base = conv->base, prec = conv->prec; 74 uintmax_t oval = val; 75 76 if (prec == -1) 77 prec = 1; 78 79 for (*buf = '\0'; val > 0; val /= base) 80 *--buf = conv->digs[val % base]; 81 while (buf0 - buf < prec) 82 *--buf = '0'; 83 84 if (flags & ALTFORM) { 85 if (base == 8 && *buf != '0') { 86 *--buf = '0'; 87 } else if (base == 16 && oval != 0) { 88 *--buf = conv->digs[16]; 89 *--buf = '0'; 90 } 91 } 92 if (conv->sign) 93 *--buf = conv->sign; 94 95 return buf; 96 } 97 98 static void 99 savecnt(va_list *va, int flags, int cnt) 100 { 101 if (flags & CHAR) 102 *va_arg(*va, char*) = cnt; 103 else if (flags & SHORT) 104 *va_arg(*va, short*) = cnt; 105 else if (flags & LONG) 106 *va_arg(*va, long*) = cnt; 107 else if (flags & LLONG) 108 *va_arg(*va, long long*) = cnt; 109 else if (flags & SIZET) 110 *va_arg(*va, size_t*) = cnt; 111 else if (flags & INTMAX) 112 *va_arg(*va, intmax_t*) = cnt; 113 else 114 *va_arg(*va, int*) = cnt; 115 } 116 117 static size_t 118 wstrout(wchar_t *ws, size_t len, int width, int fill, FILE * restrict fp) 119 { 120 int left = 0, adjust; 121 size_t cnt = 0; 122 wchar_t wc; 123 #if 0 124 125 if (width < 0) { 126 left = 1; 127 width = -width; 128 } 129 130 len *= sizeof(wchar_t); 131 adjust = (len < width) ? width - len : 0; 132 cnt = adjust + len; 133 if (left) 134 adjust = -adjust; 135 136 for ( ; adjust > 0; adjust++) 137 putc(fill, fp); 138 139 while (wc = *ws++) 140 putwc(wc, fp); 141 142 for ( ; adjust < 0; adjust--) 143 putc(' ', fp); 144 #endif 145 return cnt; 146 } 147 148 static size_t 149 strout(char *s, size_t len, int width, int fill, FILE * restrict fp) 150 { 151 int left = 0, adjust, ch, prefix; 152 size_t cnt = 0; 153 154 if (width < 0) { 155 left = 1; 156 width = -width; 157 } 158 159 adjust = (len < width) ? width - len : 0; 160 cnt = adjust + len; 161 if (left) 162 adjust = -adjust; 163 164 if (fill == '0') { 165 if (*s == '-' || *s == '+') 166 prefix = 1; 167 else if (*s == '0' && toupper(s[1]) == 'X') 168 prefix = 2; 169 else 170 prefix = 0; 171 while (prefix--) { 172 putc(*s++, fp); 173 --len; 174 } 175 } 176 177 for ( ; adjust > 0; adjust--) 178 putc(fill, fp); 179 180 while (ch = *s++) 181 putc(ch, fp); 182 183 for ( ; adjust < 0; adjust++) 184 putc(' ', fp); 185 186 return cnt; 187 } 188 189 int 190 vfprintf(FILE * restrict fp, const char *fmt, va_list va) 191 { 192 int ch, n, flags, width, left, fill, cnt = 0; 193 size_t inc, len; 194 char *s; 195 wchar_t *ws; 196 struct conv conv; 197 char buf[MAXPREC+1]; 198 wchar_t wbuf[2]; 199 va_list va2; 200 201 va_copy(va2, va); 202 for (cnt = 0; ch = *fmt++; cnt += inc) { 203 if (ch != '%') { 204 putc(ch, fp); 205 inc = 1; 206 continue; 207 } 208 209 fill = ' '; 210 left = flags = width = 0; 211 conv.prec = -1; 212 conv.base = 10; 213 conv.sign = '\0'; 214 conv.digs = "0123456789ABCDEFX"; 215 216 flags: 217 switch (*fmt++) { 218 case ' ': 219 if (conv.sign == '\0') 220 conv.sign = ' '; 221 goto flags; 222 case '+': 223 conv.sign = '+'; 224 goto flags; 225 case '#': 226 flags |= ALTFORM; 227 goto flags; 228 case '.': 229 if (*fmt == '*') { 230 fmt++; 231 n = va_arg(va2, int); 232 } else { 233 for (n = 0; isdigit(ch = *fmt); fmt++) 234 n = n * 10 + ch - '0'; 235 } 236 if (n > MAXPREC) 237 n = MAXPREC; 238 if (n > 0) 239 conv.prec = n; 240 goto flags; 241 case '*': 242 width = va_arg(va2, int); 243 goto flags; 244 case '-': 245 left = 1; 246 ++fmt; 247 case '1': 248 case '2': 249 case '3': 250 case '4': 251 case '5': 252 case '6': 253 case '7': 254 case '8': 255 case '9': 256 --fmt; 257 for (n = 0; isdigit(ch = *fmt); ++fmt) 258 n = n * 10 + ch - '0'; 259 if (left) 260 n = -n; 261 width = n; 262 goto flags; 263 case '0': 264 fill = '0'; 265 goto flags; 266 case 'l': 267 flags += LONG; 268 goto flags; 269 case 'h': 270 flags += SHORT; 271 goto flags; 272 case '%': 273 ch = '%'; 274 goto cout; 275 case 'c': 276 if (flags & LONG) { 277 wbuf[0] = va_arg(va2, wint_t); 278 wbuf[1] = L'\0'; 279 ws = wbuf; 280 len = 1; 281 goto wstrout; 282 } 283 ch = va_arg(va2, int); 284 cout: 285 buf[0] = ch; 286 buf[1] = '\0'; 287 s = buf; 288 len = 1; 289 goto strout; 290 case 'j': 291 flags |= INTMAX; 292 goto flags; 293 case 't': 294 flags |= PTRDIFF; 295 goto flags; 296 case 'z': 297 flags |= SIZET; 298 goto flags; 299 case 'u': 300 flags |= UNSIGNED; 301 case 'i': 302 case 'd': 303 conv.base = 10; 304 goto numeric; 305 case 'p': 306 flags |= VOIDPTR | ALTFORM; 307 goto numeric16; 308 case 'x': 309 conv.digs = "0123456789abcdefx"; 310 case 'X': 311 numeric16: 312 conv.base = 16; 313 flags |= UNSIGNED; 314 goto numeric; 315 case 'o': 316 conv.base = 8; 317 flags |= UNSIGNED; 318 numeric: 319 if (conv.prec != -1) 320 fill = ' '; 321 s = numtostr(getnum(&va2, flags, &conv.sign), 322 flags, 323 &conv, 324 &buf[MAXPREC]); 325 len = &buf[MAXPREC] - s; 326 goto strout; 327 case 'L': 328 case 'a': 329 case 'A': 330 case 'e': 331 case 'E': 332 case 'f': 333 case 'g': 334 case 'G': 335 /* TODO */ 336 case 's': 337 if (flags & LONG) { 338 ws = va_arg(va2, wchar_t *); 339 /* len = wcsnlen(ws, conv.prec); */ 340 goto wstrout; 341 } else { 342 s = va_arg(va2, char *); 343 len = strnlen(s, conv.prec); 344 goto strout; 345 } 346 wstrout: 347 inc = wstrout(ws, len, width, fill, fp); 348 break; 349 strout: 350 inc = strout(s, len, width, fill, fp); 351 break; 352 case 'n': 353 savecnt(&va2, flags, cnt); 354 break; 355 case '\0': 356 goto out_loop; 357 } 358 } 359 360 out_loop: 361 return (ferror(fp)) ? EOF : cnt; 362 }