Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 1171