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