Chameleon

Chameleon Svn Source Tree

Root/branches/slice/old749m/i386/modules/klibc/vsnprintf.c

1/*
2 * vsnprintf.c
3 *
4 * vsnprintf(), from which the rest of the printf()
5 * family is built
6 */
7
8#include "libsaio.h"
9#include "limits.h"
10enum flags {
11FL_ZERO= 0x01,/* Zero modifier */
12FL_MINUS= 0x02,/* Minus modifier */
13FL_PLUS= 0x04,/* Plus modifier */
14FL_TICK= 0x08,/* ' modifier */
15FL_SPACE= 0x10,/* Space modifier */
16FL_HASH= 0x20,/* # modifier */
17FL_SIGNED= 0x40,/* Number is signed */
18FL_UPPER= 0x80/* Upper case digits */
19};
20
21/* These may have to be adjusted on certain implementations */
22enum ranks {
23rank_char= -2,
24rank_short= -1,
25rank_int= 0,
26rank_long= 1,
27rank_longlong= 2
28};
29
30#define MIN_RANKrank_char
31#define MAX_RANKrank_longlong
32
33#define INTMAX_RANKrank_longlong
34#define SIZE_T_RANKrank_long
35#define PTRDIFF_T_RANKrank_long
36
37#define EMIT(x) ({ if (o<n){*q++ = (x);} o++; })
38
39static size_t
40format_int(char *q, size_t n, uintmax_t val, enum flags flags,
41 int base, int width, int prec)
42{
43char *qq;
44size_t o = 0, oo;
45static const char lcdigits[] = "0123456789abcdef";
46static const char ucdigits[] = "0123456789ABCDEF";
47const char *digits;
48uintmax_t tmpval;
49int minus = 0;
50int ndigits = 0, nchars;
51int tickskip, b4tick;
52
53/* Select type of digits */
54digits = (flags & FL_UPPER) ? ucdigits : lcdigits;
55
56/* If signed, separate out the minus */
57if (flags & FL_SIGNED && (intmax_t) val < 0) {
58minus = 1;
59val = (uintmax_t) (-(intmax_t) val);
60}
61
62/* Count the number of digits needed. This returns zero for 0. */
63tmpval = val;
64while (tmpval) {
65tmpval /= base;
66ndigits++;
67}
68
69/* Adjust ndigits for size of output */
70
71if (flags & FL_HASH && base == 8) {
72if (prec < ndigits + 1)
73prec = ndigits + 1;
74}
75
76if (ndigits < prec) {
77ndigits = prec;/* Mandatory number padding */
78} else if (val == 0) {
79ndigits = 1;/* Zero still requires space */
80}
81
82/* For ', figure out what the skip should be */
83if (flags & FL_TICK) {
84tickskip = (base == 16) ? 4 : 3;
85} else {
86tickskip = ndigits;/* No tick marks */
87}
88
89/* Tick marks aren't digits, but generated by the number converter */
90ndigits += (ndigits - 1) / tickskip;
91
92/* Now compute the number of nondigits */
93nchars = ndigits;
94
95if (minus || (flags & (FL_PLUS | FL_SPACE)))
96nchars++;/* Need space for sign */
97if ((flags & FL_HASH) && base == 16) {
98nchars += 2;/* Add 0x for hex */
99}
100
101/* Emit early space padding */
102if (!(flags & (FL_MINUS | FL_ZERO)) && width > nchars) {
103while (width > nchars) {
104EMIT(' ');
105width--;
106}
107}
108
109/* Emit nondigits */
110if (minus)
111EMIT('-');
112else if (flags & FL_PLUS)
113EMIT('+');
114else if (flags & FL_SPACE)
115EMIT(' ');
116
117if ((flags & FL_HASH) && base == 16) {
118EMIT('0');
119EMIT((flags & FL_UPPER) ? 'X' : 'x');
120}
121
122/* Emit zero padding */
123if ((flags & (FL_MINUS | FL_ZERO)) == FL_ZERO && width > ndigits) {
124while (width > nchars) {
125EMIT('0');
126width--;
127}
128}
129
130/* Generate the number. This is done from right to left. */
131q += ndigits;/* Advance the pointer to end of number */
132o += ndigits;
133qq = q;
134oo = o;/* Temporary values */
135
136b4tick = tickskip;
137while (ndigits > 0) {
138if (!b4tick--) {
139qq--;
140oo--;
141ndigits--;
142if (oo < n)
143*qq = '_';
144b4tick = tickskip - 1;
145}
146qq--;
147oo--;
148ndigits--;
149if (oo < n)
150*qq = digits[val % base];
151val /= base;
152}
153
154/* Emit late space padding */
155while ((flags & FL_MINUS) && width > nchars) {
156EMIT(' ');
157width--;
158}
159
160return o;
161}
162
163int vsnprintf(char *buffer, size_t n, const char *format, va_list ap)
164{
165const char *p = format;
166char ch;
167char *q = buffer;
168size_t o = 0;/* Number of characters output */
169uintmax_t val = 0;
170int rank = rank_int;/* Default rank */
171int width = 0;
172int prec = -1;
173int base;
174size_t sz;
175enum flags flags = 0;
176enum {
177st_normal,/* Ground state */
178st_flags,/* Special flags */
179st_width,/* Field width */
180st_prec,/* Field precision */
181st_modifiers/* Length or conversion modifiers */
182} state = st_normal;
183const char *sarg;/* %s string argument */
184char carg;/* %c char argument */
185int slen;/* String length */
186
187while ((ch = *p++)) {
188switch (state) {
189case st_normal:
190if (ch == '%') {
191state = st_flags;
192flags = 0;
193rank = rank_int;
194width = 0;
195prec = -1;
196} else {
197EMIT(ch);
198}
199break;
200
201case st_flags:
202switch (ch) {
203case '-':
204flags |= FL_MINUS;
205break;
206case '+':
207flags |= FL_PLUS;
208break;
209case '\'':
210flags |= FL_TICK;
211break;
212case ' ':
213flags |= FL_SPACE;
214break;
215case '#':
216flags |= FL_HASH;
217break;
218case '0':
219flags |= FL_ZERO;
220break;
221default:
222state = st_width;
223p--;/* Process this character again */
224break;
225}
226break;
227
228case st_width:
229if (ch >= '0' && ch <= '9') {
230width = width * 10 + (ch - '0');
231} else if (ch == '*') {
232width = va_arg(ap, int);
233if (width < 0) {
234width = -width;
235flags |= FL_MINUS;
236}
237} else if (ch == '.') {
238prec = 0;/* Precision given */
239state = st_prec;
240} else {
241state = st_modifiers;
242p--;/* Process this character again */
243}
244break;
245
246case st_prec:
247if (ch >= '0' && ch <= '9') {
248prec = prec * 10 + (ch - '0');
249} else if (ch == '*') {
250prec = va_arg(ap, int);
251if (prec < 0)
252prec = -1;
253} else {
254state = st_modifiers;
255p--;/* Process this character again */
256}
257break;
258
259case st_modifiers:
260switch (ch) {
261/* Length modifiers - nonterminal sequences */
262case 'h':
263rank--;/* Shorter rank */
264break;
265case 'l':
266rank++;/* Longer rank */
267break;
268case 'j':
269rank = INTMAX_RANK;
270break;
271case 'z':
272rank = SIZE_T_RANK;
273break;
274case 't':
275rank = PTRDIFF_T_RANK;
276break;
277case 'L':
278case 'q':
279rank += 2;
280break;
281default:
282/* Output modifiers - terminal sequences */
283
284/* Next state will be normal */
285state = st_normal;
286
287/* Canonicalize rank */
288if (rank < MIN_RANK)
289rank = MIN_RANK;
290else if (rank > MAX_RANK)
291rank = MAX_RANK;
292
293switch (ch) {
294case 'P':/* Upper case pointer */
295flags |= FL_UPPER;
296/* fall through */
297case 'p':/* Pointer */
298base = 16;
299prec = (CHAR_BIT*sizeof(void *)+3)/4;
300flags |= FL_HASH;
301val = (uintmax_t)(uintptr_t)
302va_arg(ap, void *);
303goto is_integer;
304
305case 'd':/* Signed decimal output */
306case 'i':
307base = 10;
308flags |= FL_SIGNED;
309switch (rank) {
310case rank_char:
311/* Yes, all these casts are
312 needed... */
313val = (uintmax_t)(intmax_t)
314(signed char)
315va_arg(ap, signed int);
316break;
317case rank_short:
318val = (uintmax_t)(intmax_t)
319(signed short)
320va_arg(ap, signed int);
321break;
322case rank_int:
323val = (uintmax_t)(intmax_t)
324 va_arg(ap, signed int);
325break;
326case rank_long:
327val = (uintmax_t)(intmax_t)
328 va_arg(ap, signed long);
329break;
330case rank_longlong:
331val = (uintmax_t)(intmax_t)
332 va_arg(ap,
333 signed long long);
334break;
335}
336goto is_integer;
337case 'o':/* Octal */
338base = 8;
339goto is_unsigned;
340case 'u':/* Unsigned decimal */
341base = 10;
342goto is_unsigned;
343case 'X':/* Upper case hexadecimal */
344flags |= FL_UPPER;
345/* fall through */
346case 'x':/* Hexadecimal */
347base = 16;
348goto is_unsigned;
349
350is_unsigned:
351switch (rank) {
352case rank_char:
353val = (uintmax_t)
354(unsigned char)
355va_arg(ap, unsigned
356 int);
357break;
358case rank_short:
359val = (uintmax_t)
360(unsigned short)
361va_arg(ap, unsigned
362 int);
363break;
364case rank_int:
365val = (uintmax_t)
366va_arg(ap, unsigned
367 int);
368break;
369case rank_long:
370val = (uintmax_t)
371va_arg(ap, unsigned
372 long);
373break;
374case rank_longlong:
375val = (uintmax_t)
376va_arg(ap, unsigned
377 long long);
378break;
379}
380/* fall through */
381
382is_integer:
383sz = format_int(q, (o < n) ? n - o : 0,
384val, flags, base,
385width, prec);
386q += sz;
387o += sz;
388break;
389
390case 'c':/* Character */
391carg = (char)va_arg(ap, int);
392sarg = &carg;
393slen = 1;
394goto is_string;
395case 's':/* String */
396sarg = va_arg(ap, const char *);
397sarg = sarg ? sarg : "(null)";
398slen = strlen(sarg);
399goto is_string;
400
401is_string:
402{
403char sch;
404int i;
405
406if (prec != -1 && slen > prec)
407slen = prec;
408
409if (width > slen
410 && !(flags & FL_MINUS)) {
411char pad =
412 (flags & FL_ZERO) ?
413 '0' : ' ';
414while (width > slen) {
415EMIT(pad);
416width--;
417}
418}
419for (i = slen; i; i--) {
420sch = *sarg++;
421EMIT(sch);
422}
423if (width > slen
424 && (flags & FL_MINUS)) {
425while (width > slen) {
426EMIT(' ');
427width--;
428}
429}
430}
431break;
432
433case 'n':
434{
435/* Output the number of
436 characters written */
437
438switch (rank) {
439case rank_char:
440*va_arg(ap,
441signed char *)
442= o;
443break;
444case rank_short:
445*va_arg(ap,
446signed short *)
447= o;
448break;
449case rank_int:
450*va_arg(ap,
451signed int *)
452= o;
453break;
454case rank_long:
455*va_arg(ap,
456signed long *)
457= o;
458break;
459case rank_longlong:
460*va_arg(ap,
461signed long long *)
462= o;
463break;
464}
465}
466break;
467
468default:/* Anything else, including % */
469EMIT(ch);
470break;
471}
472}
473}
474}
475
476/* Null-terminate the string */
477if (o < n)
478*q = '\0';/* No overflow */
479else if (n > 0)
480buffer[n - 1] = '\0';/* Overflow - terminate at end of buffer */
481
482return o;
483}
484

Archive Download this file

Revision: 1174