Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


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