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
initfmt(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
fmtinstall(int c,int (* f)(va_list *,Fconv *))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
pchar(Rune c,Fconv * fp)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*
doprint(char * s,char * es,char * fmt,va_list * argp)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
numbconv(va_list * arg,Fconv * fp)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
Strconv(Rune * s,Fconv * fp)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
strconv(char * s,Fconv * fp)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
noconv(va_list * va,Fconv * fp)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
cconv(va_list * arg,Fconv * fp)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
sconv(va_list * arg,Fconv * fp)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
percent(va_list * va,Fconv * fp)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
column(va_list * arg,Fconv * fp)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
flags(va_list * va,Fconv * fp)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