1#include <u.h> 2#include <libc.h> 3 4enum 5{ 6 SIZE = 1024, 7 IDIGIT = 40, 8 MAXCONV = 40, 9 FDIGIT = 30, 10 FDEFLT = 6, 11 NONE = -1000, 12 MAXFMT = 512, 13 14 FPLUS = 1<<0, 15 FMINUS = 1<<1, 16 FSHARP = 1<<2, 17 FLONG = 1<<3, 18 FSHORT = 1<<4, 19 FUNSIGN = 1<<5, 20 FVLONG = 1<<6, 21}; 22 23int printcol; 24 25static int convcount; 26static char fmtindex[MAXFMT]; 27 28static int noconv(va_list*, Fconv*); 29static int flags(va_list*, Fconv*); 30 31static int cconv(va_list*, Fconv*); 32static int rconv(va_list*, Fconv*); 33static int sconv(va_list*, Fconv*); 34static int percent(va_list*, Fconv*); 35static int column(va_list*, Fconv*); 36 37int numbconv(va_list*, Fconv*); 38 39static 40int (*fmtconv[MAXCONV])(va_list*, Fconv*) = 41{ 42 noconv 43}; 44 45static 46void 47initfmt(void) 48{ 49 int cc; 50 51 cc = 0; 52 fmtconv[cc] = noconv; 53 cc++; 54 55 fmtconv[cc] = flags; 56 fmtindex['+'] = cc; 57 fmtindex['-'] = cc; 58 fmtindex['#'] = cc; 59 fmtindex['h'] = cc; 60 fmtindex['l'] = cc; 61 fmtindex['u'] = cc; 62 cc++; 63 64 fmtconv[cc] = numbconv; 65 fmtindex['d'] = cc; 66 fmtindex['o'] = cc; 67 fmtindex['x'] = cc; 68 fmtindex['X'] = cc; 69 cc++; 70 71 fmtconv[cc] = cconv; 72 fmtindex['c'] = cc; 73 fmtindex['C'] = cc; 74 cc++; 75 76 fmtconv[cc] = rconv; 77 fmtindex['r'] = cc; 78 cc++; 79 80 fmtconv[cc] = sconv; 81 fmtindex['s'] = cc; 82 fmtindex['S'] = cc; 83 cc++; 84 85 fmtconv[cc] = percent; 86 fmtindex['%'] = cc; 87 cc++; 88 89 fmtconv[cc] = column; 90 fmtindex['|'] = cc; 91 cc++; 92 93 convcount = cc; 94} 95 96int 97fmtinstall(int c, int (*f)(va_list*, Fconv*)) 98{ 99 100 if(convcount == 0) 101 initfmt(); 102 if(c < 0 || c >= MAXFMT) 103 return -1; 104 if(convcount >= MAXCONV) 105 return -1; 106 fmtconv[convcount] = f; 107 fmtindex[c] = convcount; 108 convcount++; 109 return 0; 110} 111 112char* 113doprint(char *s, char *es, char *fmt, va_list argp) 114{ 115 int n, c; 116 Rune rune; 117 Fconv local; 118 119 if(s >= es) 120 return s; 121 local.out = s; 122 local.eout = es-UTFmax-1; 123 124loop: 125 c = *fmt & 0xff; 126 if(c >= Runeself) { 127 n = chartorune(&rune, fmt); 128 fmt += n; 129 c = rune; 130 } else 131 fmt++; 132 switch(c) { 133 case 0: 134 *local.out = 0; 135 return local.out; 136 137 default: 138 printcol++; 139 goto common; 140 141 case '\n': 142 printcol = 0; 143 goto common; 144 145 case '\t': 146 printcol = (printcol+8) & ~7; 147 goto common; 148 149 common: 150 if(local.out < local.eout) 151 if(c >= Runeself) { 152 rune = c; 153 n = runetochar(local.out, &rune); 154 local.out += n; 155 } else 156 *local.out++ = c; 157 goto loop; 158 159 case '%': 160 break; 161 } 162 local.f1 = NONE; 163 local.f2 = NONE; 164 local.f3 = 0; 165 166 /* 167 * read one of the following 168 * 1. number, => f1, f2 in order. 169 * 2. '*' same as number (from args) 170 * 3. '.' ignored (separates numbers) 171 * 4. flag => f3 172 * 5. verb and terminate 173 */ 174l0: 175 c = *fmt & 0xff; 176 if(c >= Runeself) { 177 n = chartorune(&rune, fmt); 178 fmt += n; 179 c = rune; 180 } else 181 fmt++; 182 183l1: 184 if(c == 0) { 185 fmt--; 186 goto loop; 187 } 188 if(c == '.') { 189 if(local.f1 == NONE) 190 local.f1 = 0; 191 local.f2 = 0; 192 goto l0; 193 } 194 if((c >= '1' && c <= '9') || 195 (c == '0' && local.f1 != NONE)) { /* '0' is a digit for f2 */ 196 n = 0; 197 while(c >= '0' && c <= '9') { 198 n = n*10 + c-'0'; 199 c = *fmt++; 200 } 201 if(local.f1 == NONE) 202 local.f1 = n; 203 else 204 local.f2 = n; 205 goto l1; 206 } 207 if(c == '*') { 208 n = va_arg(argp, int); 209 if(local.f1 == NONE) 210 local.f1 = n; 211 else 212 local.f2 = n; 213 goto l0; 214 } 215 n = 0; 216 if(c >= 0 && c < MAXFMT) 217 n = fmtindex[c]; 218 local.chr = c; 219 n = (*fmtconv[n])(&argp, &local); 220 if(n < 0) { 221 local.f3 |= -n; 222 goto l0; 223 } 224 goto loop; 225} 226 227int 228numbconv(va_list *arg, Fconv *fp) 229{ 230 char s[IDIGIT]; 231 int i, f, n, b, ucase; 232 short h; 233 long v; 234 vlong vl; 235 236 SET(v); 237 SET(vl); 238 239 ucase = 0; 240 b = fp->chr; 241 switch(fp->chr) { 242 case 'u': 243 fp->f3 |= FUNSIGN; 244 case 'd': 245 b = 10; 246 break; 247 248 case 'o': 249 b = 8; 250 break; 251 252 case 'X': 253 ucase = 1; 254 case 'x': 255 b = 16; 256 break; 257 } 258 259 f = 0; 260 switch(fp->f3 & (FVLONG|FLONG|FSHORT|FUNSIGN)) { 261 case FVLONG|FLONG: 262 vl = va_arg(*arg, vlong); 263 break; 264 265 case FUNSIGN|FVLONG|FLONG: 266 vl = va_arg(*arg, uvlong); 267 break; 268 269 case FLONG: 270 v = va_arg(*arg, long); 271 break; 272 273 case FUNSIGN|FLONG: 274 v = va_arg(*arg, ulong); 275 break; 276 277 case FSHORT: 278 h = va_arg(*arg, int); 279 v = h; 280 break; 281 282 case FUNSIGN|FSHORT: 283 h = va_arg(*arg, int); 284 v = (ushort)h; 285 break; 286 287 default: 288 v = va_arg(*arg, int); 289 break; 290 291 case FUNSIGN: 292 v = va_arg(*arg, unsigned); 293 break; 294 } 295 if(fp->f3 & FVLONG) { 296 if(!(fp->f3 & FUNSIGN) && vl < 0) { 297 vl = -vl; 298 f = 1; 299 } 300 } else { 301 if(!(fp->f3 & FUNSIGN) && v < 0) { 302 v = -v; 303 f = 1; 304 } 305 } 306 s[IDIGIT-1] = 0; 307 for(i = IDIGIT-2;; i--) { 308 if(fp->f3 & FVLONG) 309 n = (uvlong)vl % b; 310 else 311 n = (ulong)v % b; 312 n += '0'; 313 if(n > '9') { 314 n += 'a' - ('9'+1); 315 if(ucase) 316 n += 'A'-'a'; 317 } 318 s[i] = n; 319 if(i < 2) 320 break; 321 if(fp->f3 & FVLONG) 322 vl = (uvlong)vl / b; 323 else 324 v = (ulong)v / b; 325 if(fp->f2 != NONE && i >= IDIGIT-fp->f2) 326 continue; 327 if(fp->f3 & FVLONG) { 328 if(vl <= 0) 329 break; 330 continue; 331 } 332 if(v <= 0) 333 break; 334 } 335 336 if(fp->f3 & FSHARP) { 337 if(b == 8 && s[i] != '0') 338 s[--i] = '0'; 339 if(b == 16) { 340 if(ucase) 341 s[--i] = 'X'; 342 else 343 s[--i] = 'x'; 344 s[--i] = '0'; 345 } 346 } 347 if(f) 348 s[--i] = '-'; 349 fp->f2 = NONE; 350 strconv(s+i, fp); 351 return 0; 352} 353 354void 355Strconv(Rune *s, Fconv *fp) 356{ 357 int n, c, i; 358 Rune rune; 359 360 if(fp->f3 & FMINUS) 361 fp->f1 = -fp->f1; 362 n = 0; 363 if(fp->f1 != NONE && fp->f1 >= 0) { 364 for(; s[n]; n++) 365 ; 366 while(n < fp->f1) { 367 if(fp->out < fp->eout) 368 *fp->out++ = ' '; 369 printcol++; 370 n++; 371 } 372 } 373 for(;;) { 374 c = *s++; 375 if(c == 0) 376 break; 377 n++; 378 if(fp->f2 == NONE || fp->f2 > 0) { 379 if(fp->out < fp->eout) 380 if(c >= Runeself) { 381 rune = c; 382 i = runetochar(fp->out, &rune); 383 fp->out += i; 384 } else 385 *fp->out++ = c; 386 if(fp->f2 != NONE) 387 fp->f2--; 388 switch(c) { 389 default: 390 printcol++; 391 break; 392 case '\n': 393 printcol = 0; 394 break; 395 case '\t': 396 printcol = (printcol+8) & ~7; 397 break; 398 } 399 } 400 } 401 if(fp->f1 != NONE && fp->f1 < 0) { 402 fp->f1 = -fp->f1; 403 while(n < fp->f1) { 404 if(fp->out < fp->eout) 405 *fp->out++ = ' '; 406 printcol++; 407 n++; 408 } 409 } 410} 411 412void 413strconv(char *s, Fconv *fp) 414{ 415 int n, c, i; 416 Rune rune; 417 418 if(fp->f3 & FMINUS) 419 fp->f1 = -fp->f1; 420 n = 0; 421 if(fp->f1 != NONE && fp->f1 >= 0) { 422 n = utflen(s); 423 while(n < fp->f1) { 424 if(fp->out < fp->eout) 425 *fp->out++ = ' '; 426 printcol++; 427 n++; 428 } 429 } 430 for(;;) { 431 c = *s & 0xff; 432 if(c >= Runeself) { 433 i = chartorune(&rune, s); 434 s += i; 435 c = rune; 436 } else 437 s++; 438 if(c == 0) 439 break; 440 n++; 441 if(fp->f2 == NONE || fp->f2 > 0) { 442 if(fp->out < fp->eout) 443 if(c >= Runeself) { 444 rune = c; 445 i = runetochar(fp->out, &rune); 446 fp->out += i; 447 } else 448 *fp->out++ = c; 449 if(fp->f2 != NONE) 450 fp->f2--; 451 switch(c) { 452 default: 453 printcol++; 454 break; 455 case '\n': 456 printcol = 0; 457 break; 458 case '\t': 459 printcol = (printcol+8) & ~7; 460 break; 461 } 462 } 463 } 464 if(fp->f1 != NONE && fp->f1 < 0) { 465 fp->f1 = -fp->f1; 466 while(n < fp->f1) { 467 if(fp->out < fp->eout) 468 *fp->out++ = ' '; 469 printcol++; 470 n++; 471 } 472 } 473} 474 475static 476int 477noconv(va_list *arg, Fconv *fp) 478{ 479 int n; 480 char s[10]; 481 482 if(convcount == 0) { 483 initfmt(); 484 n = 0; 485 if(fp->chr >= 0 && fp->chr < MAXFMT) 486 n = fmtindex[fp->chr]; 487 return (*fmtconv[n])(arg, fp); 488 } 489 s[0] = '*'; 490 s[1] = fp->chr; 491 s[2] = '*'; 492 s[3] = 0; 493 fp->f1 = 0; 494 fp->f2 = NONE; 495 fp->f3 = 0; 496 strconv(s, fp); 497 return 0; 498} 499 500static 501int 502rconv(va_list*, Fconv *fp) 503{ 504 char s[ERRLEN]; 505 506 s[0] = 0; 507 errstr(s); 508 fp->f2 = NONE; 509 strconv(s, fp); 510 return 0; 511} 512 513static 514int 515cconv(va_list *arg, Fconv *fp) 516{ 517 char s[10]; 518 Rune rune; 519 520 rune = va_arg(*arg, int); 521 if(fp->chr == 'c') 522 rune &= 0xff; 523 s[runetochar(s, &rune)] = 0; 524 525 fp->f2 = NONE; 526 strconv(s, fp); 527 return 0; 528} 529 530static 531int 532sconv(va_list *arg, Fconv *fp) 533{ 534 char *s; 535 Rune *r; 536 537 if(fp->chr == 's') { 538 s = va_arg(*arg, char*); 539 if(s == 0) 540 s = "<null>"; 541 strconv(s, fp); 542 } else { 543 r = va_arg(*arg, Rune*); 544 if(r == 0) 545 r = L"<null>"; 546 Strconv(r, fp); 547 } 548 return 0; 549} 550 551static 552int 553percent(va_list*, Fconv *fp) 554{ 555 556 if(fp->out < fp->eout) 557 *fp->out++ = '%'; 558 printcol++; 559 return 0; 560} 561 562static 563int 564column(va_list *arg, Fconv *fp) 565{ 566 int col, pc; 567 568 col = va_arg(*arg, int); 569 while(fp->out < fp->eout && printcol < col) { 570 pc = (printcol+8) & ~7; 571 if(pc <= col) { 572 *fp->out++ = '\t'; 573 printcol = pc; 574 } else { 575 *fp->out++ = ' '; 576 printcol++; 577 } 578 } 579 return 0; 580} 581 582static 583int 584flags(va_list*, Fconv *fp) 585{ 586 int f; 587 588 f = 0; 589 switch(fp->chr) { 590 case '+': 591 f = FPLUS; 592 break; 593 594 case '-': 595 f = FMINUS; 596 break; 597 598 case '#': 599 f = FSHARP; 600 break; 601 602 case 'h': 603 f = FSHORT; 604 break; 605 606 case 'l': 607 f = FLONG; 608 if(fp->f3 & FLONG) 609 f = FVLONG; 610 break; 611 612 case 'u': 613 f = FUNSIGN; 614 break; 615 } 616 return -f; 617} 618