LCOV - code coverage report
Current view: top level - nsprpub/pr/src/io - prscanf.c (source / functions) Hit Total Coverage
Test: output.info Lines: 105 312 33.7 %
Date: 2017-07-14 16:53:18 Functions: 5 7 71.4 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13