dofmt.c - 9base - revived minimalist port of Plan 9 userland to Unix | |
git clone git://git.suckless.org/9base | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
dofmt.c (11231B) | |
--- | |
1 /* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ | |
2 /* Copyright (c) 2004 Google Inc.; see LICENSE */ | |
3 | |
4 #include <stdarg.h> | |
5 #include <string.h> | |
6 #include "plan9.h" | |
7 #include "fmt.h" | |
8 #include "fmtdef.h" | |
9 | |
10 /* format the output into f->to and return the number of characters fmte… | |
11 int | |
12 dofmt(Fmt *f, char *fmt) | |
13 { | |
14 Rune rune, *rt, *rs; | |
15 int r; | |
16 char *t, *s; | |
17 int n, nfmt; | |
18 | |
19 nfmt = f->nfmt; | |
20 for(;;){ | |
21 if(f->runes){ | |
22 rt = (Rune*)f->to; | |
23 rs = (Rune*)f->stop; | |
24 while((r = *(uchar*)fmt) && r != '%'){ | |
25 if(r < Runeself) | |
26 fmt++; | |
27 else{ | |
28 fmt += chartorune(&rune, fmt); | |
29 r = rune; | |
30 } | |
31 FMTRCHAR(f, rt, rs, r); | |
32 } | |
33 fmt++; | |
34 f->nfmt += rt - (Rune *)f->to; | |
35 f->to = rt; | |
36 if(!r) | |
37 return f->nfmt - nfmt; | |
38 f->stop = rs; | |
39 }else{ | |
40 t = (char*)f->to; | |
41 s = (char*)f->stop; | |
42 while((r = *(uchar*)fmt) && r != '%'){ | |
43 if(r < Runeself){ | |
44 FMTCHAR(f, t, s, r); | |
45 fmt++; | |
46 }else{ | |
47 n = chartorune(&rune, fmt); | |
48 if(t + n > s){ | |
49 t = (char*)__fmtflush(f,… | |
50 if(t != nil) | |
51 s = (char*)f->st… | |
52 else | |
53 return -1; | |
54 } | |
55 while(n--) | |
56 *t++ = *fmt++; | |
57 } | |
58 } | |
59 fmt++; | |
60 f->nfmt += t - (char *)f->to; | |
61 f->to = t; | |
62 if(!r) | |
63 return f->nfmt - nfmt; | |
64 f->stop = s; | |
65 } | |
66 | |
67 fmt = (char*)__fmtdispatch(f, fmt, 0); | |
68 if(fmt == nil) | |
69 return -1; | |
70 } | |
71 } | |
72 | |
73 void * | |
74 __fmtflush(Fmt *f, void *t, int len) | |
75 { | |
76 if(f->runes) | |
77 f->nfmt += (Rune*)t - (Rune*)f->to; | |
78 else | |
79 f->nfmt += (char*)t - (char *)f->to; | |
80 f->to = t; | |
81 if(f->flush == 0 || (*f->flush)(f) == 0 || (char*)f->to + len > … | |
82 f->stop = f->to; | |
83 return nil; | |
84 } | |
85 return f->to; | |
86 } | |
87 | |
88 /* | |
89 * put a formatted block of memory sz bytes long of n runes into the out… | |
90 * left/right justified in a field of at least f->width characters (if F… | |
91 */ | |
92 int | |
93 __fmtpad(Fmt *f, int n) | |
94 { | |
95 char *t, *s; | |
96 int i; | |
97 | |
98 t = (char*)f->to; | |
99 s = (char*)f->stop; | |
100 for(i = 0; i < n; i++) | |
101 FMTCHAR(f, t, s, ' '); | |
102 f->nfmt += t - (char *)f->to; | |
103 f->to = t; | |
104 return 0; | |
105 } | |
106 | |
107 int | |
108 __rfmtpad(Fmt *f, int n) | |
109 { | |
110 Rune *t, *s; | |
111 int i; | |
112 | |
113 t = (Rune*)f->to; | |
114 s = (Rune*)f->stop; | |
115 for(i = 0; i < n; i++) | |
116 FMTRCHAR(f, t, s, ' '); | |
117 f->nfmt += t - (Rune *)f->to; | |
118 f->to = t; | |
119 return 0; | |
120 } | |
121 | |
122 int | |
123 __fmtcpy(Fmt *f, const void *vm, int n, int sz) | |
124 { | |
125 Rune *rt, *rs, r; | |
126 char *t, *s, *m, *me; | |
127 ulong fl; | |
128 int nc, w; | |
129 | |
130 m = (char*)vm; | |
131 me = m + sz; | |
132 fl = f->flags; | |
133 w = 0; | |
134 if(fl & FmtWidth) | |
135 w = f->width; | |
136 if((fl & FmtPrec) && n > f->prec) | |
137 n = f->prec; | |
138 if(f->runes){ | |
139 if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0) | |
140 return -1; | |
141 rt = (Rune*)f->to; | |
142 rs = (Rune*)f->stop; | |
143 for(nc = n; nc > 0; nc--){ | |
144 r = *(uchar*)m; | |
145 if(r < Runeself) | |
146 m++; | |
147 else if((me - m) >= UTFmax || fullrune(m, me-m)) | |
148 m += chartorune(&r, m); | |
149 else | |
150 break; | |
151 FMTRCHAR(f, rt, rs, r); | |
152 } | |
153 f->nfmt += rt - (Rune *)f->to; | |
154 f->to = rt; | |
155 if(fl & FmtLeft && __rfmtpad(f, w - n) < 0) | |
156 return -1; | |
157 }else{ | |
158 if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0) | |
159 return -1; | |
160 t = (char*)f->to; | |
161 s = (char*)f->stop; | |
162 for(nc = n; nc > 0; nc--){ | |
163 r = *(uchar*)m; | |
164 if(r < Runeself) | |
165 m++; | |
166 else if((me - m) >= UTFmax || fullrune(m, me-m)) | |
167 m += chartorune(&r, m); | |
168 else | |
169 break; | |
170 FMTRUNE(f, t, s, r); | |
171 } | |
172 f->nfmt += t - (char *)f->to; | |
173 f->to = t; | |
174 if(fl & FmtLeft && __fmtpad(f, w - n) < 0) | |
175 return -1; | |
176 } | |
177 return 0; | |
178 } | |
179 | |
180 int | |
181 __fmtrcpy(Fmt *f, const void *vm, int n) | |
182 { | |
183 Rune r, *m, *me, *rt, *rs; | |
184 char *t, *s; | |
185 ulong fl; | |
186 int w; | |
187 | |
188 m = (Rune*)vm; | |
189 fl = f->flags; | |
190 w = 0; | |
191 if(fl & FmtWidth) | |
192 w = f->width; | |
193 if((fl & FmtPrec) && n > f->prec) | |
194 n = f->prec; | |
195 if(f->runes){ | |
196 if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0) | |
197 return -1; | |
198 rt = (Rune*)f->to; | |
199 rs = (Rune*)f->stop; | |
200 for(me = m + n; m < me; m++) | |
201 FMTRCHAR(f, rt, rs, *m); | |
202 f->nfmt += rt - (Rune *)f->to; | |
203 f->to = rt; | |
204 if(fl & FmtLeft && __rfmtpad(f, w - n) < 0) | |
205 return -1; | |
206 }else{ | |
207 if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0) | |
208 return -1; | |
209 t = (char*)f->to; | |
210 s = (char*)f->stop; | |
211 for(me = m + n; m < me; m++){ | |
212 r = *m; | |
213 FMTRUNE(f, t, s, r); | |
214 } | |
215 f->nfmt += t - (char *)f->to; | |
216 f->to = t; | |
217 if(fl & FmtLeft && __fmtpad(f, w - n) < 0) | |
218 return -1; | |
219 } | |
220 return 0; | |
221 } | |
222 | |
223 /* fmt out one character */ | |
224 int | |
225 __charfmt(Fmt *f) | |
226 { | |
227 char x[1]; | |
228 | |
229 x[0] = va_arg(f->args, int); | |
230 f->prec = 1; | |
231 return __fmtcpy(f, (const char*)x, 1, 1); | |
232 } | |
233 | |
234 /* fmt out one rune */ | |
235 int | |
236 __runefmt(Fmt *f) | |
237 { | |
238 Rune x[1]; | |
239 | |
240 x[0] = va_arg(f->args, int); | |
241 return __fmtrcpy(f, (const void*)x, 1); | |
242 } | |
243 | |
244 /* public helper routine: fmt out a null terminated string already in ha… | |
245 int | |
246 fmtstrcpy(Fmt *f, char *s) | |
247 { | |
248 int i, j; | |
249 | |
250 if(!s) | |
251 return __fmtcpy(f, "<nil>", 5, 5); | |
252 /* if precision is specified, make sure we don't wander off the … | |
253 if(f->flags & FmtPrec){ | |
254 #ifdef PLAN9PORT | |
255 Rune r; | |
256 i = 0; | |
257 for(j=0; j<f->prec && s[i]; j++) | |
258 i += chartorune(&r, s+i); | |
259 #else | |
260 /* ANSI requires precision in bytes, not Runes */ | |
261 for(i=0; i<f->prec; i++) | |
262 if(s[i] == 0) | |
263 break; | |
264 j = utfnlen(s, i); /* won't print partial at end … | |
265 #endif | |
266 return __fmtcpy(f, s, j, i); | |
267 } | |
268 return __fmtcpy(f, s, utflen(s), strlen(s)); | |
269 } | |
270 | |
271 /* fmt out a null terminated utf string */ | |
272 int | |
273 __strfmt(Fmt *f) | |
274 { | |
275 char *s; | |
276 | |
277 s = va_arg(f->args, char *); | |
278 return fmtstrcpy(f, s); | |
279 } | |
280 | |
281 /* public helper routine: fmt out a null terminated rune string already … | |
282 int | |
283 fmtrunestrcpy(Fmt *f, Rune *s) | |
284 { | |
285 Rune *e; | |
286 int n, p; | |
287 | |
288 if(!s) | |
289 return __fmtcpy(f, "<nil>", 5, 5); | |
290 /* if precision is specified, make sure we don't wander off the … | |
291 if(f->flags & FmtPrec){ | |
292 p = f->prec; | |
293 for(n = 0; n < p; n++) | |
294 if(s[n] == 0) | |
295 break; | |
296 }else{ | |
297 for(e = s; *e; e++) | |
298 ; | |
299 n = e - s; | |
300 } | |
301 return __fmtrcpy(f, s, n); | |
302 } | |
303 | |
304 /* fmt out a null terminated rune string */ | |
305 int | |
306 __runesfmt(Fmt *f) | |
307 { | |
308 Rune *s; | |
309 | |
310 s = va_arg(f->args, Rune *); | |
311 return fmtrunestrcpy(f, s); | |
312 } | |
313 | |
314 /* fmt a % */ | |
315 int | |
316 __percentfmt(Fmt *f) | |
317 { | |
318 Rune x[1]; | |
319 | |
320 x[0] = f->r; | |
321 f->prec = 1; | |
322 return __fmtrcpy(f, (const void*)x, 1); | |
323 } | |
324 | |
325 /* fmt an integer */ | |
326 int | |
327 __ifmt(Fmt *f) | |
328 { | |
329 char buf[140], *p, *conv; | |
330 /* 140: for 64 bits of binary + 3-byte sep every 4 digits */ | |
331 uvlong vu; | |
332 ulong u; | |
333 int neg, base, i, n, fl, w, isv; | |
334 int ndig, len, excess, bytelen; | |
335 char *grouping; | |
336 char *thousands; | |
337 | |
338 neg = 0; | |
339 fl = f->flags; | |
340 isv = 0; | |
341 vu = 0; | |
342 u = 0; | |
343 #ifndef PLAN9PORT | |
344 /* | |
345 * Unsigned verbs for ANSI C | |
346 */ | |
347 switch(f->r){ | |
348 case 'o': | |
349 case 'p': | |
350 case 'u': | |
351 case 'x': | |
352 case 'X': | |
353 fl |= FmtUnsigned; | |
354 fl &= ~(FmtSign|FmtSpace); | |
355 break; | |
356 } | |
357 #endif | |
358 if(f->r == 'p'){ | |
359 u = (ulong)va_arg(f->args, void*); | |
360 f->r = 'x'; | |
361 fl |= FmtUnsigned; | |
362 }else if(fl & FmtVLong){ | |
363 isv = 1; | |
364 if(fl & FmtUnsigned) | |
365 vu = va_arg(f->args, uvlong); | |
366 else | |
367 vu = va_arg(f->args, vlong); | |
368 }else if(fl & FmtLong){ | |
369 if(fl & FmtUnsigned) | |
370 u = va_arg(f->args, ulong); | |
371 else | |
372 u = va_arg(f->args, long); | |
373 }else if(fl & FmtByte){ | |
374 if(fl & FmtUnsigned) | |
375 u = (uchar)va_arg(f->args, int); | |
376 else | |
377 u = (char)va_arg(f->args, int); | |
378 }else if(fl & FmtShort){ | |
379 if(fl & FmtUnsigned) | |
380 u = (ushort)va_arg(f->args, int); | |
381 else | |
382 u = (short)va_arg(f->args, int); | |
383 }else{ | |
384 if(fl & FmtUnsigned) | |
385 u = va_arg(f->args, uint); | |
386 else | |
387 u = va_arg(f->args, int); | |
388 } | |
389 conv = "0123456789abcdef"; | |
390 grouping = "\4"; /* for hex, octal etc. (undefined by spe… | |
391 thousands = f->thousands; | |
392 switch(f->r){ | |
393 case 'd': | |
394 case 'i': | |
395 case 'u': | |
396 base = 10; | |
397 grouping = f->grouping; | |
398 break; | |
399 case 'X': | |
400 conv = "0123456789ABCDEF"; | |
401 /* fall through */ | |
402 case 'x': | |
403 base = 16; | |
404 thousands = ":"; | |
405 break; | |
406 case 'b': | |
407 base = 2; | |
408 thousands = ":"; | |
409 break; | |
410 case 'o': | |
411 base = 8; | |
412 break; | |
413 default: | |
414 return -1; | |
415 } | |
416 if(!(fl & FmtUnsigned)){ | |
417 if(isv && (vlong)vu < 0){ | |
418 vu = -(vlong)vu; | |
419 neg = 1; | |
420 }else if(!isv && (long)u < 0){ | |
421 u = -(long)u; | |
422 neg = 1; | |
423 } | |
424 } | |
425 p = buf + sizeof buf - 1; | |
426 n = 0; /* in runes */ | |
427 excess = 0; /* number of bytes > number runes */ | |
428 ndig = 0; | |
429 len = utflen(thousands); | |
430 bytelen = strlen(thousands); | |
431 if(isv){ | |
432 while(vu){ | |
433 i = vu % base; | |
434 vu /= base; | |
435 if((fl & FmtComma) && n % 4 == 3){ | |
436 *p-- = ','; | |
437 n++; | |
438 } | |
439 if((fl & FmtApost) && __needsep(&ndig, &grouping… | |
440 n += len; | |
441 excess += bytelen - len; | |
442 p -= bytelen; | |
443 memmove(p+1, thousands, bytelen); | |
444 } | |
445 *p-- = conv[i]; | |
446 n++; | |
447 } | |
448 }else{ | |
449 while(u){ | |
450 i = u % base; | |
451 u /= base; | |
452 if((fl & FmtComma) && n % 4 == 3){ | |
453 *p-- = ','; | |
454 n++; | |
455 } | |
456 if((fl & FmtApost) && __needsep(&ndig, &grouping… | |
457 n += len; | |
458 excess += bytelen - len; | |
459 p -= bytelen; | |
460 memmove(p+1, thousands, bytelen); | |
461 } | |
462 *p-- = conv[i]; | |
463 n++; | |
464 } | |
465 } | |
466 if(n == 0){ | |
467 /* | |
468 * "The result of converting a zero value with | |
469 * a precision of zero is no characters." - ANSI | |
470 * | |
471 * "For o conversion, # increases the precision, if and … | |
472 * necessary, to force the first digit of the result to … | |
473 * (if the value and precision are both 0, a single 0 is… | |
474 */ | |
475 if(!(fl & FmtPrec) || f->prec != 0 || (f->r == 'o' && (f… | |
476 *p-- = '0'; | |
477 n = 1; | |
478 if(fl & FmtApost) | |
479 __needsep(&ndig, &grouping); | |
480 } | |
481 | |
482 /* | |
483 * Zero values don't get 0x. | |
484 */ | |
485 if(f->r == 'x' || f->r == 'X') | |
486 fl &= ~FmtSharp; | |
487 } | |
488 for(w = f->prec; n < w && p > buf+3; n++){ | |
489 if((fl & FmtApost) && __needsep(&ndig, &grouping)){ | |
490 n += len; | |
491 excess += bytelen - len; | |
492 p -= bytelen; | |
493 memmove(p+1, thousands, bytelen); | |
494 } | |
495 *p-- = '0'; | |
496 } | |
497 if(neg || (fl & (FmtSign|FmtSpace))) | |
498 n++; | |
499 if(fl & FmtSharp){ | |
500 if(base == 16) | |
501 n += 2; | |
502 else if(base == 8){ | |
503 if(p[1] == '0') | |
504 fl &= ~FmtSharp; | |
505 else | |
506 n++; | |
507 } | |
508 } | |
509 if((fl & FmtZero) && !(fl & (FmtLeft|FmtPrec))){ | |
510 w = 0; | |
511 if(fl & FmtWidth) | |
512 w = f->width; | |
513 for(; n < w && p > buf+3; n++){ | |
514 if((fl & FmtApost) && __needsep(&ndig, &grouping… | |
515 n += len; | |
516 excess += bytelen - len; | |
517 p -= bytelen; | |
518 memmove(p+1, thousands, bytelen); | |
519 } | |
520 *p-- = '0'; | |
521 } | |
522 f->flags &= ~FmtWidth; | |
523 } | |
524 if(fl & FmtSharp){ | |
525 if(base == 16) | |
526 *p-- = f->r; | |
527 if(base == 16 || base == 8) | |
528 *p-- = '0'; | |
529 } | |
530 if(neg) | |
531 *p-- = '-'; | |
532 else if(fl & FmtSign) | |
533 *p-- = '+'; | |
534 else if(fl & FmtSpace) | |
535 *p-- = ' '; | |
536 f->flags &= ~FmtPrec; | |
537 return __fmtcpy(f, p + 1, n, n + excess); | |
538 } | |
539 | |
540 int | |
541 __countfmt(Fmt *f) | |
542 { | |
543 void *p; | |
544 ulong fl; | |
545 | |
546 fl = f->flags; | |
547 p = va_arg(f->args, void*); | |
548 if(fl & FmtVLong){ | |
549 *(vlong*)p = f->nfmt; | |
550 }else if(fl & FmtLong){ | |
551 *(long*)p = f->nfmt; | |
552 }else if(fl & FmtByte){ | |
553 *(char*)p = f->nfmt; | |
554 }else if(fl & FmtShort){ | |
555 *(short*)p = f->nfmt; | |
556 }else{ | |
557 *(int*)p = f->nfmt; | |
558 } | |
559 return 0; | |
560 } | |
561 | |
562 int | |
563 __flagfmt(Fmt *f) | |
564 { | |
565 switch(f->r){ | |
566 case ',': | |
567 f->flags |= FmtComma; | |
568 break; | |
569 case '-': | |
570 f->flags |= FmtLeft; | |
571 break; | |
572 case '+': | |
573 f->flags |= FmtSign; | |
574 break; | |
575 case '#': | |
576 f->flags |= FmtSharp; | |
577 break; | |
578 case '\'': | |
579 f->flags |= FmtApost; | |
580 break; | |
581 case ' ': | |
582 f->flags |= FmtSpace; | |
583 break; | |
584 case 'u': | |
585 f->flags |= FmtUnsigned; | |
586 break; | |
587 case 'h': | |
588 if(f->flags & FmtShort) | |
589 f->flags |= FmtByte; | |
590 f->flags |= FmtShort; | |
591 break; | |
592 case 'L': | |
593 f->flags |= FmtLDouble; | |
594 break; | |
595 case 'l': | |
596 if(f->flags & FmtLong) | |
597 f->flags |= FmtVLong; | |
598 f->flags |= FmtLong; | |
599 break; | |
600 } | |
601 return 1; | |
602 } | |
603 | |
604 /* default error format */ | |
605 int | |
606 __badfmt(Fmt *f) | |
607 { | |
608 char x[2+UTFmax]; | |
609 int n; | |
610 | |
611 x[0] = '%'; | |
612 n = 1 + runetochar(x+1, &f->r); | |
613 x[n++] = '%'; | |
614 f->prec = n; | |
615 __fmtcpy(f, (const void*)x, n, n); | |
616 return 0; | |
617 } |