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.


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