A tiny printf for embedded applications - by Kustaa Nyholm

Dependents:   lpc1768-picotcp-demo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers printf.c Source File

printf.c

00001 /*
00002 File: printf.c
00003 
00004 Copyright (C) 2004  Kustaa Nyholm
00005 
00006 This library is free software; you can redistribute it and/or
00007 modify it under the terms of the GNU Lesser General Public
00008 License as published by the Free Software Foundation; either
00009 version 2.1 of the License, or (at your option) any later version.
00010 
00011 This library is distributed in the hope that it will be useful,
00012 but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014 Lesser General Public License for more details.
00015 
00016 You should have received a copy of the GNU Lesser General Public
00017 License along with this library; if not, write to the Free Software
00018 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 
00020 */
00021 
00022 #include "printf.h"
00023 
00024 typedef void (*putcf) (void *, char);
00025 static putcf stdout_putf;
00026 static void *stdout_putp;
00027 
00028 struct param {
00029     char lz;            /**< Leading zeros */
00030     unsigned int width; /**< field width */
00031     char sign;          /**<  The sign to display (if any) */
00032     char alt;           /**< alternate form */
00033     unsigned int base;  /**<  number base (e.g.: 8, 10, 16) */
00034     char uc;            /**<  Upper case (for base16 only) */
00035     char *bf;           /**<  Buffer to output */
00036 };
00037 
00038 #ifdef PRINTF_LONG_SUPPORT
00039 
00040 static void uli2a(unsigned long int num, struct param *p)
00041 {
00042     int n = 0;
00043     unsigned long int d = 1;
00044     char *bf = p->bf;
00045     while (num / d >= p->base)
00046         d *= p->base;
00047     while (d != 0) {
00048         int dgt = num / d;
00049         num %= d;
00050         d /= p->base;
00051         if (n || dgt > 0 || d == 0) {
00052             *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
00053             ++n;
00054         }
00055     }
00056     *bf = 0;
00057 }
00058 
00059 static void li2a(long num, struct param *p)
00060 {
00061     if (num < 0) {
00062         num = -num;
00063         p->sign = '-';
00064     }
00065     uli2a(num, p);
00066 }
00067 #endif
00068 
00069 static void ui2a(unsigned int num, struct param *p)
00070 {
00071     int n = 0;
00072     unsigned int d = 1;
00073     char *bf = p->bf;
00074     while (num / d >= p->base)
00075         d *= p->base;
00076     while (d != 0) {
00077         int dgt = num / d;
00078         num %= d;
00079         d /= p->base;
00080         if (n || dgt > 0 || d == 0) {
00081             *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
00082             ++n;
00083         }
00084     }
00085     *bf = 0;
00086 }
00087 
00088 static void i2a(int num, struct param *p)
00089 {
00090     if (num < 0) {
00091         num = -num;
00092         p->sign = '-';
00093     }
00094     ui2a(num, p);
00095 }
00096 
00097 static int a2d(char ch)
00098 {
00099     if (ch >= '0' && ch <= '9')
00100         return ch - '0';
00101     else if (ch >= 'a' && ch <= 'f')
00102         return ch - 'a' + 10;
00103     else if (ch >= 'A' && ch <= 'F')
00104         return ch - 'A' + 10;
00105     else
00106         return -1;
00107 }
00108 
00109 static char a2i(char ch, char **src, int base, int *nump)
00110 {
00111     char *p = *src;
00112     int num = 0;
00113     int digit;
00114     while ((digit = a2d(ch)) >= 0) {
00115         if (digit > base)
00116             break;
00117         num = num * base + digit;
00118         ch = *p++;
00119     }
00120     *src = p;
00121     *nump = num;
00122     return ch;
00123 }
00124 
00125 static void putchw(void *putp, putcf putf, struct param *p)
00126 {
00127     char ch;
00128     int n = p->width;
00129     char *bf = p->bf;
00130 
00131     /* Number of filling characters */
00132     while (*bf++ && n > 0)
00133         n--;
00134     if (p->sign)
00135         n--;
00136     if (p->alt && p->base == 16)
00137         n -= 2;
00138     else if (p->alt && p->base == 8)
00139         n--;
00140 
00141     /* Fill with space, before alternate or sign */
00142     if (!p->lz) {
00143         while (n-- > 0)
00144             putf(putp, ' ');
00145     }
00146 
00147     /* print sign */
00148     if (p->sign)
00149         putf(putp, p->sign);
00150 
00151     /* Alternate */
00152     if (p->alt && p->base == 16) {
00153         putf(putp, '0');
00154         putf(putp, (p->uc ? 'X' : 'x'));
00155     } else if (p->alt && p->base == 8) {
00156         putf(putp, '0');
00157     }
00158 
00159     /* Fill with zeros, after alternate or sign */
00160     if (p->lz) {
00161         while (n-- > 0)
00162             putf(putp, '0');
00163     }
00164 
00165     /* Put actual buffer */
00166     bf = p->bf;
00167     while ((ch = *bf++))
00168         putf(putp, ch);
00169 }
00170 
00171 void tfp_format(void *putp, putcf putf, char *fmt, va_list va)
00172 {
00173     struct param p;
00174 #ifdef PRINTF_LONG_SUPPORT
00175     char bf[23];
00176 #else
00177     char bf[12];
00178 #endif
00179     p.bf = bf;
00180 
00181     char ch;
00182 
00183     while ((ch = *(fmt++))) {
00184         if (ch != '%') {
00185             putf(putp, ch);
00186         } else {
00187             /* Init parameter struct */
00188             p.lz = 0;
00189             p.alt = 0;
00190             p.width = 0;
00191             p.sign = 0;
00192 #ifdef PRINTF_LONG_SUPPORT
00193             char lng = 0;
00194 #endif
00195 
00196             /* Flags */
00197             while ((ch = *(fmt++))) {
00198                 switch (ch) {
00199                 case '0':
00200                     p.lz = 1;
00201                     continue;
00202                 case '#':
00203                     p.alt = 1;
00204                     continue;
00205                 default:
00206                     break;
00207                 }
00208                 break;
00209             }
00210 
00211             /* Width */
00212             if (ch >= '0' && ch <= '9') {
00213                 ch = a2i(ch, &fmt, 10, &(p.width));
00214             }
00215 #ifdef PRINTF_LONG_SUPPORT
00216             if (ch == 'l') {
00217                 ch = *(fmt++);
00218                 lng = 1;
00219             }
00220 #endif
00221             switch (ch) {
00222             case 0:
00223                 goto abort;
00224             case 'u':
00225                 p.base = 10;
00226 #ifdef PRINTF_LONG_SUPPORT
00227                 if (lng)
00228                     uli2a(va_arg(va, unsigned long int), &p);
00229                 else
00230 #endif
00231                     ui2a(va_arg(va, unsigned int), &p);
00232                 putchw(putp, putf, &p);
00233                 break;
00234             case 'd':
00235             case 'i':
00236                 p.base = 10;
00237 #ifdef PRINTF_LONG_SUPPORT
00238                 if (lng)
00239                     li2a(va_arg(va, unsigned long int), &p);
00240                 else
00241 #endif
00242                     i2a(va_arg(va, int), &p);
00243                 putchw(putp, putf, &p);
00244                 break;
00245             case 'x':
00246             case 'X':
00247                 p.base = 16;
00248                 p.uc = (ch == 'X');
00249 #ifdef PRINTF_LONG_SUPPORT
00250                 if (lng)
00251                     uli2a(va_arg(va, unsigned long int), &p);
00252                 else
00253 #endif
00254                     ui2a(va_arg(va, unsigned int), &p);
00255                 putchw(putp, putf, &p);
00256                 break;
00257             case 'o':
00258                 p.base = 8;
00259                 ui2a(va_arg(va, unsigned int), &p);
00260                 putchw(putp, putf, &p);
00261                 break;
00262             case 'c':
00263                 putf(putp, (char)(va_arg(va, int)));
00264                 break;
00265             case 's':
00266                 p.bf = va_arg(va, char *);
00267                 putchw(putp, putf, &p);
00268                 p.bf = bf;
00269                 break;
00270             case '%':
00271                 putf(putp, ch);
00272             default:
00273                 break;
00274             }
00275         }
00276     }
00277  abort:;
00278 }
00279 
00280 void init_printf(void *putp, void (*putf) (void *, char))
00281 {
00282     stdout_putf = putf;
00283     stdout_putp = putp;
00284 }
00285 
00286 void tfp_printf(char *fmt, ...)
00287 {
00288     va_list va;
00289     va_start(va, fmt);
00290     tfp_format(stdout_putp, stdout_putf, fmt, va);
00291     va_end(va);
00292 }
00293 
00294 static void putcp(void *p, char c)
00295 {
00296     *(*((char **)p))++ = c;
00297 }
00298 
00299 void tfp_sprintf(char *s, char *fmt, ...)
00300 {
00301     va_list va;
00302     va_start(va, fmt);
00303     tfp_format(&s, putcp, fmt, va);
00304     putcp(&s, 0);
00305     va_end(va);
00306 }