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