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