Nut/OS  4.10.3
API Reference
getf.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2003 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$
00042  * Revision 1.7  2008/08/11 06:59:40  haraldkipp
00043  * BSD types replaced by stdint types (feature request #1282721).
00044  *
00045  * Revision 1.6  2008/07/09 14:20:25  haraldkipp
00046  * Added support for length modifier h. According to C99, F, G and X type
00047  * specifiers should work like f, g, and x.
00048  *
00049  * Revision 1.5  2006/05/15 15:31:11  freckle
00050  * Take care of first character after integer
00051  *
00052  * Revision 1.4  2006/05/05 15:43:07  freckle
00053  * Fixes for bugs #1477658 and #1477676
00054  *
00055  * Revision 1.3  2004/11/24 15:24:07  haraldkipp
00056  * Floating point configuration works again.
00057  *
00058  * Revision 1.2  2004/02/28 20:14:38  drsung
00059  * Merge from nut-3_4-release b/c of bugfixes.
00060  *
00061  * Revision 1.1.1.1.2.1  2004/02/28 18:47:34  drsung
00062  * Several bugfixes provided by Francois Rademeyer.
00063  * - "%%" didnt work
00064  * - integer parsing corrected
00065  * - support for "%u"
00066  *
00067  * Revision 1.1.1.1  2003/05/09 14:40:29  haraldkipp
00068  * Initial using 3.2.1
00069  *
00070  * Revision 1.1  2003/02/04 17:49:07  harald
00071  * *** empty log message ***
00072  *
00073  */
00074 
00075 #include <cfg/crt.h>
00076 
00077 #include "nut_io.h"
00078 
00079 #include <ctype.h>
00080 #include <limits.h>
00081 #include <stdlib.h>
00082 #include <string.h>
00083 
00088 
00089 #define CF_LONG         0x01    /* 1: long or double */
00090 #define CF_SUPPRESS     0x02    /* suppress assignment */
00091 #define CF_SIGNOK       0x04    /* +/- is (still) legal */
00092 #define CF_NDIGITS      0x08    /* no digits detected */
00093 #define CF_PFXOK        0x10    /* 0x prefix is (still) legal */
00094 #define CF_NZDIGITS     0x20    /* no zero digits detected */
00095 #define CF_DPTOK        0x10    /* (float) decimal point is still legal */
00096 #define CF_EXPOK        0x20    /* (float) exponent (e+3, etc) still legal */
00097 
00098 /*
00099  * Conversion types.
00100  */
00101 #define CT_CHAR         0       /* %c conversion */
00102 #define CT_STRING       2       /* %s conversion */
00103 #define CT_INT          3       /* integer, i.e., strtoq or strtouq */
00104 #define CT_FLOAT        4       /* floating, i.e., strtod */
00105 
00119 int _getf(int _getb(int, void *, size_t), int fd, CONST char *fmt, va_list ap)
00120 {
00121 
00122     uint8_t cf;                 /* Character from format. */
00123     uint8_t ch;                 /* Character from input. */
00124     size_t width;               /* Field width. */
00125     uint8_t flags;              /* CF_ flags. */
00126     uint8_t ct;                 /* CT_ conversion type. */
00127     uint8_t base;               /* Conversion base. */
00128     uint8_t ccnt = 0;           /* Number of conversions. */
00129     uint8_t acnt = 0;           /* Number of fields assigned. */
00130     uint8_t hcnt;               /* Number of 'half' specifiers. */
00131     char buf[16];               /* Temporary buffer. */
00132     char *cp;                   /* Temporary pointer. */
00133     uint8_t ch_ready = 0;       /* Character available from previous peek
00134                                    This is necessary as a hack to get around a missing ungetc */
00135     
00136     for (;;) {
00137         cf = *fmt++;
00138         if (cf == 0)
00139             return acnt;
00140 
00141         /*
00142          * Match whitespace.
00143          */
00144         if (isspace(cf)) {
00145             for (;;) {
00146                 if (_getb(fd, &ch, 1) != 1)
00147                     break;
00148                 if (!isspace(ch)) {
00149                     ch_ready = 1; /* character avail without read */
00150                     break;
00151                 }
00152             }
00153             continue;
00154         }
00155 
00156         /*
00157          * Match literals.
00158          */
00159         if (cf != '%') {
00160             if (!ch_ready && _getb(fd, &ch, 1) != 1)
00161                 return ccnt ? acnt : EOF;
00162             if (ch != cf)
00163                 return acnt;
00164             ch_ready = 0; /* character used now */
00165             continue;
00166         }
00167 
00168         cf = *fmt++;
00169         /*
00170          * Check for a '%' literal.
00171          */
00172         if (cf == '%') {
00173             if (!ch_ready && _getb(fd, &ch, 1) != 1)
00174                 return ccnt ? acnt : EOF;
00175             if (ch != cf)
00176                 return acnt;
00177             ch_ready = 0; /* character used now */
00178             continue;
00179         }
00180 
00181         /*
00182          * Collect modifiers.
00183          */
00184         width = 0;
00185         flags = 0;
00186         hcnt = 0;
00187         for (;;) {
00188             if (cf == '*')
00189                 flags |= CF_SUPPRESS;
00190             else if (cf == 'l')
00191                 flags |= CF_LONG;
00192             else if (cf == 'h')
00193                 hcnt++;
00194             else if (cf >= '0' && cf <= '9')
00195                 width = width * 10 + cf - '0';
00196             else
00197                 break;
00198 
00199             cf = *fmt++;
00200         }
00201 
00202         /*
00203          * Determine the types.
00204          */
00205         base = 10;
00206         ct = CT_INT;
00207         switch (cf) {
00208         case '\0':
00209             return EOF;
00210         case 's':
00211             ct = CT_STRING;
00212             break;
00213         case 'c':
00214             ct = CT_CHAR;
00215             break;
00216         case 'i':
00217             base = 0;
00218             break;
00219         case 'u':
00220             base = 10;
00221             break;
00222         case 'o':
00223             base = 8;
00224             break;
00225         case 'x':
00226         case 'X':
00227             flags |= CF_PFXOK;
00228             base = 16;
00229             break;
00230 #ifdef STDIO_FLOATING_POINT
00231         case 'e':
00232         case 'f':
00233         case 'F':
00234         case 'g':
00235         case 'G':
00236             ct = CT_FLOAT;
00237             break;
00238 #endif
00239         }
00240 
00241         /*
00242          * Process characters.
00243          */
00244         if (ct == CT_CHAR) {
00245             if (width == 0)
00246                 width = 1;
00247             if (flags & CF_SUPPRESS) {
00248                 while (width > sizeof(buf)) {
00249                     if (!ch_ready && _getb(fd, buf, sizeof(buf)) <= 0)
00250                         return ccnt ? acnt : EOF;
00251                     width -= sizeof(buf);
00252                 }
00253                 if (width)
00254                     if (!ch_ready && _getb(fd, &buf, width) <= 0)
00255                         return ccnt ? acnt : EOF;
00256             } else {
00257                 if (!ch_ready && _getb(fd, (void *) va_arg(ap, char *), width) <= 0)
00258                      return ccnt ? acnt : EOF;
00259                 acnt++;
00260             }
00261             ch_ready = 0; /* character used now */
00262             ccnt++;
00263             continue;
00264         }
00265 
00266         /*
00267          * Skip whitespaces.
00268          */
00269         if (!ch_ready && _getb(fd, &ch, 1) != 1)
00270             return ccnt ? acnt : EOF;
00271         ch_ready = 0; /* no character ready anymore */
00272         while (isspace(ch)) {
00273             if (_getb(fd, &ch, 1) != 1)
00274                 return ccnt ? acnt : EOF;
00275         }
00276 
00277         /*
00278          * Process string.
00279          */
00280         if (ct == CT_STRING) {
00281             if (width == 0)
00282                 width = 0xFFFF;
00283             if (flags & CF_SUPPRESS) {
00284                 while (!isspace(ch)) {
00285                     if (--width == 0)
00286                         break;
00287                     if (_getb(fd, &ch, 1) != 1)
00288                         break;
00289                 }
00290             } else {
00291                 cp = va_arg(ap, char *);
00292                 while (!isspace(ch)) {
00293                     *cp++ = ch;
00294                     if (--width == 0)
00295                         break;
00296                     if (_getb(fd, &ch, 1) != 1)
00297                         break;
00298                 }
00299                 *cp = 0;
00300                 acnt++;
00301             }
00302             ccnt++;
00303         }
00304 
00305         /*
00306          * Process integer.
00307          */
00308         else if (ct == CT_INT) {
00309             if (width == 0 || width > sizeof(buf) - 1)
00310                 width = sizeof(buf) - 1;
00311 
00312             flags |= CF_SIGNOK | CF_NDIGITS | CF_NZDIGITS;
00313 
00314             for (cp = buf; width; width--) {
00315                 if (ch == '0') {
00316                     if (base == 0) {
00317                         base = 8;
00318                         flags |= CF_PFXOK;
00319                     }
00320                     if (flags & CF_NZDIGITS)
00321                         flags &= ~(CF_SIGNOK | CF_NZDIGITS | CF_NDIGITS);
00322                     else
00323                         flags &= ~(CF_SIGNOK | CF_PFXOK | CF_NDIGITS);
00324                 } else if (ch >= '1' && ch <= '7') {
00325                     if (base == 0)
00326                         base = 10;
00327                     flags &= ~(CF_SIGNOK | CF_PFXOK | CF_NDIGITS);
00328                 } else if (ch == '8' || ch == '9') {
00329                     if (base == 0)
00330                         base = 10;
00331                     else if (base <= 8)
00332                         break;
00333                     flags &= ~(CF_SIGNOK | CF_PFXOK | CF_NDIGITS);
00334                 } else if ((ch >= 'A' && ch <= 'F')
00335                            || (ch >= 'a' && ch <= 'f')) {
00336                     if (base <= 10)
00337                         break;
00338                     flags &= ~(CF_SIGNOK | CF_PFXOK | CF_NDIGITS);
00339                 } else if (ch == '-' || ch == '+') {
00340                     if ((flags & CF_SIGNOK) == 0)
00341                         break;
00342                     flags &= ~CF_SIGNOK;
00343                 } else if (ch == 'x' || ch == 'X') {
00344                     if ((flags & CF_PFXOK) == 0)
00345                         break;
00346                     base = 16;
00347                     flags &= ~CF_PFXOK;
00348                 } else {
00349                     ch_ready = 1; /* character avail without read */
00350                     break;
00351                 }
00352                 *cp++ = ch;
00353                 if (width > 1) {
00354                     if (_getb(fd, &ch, 1) != 1)
00355                         break;
00356                 }
00357             }
00358 
00359             if (flags & CF_NDIGITS)
00360                 return acnt;
00361 
00362             if ((flags & CF_SUPPRESS) == 0) {
00363                 uint32_t res;
00364 
00365                 *cp = 0;
00366                 res = strtol(buf, 0, base);
00367                 if (flags & CF_LONG)
00368                     *va_arg(ap, long *) = res;
00369                 else if (hcnt == 1)
00370                     *va_arg(ap, short *) = res;
00371                 else if (hcnt)
00372                     *va_arg(ap, char *) = res;
00373                 else
00374                     *va_arg(ap, int *) = res;
00375                 acnt++;
00376             }
00377             ccnt++;
00378         }
00379 #ifdef STDIO_FLOATING_POINT
00380         else if (ct == CT_FLOAT) {
00381             if (width == 0 || width > sizeof(buf) - 1)
00382                 width = sizeof(buf) - 1;
00383             flags |= CF_SIGNOK | CF_NDIGITS | CF_DPTOK | CF_EXPOK;
00384             for (cp = buf; width; width--) {
00385                 if (ch >= '0' && ch <= '9')
00386                     flags &= ~(CF_SIGNOK | CF_NDIGITS);
00387                 else if (ch == '+' || ch == '-') {
00388                     if ((flags & CF_SIGNOK) == 0)
00389                         break;
00390                     flags &= ~CF_SIGNOK;
00391                 } else if (ch == '.') {
00392                     if ((flags & CF_DPTOK) == 0)
00393                         break;
00394                     flags &= ~(CF_SIGNOK | CF_DPTOK);
00395                 } else if (ch == 'e' || ch == 'E') {
00396                     if ((flags & (CF_NDIGITS | CF_EXPOK)) != CF_EXPOK)
00397                         break;
00398                     flags = (flags & ~(CF_EXPOK | CF_DPTOK)) | CF_SIGNOK | CF_NDIGITS;
00399                 } else {
00400                     ch_ready = 1; /* character avail without read */
00401                     break;
00402                 }
00403                 *cp++ = ch;
00404                 if (_getb(fd, &ch, 1) != 1)
00405                     break;
00406             }
00407             if (flags & CF_NDIGITS) {
00408                 if (flags & CF_EXPOK)
00409                     return acnt;
00410             }
00411             if ((flags & CF_SUPPRESS) == 0) {
00412                 double res;
00413 
00414                 *cp = 0;
00415                 res = strtod(buf, 0);
00416                 *va_arg(ap, double *) = res;
00417                 acnt++;
00418             }
00419             ccnt++;
00420         }
00421 #endif                          /* STDIO_FLOATING_POINT */
00422     }
00423 }
00424