POK
/home/jaouen/pok_official/pok/trunk/libpok/libc/stdio/printf.c
00001 /*
00002  *                               POK header
00003  *
00004  * The following file is a part of the POK project. Any modification should
00005  * made according to the POK licence. You CANNOT use this file or a part of
00006  * this file is this part of a file for your own project
00007  *
00008  * For more information on the POK licence, please see our LICENCE FILE
00009  *
00010  * Please follow the coding guidelines described in doc/CODING_GUIDELINES
00011  *
00012  *                                      Copyright (c) 2007-2009 POK team
00013  *
00014  * Created by julien on Thu Jan 15 23:34:13 2009
00015  */
00016 
00017 
00018 #include <types.h>
00019 #include <core/syscall.h>
00020 #include <libc/stdio.h>
00021 
00022 static const char digits[] = "0123456789abcdef";
00023 
00024 #define INT_UNSIGNED       1
00025 #define INT_SIGNED      2
00026 #define FLOAT_SIGNED    3
00027 #define DOUBLE_PRECISION        (6)
00028 
00029 #define BASE_HEX        16
00030 #define BASE_OCT        8
00031 
00032 #define MY_BUF_SIZE     128
00033 
00034 /*
00035  * some types
00036  */
00037 
00038 struct          s_file
00039 {
00040    char     buff[MY_BUF_SIZE];
00041    size_t    pos;
00042 };
00043 
00044 union           u_arg
00045 {
00046    uint32_t    value;
00047    uint32_t    uint;
00048    int         sint;
00049    double       vdouble;
00050    void        *ptr;
00051 };
00052 
00053 typedef int (*t_fmtfun)(union u_arg *arg, struct s_file *file, int flags);
00054 
00055 struct          s_format
00056 {
00057    char         ch;
00058    t_fmtfun     fun;
00059    int          flags;
00060 };
00061 
00062 /*
00063  * buffered I/O
00064  */
00065 
00066 static void     my_fflush(struct s_file *file)
00067 {
00068    pok_syscall2 (POK_SYSCALL_CONSWRITE, (uint32_t)file->buff, file->pos);
00069    file->pos = 0;
00070 }
00071 
00072 static struct s_file*   init_buffered_output(void)
00073 {
00074   static struct s_file  res;
00075 
00076   res.pos = 0;
00077   return &res;
00078 }
00079 
00080 static void     my_putc(char c, struct s_file *file)
00081 {
00082   file->buff[file->pos++] = c;
00083   if (file->pos == MY_BUF_SIZE)
00084     my_fflush(file);
00085 }
00086 
00087 static void     close_buffered_output(struct s_file *file)
00088 {
00089   my_fflush(file);
00090 }
00091 
00092 /*
00093  * formatting functions
00094  */
00095 
00096 static int      conv (uint32_t n, int base, int dig[])
00097 {
00098   int           i = 0;
00099 
00100   while (n)
00101     {
00102       dig[i] = n % base;
00103       ++i;
00104       n /= base;
00105     }
00106   return i - 1;
00107 }
00108 
00109 static int      my_printnbr_base (uint32_t       n,
00110                               const char     base[],
00111                               int            card,
00112                               struct s_file  *file)
00113 {
00114   int           digits[96];
00115   int           i;
00116   int           count;
00117 
00118   if (n == 0)
00119     {
00120       my_putc('0', file);
00121       return 1;
00122     }
00123   count = i = conv(n, card, digits);
00124   for (; i >= 0; --i)
00125   {
00126     my_putc(base[digits[i]], file);
00127   }
00128   return count;
00129 }
00130 
00131 static int print_int (union u_arg* value, struct s_file* file, int flags)
00132 {
00133    int sh = 0;
00134 
00135    if (value->sint == 0)
00136    {
00137       my_putc('0', file);
00138       return 1;
00139    }
00140    if (flags == INT_SIGNED)
00141    {
00142       if (value->sint < 0)
00143       {
00144          my_putc('-', file);
00145          value->uint = -value->sint;
00146          sh = 1;
00147       }
00148       else
00149       {
00150          value->uint = value->sint;
00151       }
00152    }
00153    return my_printnbr_base(value->uint, digits, 10, file) + sh;
00154 }
00155 
00156 
00157 static int print_float (union u_arg* value, struct s_file* file, int flags)
00158 {
00159   int floor = value->vdouble;
00160   uint32_t fractional = 0;
00161   int res = 0;
00162   int precision = 0;
00163   int decimal = 10;
00164 
00165   res +=  my_printnbr_base(floor, digits, 10, file);
00166   my_putc('.', file);
00167   (void)flags;
00168 
00169   while (precision < DOUBLE_PRECISION)
00170   {
00171     fractional = (value->vdouble - floor) * decimal;
00172     fractional %= 10;
00173     res += my_printnbr_base(fractional, digits, 10, file);
00174     decimal *= 10;
00175     ++precision;
00176   }
00177 
00178   return res;
00179 }
00180 
00181 
00182 static int print_str (union u_arg* value, struct s_file* file, int flags)
00183 {
00184    int  count = 0;
00185    char*        s = value->ptr;
00186 
00187    flags = flags;
00188    for (; *s; ++count, ++s)
00189       my_putc(*s, file);
00190    return count;
00191 }
00192 
00193 static int print_char (union u_arg* value, struct s_file* file, int flags)
00194 {
00195    char c;
00196 
00197    flags = flags;
00198    c = value->sint;
00199    my_putc(c, file);
00200    return 1;
00201 }
00202 
00203 static int print_base(union u_arg* value, struct s_file* file, int flags)
00204 {
00205    return my_printnbr_base(value->uint, digits, flags, file);
00206 }
00207 
00208 static const struct s_format formats[] =
00209 {
00210    { 'd', print_int, INT_SIGNED },
00211    { 'f', print_float, FLOAT_SIGNED },
00212    { 'i', print_int, INT_SIGNED },
00213    { 'u', print_int, INT_UNSIGNED },
00214    { 's', print_str, 0 },
00215    { 'c', print_char, 0 },
00216    { 'o', print_base, BASE_OCT },
00217    { 'x', print_base, BASE_HEX },
00218    { 0, NULL, 0 }
00219 };
00220 
00221 static int special_char(char fmt, union u_arg* value, struct s_file* file)
00222 {
00223    int i = 0;
00224 
00225    for (i = 0; formats[i].fun; ++i)
00226    {
00227       if (formats[i].ch == fmt)
00228          break;
00229    }
00230 
00231    if (formats[i].fun)
00232    {
00233      //printf("special_char: %i\n", value->vfloat);
00234      return formats[i].fun(value, file, formats[i].flags);
00235    }
00236    else
00237    {
00238       if (fmt != '%')
00239       {
00240          my_putc('%', file);
00241       }
00242 
00243       my_putc(fmt, file);
00244 
00245       return 1 + (fmt != '%');
00246    }
00247 }
00248 
00249 /*
00250  * finally, printf
00251  */
00252 
00253 int             vprintf(const char* format, va_list args)
00254 {
00255    struct   s_file*  file;
00256    union    u_arg    arg;
00257    int      count;
00258 
00259    count = 0;
00260    arg.uint = 0;
00261 
00262    for (file = init_buffered_output(); *format; format += (*format == '%' ? 2 : 1))
00263    {
00264       if (*format == '%')
00265       {
00266          if (!*(format + 1))
00267          {
00268             break;
00269          }
00270          if (*(format + 1) != '%')
00271          {
00272             switch (*(format + 1))
00273             {
00274                case 'f':
00275                   arg.vdouble = va_arg (args, double);
00276                   break;
00277 
00278                default:
00279                   arg.value = va_arg (args, uint32_t);
00280                   break;
00281             }
00282          }
00283          count += special_char(*(format + 1), &arg, file);
00284       }
00285       else
00286       {
00287          my_putc(*format, file);
00288          ++count;
00289       }
00290    }
00291 
00292    close_buffered_output(file);
00293    return count;
00294 }
00295 
00296 int             printf(const char *format, ...)
00297 {
00298   int           res;
00299   va_list       args;
00300 
00301   va_start(args, format);
00302   res = vprintf (format, args);
00303   va_end(args);
00304   return res;
00305 }
00306