LCOV - code coverage report
Current view: directory - nsprpub/pr/src/io - prscanf.c (source / functions) Found Hit Coverage
Test: app.info Lines: 312 149 47.8 %
Date: 2012-06-02 Functions: 8 7 87.5 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is the Netscape Portable Runtime (NSPR).
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998-2000
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *
      24                 :  * Alternatively, the contents of this file may be used under the terms of
      25                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      26                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      28                 :  * of those above. If you wish to allow use of your version of this file only
      29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      30                 :  * use your version of this file under the terms of the MPL, indicate your
      31                 :  * decision by deleting the provisions above and replace them with the notice
      32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      33                 :  * the provisions above, a recipient may use your version of this file under
      34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      35                 :  *
      36                 :  * ***** END LICENSE BLOCK ***** */
      37                 : 
      38                 : /*
      39                 :  * Scan functions for NSPR types
      40                 :  *
      41                 :  * Author: Wan-Teh Chang
      42                 :  *
      43                 :  * Acknowledgment: The implementation is inspired by the source code
      44                 :  * in P.J. Plauger's "The Standard C Library," Prentice-Hall, 1992.
      45                 :  */
      46                 : 
      47                 : #include <limits.h>
      48                 : #include <ctype.h>
      49                 : #include <string.h>
      50                 : #include <stdlib.h>
      51                 : #ifdef SUNOS4
      52                 : #include "md/sunos4.h"  /* for strtoul */
      53                 : #endif
      54                 : #include "prprf.h"
      55                 : #include "prdtoa.h"
      56                 : #include "prlog.h"
      57                 : #include "prerror.h"
      58                 : 
      59                 : /*
      60                 :  * A function that reads a character from 'stream'.
      61                 :  * Returns the character read, or EOF if end of stream is reached.
      62                 :  */
      63                 : typedef int (*_PRGetCharFN)(void *stream);
      64                 : 
      65                 : /*
      66                 :  * A function that pushes the character 'ch' back to 'stream'.
      67                 :  */
      68                 : typedef void (*_PRUngetCharFN)(void *stream, int ch); 
      69                 : 
      70                 : /*
      71                 :  * The size specifier for the integer and floating point number
      72                 :  * conversions in format control strings.
      73                 :  */
      74                 : typedef enum {
      75                 :     _PR_size_none,  /* No size specifier is given */
      76                 :     _PR_size_h,     /* The 'h' specifier, suggesting "short" */
      77                 :     _PR_size_l,     /* The 'l' specifier, suggesting "long" */
      78                 :     _PR_size_L,     /* The 'L' specifier, meaning a 'long double' */
      79                 :     _PR_size_ll     /* The 'll' specifier, suggesting "long long" */
      80                 : } _PRSizeSpec;
      81                 : 
      82                 : /*
      83                 :  * The collection of data that is passed between the scan function
      84                 :  * and its subordinate functions.  The fields of this structure
      85                 :  * serve as the input or output arguments for these functions.
      86                 :  */
      87                 : typedef struct {
      88                 :     _PRGetCharFN get;        /* get a character from input stream */
      89                 :     _PRUngetCharFN unget;    /* unget (push back) a character */
      90                 :     void *stream;            /* argument for get and unget */
      91                 :     va_list ap;              /* the variable argument list */
      92                 :     int nChar;               /* number of characters read from 'stream' */
      93                 : 
      94                 :     PRBool assign;           /* assign, or suppress assignment? */
      95                 :     int width;               /* field width */
      96                 :     _PRSizeSpec sizeSpec;    /* 'h', 'l', 'L', or 'll' */
      97                 : 
      98                 :     PRBool converted;        /* is the value actually converted? */
      99                 : } ScanfState;
     100                 : 
     101                 : #define GET(state) ((state)->nChar++, (state)->get((state)->stream))
     102                 : #define UNGET(state, ch) \
     103                 :         ((state)->nChar--, (state)->unget((state)->stream, ch))
     104                 : 
     105                 : /*
     106                 :  * The following two macros, GET_IF_WITHIN_WIDTH and WITHIN_WIDTH,
     107                 :  * are always used together.
     108                 :  *
     109                 :  * GET_IF_WITHIN_WIDTH calls the GET macro and assigns its return
     110                 :  * value to 'ch' only if we have not exceeded the field width of
     111                 :  * 'state'.  Therefore, after GET_IF_WITHIN_WIDTH, the value of
     112                 :  * 'ch' is valid only if the macro WITHIN_WIDTH evaluates to true.
     113                 :  */
     114                 : 
     115                 : #define GET_IF_WITHIN_WIDTH(state, ch) \
     116                 :         if (--(state)->width >= 0) { \
     117                 :             (ch) = GET(state); \
     118                 :         }
     119                 : #define WITHIN_WIDTH(state) ((state)->width >= 0)
     120                 : 
     121                 : /*
     122                 :  * _pr_strtoull:
     123                 :  *     Convert a string to an unsigned 64-bit integer.  The string
     124                 :  *     'str' is assumed to be a representation of the integer in
     125                 :  *     base 'base'.
     126                 :  *
     127                 :  * Warning: 
     128                 :  *     - Only handle base 8, 10, and 16.
     129                 :  *     - No overflow checking.
     130                 :  */
     131                 : 
     132                 : static PRUint64
     133           16862 : _pr_strtoull(const char *str, char **endptr, int base)
     134                 : {
     135                 :     static const int BASE_MAX = 16;
     136                 :     static const char digits[] = "0123456789abcdef";
     137                 :     char *digitPtr;
     138                 :     PRUint64 x;    /* return value */
     139                 :     PRInt64 base64;
     140                 :     const char *cPtr;
     141                 :     PRBool negative;
     142                 :     const char *digitStart;
     143                 : 
     144           16862 :     PR_ASSERT(base == 0 || base == 8 || base == 10 || base == 16);
     145           16862 :     if (base < 0 || base == 1 || base > BASE_MAX) {
     146               0 :         if (endptr) {
     147               0 :             *endptr = (char *) str;
     148               0 :             return LL_ZERO;
     149                 :         }
     150                 :     }
     151                 : 
     152           16862 :     cPtr = str;
     153           33724 :     while (isspace(*cPtr)) {
     154               0 :         ++cPtr;
     155                 :     }
     156                 : 
     157           16862 :     negative = PR_FALSE;
     158           16862 :     if (*cPtr == '-') {
     159              15 :         negative = PR_TRUE;
     160              15 :         cPtr++;
     161           16847 :     } else if (*cPtr == '+') {
     162               0 :         cPtr++;
     163                 :     }
     164                 : 
     165           16862 :     if (base == 16) {
     166               0 :         if (*cPtr == '0' && (cPtr[1] == 'x' || cPtr[1] == 'X')) {
     167               0 :             cPtr += 2;
     168                 :         }
     169           16862 :     } else if (base == 0) {
     170               0 :         if (*cPtr != '0') {
     171               0 :             base = 10;
     172               0 :         } else if (cPtr[1] == 'x' || cPtr[1] == 'X') {
     173               0 :             base = 16;
     174               0 :             cPtr += 2;
     175                 :         } else {
     176               0 :             base = 8;
     177                 :         } 
     178                 :     }
     179           16862 :     PR_ASSERT(base != 0);
     180           16862 :     LL_I2L(base64, base);
     181           16862 :     digitStart = cPtr;
     182                 : 
     183                 :     /* Skip leading zeros */
     184           33734 :     while (*cPtr == '0') {
     185              10 :         cPtr++;
     186                 :     }
     187                 : 
     188           16862 :     LL_I2L(x, 0);
     189          119835 :     while ((digitPtr = (char*)memchr(digits, tolower(*cPtr), base)) != NULL) {
     190                 :         PRUint64 d;
     191                 : 
     192           86111 :         LL_I2L(d, (digitPtr - digits));
     193           86111 :         LL_MUL(x, x, base64);
     194           86111 :         LL_ADD(x, x, d);
     195           86111 :         cPtr++;
     196                 :     }
     197                 : 
     198           16862 :     if (cPtr == digitStart) {
     199               0 :         if (endptr) {
     200               0 :             *endptr = (char *) str;
     201                 :         }
     202               0 :         return LL_ZERO;
     203                 :     }
     204                 : 
     205           16862 :     if (negative) {
     206                 : #ifdef HAVE_LONG_LONG
     207                 :         /* The cast to a signed type is to avoid a compiler warning */
     208              15 :         x = -(PRInt64)x;
     209                 : #else
     210                 :         LL_NEG(x, x);
     211                 : #endif
     212                 :     }
     213                 : 
     214           16862 :     if (endptr) {
     215               0 :         *endptr = (char *) cPtr;
     216                 :     }
     217           16862 :     return x;
     218                 : }
     219                 : 
     220                 : /*
     221                 :  * The maximum field width (in number of characters) that is enough
     222                 :  * (may be more than necessary) to represent a 64-bit integer or
     223                 :  * floating point number.
     224                 :  */
     225                 : #define FMAX 31
     226                 : #define DECIMAL_POINT '.'
     227                 : 
     228                 : static PRStatus
     229           18791 : GetInt(ScanfState *state, int code)
     230                 : {
     231                 :     char buf[FMAX + 1], *p;
     232                 :     int ch;
     233                 :     static const char digits[] = "0123456789abcdefABCDEF";
     234           18791 :     PRBool seenDigit = PR_FALSE;
     235                 :     int base;
     236                 :     int dlen;
     237                 : 
     238           18791 :     switch (code) {
     239                 :         case 'd': case 'u':
     240           18791 :             base = 10;
     241           18791 :             break;
     242                 :         case 'i':
     243               0 :             base = 0;
     244               0 :             break;
     245                 :         case 'x': case 'X': case 'p':
     246               0 :             base = 16;
     247               0 :             break;
     248                 :         case 'o':
     249               0 :             base = 8;
     250               0 :             break;
     251                 :         default:
     252               0 :             return PR_FAILURE;
     253                 :     }
     254           18791 :     if (state->width == 0 || state->width > FMAX) {
     255           18791 :         state->width = FMAX;
     256                 :     }
     257           18791 :     p = buf;
     258           18791 :     GET_IF_WITHIN_WIDTH(state, ch);
     259           18791 :     if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) {
     260              15 :         *p++ = ch;
     261              15 :         GET_IF_WITHIN_WIDTH(state, ch);
     262                 :     }
     263           18791 :     if (WITHIN_WIDTH(state) && ch == '0') {
     264              28 :         seenDigit = PR_TRUE;
     265              28 :         *p++ = ch;
     266              28 :         GET_IF_WITHIN_WIDTH(state, ch);
     267              28 :         if (WITHIN_WIDTH(state)
     268              28 :                 && (ch == 'x' || ch == 'X')
     269               0 :                 && (base == 0 || base == 16)) {
     270               0 :             base = 16;
     271               0 :             *p++ = ch;
     272               0 :             GET_IF_WITHIN_WIDTH(state, ch);
     273              28 :         } else if (base == 0) {
     274               0 :             base = 8;
     275                 :         }
     276                 :     }
     277           18791 :     if (base == 0 || base == 10) {
     278           18791 :         dlen = 10;
     279               0 :     } else if (base == 8) {
     280               0 :         dlen = 8;
     281                 :     } else {
     282               0 :         PR_ASSERT(base == 16);
     283               0 :         dlen = 16 + 6; /* 16 digits, plus 6 in uppercase */
     284                 :     }
     285          124785 :     while (WITHIN_WIDTH(state) && memchr(digits, ch, dlen)) {
     286           87203 :         *p++ = ch;
     287           87203 :         GET_IF_WITHIN_WIDTH(state, ch);
     288           87203 :         seenDigit = PR_TRUE;
     289                 :     }
     290           18791 :     if (WITHIN_WIDTH(state)) {
     291           18791 :         UNGET(state, ch);
     292                 :     }
     293           18791 :     if (!seenDigit) {
     294            1362 :         return PR_FAILURE;
     295                 :     }
     296           17429 :     *p = '\0';
     297           17429 :     if (state->assign) {
     298           17429 :         if (code == 'd' || code == 'i') {
     299           34604 :             if (state->sizeSpec == _PR_size_ll) {
     300           16862 :                 PRInt64 llval = _pr_strtoull(buf, NULL, base);
     301           16862 :                 *va_arg(state->ap, PRInt64 *) = llval;
     302                 :             } else {
     303             440 :                 long lval = strtol(buf, NULL, base);
     304                 : 
     305             440 :                 if (state->sizeSpec == _PR_size_none) {
     306             440 :                     *va_arg(state->ap, PRIntn *) = lval;
     307               0 :                 } else if (state->sizeSpec == _PR_size_h) {
     308               0 :                     *va_arg(state->ap, PRInt16 *) = (PRInt16)lval;
     309               0 :                 } else if (state->sizeSpec == _PR_size_l) {
     310               0 :                     *va_arg(state->ap, PRInt32 *) = lval;
     311                 :                 } else {
     312               0 :                     return PR_FAILURE;
     313                 :                 }
     314                 :             }
     315                 :         } else {
     316             127 :             if (state->sizeSpec == _PR_size_ll) {
     317               0 :                 PRUint64 llval = _pr_strtoull(buf, NULL, base);
     318               0 :                 *va_arg(state->ap, PRUint64 *) = llval;
     319                 :             } else {
     320             127 :                 unsigned long lval = strtoul(buf, NULL, base);
     321                 : 
     322             127 :                 if (state->sizeSpec == _PR_size_none) {
     323             127 :                     *va_arg(state->ap, PRUintn *) = lval;
     324               0 :                 } else if (state->sizeSpec == _PR_size_h) {
     325               0 :                     *va_arg(state->ap, PRUint16 *) = (PRUint16)lval;
     326               0 :                 } else if (state->sizeSpec == _PR_size_l) {
     327               0 :                     *va_arg(state->ap, PRUint32 *) = lval;
     328                 :                 } else {
     329               0 :                     return PR_FAILURE;
     330                 :                 }
     331                 :             }
     332                 :         }
     333           17429 :         state->converted = PR_TRUE;
     334                 :     }
     335           17429 :     return PR_SUCCESS;
     336                 : }
     337                 : 
     338                 : static PRStatus
     339               0 : GetFloat(ScanfState *state)
     340                 : {
     341                 :     char buf[FMAX + 1], *p;
     342                 :     int ch;
     343               0 :     PRBool seenDigit = PR_FALSE;
     344                 : 
     345               0 :     if (state->width == 0 || state->width > FMAX) {
     346               0 :         state->width = FMAX;
     347                 :     }
     348               0 :     p = buf;
     349               0 :     GET_IF_WITHIN_WIDTH(state, ch);
     350               0 :     if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) {
     351               0 :         *p++ = ch;
     352               0 :         GET_IF_WITHIN_WIDTH(state, ch);
     353                 :     }
     354               0 :     while (WITHIN_WIDTH(state) && isdigit(ch)) {
     355               0 :         *p++ = ch;
     356               0 :         GET_IF_WITHIN_WIDTH(state, ch);
     357               0 :         seenDigit = PR_TRUE;
     358                 :     }
     359               0 :     if (WITHIN_WIDTH(state) && ch == DECIMAL_POINT) {
     360               0 :         *p++ = ch;
     361               0 :         GET_IF_WITHIN_WIDTH(state, ch);
     362               0 :         while (WITHIN_WIDTH(state) && isdigit(ch)) {
     363               0 :             *p++ = ch;
     364               0 :             GET_IF_WITHIN_WIDTH(state, ch);
     365               0 :             seenDigit = PR_TRUE;
     366                 :         }
     367                 :     }
     368                 : 
     369                 :     /*
     370                 :      * This is not robust.  For example, "1.2e+" would confuse
     371                 :      * the code below to read 'e' and '+', only to realize that
     372                 :      * it should have stopped at "1.2".  But we can't push back
     373                 :      * more than one character, so there is nothing I can do.
     374                 :      */
     375                 : 
     376                 :     /* Parse exponent */
     377               0 :     if (WITHIN_WIDTH(state) && (ch == 'e' || ch == 'E') && seenDigit) {
     378               0 :         *p++ = ch;
     379               0 :         GET_IF_WITHIN_WIDTH(state, ch);
     380               0 :         if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) {
     381               0 :             *p++ = ch;
     382               0 :             GET_IF_WITHIN_WIDTH(state, ch);
     383                 :         }
     384               0 :         while (WITHIN_WIDTH(state) && isdigit(ch)) {
     385               0 :             *p++ = ch;
     386               0 :             GET_IF_WITHIN_WIDTH(state, ch);
     387                 :         }
     388                 :     }
     389               0 :     if (WITHIN_WIDTH(state)) {
     390               0 :         UNGET(state, ch);
     391                 :     }
     392               0 :     if (!seenDigit) {
     393               0 :         return PR_FAILURE;
     394                 :     }
     395               0 :     *p = '\0';
     396               0 :     if (state->assign) {
     397               0 :         PRFloat64 dval = PR_strtod(buf, NULL);
     398                 : 
     399               0 :         state->converted = PR_TRUE;
     400               0 :         if (state->sizeSpec == _PR_size_l) {
     401               0 :             *va_arg(state->ap, PRFloat64 *) = dval;
     402               0 :         } else if (state->sizeSpec == _PR_size_L) {
     403                 : #if defined(OSF1) || defined(IRIX)
     404                 :             *va_arg(state->ap, double *) = dval;
     405                 : #else
     406               0 :             *va_arg(state->ap, long double *) = dval;
     407                 : #endif
     408                 :         } else {
     409               0 :             *va_arg(state->ap, float *) = (float) dval;
     410                 :         }
     411                 :     }
     412               0 :     return PR_SUCCESS;
     413                 : }
     414                 : 
     415                 : /*
     416                 :  * Convert, and return the end of the conversion spec.
     417                 :  * Return NULL on error.
     418                 :  */
     419                 : 
     420                 : static const char *
     421           18903 : Convert(ScanfState *state, const char *fmt)
     422                 : {
     423                 :     const char *cPtr;
     424                 :     int ch;
     425           18903 :     char *cArg = NULL;
     426                 : 
     427           18903 :     state->converted = PR_FALSE;
     428           18903 :     cPtr = fmt;
     429           18903 :     if (*cPtr != 'c' && *cPtr != 'n' && *cPtr != '[') {
     430                 :         do {
     431           18811 :             ch = GET(state);
     432           18811 :         } while (isspace(ch));
     433           18791 :         UNGET(state, ch);
     434                 :     }
     435           18903 :     switch (*cPtr) {
     436                 :         case 'c':
     437             112 :             if (state->assign) {
     438             112 :                 cArg = va_arg(state->ap, char *);
     439                 :             }
     440             112 :             if (state->width == 0) {
     441             112 :                 state->width = 1;
     442                 :             }
     443             216 :             for (; state->width > 0; state->width--) {
     444             112 :                 ch = GET(state);
     445             112 :                 if (ch == EOF) {
     446               8 :                     return NULL;
     447             104 :                 } else if (state->assign) {
     448             104 :                     *cArg++ = ch;
     449                 :                 }
     450                 :             }
     451             104 :             if (state->assign) {
     452             104 :                 state->converted = PR_TRUE;
     453                 :             }
     454             104 :             break;
     455                 :         case 'p':
     456                 :         case 'd': case 'i': case 'o':
     457                 :         case 'u': case 'x': case 'X':
     458           18791 :             if (GetInt(state, *cPtr) == PR_FAILURE) {
     459            1362 :                 return NULL;
     460                 :             }
     461           17429 :             break;
     462                 :         case 'e': case 'E': case 'f':
     463                 :         case 'g': case 'G':
     464               0 :             if (GetFloat(state) == PR_FAILURE) {
     465               0 :                 return NULL;
     466                 :             }
     467               0 :             break;
     468                 :         case 'n':
     469                 :             /* do not consume any input */
     470               0 :             if (state->assign) {
     471               0 :                 switch (state->sizeSpec) {
     472                 :                     case _PR_size_none:
     473               0 :                         *va_arg(state->ap, PRIntn *) = state->nChar;
     474               0 :                         break;
     475                 :                     case _PR_size_h:
     476               0 :                         *va_arg(state->ap, PRInt16 *) = state->nChar;
     477               0 :                         break;
     478                 :                     case _PR_size_l:
     479               0 :                         *va_arg(state->ap, PRInt32 *) = state->nChar;
     480               0 :                         break;
     481                 :                     case _PR_size_ll:
     482               0 :                         LL_I2L(*va_arg(state->ap, PRInt64 *), state->nChar);
     483               0 :                         break;
     484                 :                     default:
     485               0 :                         PR_ASSERT(0);
     486                 :                 }
     487                 :             }
     488               0 :             break;
     489                 :         case 's':
     490               0 :             if (state->width == 0) {
     491               0 :                 state->width = INT_MAX;
     492                 :             }
     493               0 :             if (state->assign) {
     494               0 :                 cArg = va_arg(state->ap, char *);
     495                 :             }
     496               0 :             for (; state->width > 0; state->width--) {
     497               0 :                 ch = GET(state);
     498               0 :                 if ((ch == EOF) || isspace(ch)) {
     499               0 :                     UNGET(state, ch);
     500               0 :                     break;
     501                 :                 }
     502               0 :                 if (state->assign) {
     503               0 :                     *cArg++ = ch;
     504                 :                 }
     505                 :             }
     506               0 :             if (state->assign) {
     507               0 :                 *cArg = '\0';
     508               0 :                 state->converted = PR_TRUE;
     509                 :             }
     510               0 :             break;
     511                 :         case '%':
     512               0 :             ch = GET(state);
     513               0 :             if (ch != '%') {
     514               0 :                 UNGET(state, ch);
     515               0 :                 return NULL;
     516                 :             }
     517               0 :             break;
     518                 :         case '[':
     519                 :             {
     520               0 :                 PRBool complement = PR_FALSE;
     521                 :                 const char *closeBracket;
     522                 :                 size_t n;
     523                 : 
     524               0 :                 if (*++cPtr == '^') {
     525               0 :                     complement = PR_TRUE;
     526               0 :                     cPtr++;
     527                 :                 }
     528               0 :                 closeBracket = strchr(*cPtr == ']' ? cPtr + 1 : cPtr, ']');
     529               0 :                 if (closeBracket == NULL) {
     530               0 :                     return NULL;
     531                 :                 }
     532               0 :                 n = closeBracket - cPtr;
     533               0 :                 if (state->width == 0) {
     534               0 :                     state->width = INT_MAX;
     535                 :                 }
     536               0 :                 if (state->assign) {
     537               0 :                     cArg = va_arg(state->ap, char *);
     538                 :                 }
     539               0 :                 for (; state->width > 0; state->width--) {
     540               0 :                     ch = GET(state);
     541               0 :                     if ((ch == EOF) 
     542               0 :                             || (!complement && !memchr(cPtr, ch, n))
     543               0 :                             || (complement && memchr(cPtr, ch, n))) {
     544               0 :                         UNGET(state, ch);
     545               0 :                         break;
     546                 :                     }
     547               0 :                     if (state->assign) {
     548               0 :                         *cArg++ = ch;
     549                 :                     }
     550                 :                 }
     551               0 :                 if (state->assign) {
     552               0 :                     *cArg = '\0';
     553               0 :                     state->converted = PR_TRUE;
     554                 :                 }
     555               0 :                 cPtr = closeBracket;
     556                 :             }
     557               0 :             break;
     558                 :         default:
     559               0 :             return NULL;
     560                 :     }
     561           17533 :     return cPtr;
     562                 : }
     563                 : 
     564                 : static PRInt32
     565           18548 : DoScanf(ScanfState *state, const char *fmt)
     566                 : {
     567           18548 :     PRInt32 nConverted = 0;
     568                 :     const char *cPtr;
     569                 :     int ch;
     570                 : 
     571           18548 :     state->nChar = 0;
     572           18548 :     cPtr = fmt;
     573                 :     while (1) {
     574           36657 :         if (isspace(*cPtr)) {
     575                 :             /* white space: skip */
     576                 :             do {
     577               0 :                 cPtr++;
     578               0 :             } while (isspace(*cPtr));
     579                 :             do {
     580               0 :                 ch = GET(state);
     581               0 :             } while (isspace(ch));
     582               0 :             UNGET(state, ch);
     583           36657 :         } else if (*cPtr == '%') {
     584                 :             /* format spec: convert */
     585           18903 :             cPtr++;
     586           18903 :             state->assign = PR_TRUE;
     587           18903 :             if (*cPtr == '*') {
     588               0 :                 cPtr++;
     589               0 :                 state->assign = PR_FALSE;
     590                 :             }
     591           18903 :             for (state->width = 0; isdigit(*cPtr); cPtr++) {
     592               0 :                 state->width = state->width * 10 + *cPtr - '0';
     593                 :             }
     594           18903 :             state->sizeSpec = _PR_size_none;
     595           18903 :             if (*cPtr == 'h') {
     596               0 :                 cPtr++;
     597               0 :                 state->sizeSpec = _PR_size_h;
     598           18903 :             } else if (*cPtr == 'l') {
     599           17556 :                 cPtr++;
     600           17556 :                 if (*cPtr == 'l') {
     601           17556 :                     cPtr++;
     602           17556 :                     state->sizeSpec = _PR_size_ll;
     603                 :                 } else {
     604               0 :                     state->sizeSpec = _PR_size_l;
     605                 :                 }
     606            1347 :             } else if (*cPtr == 'L') {
     607               0 :                 cPtr++;
     608               0 :                 state->sizeSpec = _PR_size_L;
     609                 :             }
     610           18903 :             cPtr = Convert(state, cPtr);
     611           18903 :             if (cPtr == NULL) {
     612            1370 :                 return (nConverted > 0 ? nConverted : EOF);
     613                 :             }
     614           17533 :             if (state->converted) {
     615           17533 :                 nConverted++;
     616                 :             }
     617           17533 :             cPtr++;
     618                 :         } else {
     619                 :             /* others: must match */
     620           17754 :             if (*cPtr == '\0') {
     621           17148 :                 return nConverted;
     622                 :             }
     623             606 :             ch = GET(state);
     624             606 :             if (ch != *cPtr) {
     625              30 :                 UNGET(state, ch);
     626              30 :                 return nConverted;
     627                 :             }
     628             576 :             cPtr++;
     629                 :         }
     630           18109 :     }
     631                 : }
     632                 : 
     633                 : static int
     634          125566 : StringGetChar(void *stream)
     635                 : {
     636          125566 :     char *cPtr = *((char **) stream);
     637                 : 
     638          125566 :     if (*cPtr == '\0') {
     639           17171 :         return EOF;
     640                 :     } else {
     641          108395 :         *((char **) stream) = cPtr + 1;
     642          108395 :         return (unsigned char) *cPtr;
     643                 :     }
     644                 : }
     645                 : 
     646                 : static void
     647           37612 : StringUngetChar(void *stream, int ch)
     648                 : {
     649           37612 :     char *cPtr = *((char **) stream);
     650                 : 
     651           37612 :     if (ch != EOF) {
     652           20449 :         *((char **) stream) = cPtr - 1;
     653                 :     }
     654           37612 : }
     655                 : 
     656                 : PR_IMPLEMENT(PRInt32)
     657           18548 : PR_sscanf(const char *buf, const char *fmt, ...)
     658                 : {
     659                 :     PRInt32 rv;
     660                 :     ScanfState state;
     661                 : 
     662           18548 :     state.get = &StringGetChar;
     663           18548 :     state.unget = &StringUngetChar;
     664           18548 :     state.stream = (void *) &buf;
     665           18548 :     va_start(state.ap, fmt);
     666           18548 :     rv = DoScanf(&state, fmt);
     667           18548 :     va_end(state.ap);
     668           18548 :     return rv;
     669                 : }

Generated by: LCOV version 1.7