print.c (7831B)
1 /* print.c -- formatted printing routines (Paul Haahr, 12/91) */ 2 3 #include "rc.h" 4 #include <setjmp.h> 5 6 #define PRINT_ALLOCSIZE ((size_t)64) 7 #define SPRINT_BUFSIZ ((size_t)1024) 8 9 #define MAXCONV 256 10 11 /* 12 * conversion functions 13 * true return -> flag changes only, not a conversion 14 */ 15 16 #define Flag(name, flag) \ 17 static bool name(Format *format, int ignore) { \ 18 format->flags |= flag; \ 19 return TRUE; \ 20 } 21 22 Flag(uconv, FMT_unsigned) 23 Flag(rc_lconv, FMT_long) 24 25 #if HAVE_QUAD_T 26 Flag(qconv, FMT_quad) 27 #endif 28 29 Flag(altconv, FMT_altform) 30 Flag(leftconv, FMT_leftside) 31 Flag(dotconv, FMT_f2set) 32 33 static bool digitconv(Format *format, int c) { 34 if (format->flags & FMT_f2set) 35 format->f2 = 10 * format->f2 + c - '0'; 36 else { 37 format->flags |= FMT_f1set; 38 format->f1 = 10 * format->f1 + c - '0'; 39 } 40 return TRUE; 41 } 42 43 static bool zeroconv(Format *format, int ignore) { 44 if (format->flags & (FMT_f1set | FMT_f2set)) 45 return digitconv(format, '0'); 46 format->flags |= FMT_zeropad; 47 return TRUE; 48 } 49 50 static void pad(Format *format, size_t len, int c) { 51 while (len-- != 0) 52 fmtputc(format, c); 53 } 54 55 static bool sconv(Format *format, int ignore) { 56 char *s = va_arg(format->args, char *); 57 if ((format->flags & FMT_f1set) == 0) 58 fmtcat(format, s); 59 else { 60 size_t len = strlen(s), width = format->f1 - len; 61 if (format->flags & FMT_leftside) { 62 fmtappend(format, s, len); 63 pad(format, width, ' '); 64 } else { 65 pad(format, width, ' '); 66 fmtappend(format, s, len); 67 } 68 } 69 return FALSE; 70 } 71 72 static char *rc_utoa(unsigned long u, char *t, unsigned int radix, const char *digit) { 73 if (u >= radix) { 74 t = rc_utoa(u / radix, t, radix, digit); 75 u %= radix; 76 } 77 *t++ = digit[u]; 78 return t; 79 } 80 81 static void intconv(Format *format, unsigned int radix, int upper, const char *altform) { 82 static const char * const table[] = { 83 "0123456789abcdefghijklmnopqrstuvwxyz", 84 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", 85 }; 86 char padchar; 87 size_t len, pre, zeroes, padding, width; 88 long n, flags; 89 unsigned long u; 90 char number[64], prefix[20]; 91 92 if (radix > 36) 93 return; 94 95 flags = format->flags; 96 97 #if HAVE_QUAD_T 98 if (flags & FMT_quad) 99 n = va_arg(format->args, quad_t); 100 else 101 #endif 102 103 if (flags & FMT_long) 104 n = va_arg(format->args, long); 105 else 106 n = va_arg(format->args, int); 107 108 pre = 0; 109 if ((flags & FMT_unsigned) || n >= 0) 110 u = n; 111 else { 112 prefix[pre++] = '-'; 113 u = -n; 114 } 115 116 if (flags & FMT_altform) 117 while (*altform != '\0') 118 prefix[pre++] = *altform++; 119 120 len = rc_utoa(u, number, radix, table[upper]) - number; 121 if ((flags & FMT_f2set) && (size_t) format->f2 > len) 122 zeroes = format->f2 - len; 123 else 124 zeroes = 0; 125 126 width = pre + zeroes + len; 127 if ((flags & FMT_f1set) && (size_t) format->f1 > width) { 128 padding = format->f1 - width; 129 } else 130 padding = 0; 131 132 padchar = ' '; 133 if (padding > 0 && flags & FMT_zeropad) { 134 padchar = '0'; 135 if ((flags & FMT_leftside) == 0) { 136 zeroes += padding; 137 padding = 0; 138 } 139 } 140 141 142 if ((flags & FMT_leftside) == 0) 143 pad(format, padding, padchar); 144 fmtappend(format, prefix, pre); 145 pad(format, zeroes, '0'); 146 fmtappend(format, number, len); 147 if (flags & FMT_leftside) 148 pad(format, padding, padchar); 149 } 150 151 static bool cconv(Format *format, int ignore) { 152 fmtputc(format, va_arg(format->args, int)); 153 return FALSE; 154 } 155 156 static bool dconv(Format *format, int ignore) { 157 intconv(format, 10, 0, ""); 158 return FALSE; 159 } 160 161 static bool oconv(Format *format, int ignore) { 162 intconv(format, 8, 0, "0"); 163 return FALSE; 164 } 165 166 static bool xconv(Format *format, int ignore) { 167 intconv(format, 16, 0, "0x"); 168 return FALSE; 169 } 170 171 static bool pctconv(Format *format, int ignore) { 172 fmtputc(format, '%'); 173 return FALSE; 174 } 175 176 static bool badconv(Format *ignore, int ign0re) { 177 panic("bad conversion character in printfmt"); 178 /* NOTREACHED */ 179 return FALSE; /* hush up gcc -Wall */ 180 } 181 182 183 /* 184 * conversion table management 185 */ 186 187 static Conv fmttab[MAXCONV]; 188 189 static void inittab(void) { 190 int i; 191 for (i = 0; i < MAXCONV; i++) 192 fmttab[i] = badconv; 193 194 fmttab['s'] = sconv; 195 fmttab['c'] = cconv; 196 fmttab['d'] = dconv; 197 fmttab['o'] = oconv; 198 fmttab['x'] = xconv; 199 fmttab['%'] = pctconv; 200 201 fmttab['u'] = uconv; 202 fmttab['l'] = rc_lconv; 203 fmttab['#'] = altconv; 204 fmttab['-'] = leftconv; 205 fmttab['.'] = dotconv; 206 207 #if HAVE_QUAD_T 208 fmttab['q'] = qconv; 209 #endif 210 211 fmttab['0'] = zeroconv; 212 for (i = '1'; i <= '9'; i++) 213 fmttab[i] = digitconv; 214 } 215 216 extern bool (*fmtinstall(int c, bool (*f)(Format *, int)))(Format *, int) { 217 /*Conv fmtinstall(int c, Conv f) {*/ 218 Conv oldf; 219 if (fmttab[0] == NULL) 220 inittab(); 221 c &= MAXCONV - 1; 222 oldf = fmttab[c]; 223 if (f != NULL) 224 fmttab[c] = f; 225 return oldf; 226 } 227 228 229 /* 230 * functions for inserting strings in the format buffer 231 */ 232 233 extern void fmtappend(Format *format, const char *s, size_t len) { 234 while (format->buf + len > format->bufend) { 235 size_t split = format->bufend - format->buf; 236 memcpy(format->buf, s, split); 237 format->buf += split; 238 s += split; 239 len -= split; 240 (*format->grow)(format, len); 241 } 242 memcpy(format->buf, s, len); 243 format->buf += len; 244 } 245 246 extern void fmtcat(Format *format, const char *s) { 247 fmtappend(format, s, strlen(s)); 248 } 249 250 /* 251 * printfmt -- the driver routine 252 */ 253 254 extern int printfmt(Format *format, const char *fmt) { 255 unsigned const char *s = (unsigned const char *) fmt; 256 257 if (fmttab[0] == NULL) 258 inittab(); 259 260 for (;;) { 261 int c = *s++; 262 switch (c) { 263 case '%': 264 format->flags = format->f1 = format->f2 = 0; 265 do 266 c = *s++; 267 while ((*fmttab[c])(format, c)); 268 break; 269 case '\0': 270 return format->buf - format->bufbegin + format->flushed; 271 default: 272 fmtputc(format, c); 273 break; 274 } 275 } 276 } 277 278 279 /* 280 * the public entry points 281 */ 282 283 extern int fmtprint(Format *format, const char *fmt,...) { 284 int n = -format->flushed; 285 va_list ap, saveargs; 286 287 va_start(ap, fmt); 288 va_copy(saveargs, format->args); 289 va_copy(format->args, ap); 290 n += printfmt(format, fmt); 291 va_end(format->args); 292 va_copy(format->args, saveargs); 293 294 return n + format->flushed; 295 } 296 297 static void fprint_flush(Format *format, size_t ignore) { 298 size_t n = format->buf - format->bufbegin; 299 char *buf = format->bufbegin; 300 301 format->flushed += n; 302 format->buf = format->bufbegin; 303 writeall(format->u.n, buf, n); 304 } 305 306 extern int fprint(int fd, const char *fmt,...) { 307 char buf[1024]; 308 Format format; 309 va_list ap; 310 311 format.buf = buf; 312 format.bufbegin = buf; 313 format.bufend = buf + sizeof buf; 314 format.grow = fprint_flush; 315 format.flushed = 0; 316 format.u.n = fd; 317 318 va_start(ap, fmt); 319 va_copy(format.args, ap); 320 printfmt(&format, fmt); 321 va_end(format.args); 322 323 fprint_flush(&format, 0); 324 return format.flushed; 325 } 326 327 static void memprint_grow(Format *format, size_t more) { 328 char *buf; 329 size_t len = format->bufend - format->bufbegin + 1; 330 size_t used = format->buf - format->bufbegin; 331 332 len = (len >= more) 333 ? len * 2 334 : ((len + more) + PRINT_ALLOCSIZE) &~ (PRINT_ALLOCSIZE - 1); 335 if (format->u.n) 336 buf = erealloc(format->bufbegin, len); 337 else { 338 buf = nalloc(len); 339 memcpy(buf, format->bufbegin, used); 340 } 341 format->buf = buf + used; 342 format->bufbegin = buf; 343 format->bufend = buf + len - 1; 344 } 345 346 static char *memprint(Format *format, const char *fmt, char *buf, size_t len) { 347 format->buf = buf; 348 format->bufbegin = buf; 349 format->bufend = buf + len - 1; 350 format->grow = memprint_grow; 351 format->flushed = 0; 352 printfmt(format, fmt); 353 *format->buf = '\0'; 354 return format->bufbegin; 355 } 356 357 extern char *mprint(const char *fmt,...) { 358 Format format; 359 char *result; 360 va_list ap; 361 362 format.u.n = 1; 363 va_start(ap, fmt); 364 va_copy(format.args, ap); 365 result = memprint(&format, fmt, ealloc(PRINT_ALLOCSIZE), PRINT_ALLOCSIZE); 366 va_end(format.args); 367 return result; 368 } 369 370 extern char *nprint(const char *fmt,...) { 371 Format format; 372 char *result; 373 va_list ap; 374 375 format.u.n = 0; 376 va_start(ap, fmt); 377 va_copy(format.args, ap); 378 result = memprint(&format, fmt, nalloc(PRINT_ALLOCSIZE), PRINT_ALLOCSIZE); 379 va_end(format.args); 380 return result; 381 }