Annotation of embedaddon/php/ext/standard/scanf.c, revision 1.1.1.3

1.1       misho       1: /*
                      2:    +----------------------------------------------------------------------+
                      3:    | PHP Version 5                                                        |
                      4:    +----------------------------------------------------------------------+
1.1.1.3 ! misho       5:    | Copyright (c) 1997-2013 The PHP Group                                |
1.1       misho       6:    +----------------------------------------------------------------------+
                      7:    | This source file is subject to version 3.01 of the PHP license,      |
                      8:    | that is bundled with this package in the file LICENSE, and is        |
                      9:    | available through the world-wide-web at the following url:           |
                     10:    | http://www.php.net/license/3_01.txt                                  |
                     11:    | If you did not receive a copy of the PHP license and are unable to   |
                     12:    | obtain it through the world-wide-web, please send a note to          |
                     13:    | license@php.net so we can mail you a copy immediately.               |
                     14:    +----------------------------------------------------------------------+
                     15:    | Author: Clayton Collie <clcollie@mindspring.com>                     |
                     16:    +----------------------------------------------------------------------+
                     17: */
                     18: 
1.1.1.2   misho      19: /* $Id$ */
1.1       misho      20: 
                     21: /*
                     22:        scanf.c --
                     23: 
                     24:        This file contains the base code which implements sscanf and by extension
                     25:        fscanf. Original code is from TCL8.3.0 and bears the following copyright:
                     26: 
                     27:        This software is copyrighted by the Regents of the University of
                     28:        California, Sun Microsystems, Inc., Scriptics Corporation,
                     29:        and other parties.  The following terms apply to all files associated
                     30:        with the software unless explicitly disclaimed in individual files.
                     31: 
                     32:        The authors hereby grant permission to use, copy, modify, distribute,
                     33:        and license this software and its documentation for any purpose, provided
                     34:        that existing copyright notices are retained in all copies and that this
                     35:        notice is included verbatim in any distributions. No written agreement,
                     36:        license, or royalty fee is required for any of the authorized uses.
                     37:        Modifications to this software may be copyrighted by their authors
                     38:        and need not follow the licensing terms described here, provided that
                     39:        the new terms are clearly indicated on the first page of each file where
                     40:        they apply.
                     41: 
                     42:        IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
                     43:        FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
                     44:        ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
                     45:        DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
                     46:        POSSIBILITY OF SUCH DAMAGE.
                     47: 
                     48:        THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
                     49:        INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
                     50:        FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
                     51:        IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
                     52:        NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
                     53:        MODIFICATIONS.
                     54: 
                     55:        GOVERNMENT USE: If you are acquiring this software on behalf of the
                     56:        U.S. government, the Government shall have only "Restricted Rights"
                     57:        in the software and related documentation as defined in the Federal
                     58:        Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
                     59:        are acquiring the software on behalf of the Department of Defense, the
                     60:        software shall be classified as "Commercial Computer Software" and the
                     61:        Government shall have only "Restricted Rights" as defined in Clause
                     62:        252.227-7013 (c) (1) of DFARs.  Notwithstanding the foregoing, the
                     63:        authors grant the U.S. Government and others acting in its behalf
                     64:        permission to use and distribute the software in accordance with the
                     65:        terms specified in this license.
                     66: */
                     67: 
                     68: #include <stdio.h>
                     69: #include <limits.h>
                     70: #include <ctype.h>
                     71: #include "php.h"
                     72: #include "php_variables.h"
                     73: #ifdef HAVE_LOCALE_H
                     74: #include <locale.h>
                     75: #endif
                     76: #include "zend_execute.h"
                     77: #include "zend_operators.h"
                     78: #include "zend_strtod.h"
                     79: #include "php_globals.h"
                     80: #include "basic_functions.h"
                     81: #include "scanf.h"
                     82: 
                     83: /*
                     84:  * Flag values used internally by [f|s]canf.
                     85:  */
                     86: #define SCAN_NOSKIP     0x1       /* Don't skip blanks. */
                     87: #define SCAN_SUPPRESS  0x2       /* Suppress assignment. */
                     88: #define SCAN_UNSIGNED  0x4       /* Read an unsigned value. */
                     89: #define SCAN_WIDTH      0x8       /* A width value was supplied. */
                     90: 
                     91: #define SCAN_SIGNOK     0x10      /* A +/- character is allowed. */
                     92: #define SCAN_NODIGITS   0x20      /* No digits have been scanned. */
                     93: #define SCAN_NOZERO     0x40      /* No zero digits have been scanned. */
                     94: #define SCAN_XOK        0x80      /* An 'x' is allowed. */
                     95: #define SCAN_PTOK       0x100     /* Decimal point is allowed. */
                     96: #define SCAN_EXPOK      0x200     /* An exponent is allowed. */
                     97: 
                     98: #define UCHAR(x)               (zend_uchar)(x)
                     99: 
                    100: /*
                    101:  * The following structure contains the information associated with
                    102:  * a character set.
                    103:  */
                    104: typedef struct CharSet {
                    105:        int exclude;            /* 1 if this is an exclusion set. */
                    106:        int nchars;
                    107:        char *chars;
                    108:        int nranges;
                    109:        struct Range {
                    110:                char start;
                    111:                char end;
                    112:        } *ranges;
                    113: } CharSet;
                    114: 
                    115: /*
                    116:  * Declarations for functions used only in this file.
                    117:  */
                    118: static char *BuildCharSet(CharSet *cset, char *format);
                    119: static int     CharInSet(CharSet *cset, int ch);
                    120: static void    ReleaseCharSet(CharSet *cset);
                    121: static inline void scan_set_error_return(int numVars, zval **return_value);
                    122: 
                    123: 
                    124: /* {{{ BuildCharSet
                    125:  *----------------------------------------------------------------------
                    126:  *
                    127:  * BuildCharSet --
                    128:  *
                    129:  *     This function examines a character set format specification
                    130:  *     and builds a CharSet containing the individual characters and
                    131:  *     character ranges specified.
                    132:  *
                    133:  * Results:
                    134:  *     Returns the next format position.
                    135:  *
                    136:  * Side effects:
                    137:  *     Initializes the charset.
                    138:  *
                    139:  *----------------------------------------------------------------------
                    140:  */
                    141: static char * BuildCharSet(CharSet *cset, char *format)
                    142: {
                    143:        char *ch, start;
                    144:        int  nranges;
                    145:        char *end;
                    146: 
                    147:        memset(cset, 0, sizeof(CharSet));
                    148: 
                    149:        ch = format;
                    150:        if (*ch == '^') {
                    151:                cset->exclude = 1;
                    152:                ch = ++format;
                    153:        }
                    154:        end = format + 1;       /* verify this - cc */
                    155: 
                    156:        /*
                    157:         * Find the close bracket so we can overallocate the set.
                    158:         */
                    159:        if (*ch == ']') {
                    160:                ch = end++;
                    161:        }
                    162:        nranges = 0;
                    163:        while (*ch != ']') {
                    164:                if (*ch == '-') {
                    165:                        nranges++;
                    166:                }
                    167:                ch = end++;
                    168:        }
                    169: 
                    170:        cset->chars = (char *) safe_emalloc(sizeof(char), (end - format - 1), 0);
                    171:        if (nranges > 0) {
                    172:                cset->ranges = (struct Range *) safe_emalloc(sizeof(struct Range), nranges, 0);
                    173:        } else {
                    174:                cset->ranges = NULL;
                    175:        }
                    176: 
                    177:        /*
                    178:         * Now build the character set.
                    179:         */
                    180:        cset->nchars = cset->nranges = 0;
                    181:        ch    = format++;
                    182:        start = *ch;
                    183:        if (*ch == ']' || *ch == '-') {
                    184:                cset->chars[cset->nchars++] = *ch;
                    185:                ch = format++;
                    186:        }
                    187:        while (*ch != ']') {
                    188:                if (*format == '-') {
                    189:                        /*
                    190:                         * This may be the first character of a range, so don't add
                    191:                         * it yet.
                    192:                         */
                    193:                        start = *ch;
                    194:                } else if (*ch == '-') {
                    195:                        /*
                    196:                         * Check to see if this is the last character in the set, in which
                    197:                         * case it is not a range and we should add the previous character
                    198:                         * as well as the dash.
                    199:                         */
                    200:                        if (*format == ']') {
                    201:                                cset->chars[cset->nchars++] = start;
                    202:                                cset->chars[cset->nchars++] = *ch;
                    203:                        } else {
                    204:                                ch = format++;
                    205: 
                    206:                                /*
                    207:                                 * Check to see if the range is in reverse order.
                    208:                                 */
                    209:                                if (start < *ch) {
                    210:                                        cset->ranges[cset->nranges].start = start;
                    211:                                        cset->ranges[cset->nranges].end = *ch;
                    212:                                } else {
                    213:                                        cset->ranges[cset->nranges].start = *ch;
                    214:                                        cset->ranges[cset->nranges].end = start;
                    215:                                }
                    216:                                cset->nranges++;
                    217:                        }
                    218:                } else {
                    219:                        cset->chars[cset->nchars++] = *ch;
                    220:                }
                    221:                ch = format++;
                    222:        }
                    223:        return format;
                    224: }
                    225: /* }}} */
                    226: 
                    227: /* {{{ CharInSet
                    228:  *----------------------------------------------------------------------
                    229:  *
                    230:  * CharInSet --
                    231:  *
                    232:  *     Check to see if a character matches the given set.
                    233:  *
                    234:  * Results:
                    235:  *     Returns non-zero if the character matches the given set.
                    236:  *
                    237:  * Side effects:
                    238:  *     None.
                    239:  *
                    240:  *----------------------------------------------------------------------
                    241:  */
                    242: static int CharInSet(CharSet *cset, int c)
                    243: {
                    244:        char ch = (char) c;
                    245:        int i, match = 0;
                    246: 
                    247:        for (i = 0; i < cset->nchars; i++) {
                    248:                if (cset->chars[i] == ch) {
                    249:                        match = 1;
                    250:                        break;
                    251:                }
                    252:        }
                    253:        if (!match) {
                    254:                for (i = 0; i < cset->nranges; i++) {
                    255:                        if ((cset->ranges[i].start <= ch)
                    256:                                && (ch <= cset->ranges[i].end)) {
                    257:                                match = 1;
                    258:                                break;
                    259:                        }
                    260:                }
                    261:        }
                    262:        return (cset->exclude ? !match : match);
                    263: }
                    264: /* }}} */
                    265: 
                    266: /* {{{ ReleaseCharSet
                    267:  *----------------------------------------------------------------------
                    268:  *
                    269:  * ReleaseCharSet --
                    270:  *
                    271:  *     Free the storage associated with a character set.
                    272:  *
                    273:  * Results:
                    274:  *     None.
                    275:  *
                    276:  * Side effects:
                    277:  *     None.
                    278:  *
                    279:  *----------------------------------------------------------------------
                    280:  */
                    281: static void ReleaseCharSet(CharSet *cset)
                    282: {
                    283:        efree((char *)cset->chars);
                    284:        if (cset->ranges) {
                    285:                efree((char *)cset->ranges);
                    286:        }
                    287: }
                    288: /* }}} */
                    289: 
                    290: /* {{{ ValidateFormat
                    291:  *----------------------------------------------------------------------
                    292:  *
                    293:  * ValidateFormat --
                    294:  *
                    295:  *     Parse the format string and verify that it is properly formed
                    296:  *     and that there are exactly enough variables on the command line.
                    297:  *
                    298:  * Results:
                    299:  *    FAILURE or SUCCESS.
                    300:  *
                    301:  * Side effects:
                    302:  *     May set php_error based on abnormal conditions.
                    303:  *
                    304:  * Parameters :
                    305:  *     format     The format string.
                    306:  *     numVars    The number of variables passed to the scan command.
                    307:  *     totalSubs  The number of variables that will be required.
                    308:  *
                    309:  *----------------------------------------------------------------------
                    310: */
                    311: PHPAPI int ValidateFormat(char *format, int numVars, int *totalSubs)
                    312: {
                    313: #define STATIC_LIST_SIZE 16
                    314:        int gotXpg, gotSequential, value, i, flags;
                    315:        char *end, *ch = NULL;
                    316:        int staticAssign[STATIC_LIST_SIZE];
                    317:        int *nassign = staticAssign;
                    318:        int objIndex, xpgSize, nspace = STATIC_LIST_SIZE;
                    319:        TSRMLS_FETCH();
                    320: 
                    321:        /*
                    322:         * Initialize an array that records the number of times a variable
                    323:         * is assigned to by the format string.  We use this to detect if
                    324:         * a variable is multiply assigned or left unassigned.
                    325:         */
                    326:        if (numVars > nspace) {
                    327:                nassign = (int*)safe_emalloc(sizeof(int), numVars, 0);
                    328:                nspace = numVars;
                    329:        }
                    330:        for (i = 0; i < nspace; i++) {
                    331:                nassign[i] = 0;
                    332:        }
                    333: 
                    334:        xpgSize = objIndex = gotXpg = gotSequential = 0;
                    335: 
                    336:        while (*format != '\0') {
                    337:                ch = format++;
                    338:                flags = 0;
                    339: 
                    340:                if (*ch != '%') {
                    341:                        continue;
                    342:                }
                    343:                ch = format++;
                    344:                if (*ch == '%') {
                    345:                        continue;
                    346:                }
                    347:                if (*ch == '*') {
                    348:                        flags |= SCAN_SUPPRESS;
                    349:                        ch = format++;
                    350:                        goto xpgCheckDone;
                    351:                }
                    352: 
                    353:                if ( isdigit( (int)*ch ) ) {
                    354:                        /*
                    355:                         * Check for an XPG3-style %n$ specification.  Note: there
                    356:                         * must not be a mixture of XPG3 specs and non-XPG3 specs
                    357:                         * in the same format string.
                    358:                         */
                    359:                        value = strtoul(format-1, &end, 10);
                    360:                        if (*end != '$') {
                    361:                                goto notXpg;
                    362:                        }
                    363:                        format = end+1;
                    364:                        ch     = format++;
                    365:                        gotXpg = 1;
                    366:                        if (gotSequential) {
                    367:                                goto mixedXPG;
                    368:                        }
                    369:                        objIndex = value - 1;
                    370:                        if ((objIndex < 0) || (numVars && (objIndex >= numVars))) {
                    371:                                goto badIndex;
                    372:                        } else if (numVars == 0) {
                    373:                                /*
                    374:                                 * In the case where no vars are specified, the user can
                    375:                                 * specify %9999$ legally, so we have to consider special
                    376:                                 * rules for growing the assign array.  'value' is
                    377:                                 * guaranteed to be > 0.
                    378:                                 */
                    379: 
                    380:                                /* set a lower artificial limit on this
                    381:                                 * in the interest of security and resource friendliness
                    382:                                 * 255 arguments should be more than enough. - cc
                    383:                                 */
                    384:                                if (value > SCAN_MAX_ARGS) {
                    385:                                        goto badIndex;
                    386:                                }
                    387: 
                    388:                                xpgSize = (xpgSize > value) ? xpgSize : value;
                    389:                        }
                    390:                        goto xpgCheckDone;
                    391:                }
                    392: 
                    393: notXpg:
                    394:                gotSequential = 1;
                    395:                if (gotXpg) {
                    396: mixedXPG:
                    397:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", "cannot mix \"%\" and \"%n$\" conversion specifiers");
                    398:                        goto error;
                    399:                }
                    400: 
                    401: xpgCheckDone:
                    402:                /*
                    403:                 * Parse any width specifier.
                    404:                 */
                    405:                if (isdigit(UCHAR(*ch))) {
                    406:                        value = strtoul(format-1, &format, 10);
                    407:                        flags |= SCAN_WIDTH;
                    408:                        ch = format++;
                    409:                }
                    410: 
                    411:                /*
                    412:                 * Ignore size specifier.
                    413:                 */
                    414:                if ((*ch == 'l') || (*ch == 'L') || (*ch == 'h')) {
                    415:                        ch = format++;
                    416:                }
                    417: 
                    418:                if (!(flags & SCAN_SUPPRESS) && numVars && (objIndex >= numVars)) {
                    419:                        goto badIndex;
                    420:                }
                    421: 
                    422:                /*
                    423:                 * Handle the various field types.
                    424:                 */
                    425:                switch (*ch) {
                    426:                        case 'n':
                    427:                        case 'd':
                    428:                        case 'D':
                    429:                        case 'i':
                    430:                        case 'o':
                    431:                        case 'x':
                    432:                        case 'X':
                    433:                        case 'u':
                    434:                        case 'f':
                    435:                        case 'e':
                    436:                        case 'E':
                    437:                        case 'g':
                    438:                        case 's':
                    439:                                break;
                    440: 
                    441:                        case 'c':
                    442:                                /* we differ here with the TCL implementation in allowing for */
                    443:                                /* a character width specification, to be more consistent with */
                    444:                                /* ANSI. since Zend auto allocates space for vars, this is no */
                    445:                                /* problem - cc                                               */
                    446:                                /*
                    447:                                if (flags & SCAN_WIDTH) {
                    448:                                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Field width may not be specified in %c conversion");
                    449:                                        goto error;
                    450:                                }
                    451:                                */
                    452:                                break;
                    453: 
                    454:                        case '[':
                    455:                                if (*format == '\0') {
                    456:                                        goto badSet;
                    457:                                }
                    458:                                ch = format++;
                    459:                                if (*ch == '^') {
                    460:                                        if (*format == '\0') {
                    461:                                                goto badSet;
                    462:                                        }
                    463:                                        ch = format++;
                    464:                                }
                    465:                                if (*ch == ']') {
                    466:                                        if (*format == '\0') {
                    467:                                                goto badSet;
                    468:                                        }
                    469:                                        ch = format++;
                    470:                                }
                    471:                                while (*ch != ']') {
                    472:                                        if (*format == '\0') {
                    473:                                                goto badSet;
                    474:                                        }
                    475:                                        ch = format++;
                    476:                                }
                    477:                                break;
                    478: badSet:
                    479:                                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unmatched [ in format string");
                    480:                                goto error;
                    481: 
                    482:                        default: {
                    483:                                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Bad scan conversion character \"%c\"", *ch);
                    484:                                goto error;
                    485:                        }
                    486:                }
                    487: 
                    488:                if (!(flags & SCAN_SUPPRESS)) {
                    489:                        if (objIndex >= nspace) {
                    490:                                /*
                    491:                                 * Expand the nassign buffer.  If we are using XPG specifiers,
                    492:                                 * make sure that we grow to a large enough size.  xpgSize is
                    493:                                 * guaranteed to be at least one larger than objIndex.
                    494:                                 */
                    495:                                value = nspace;
                    496:                                if (xpgSize) {
                    497:                                        nspace = xpgSize;
                    498:                                } else {
                    499:                                        nspace += STATIC_LIST_SIZE;
                    500:                                }
                    501:                                if (nassign == staticAssign) {
                    502:                                        nassign = (void *)safe_emalloc(nspace, sizeof(int), 0);
                    503:                                        for (i = 0; i < STATIC_LIST_SIZE; ++i) {
                    504:                                                nassign[i] = staticAssign[i];
                    505:                                        }
                    506:                                } else {
                    507:                                        nassign = (void *)erealloc((void *)nassign, nspace * sizeof(int));
                    508:                                }
                    509:                                for (i = value; i < nspace; i++) {
                    510:                                        nassign[i] = 0;
                    511:                                }
                    512:                        }
                    513:                        nassign[objIndex]++;
                    514:                        objIndex++;
                    515:                }
                    516:        } /* while (*format != '\0') */
                    517: 
                    518:        /*
                    519:         * Verify that all of the variable were assigned exactly once.
                    520:         */
                    521:        if (numVars == 0) {
                    522:                if (xpgSize) {
                    523:                        numVars = xpgSize;
                    524:                } else {
                    525:                        numVars = objIndex;
                    526:                }
                    527:        }
                    528:        if (totalSubs) {
                    529:                *totalSubs = numVars;
                    530:        }
                    531:        for (i = 0; i < numVars; i++) {
                    532:                if (nassign[i] > 1) {
                    533:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", "Variable is assigned by multiple \"%n$\" conversion specifiers");
                    534:                        goto error;
                    535:                } else if (!xpgSize && (nassign[i] == 0)) {
                    536:                        /*
                    537:                         * If the space is empty, and xpgSize is 0 (means XPG wasn't
                    538:                         * used, and/or numVars != 0), then too many vars were given
                    539:                         */
                    540:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Variable is not assigned by any conversion specifiers");
                    541:                        goto error;
                    542:                }
                    543:        }
                    544: 
                    545:        if (nassign != staticAssign) {
                    546:                efree((char *)nassign);
                    547:        }
                    548:        return SCAN_SUCCESS;
                    549: 
                    550: badIndex:
                    551:        if (gotXpg) {
                    552:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", "\"%n$\" argument index out of range");
                    553:        } else {
                    554:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Different numbers of variable names and field specifiers");
                    555:        }
                    556: 
                    557: error:
                    558:        if (nassign != staticAssign) {
                    559:                efree((char *)nassign);
                    560:        }
                    561:        return SCAN_ERROR_INVALID_FORMAT;
                    562: #undef STATIC_LIST_SIZE
                    563: }
                    564: /* }}} */
                    565: 
                    566: /* {{{ php_sscanf_internal
                    567:  * This is the internal function which does processing on behalf of
                    568:  * both sscanf() and fscanf()
                    569:  *
                    570:  * parameters :
                    571:  *             string          literal string to be processed
                    572:  *             format          format string
                    573:  *             argCount        total number of elements in the args array
                    574:  *             args            arguments passed in from user function (f|s)scanf
                    575:  *             varStart        offset (in args) of 1st variable passed in to (f|s)scanf
                    576:  *             return_value set with the results of the scan
                    577:  */
                    578: 
                    579: PHPAPI int php_sscanf_internal( char *string, char *format,
                    580:                                int argCount, zval ***args,
                    581:                                int varStart, zval **return_value TSRMLS_DC)
                    582: {
                    583:        int  numVars, nconversions, totalVars = -1;
                    584:        int  i, result;
                    585:        long value;
                    586:        int  objIndex;
                    587:        char *end, *baseString;
                    588:        zval **current;
                    589:        char op   = 0;
                    590:        int  base = 0;
                    591:        int  underflow = 0;
                    592:        size_t width;
                    593:        long (*fn)() = NULL;
                    594:        char *ch, sch;
                    595:        int  flags;
                    596:        char buf[64];   /* Temporary buffer to hold scanned number
                    597:                                         * strings before they are passed to strtoul() */
                    598: 
                    599:        /* do some sanity checking */
                    600:        if ((varStart > argCount) || (varStart < 0)){
                    601:                varStart = SCAN_MAX_ARGS + 1;
                    602:        }
                    603:        numVars = argCount - varStart;
                    604:        if (numVars < 0) {
                    605:                numVars = 0;
                    606:        }
                    607: 
                    608: #if 0
                    609:        zend_printf("<br>in sscanf_internal : <br> string is \"%s\", format = \"%s\"<br> NumVars = %d. VarStart = %d<br>-------------------------<br>",
                    610:                                        string, format, numVars, varStart);
                    611: #endif
                    612:        /*
                    613:         * Check for errors in the format string.
                    614:         */
                    615:        if (ValidateFormat(format, numVars, &totalVars) != SCAN_SUCCESS) {
                    616:                scan_set_error_return( numVars, return_value );
                    617:                return SCAN_ERROR_INVALID_FORMAT;
                    618:        }
                    619: 
                    620:        objIndex = numVars ? varStart : 0;
                    621: 
                    622:        /*
                    623:         * If any variables are passed, make sure they are all passed by reference
                    624:         */
                    625:        if (numVars) {
                    626:                for (i = varStart;i < argCount;i++){
                    627:                        if ( ! PZVAL_IS_REF( *args[ i ] ) ) {
                    628:                                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Parameter %d must be passed by reference", i);
                    629:                                scan_set_error_return(numVars, return_value);
                    630:                                return SCAN_ERROR_VAR_PASSED_BYVAL;
                    631:                        }
                    632:                }
                    633:        }
                    634: 
                    635:        /*
                    636:         * Allocate space for the result objects. Only happens when no variables
                    637:         * are specified
                    638:         */
                    639:        if (!numVars) {
                    640:                zval *tmp;
                    641: 
                    642:                /* allocate an array for return */
                    643:                array_init(*return_value);
                    644: 
                    645:                for (i = 0; i < totalVars; i++) {
                    646:                        MAKE_STD_ZVAL(tmp);
                    647:                        ZVAL_NULL(tmp);
                    648:                        if (add_next_index_zval(*return_value, tmp) == FAILURE) {
                    649:                                scan_set_error_return(0, return_value);
                    650:                                return FAILURE;
                    651:                        }
                    652:                }
                    653:                varStart = 0; /* Array index starts from 0 */
                    654:        }
                    655: 
                    656:        baseString = string;
                    657: 
                    658:        /*
                    659:         * Iterate over the format string filling in the result objects until
                    660:         * we reach the end of input, the end of the format string, or there
                    661:         * is a mismatch.
                    662:         */
                    663:        nconversions = 0;
                    664:        /* note ! - we need to limit the loop for objIndex to keep it in bounds */
                    665: 
                    666:        while (*format != '\0') {
                    667:                ch    = format++;
                    668:                flags = 0;
                    669: 
                    670:                /*
                    671:                 * If we see whitespace in the format, skip whitespace in the string.
                    672:                 */
                    673:                if ( isspace( (int)*ch ) ) {
                    674:                        sch = *string;
                    675:                        while ( isspace( (int)sch ) ) {
                    676:                                if (*string == '\0') {
                    677:                                        goto done;
                    678:                                }
                    679:                                string++;
                    680:                                sch = *string;
                    681:                        }
                    682:                        continue;
                    683:                }
                    684: 
                    685:                if (*ch != '%') {
                    686: literal:
                    687:                        if (*string == '\0') {
                    688:                                underflow = 1;
                    689:                                goto done;
                    690:                        }
                    691:                        sch = *string;
                    692:                        string++;
                    693:                        if (*ch != sch) {
                    694:                                goto done;
                    695:                        }
                    696:                        continue;
                    697:                }
                    698: 
                    699:                ch = format++;
                    700:                if (*ch == '%') {
                    701:                        goto literal;
                    702:                }
                    703: 
                    704:                /*
                    705:                 * Check for assignment suppression ('*') or an XPG3-style
                    706:                 * assignment ('%n$').
                    707:                 */
                    708:                if (*ch == '*') {
                    709:                        flags |= SCAN_SUPPRESS;
                    710:                        ch = format++;
                    711:                } else if ( isdigit(UCHAR(*ch))) {
                    712:                        value = strtoul(format-1, &end, 10);
                    713:                        if (*end == '$') {
                    714:                                format = end+1;
                    715:                                ch = format++;
                    716:                                objIndex = varStart + value - 1;
                    717:                        }
                    718:                }
                    719: 
                    720:                /*
                    721:                 * Parse any width specifier.
                    722:                 */
                    723:                if ( isdigit(UCHAR(*ch))) {
                    724:                        width = strtoul(format-1, &format, 10);
                    725:                        ch = format++;
                    726:                } else {
                    727:                        width = 0;
                    728:                }
                    729: 
                    730:                /*
                    731:                 * Ignore size specifier.
                    732:                 */
                    733:                if ((*ch == 'l') || (*ch == 'L') || (*ch == 'h')) {
                    734:                        ch = format++;
                    735:                }
                    736: 
                    737:                /*
                    738:                 * Handle the various field types.
                    739:                 */
                    740:                switch (*ch) {
                    741:                        case 'n':
                    742:                                if (!(flags & SCAN_SUPPRESS)) {
                    743:                                        if (numVars && objIndex >= argCount) {
                    744:                                                break;
                    745:                                        } else if (numVars) {
                    746:                                                zend_uint refcount;
                    747: 
                    748:                                                current = args[objIndex++];
                    749:                                                refcount = Z_REFCOUNT_PP(current);
                    750:                                                zval_dtor( *current );
                    751:                                                ZVAL_LONG( *current, (long)(string - baseString) );
                    752:                                                Z_SET_REFCOUNT_PP(current, refcount);
                    753:                                                Z_SET_ISREF_PP(current);
                    754:                                        } else {
                    755:                                                add_index_long(*return_value, objIndex++, string - baseString);
                    756:                                        }
                    757:                                }
                    758:                                nconversions++;
                    759:                                continue;
                    760: 
                    761:                        case 'd':
                    762:                        case 'D':
                    763:                                op = 'i';
                    764:                                base = 10;
                    765:                                fn = (long (*)())strtol;
                    766:                                break;
                    767:                        case 'i':
                    768:                                op = 'i';
                    769:                                base = 0;
                    770:                                fn = (long (*)())strtol;
                    771:                                break;
                    772:                        case 'o':
                    773:                                op = 'i';
                    774:                                base = 8;
                    775:                                fn = (long (*)())strtol;
                    776:                                break;
                    777:                        case 'x':
                    778:                        case 'X':
                    779:                                op = 'i';
                    780:                                base = 16;
                    781:                                fn = (long (*)())strtol;
                    782:                                break;
                    783:                        case 'u':
                    784:                                op = 'i';
                    785:                                base = 10;
                    786:                                flags |= SCAN_UNSIGNED;
                    787:                                fn = (long (*)())strtoul;
                    788:                                break;
                    789: 
                    790:                        case 'f':
                    791:                        case 'e':
                    792:                        case 'E':
                    793:                        case 'g':
                    794:                                op = 'f';
                    795:                                break;
                    796: 
                    797:                        case 's':
                    798:                                op = 's';
                    799:                                break;
                    800: 
                    801:                        case 'c':
                    802:                                op = 's';
                    803:                                flags |= SCAN_NOSKIP;
                    804:                                /*-cc-*/
                    805:                                if (0 == width) {
                    806:                                        width = 1;
                    807:                                }
                    808:                                /*-cc-*/
                    809:                                break;
                    810:                        case '[':
                    811:                                op = '[';
                    812:                                flags |= SCAN_NOSKIP;
                    813:                                break;
                    814:                }   /* switch */
                    815: 
                    816:                /*
                    817:                 * At this point, we will need additional characters from the
                    818:                 * string to proceed.
                    819:                 */
                    820:                if (*string == '\0') {
                    821:                        underflow = 1;
                    822:                        goto done;
                    823:                }
                    824: 
                    825:                /*
                    826:                 * Skip any leading whitespace at the beginning of a field unless
                    827:                 * the format suppresses this behavior.
                    828:                 */
                    829:                if (!(flags & SCAN_NOSKIP)) {
                    830:                        while (*string != '\0') {
                    831:                                sch = *string;
                    832:                                if (! isspace((int)sch) ) {
                    833:                                        break;
                    834:                                }
                    835:                                string++;
                    836:                        }
                    837:                        if (*string == '\0') {
                    838:                                underflow = 1;
                    839:                                goto done;
                    840:                        }
                    841:                }
                    842: 
                    843:                /*
                    844:                 * Perform the requested scanning operation.
                    845:                 */
                    846:                switch (op) {
                    847:                        case 'c':
                    848:                        case 's':
                    849:                                /*
                    850:                                 * Scan a string up to width characters or whitespace.
                    851:                                 */
                    852:                                if (width == 0) {
                    853:                                        width = (size_t) ~0;
                    854:                                }
                    855:                                end = string;
                    856:                                while (*end != '\0') {
                    857:                                        sch = *end;
                    858:                                        if ( isspace( (int)sch ) ) {
                    859:                                                break;
                    860:                                        }
                    861:                                        end++;
                    862:                                        if (--width == 0) {
                    863:                                           break;
                    864:                                        }
                    865:                                }
                    866:                                if (!(flags & SCAN_SUPPRESS)) {
                    867:                                        if (numVars && objIndex >= argCount) {
                    868:                                                break;
                    869:                                        } else if (numVars) {
                    870:                                                zend_uint refcount;
                    871: 
                    872:                                                current = args[objIndex++];
                    873:                                                refcount = Z_REFCOUNT_PP(current);
                    874:                                                zval_dtor( *current );
                    875:                                                ZVAL_STRINGL( *current, string, end-string, 1);
                    876:                                                Z_SET_REFCOUNT_PP(current, refcount);
                    877:                                                Z_SET_ISREF_PP(current);
                    878:                                        } else {
                    879:                                                add_index_stringl( *return_value, objIndex++, string, end-string, 1);
                    880:                                        }
                    881:                                }
                    882:                                string = end;
                    883:                                break;
                    884: 
                    885:                        case '[': {
                    886:                                CharSet cset;
                    887: 
                    888:                                if (width == 0) {
                    889:                                        width = (size_t) ~0;
                    890:                                }
                    891:                                end = string;
                    892: 
                    893:                                format = BuildCharSet(&cset, format);
                    894:                                while (*end != '\0') {
                    895:                                        sch = *end;
                    896:                                        if (!CharInSet(&cset, (int)sch)) {
                    897:                                                break;
                    898:                                        }
                    899:                                        end++;
                    900:                                        if (--width == 0) {
                    901:                                                break;
                    902:                                        }
                    903:                                }
                    904:                                ReleaseCharSet(&cset);
                    905: 
                    906:                                if (string == end) {
                    907:                                        /*
                    908:                                         * Nothing matched the range, stop processing
                    909:                                         */
                    910:                                        goto done;
                    911:                                }
                    912:                                if (!(flags & SCAN_SUPPRESS)) {
                    913:                                        if (numVars && objIndex >= argCount) {
                    914:                                                break;
                    915:                                        } else if (numVars) {
                    916:                                                current = args[objIndex++];
                    917:                                                zval_dtor( *current );
                    918:                                                ZVAL_STRINGL( *current, string, end-string, 1);
                    919:                                        } else {
                    920:                                                add_index_stringl(*return_value, objIndex++, string, end-string, 1);
                    921:                                        }
                    922:                                }
                    923:                                string = end;
                    924:                                break;
                    925:                        }
                    926: /*
                    927:                        case 'c':
                    928:                           / Scan a single character./
                    929: 
                    930:                                sch = *string;
                    931:                                string++;
                    932:                                if (!(flags & SCAN_SUPPRESS)) {
                    933:                                        if (numVars) {
                    934:                                                char __buf[2];
                    935:                                                __buf[0] = sch;
                    936:                                                __buf[1] = '\0';;
                    937:                                                current = args[objIndex++];
                    938:                                                zval_dtor(*current);
                    939:                                                ZVAL_STRINGL( *current, __buf, 1, 1);
                    940:                                        } else {
                    941:                                                add_index_stringl(*return_value, objIndex++, &sch, 1, 1);
                    942:                                        }
                    943:                                }
                    944:                                break;
                    945: */
                    946:                        case 'i':
                    947:                                /*
                    948:                                 * Scan an unsigned or signed integer.
                    949:                                 */
                    950:                                /*-cc-*/
                    951:                                buf[0] = '\0';
                    952:                                /*-cc-*/
                    953:                                if ((width == 0) || (width > sizeof(buf) - 1)) {
                    954:                                        width = sizeof(buf) - 1;
                    955:                                }
                    956: 
                    957:                                flags |= SCAN_SIGNOK | SCAN_NODIGITS | SCAN_NOZERO;
                    958:                                for (end = buf; width > 0; width--) {
                    959:                                        switch (*string) {
                    960:                                                /*
                    961:                                                 * The 0 digit has special meaning at the beginning of
                    962:                                                 * a number.  If we are unsure of the base, it
                    963:                                                 * indicates that we are in base 8 or base 16 (if it is
                    964:                                                 * followed by an 'x').
                    965:                                                 */
                    966:                                                case '0':
                    967:                                                        /*-cc-*/
                    968:                                                        if (base == 16) {
                    969:                                                                flags |= SCAN_XOK;
                    970:                                                        }
                    971:                                                        /*-cc-*/
                    972:                                                        if (base == 0) {
                    973:                                                                base = 8;
                    974:                                                                flags |= SCAN_XOK;
                    975:                                                        }
                    976:                                                        if (flags & SCAN_NOZERO) {
                    977:                                                                flags &= ~(SCAN_SIGNOK | SCAN_NODIGITS | SCAN_NOZERO);
                    978:                                                        } else {
                    979:                                                                flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS);
                    980:                                                        }
                    981:                                                        goto addToInt;
                    982: 
                    983:                                                case '1': case '2': case '3': case '4':
                    984:                                                case '5': case '6': case '7':
                    985:                                                        if (base == 0) {
                    986:                                                                base = 10;
                    987:                                                        }
                    988:                                                        flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS);
                    989:                                                        goto addToInt;
                    990: 
                    991:                                                case '8': case '9':
                    992:                                                        if (base == 0) {
                    993:                                                                base = 10;
                    994:                                                        }
                    995:                                                        if (base <= 8) {
                    996:                                                           break;
                    997:                                                        }
                    998:                                                        flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS);
                    999:                                                        goto addToInt;
                   1000: 
                   1001:                                                case 'A': case 'B': case 'C':
                   1002:                                                case 'D': case 'E': case 'F':
                   1003:                                                case 'a': case 'b': case 'c':
                   1004:                                                case 'd': case 'e': case 'f':
                   1005:                                                        if (base <= 10) {
                   1006:                                                                break;
                   1007:                                                        }
                   1008:                                                        flags &= ~(SCAN_SIGNOK | SCAN_XOK | SCAN_NODIGITS);
                   1009:                                                        goto addToInt;
                   1010: 
                   1011:                                                case '+': case '-':
                   1012:                                                        if (flags & SCAN_SIGNOK) {
                   1013:                                                                flags &= ~SCAN_SIGNOK;
                   1014:                                                                goto addToInt;
                   1015:                                                        }
                   1016:                                                        break;
                   1017: 
                   1018:                                                case 'x': case 'X':
                   1019:                                                        if ((flags & SCAN_XOK) && (end == buf+1)) {
                   1020:                                                                base = 16;
                   1021:                                                                flags &= ~SCAN_XOK;
                   1022:                                                                goto addToInt;
                   1023:                                                        }
                   1024:                                                        break;
                   1025:                                        }
                   1026: 
                   1027:                                        /*
                   1028:                                         * We got an illegal character so we are done accumulating.
                   1029:                                         */
                   1030:                                        break;
                   1031: 
                   1032: addToInt:
                   1033:                                        /*
                   1034:                                         * Add the character to the temporary buffer.
                   1035:                                         */
                   1036:                                        *end++ = *string++;
                   1037:                                        if (*string == '\0') {
                   1038:                                                break;
                   1039:                                        }
                   1040:                                }
                   1041: 
                   1042:                                /*
                   1043:                                 * Check to see if we need to back up because we only got a
                   1044:                                 * sign or a trailing x after a 0.
                   1045:                                 */
                   1046:                                if (flags & SCAN_NODIGITS) {
                   1047:                                        if (*string == '\0') {
                   1048:                                                underflow = 1;
                   1049:                                        }
                   1050:                                        goto done;
                   1051:                                } else if (end[-1] == 'x' || end[-1] == 'X') {
                   1052:                                        end--;
                   1053:                                        string--;
                   1054:                                }
                   1055: 
                   1056:                                /*
                   1057:                                 * Scan the value from the temporary buffer.  If we are
                   1058:                                 * returning a large unsigned value, we have to convert it back
                   1059:                                 * to a string since PHP only supports signed values.
                   1060:                                 */
                   1061:                                if (!(flags & SCAN_SUPPRESS)) {
                   1062:                                        *end = '\0';
                   1063:                                        value = (long) (*fn)(buf, NULL, base);
                   1064:                                        if ((flags & SCAN_UNSIGNED) && (value < 0)) {
                   1065:                                                snprintf(buf, sizeof(buf), "%lu", value); /* INTL: ISO digit */
                   1066:                                                if (numVars && objIndex >= argCount) {
                   1067:                                                        break;
                   1068:                                                } else if (numVars) {
                   1069:                                                  /* change passed value type to string */
                   1070:                                                        current = args[objIndex++];
                   1071:                                                        zval_dtor(*current);
                   1072:                                                        ZVAL_STRING( *current, buf, 1 );
                   1073:                                                } else {
                   1074:                                                        add_index_string(*return_value, objIndex++, buf, 1);
                   1075:                                                }
                   1076:                                        } else {
                   1077:                                                if (numVars && objIndex >= argCount) {
                   1078:                                                        break;
                   1079:                                                } else if (numVars) {
                   1080:                                                        current = args[objIndex++];
                   1081:                                                        zval_dtor(*current);
                   1082:                                                        ZVAL_LONG(*current, value);
                   1083:                                                } else {
                   1084:                                                        add_index_long(*return_value, objIndex++, value);
                   1085:                                                }
                   1086:                                        }
                   1087:                                }
                   1088:                                break;
                   1089: 
                   1090:                        case 'f':
                   1091:                                /*
                   1092:                                 * Scan a floating point number
                   1093:                                 */
                   1094:                                buf[0] = '\0';     /* call me pedantic */
                   1095:                                if ((width == 0) || (width > sizeof(buf) - 1)) {
                   1096:                                        width = sizeof(buf) - 1;
                   1097:                                }
                   1098:                                flags |= SCAN_SIGNOK | SCAN_NODIGITS | SCAN_PTOK | SCAN_EXPOK;
                   1099:                                for (end = buf; width > 0; width--) {
                   1100:                                        switch (*string) {
                   1101:                                                case '0': case '1': case '2': case '3':
                   1102:                                                case '4': case '5': case '6': case '7':
                   1103:                                                case '8': case '9':
                   1104:                                                        flags &= ~(SCAN_SIGNOK | SCAN_NODIGITS);
                   1105:                                                        goto addToFloat;
                   1106:                                                case '+':
                   1107:                                                case '-':
                   1108:                                                        if (flags & SCAN_SIGNOK) {
                   1109:                                                                flags &= ~SCAN_SIGNOK;
                   1110:                                                                goto addToFloat;
                   1111:                                                        }
                   1112:                                                        break;
                   1113:                                                case '.':
                   1114:                                                        if (flags & SCAN_PTOK) {
                   1115:                                                                flags &= ~(SCAN_SIGNOK | SCAN_PTOK);
                   1116:                                                                goto addToFloat;
                   1117:                                                        }
                   1118:                                                        break;
                   1119:                                                case 'e':
                   1120:                                                case 'E':
                   1121:                                                        /*
                   1122:                                                         * An exponent is not allowed until there has
                   1123:                                                         * been at least one digit.
                   1124:                                                         */
                   1125:                                                        if ((flags & (SCAN_NODIGITS | SCAN_EXPOK)) == SCAN_EXPOK) {
                   1126:                                                                flags = (flags & ~(SCAN_EXPOK|SCAN_PTOK))
                   1127:                                                                        | SCAN_SIGNOK | SCAN_NODIGITS;
                   1128:                                                                goto addToFloat;
                   1129:                                                        }
                   1130:                                                        break;
                   1131:                                        }
                   1132: 
                   1133:                                        /*
                   1134:                                         * We got an illegal character so we are done accumulating.
                   1135:                                         */
                   1136:                                        break;
                   1137: 
                   1138: addToFloat:
                   1139:                                        /*
                   1140:                                         * Add the character to the temporary buffer.
                   1141:                                         */
                   1142:                                        *end++ = *string++;
                   1143:                                        if (*string == '\0') {
                   1144:                                                break;
                   1145:                                        }
                   1146:                                }
                   1147: 
                   1148:                                /*
                   1149:                                 * Check to see if we need to back up because we saw a
                   1150:                                 * trailing 'e' or sign.
                   1151:                                 */
                   1152:                                if (flags & SCAN_NODIGITS) {
                   1153:                                        if (flags & SCAN_EXPOK) {
                   1154:                                                /*
                   1155:                                                 * There were no digits at all so scanning has
                   1156:                                                 * failed and we are done.
                   1157:                                                 */
                   1158:                                                if (*string == '\0') {
                   1159:                                                        underflow = 1;
                   1160:                                                }
                   1161:                                                goto done;
                   1162:                                        }
                   1163: 
                   1164:                                        /*
                   1165:                                         * We got a bad exponent ('e' and maybe a sign).
                   1166:                                         */
                   1167:                                        end--;
                   1168:                                        string--;
                   1169:                                        if (*end != 'e' && *end != 'E') {
                   1170:                                                end--;
                   1171:                                                string--;
                   1172:                                        }
                   1173:                                }
                   1174: 
                   1175:                                /*
                   1176:                                 * Scan the value from the temporary buffer.
                   1177:                                 */
                   1178:                                if (!(flags & SCAN_SUPPRESS)) {
                   1179:                                        double dvalue;
                   1180:                                        *end = '\0';
                   1181:                                        dvalue = zend_strtod(buf, NULL);
                   1182:                                        if (numVars && objIndex >= argCount) {
                   1183:                                                break;
                   1184:                                        } else if (numVars) {
                   1185:                                                current = args[objIndex++];
                   1186:                                                zval_dtor(*current);
                   1187:                                                ZVAL_DOUBLE(*current, dvalue);
                   1188:                                        } else {
                   1189:                                                add_index_double( *return_value, objIndex++, dvalue );
                   1190:                                        }
                   1191:                                }
                   1192:                                break;
                   1193:                } /* switch (op) */
                   1194:                nconversions++;
                   1195:        } /*  while (*format != '\0') */
                   1196: 
                   1197: done:
                   1198:        result = SCAN_SUCCESS;
                   1199: 
                   1200:        if (underflow && (0==nconversions)) {
                   1201:                scan_set_error_return( numVars, return_value );
                   1202:                result = SCAN_ERROR_EOF;
                   1203:        } else if (numVars) {
                   1204:                convert_to_long( *return_value );
                   1205:                Z_LVAL_PP(return_value) = nconversions;
                   1206:        } else if (nconversions < totalVars) {
                   1207:                /* TODO: not all elements converted. we need to prune the list - cc */
                   1208:        }
                   1209:        return result;
                   1210: }
                   1211: /* }}} */
                   1212: 
                   1213: /* the compiler choked when i tried to make this a macro    */
                   1214: static inline void scan_set_error_return(int numVars, zval **return_value) /* {{{ */
                   1215: {
                   1216:        if (numVars) {
                   1217:                Z_TYPE_PP(return_value) = IS_LONG;
                   1218:                Z_LVAL_PP(return_value) = SCAN_ERROR_EOF;  /* EOF marker */
                   1219:        } else {
                   1220:                /* convert_to_null calls destructor */
                   1221:                convert_to_null( *return_value );
                   1222:        }
                   1223: }
                   1224: /* }}} */
                   1225: 
                   1226: /*
                   1227:  * Local variables:
                   1228:  * tab-width: 4
                   1229:  * c-basic-offset: 4
                   1230:  * End:
                   1231:  * vim600: sw=4 ts=4 fdm=marker
                   1232:  * vim<600: sw=4 ts=4
                   1233:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>