putf.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2005 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Copyright (c) 1990, 1993
00005  *      The Regents of the University of California.  All rights reserved.
00006  *
00007  * This code is partly derived from software contributed to Berkeley by
00008  * Chris Torek, but heavily rewritten for Nut/OS.
00009  *
00010  * Redistribution and use in source and binary forms, with or without
00011  * modification, are permitted provided that the following conditions
00012  * are met:
00013  *
00014  * 1. Redistributions of source code must retain the above copyright
00015  *    notice, this list of conditions and the following disclaimer.
00016  * 2. Redistributions in binary form must reproduce the above copyright
00017  *    notice, this list of conditions and the following disclaimer in the
00018  *    documentation and/or other materials provided with the distribution.
00019  * 3. Neither the name of the copyright holders nor the names of
00020  *    contributors may be used to endorse or promote products derived
00021  *    from this software without specific prior written permission.
00022  *
00023  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00024  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00025  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00026  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00027  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00028  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00029  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00030  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00031  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00032  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00033  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00034  * SUCH DAMAGE.
00035  *
00036  * For additional information see http://www.ethernut.de/
00037  *
00038  */
00039 
00040 /*
00041  * $Log: putf.c,v $
00042  * Revision 1.15  2008/08/11 06:59:40  haraldkipp
00043  * BSD types replaced by stdint types (feature request #1282721).
00044  *
00045  * Revision 1.14  2008/07/08 13:26:58  haraldkipp
00046  * Floating point bug with avr-libc 1.16 fixed (bug #1871390).
00047  *
00048  * Revision 1.13  2008/06/28 07:49:33  haraldkipp
00049  * Added floating point support for stdio running on ARM.
00050  *
00051  * Revision 1.12  2008/04/18 13:22:26  haraldkipp
00052  * Added type casts to fix ICCAVR V7.16 compile errors.
00053  *
00054  * Revision 1.11  2005/04/19 10:21:30  haraldkipp
00055  * Support for size_t modifier added. Thanks to Tom Lynn
00056  *
00057  * Revision 1.10  2004/11/24 15:24:07  haraldkipp
00058  * Floating point configuration works again.
00059  *
00060  * Revision 1.9  2004/08/18 16:30:05  haraldkipp
00061  * Compile error on non-Harvard architecture fixed
00062  *
00063  * Revision 1.8  2004/03/16 16:48:27  haraldkipp
00064  * Added Jan Dubiec's H8/300 port.
00065  *
00066  * Revision 1.7  2003/12/17 14:33:24  drsung
00067  * Another bug fix for putf. Thanks to Dusan Ferbas.
00068  *
00069  * Revision 1.6  2003/12/12 23:14:11  drsung
00070  * Rewritten %P handling for program space strings
00071  *
00072  * Revision 1.5  2003/12/12 20:23:17  drsung
00073  * Fixed %P handling
00074  *
00075  * Revision 1.4  2003/11/26 12:45:20  drsung
00076  * Portability issues ... again
00077  *
00078  * Revision 1.3  2003/11/24 18:21:50  drsung
00079  * Added support for program space strings (%P)
00080  *
00081  * Revision 1.2  2003/08/14 15:21:51  haraldkipp
00082  * Formatted output of unsigned int fixed
00083  *
00084  * Revision 1.1.1.1  2003/05/09 14:40:32  haraldkipp
00085  * Initial using 3.2.1
00086  *
00087  * Revision 1.1  2003/02/04 17:49:08  harald
00088  * *** empty log message ***
00089  *
00090  */
00091 
00092 #include <cfg/crt.h>
00093 
00094 #include <string.h>
00095 #include "nut_io.h"
00096 #include <stdlib.h>
00097 
00102 
00103 #ifdef STDIO_FLOATING_POINT
00104 
00105 #include <math.h>
00106 #define BUF 16
00107 #define DEFPREC 6
00108 
00109 #if defined(__arm__)
00110 /*
00111  * Newlib needs _sbrk for floating point conversion. Because newlib 
00112  * libraries are linked after Nut/OS libraries, this function is referenced 
00113  * too late. So we include a reference here to force _sbrk inclusion.
00114  * This reference should depend on newlib usage, not generally on ARM CPUs,
00115  * but how to find out if we have newlib or not?
00116  */
00117 extern char *_sbrk(size_t nbytes);
00118 char *(*sbrk_force)(size_t) = _sbrk;
00119 #endif
00120 
00121 #else
00122 
00123 #define BUF 16
00124 
00125 #endif                          /* STDIO_FLOATING_POINT */
00126 
00127 #define PADSIZE 16
00128 static char blanks[PADSIZE] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
00129     ' ', ' '
00130 };
00131 static char zeroes[PADSIZE] = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
00132     '0', '0'
00133 };
00134 
00135 /*
00136  *
00137  */
00138 static void _putpad(int _putb(int fd, CONST void *, size_t), int fd, char *padch, int count)
00139 {
00140     while (count > PADSIZE) {
00141         _putb(fd, padch, PADSIZE);
00142         count -= PADSIZE;
00143     }
00144     if (count > 0)
00145         _putb(fd, padch, count);
00146 }
00147 
00148 /*
00149  * Flags used during conversion.
00150  */
00151 #define ALT     0x01    /* alternate form */
00152 #define LADJUST     0x04    /* left adjustment */
00153 #define LONGINT     0x08    /* long integer */
00154 #define ZEROPAD     0x10    /* zero (as opposed to blank) pad */
00155 
00167 int _putf(int _putb(int, CONST void *, size_t), int fd, CONST char *fmt, va_list ap)
00168 {
00169     uint8_t ch;                 /* character from fmt */
00170     int n;                      /* handy integer (short term usage) */
00171     char *cp;                   /* handy char pointer (short term usage) */
00172     uint8_t flags;              /* flags as above */
00173     int rc;                     /* return value accumulator */
00174     int width;                  /* width from format (%8d), or 0 */
00175     int prec;                   /* precision from format (%.3d), or -1 */
00176     int dprec;                  /* a copy of prec if [diouxX], 0 otherwise */
00177     int realsz;                 /* field size expanded by dprec, sign, etc */
00178     uint8_t sign;               /* sign prefix (' ', '+', '-', or \0) */
00179     uint32_t ulval;               /* integer arguments %[diouxX] */
00180     int size;                   /* size of converted field or string */
00181     char *xdigs;                /* digits for [xX] conversion */
00182     char buf[BUF];              /* space for %c, %[diouxX], %[eEfgG] */
00183 
00184 #ifdef STDIO_FLOATING_POINT
00185     double _double;             /* double precision arguments %[eEfgG] */
00186 
00187 #ifdef __IMAGECRAFT__
00188     int iccfmt;
00189     int fps;
00190     extern char *FormatFP_1(int format, float f, unsigned flag, int field_width, int prec);
00191     extern char *ftoa(float f, int *status);
00192 #else /* __IMAGECRAFT__ */
00193     char *dtostre(double f, char *str, uint8_t prec, uint8_t flags);
00194 #if __AVR_LIBC_VERSION__  >= 10600
00195     char *dtostrf(double f, signed char width, unsigned char prec, char *str);
00196 #else /* __AVR_LIBC_VERSION__ */
00197     char *dtostrf(double f, char width, char prec, char *str);
00198 #endif /* __AVR_LIBC_VERSION__ */
00199 #endif /* __IMAGECRAFT__ */
00200 
00201 #endif /* STDIO_FLOATING_POINT */
00202 
00203     rc = 0;
00204 
00205     for (;;) {
00206 
00207         /*
00208          * Print format string until next percent sign.
00209          */
00210         for (cp = (char *) fmt; (ch = *fmt) != 0 && ch != '%'; fmt++);
00211         if ((n = fmt - cp) != 0) {
00212             _putb(fd, cp, n);
00213             rc += n;
00214         }
00215         if (ch == 0)
00216             break;
00217         fmt++;
00218 
00219         /*
00220          * Process modifiers.
00221          */
00222         flags = 0;
00223         sign = 0;
00224         width = 0;
00225         dprec = 0;
00226         prec = -1;
00227 #if defined(STDIO_FLOATING_POINT) && defined(__IMAGECRAFT__)
00228         iccfmt = 0;
00229 #endif
00230         for (;;) {
00231             ch = *fmt++;
00232             if (ch == ' ') {
00233                 if (!sign)
00234                     sign = ' ';
00235             } else if (ch == '+')
00236                 sign = '+';
00237             else if (ch == '-')
00238                 flags |= LADJUST;
00239             else if (ch == '#')
00240                 flags |= ALT;
00241             else if (ch == '0')
00242                 flags |= ZEROPAD;
00243             else if (ch == 'l')
00244                 flags |= LONGINT;
00245             else if (ch == 'z') {
00246                 if (sizeof(size_t) > sizeof(int)) {
00247                     flags |= LONGINT;
00248                 }
00249             }
00250             else if (ch == '*') {
00251                 width = va_arg(ap, int);
00252                 if (width < 0) {
00253                     flags |= LADJUST;
00254                     width = -width;
00255                 }
00256             } else if (ch == '.') {
00257                 if (*fmt == '*') {
00258                     fmt++;
00259                     prec = va_arg(ap, int);
00260                 } else {
00261                     prec = 0;
00262                     while (*fmt >= '0' && *fmt <= '9')
00263                         prec = 10 * prec + (*fmt++ - '0');
00264                 }
00265                 if (prec < 0)
00266                     prec = -1;
00267             } else if (ch >= '1' && ch <= '9') {
00268                 width = ch - '0';
00269                 while (*fmt >= '0' && *fmt <= '9')
00270                     width = 10 * width + (*fmt++ - '0');
00271             } else
00272                 break;
00273         }
00274 
00275         /*
00276          * Process type field.
00277          */
00278         switch (ch) {
00279         case 'c':
00280             *(cp = buf) = va_arg(ap, int);
00281             size = 1;
00282             sign = 0;
00283             break;
00284             
00285         case 'P':
00286 #ifdef __HARVARD_ARCH__
00287             /*
00288              * Thanks to Ralph Mason and Damian Slee, who provided some ideas of
00289              * handling prog_char strings
00290              */
00291             cp = va_arg(ap, char *);    /* retrieve pointer */
00292             if (cp == 0) {      /* if NULL pointer jump to std %s handling */
00293                 ch = 's';       /* manipulate ch, so 'free' is later not called */
00294                 goto putf_s;
00295             }
00296             size = strlen_P((PGM_P)cp);        /* get length of string */
00297             xdigs = malloc(size + 1);   /* allocate buffer to store string */
00298             strcpy_P(xdigs, (PGM_P)cp);        /* copy the string to RAM */
00299             cp = xdigs;         /* use cp for further processing */
00300             goto putf_s;        /* jump to std %s handling */
00301 #endif /* __HARVARD_ARCH__ */
00302 
00303         case 's':
00304             cp = va_arg(ap, char *);
00305 
00306 #ifdef __HARVARD_ARCH__
00307           putf_s:
00308 #endif /* __HARVARD_ARCH__ */
00309 
00310             if (cp == 0)
00311                 cp = "(null)";
00312             if (prec >= 0) {
00313                 char *p = memchr(cp, 0, (size_t) prec);
00314 
00315                 if (p) {
00316                     size = p - cp;
00317                     if (size > prec)
00318                         size = prec;
00319                 } else
00320                     size = prec;
00321             } else
00322                 size = strlen(cp);
00323             sign = 0;
00324             break;
00325 
00326         case 'u':
00327             sign = 0;
00328         case 'd':
00329         case 'i':
00330             /* Thanks to Ralph Mason for fixing the u_int bug. */
00331             if (flags & LONGINT)
00332                 ulval = va_arg(ap, uint32_t);
00333             else if (ch == 'u')
00334                 ulval = va_arg(ap, u_int);
00335             else
00336                 ulval = va_arg(ap, int);
00337             if (ch != 'u' && (long) ulval < 0) {
00338                 ulval = (uint32_t) (-((long) ulval));
00339                 sign = '-';
00340             }
00341             if ((dprec = prec) >= 0)
00342                 flags &= ~ZEROPAD;
00343             cp = buf + BUF;
00344             if (ulval || prec) {
00345                 if (ulval < 10)
00346                     *--cp = (char) ulval + '0';
00347                 else
00348                     do {
00349                         *--cp = (char) (ulval % 10) + '0';
00350                         ulval /= 10;
00351                     } while (ulval);
00352             }
00353             size = buf + BUF - cp;
00354             break;
00355 
00356         case 'o':
00357             ulval = (flags & LONGINT) ? va_arg(ap, uint32_t) : va_arg(ap, u_int);
00358             sign = 0;
00359             if ((dprec = prec) >= 0)
00360                 flags &= ~ZEROPAD;
00361             cp = buf + BUF;
00362             if (ulval || prec) {
00363                 do {
00364                     *--cp = (char) (ulval & 7) + '0';
00365                     ulval >>= 3;
00366                 } while (ulval);
00367                 if ((flags & ALT) != 0 && *cp != '0')
00368                     *--cp = '0';
00369             }
00370             size = buf + BUF - cp;
00371             break;
00372 
00373         case 'p':
00374         case 'X':
00375         case 'x':
00376             if (ch == 'p') {
00377                 ulval = (uptr_t) va_arg(ap, void *);
00378                 flags |= ALT;
00379                 ch = 'x';
00380             } else
00381                 ulval = (flags & LONGINT) ? va_arg(ap, uint32_t) : (uint32_t)
00382                     va_arg(ap, u_int);
00383 
00384             sign = 0;
00385             if ((dprec = prec) >= 0)
00386                 flags &= ~ZEROPAD;
00387 
00388             if (ch == 'X')
00389                 xdigs = "0123456789ABCDEF";
00390             else
00391                 xdigs = "0123456789abcdef";
00392 
00393             cp = buf + BUF;
00394             do {
00395                 *--cp = xdigs[ulval & 0x0f];
00396                 ulval >>= 4;
00397             } while (ulval);
00398             if (flags & ALT) {
00399                 *--cp = ch;
00400                 *--cp = '0';
00401             }
00402             size = buf + BUF - cp;
00403             break;
00404 
00405 #ifdef STDIO_FLOATING_POINT
00406 #ifdef __IMAGECRAFT__
00407         case 'G':
00408             iccfmt++;
00409         case 'g':
00410             iccfmt++;
00411         case 'E':
00412             iccfmt++;
00413         case 'e':
00414             iccfmt++;
00415         case 'f':
00416             if (prec == -1)
00417                 prec = DEFPREC;
00418             _double = va_arg(ap, double);
00419             /* ICCAVR bug, we use a hack */
00420             /* cp = FormatFP_1(iccfmt, _double, 0, 1, prec); */
00421             cp = ftoa(_double, &fps);
00422             size = strlen(cp);
00423             break;
00424 #elif defined(__arm__)
00425         case 'g':
00426         case 'G':
00427         case 'e':
00428         case 'E':
00429         case 'f':
00430             {
00431                 int decpt;
00432                 int sign;
00433                 char *rve = buf;
00434                 char *bp = buf;
00435 
00436                 if (prec == -1)
00437                     prec = DEFPREC;
00438                 _double = va_arg(ap, double);
00439                 cp = _dtoa_r(_REENT, _double, 3, prec, &decpt, &sign, &rve);
00440                 if (decpt == 9999) {
00441                     /* Infinite or invalid. */
00442                     strcpy(bp, cp);
00443                 } else {
00444                     /* Left of decimal dot. */
00445                     if (decpt > 0) {
00446                         while (*cp && decpt > 0) {
00447                             *bp++ = *cp++;
00448                             decpt--;
00449                         }
00450                         while (decpt > 0) {
00451                             *bp++ = '0';
00452                             decpt--;
00453                         }
00454                     } else {
00455                         *bp++ = '0';
00456                     }
00457                     *bp++ = '.';
00458                     /* Right of decimal dot. */
00459                     while (decpt < 0 && prec > 0) {
00460                         *bp++ = '0';
00461                         decpt++;
00462                         prec--;
00463                     }
00464                     while (*cp && prec > 0) {
00465                         *bp++ = *cp++;
00466                         prec--;
00467                     }
00468                     while (prec > 0) {
00469                         *bp++ = '0';
00470                         prec--;
00471                     }
00472                     *bp = 0;
00473                 }
00474                 cp = buf;
00475                 size = strlen(cp);
00476             }
00477             break;
00478 #else
00479         case 'g':
00480         case 'G':
00481         case 'e':
00482         case 'E':
00483         case 'f':
00484             if (prec == -1)
00485                 prec = DEFPREC;
00486             _double = va_arg(ap, double);
00487             if (ch == 'f')
00488                 dtostrf(_double, 1, prec, buf);
00489             else
00490                 dtostre(_double, buf, prec, 1);
00491             cp = buf;
00492             size = strlen(buf);
00493             break;
00494 #endif
00495 #else
00496         case 'g':
00497         case 'G':
00498         case 'e':
00499         case 'E':
00500         case 'f':
00501             (void) va_arg(ap, long);
00502 #endif                          /* STDIO_FLOATING_POINT */
00503 
00504         default:
00505             if (ch == 0)
00506                 return rc;
00507             cp = buf;
00508             *cp = ch;
00509             size = 1;
00510             sign = '\0';
00511             break;
00512         }                       /* switch */
00513 
00514         /*
00515          * Output.
00516          */
00517         realsz = dprec > size ? dprec : size;
00518         if (sign)
00519             realsz++;
00520 
00521         if ((flags & (LADJUST | ZEROPAD)) == 0)
00522             _putpad(_putb, fd, blanks, width - realsz);
00523 
00524         if (sign)
00525             _putb(fd, &sign, 1);
00526 
00527         if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD)
00528             _putpad(_putb, fd, zeroes, width - realsz);
00529 
00530         _putpad(_putb, fd, zeroes, dprec - size);
00531 
00532         if (size)       /* DF 12/16/03 - zero length is "flush" in NutTcpDeviceWrite() */
00533             _putb(fd, cp, size);
00534 
00535 #ifdef __HARVARD_ARCH__
00536         if (ch == 'P')
00537             free(cp);
00538 #endif
00539 
00540         if (flags & LADJUST)
00541             _putpad(_putb, fd, blanks, width - realsz);
00542 
00543         if (width >= realsz)
00544             rc += width;
00545         else
00546             rc += realsz;
00547     }
00548     return rc;
00549 }
00550 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/