Annotation of embedaddon/php/ext/pcre/php_pcre.c, revision 1.1

1.1     ! misho       1: /*
        !             2:    +----------------------------------------------------------------------+
        !             3:    | PHP Version 5                                                        |
        !             4:    +----------------------------------------------------------------------+
        !             5:    | Copyright (c) 1997-2012 The PHP Group                                |
        !             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: Andrei Zmievski <andrei@php.net>                             |
        !            16:    +----------------------------------------------------------------------+
        !            17:  */
        !            18: 
        !            19: /* $Id: php_pcre.c 321634 2012-01-01 13:15:04Z felipe $ */
        !            20: 
        !            21: #include "php.h"
        !            22: #include "php_ini.h"
        !            23: #include "php_globals.h"
        !            24: #include "php_pcre.h"
        !            25: #include "ext/standard/info.h"
        !            26: #include "ext/standard/php_smart_str.h"
        !            27: 
        !            28: #if HAVE_PCRE || HAVE_BUNDLED_PCRE
        !            29: 
        !            30: #include "ext/standard/php_string.h"
        !            31: 
        !            32: #define PREG_PATTERN_ORDER                     1
        !            33: #define PREG_SET_ORDER                         2
        !            34: #define PREG_OFFSET_CAPTURE                    (1<<8)
        !            35: 
        !            36: #define        PREG_SPLIT_NO_EMPTY                     (1<<0)
        !            37: #define PREG_SPLIT_DELIM_CAPTURE       (1<<1)
        !            38: #define PREG_SPLIT_OFFSET_CAPTURE      (1<<2)
        !            39: 
        !            40: #define PREG_REPLACE_EVAL                      (1<<0)
        !            41: 
        !            42: #define PREG_GREP_INVERT                       (1<<0)
        !            43: 
        !            44: #define PCRE_CACHE_SIZE 4096
        !            45: 
        !            46: enum {
        !            47:        PHP_PCRE_NO_ERROR = 0,
        !            48:        PHP_PCRE_INTERNAL_ERROR,
        !            49:        PHP_PCRE_BACKTRACK_LIMIT_ERROR,
        !            50:        PHP_PCRE_RECURSION_LIMIT_ERROR,
        !            51:        PHP_PCRE_BAD_UTF8_ERROR,
        !            52:        PHP_PCRE_BAD_UTF8_OFFSET_ERROR
        !            53: };
        !            54: 
        !            55: 
        !            56: ZEND_DECLARE_MODULE_GLOBALS(pcre)
        !            57: 
        !            58: 
        !            59: static void pcre_handle_exec_error(int pcre_code TSRMLS_DC) /* {{{ */
        !            60: {
        !            61:        int preg_code = 0;
        !            62: 
        !            63:        switch (pcre_code) {
        !            64:                case PCRE_ERROR_MATCHLIMIT:
        !            65:                        preg_code = PHP_PCRE_BACKTRACK_LIMIT_ERROR;
        !            66:                        break;
        !            67: 
        !            68:                case PCRE_ERROR_RECURSIONLIMIT:
        !            69:                        preg_code = PHP_PCRE_RECURSION_LIMIT_ERROR;
        !            70:                        break;
        !            71: 
        !            72:                case PCRE_ERROR_BADUTF8:
        !            73:                        preg_code = PHP_PCRE_BAD_UTF8_ERROR;
        !            74:                        break;
        !            75: 
        !            76:                case PCRE_ERROR_BADUTF8_OFFSET:
        !            77:                        preg_code = PHP_PCRE_BAD_UTF8_OFFSET_ERROR;
        !            78:                        break;
        !            79: 
        !            80:                default:
        !            81:                        preg_code = PHP_PCRE_INTERNAL_ERROR;
        !            82:                        break;
        !            83:        }
        !            84: 
        !            85:        PCRE_G(error_code) = preg_code;
        !            86: }
        !            87: /* }}} */
        !            88: 
        !            89: static void php_free_pcre_cache(void *data) /* {{{ */
        !            90: {
        !            91:        pcre_cache_entry *pce = (pcre_cache_entry *) data;
        !            92:        if (!pce) return;
        !            93:        pefree(pce->re, 1);
        !            94:        if (pce->extra) pefree(pce->extra, 1);
        !            95: #if HAVE_SETLOCALE
        !            96:        if ((void*)pce->tables) pefree((void*)pce->tables, 1);
        !            97:        pefree(pce->locale, 1);
        !            98: #endif
        !            99: }
        !           100: /* }}} */
        !           101: 
        !           102: static PHP_GINIT_FUNCTION(pcre) /* {{{ */
        !           103: {
        !           104:        zend_hash_init(&pcre_globals->pcre_cache, 0, NULL, php_free_pcre_cache, 1);
        !           105:        pcre_globals->backtrack_limit = 0;
        !           106:        pcre_globals->recursion_limit = 0;
        !           107:        pcre_globals->error_code      = PHP_PCRE_NO_ERROR;
        !           108: }
        !           109: /* }}} */
        !           110: 
        !           111: static PHP_GSHUTDOWN_FUNCTION(pcre) /* {{{ */
        !           112: {
        !           113:        zend_hash_destroy(&pcre_globals->pcre_cache);
        !           114: }
        !           115: /* }}} */
        !           116: 
        !           117: PHP_INI_BEGIN()
        !           118:        STD_PHP_INI_ENTRY("pcre.backtrack_limit", "1000000", PHP_INI_ALL, OnUpdateLong, backtrack_limit, zend_pcre_globals, pcre_globals)
        !           119:        STD_PHP_INI_ENTRY("pcre.recursion_limit", "100000", PHP_INI_ALL, OnUpdateLong, recursion_limit, zend_pcre_globals, pcre_globals)
        !           120: PHP_INI_END()
        !           121: 
        !           122: 
        !           123: /* {{{ PHP_MINFO_FUNCTION(pcre) */
        !           124: static PHP_MINFO_FUNCTION(pcre)
        !           125: {
        !           126:        php_info_print_table_start();
        !           127:        php_info_print_table_row(2, "PCRE (Perl Compatible Regular Expressions) Support", "enabled" );
        !           128:        php_info_print_table_row(2, "PCRE Library Version", pcre_version() );
        !           129:        php_info_print_table_end();
        !           130: 
        !           131:        DISPLAY_INI_ENTRIES();
        !           132: }
        !           133: /* }}} */
        !           134: 
        !           135: /* {{{ PHP_MINIT_FUNCTION(pcre) */
        !           136: static PHP_MINIT_FUNCTION(pcre)
        !           137: {
        !           138:        REGISTER_INI_ENTRIES();
        !           139:        
        !           140:        REGISTER_LONG_CONSTANT("PREG_PATTERN_ORDER", PREG_PATTERN_ORDER, CONST_CS | CONST_PERSISTENT);
        !           141:        REGISTER_LONG_CONSTANT("PREG_SET_ORDER", PREG_SET_ORDER, CONST_CS | CONST_PERSISTENT);
        !           142:        REGISTER_LONG_CONSTANT("PREG_OFFSET_CAPTURE", PREG_OFFSET_CAPTURE, CONST_CS | CONST_PERSISTENT);
        !           143:        REGISTER_LONG_CONSTANT("PREG_SPLIT_NO_EMPTY", PREG_SPLIT_NO_EMPTY, CONST_CS | CONST_PERSISTENT);
        !           144:        REGISTER_LONG_CONSTANT("PREG_SPLIT_DELIM_CAPTURE", PREG_SPLIT_DELIM_CAPTURE, CONST_CS | CONST_PERSISTENT);
        !           145:        REGISTER_LONG_CONSTANT("PREG_SPLIT_OFFSET_CAPTURE", PREG_SPLIT_OFFSET_CAPTURE, CONST_CS | CONST_PERSISTENT);
        !           146:        REGISTER_LONG_CONSTANT("PREG_GREP_INVERT", PREG_GREP_INVERT, CONST_CS | CONST_PERSISTENT);
        !           147: 
        !           148:        REGISTER_LONG_CONSTANT("PREG_NO_ERROR", PHP_PCRE_NO_ERROR, CONST_CS | CONST_PERSISTENT);
        !           149:        REGISTER_LONG_CONSTANT("PREG_INTERNAL_ERROR", PHP_PCRE_INTERNAL_ERROR, CONST_CS | CONST_PERSISTENT);
        !           150:        REGISTER_LONG_CONSTANT("PREG_BACKTRACK_LIMIT_ERROR", PHP_PCRE_BACKTRACK_LIMIT_ERROR, CONST_CS | CONST_PERSISTENT);
        !           151:        REGISTER_LONG_CONSTANT("PREG_RECURSION_LIMIT_ERROR", PHP_PCRE_RECURSION_LIMIT_ERROR, CONST_CS | CONST_PERSISTENT);
        !           152:        REGISTER_LONG_CONSTANT("PREG_BAD_UTF8_ERROR", PHP_PCRE_BAD_UTF8_ERROR, CONST_CS | CONST_PERSISTENT);
        !           153:        REGISTER_LONG_CONSTANT("PREG_BAD_UTF8_OFFSET_ERROR", PHP_PCRE_BAD_UTF8_OFFSET_ERROR, CONST_CS | CONST_PERSISTENT);
        !           154:        REGISTER_STRING_CONSTANT("PCRE_VERSION", (char *)pcre_version(), CONST_CS | CONST_PERSISTENT);
        !           155: 
        !           156:        return SUCCESS;
        !           157: }
        !           158: /* }}} */
        !           159: 
        !           160: /* {{{ PHP_MSHUTDOWN_FUNCTION(pcre) */
        !           161: static PHP_MSHUTDOWN_FUNCTION(pcre)
        !           162: {
        !           163:        UNREGISTER_INI_ENTRIES();
        !           164: 
        !           165:        return SUCCESS;
        !           166: }
        !           167: /* }}} */
        !           168: 
        !           169: /* {{{ static pcre_clean_cache */
        !           170: static int pcre_clean_cache(void *data, void *arg TSRMLS_DC)
        !           171: {
        !           172:        int *num_clean = (int *)arg;
        !           173: 
        !           174:        if (*num_clean > 0) {
        !           175:                (*num_clean)--;
        !           176:                return 1;
        !           177:        } else {
        !           178:                return 0;
        !           179:        }
        !           180: }
        !           181: /* }}} */
        !           182: 
        !           183: /* {{{ static make_subpats_table */
        !           184: static char **make_subpats_table(int num_subpats, pcre_cache_entry *pce TSRMLS_DC)
        !           185: {
        !           186:        pcre_extra *extra = pce->extra;
        !           187:        int name_cnt = 0, name_size, ni = 0;
        !           188:        int rc;
        !           189:        char *name_table;
        !           190:        unsigned short name_idx;
        !           191:        char **subpat_names = (char **)ecalloc(num_subpats, sizeof(char *));
        !           192: 
        !           193:        rc = pcre_fullinfo(pce->re, extra, PCRE_INFO_NAMECOUNT, &name_cnt);
        !           194:        if (rc < 0) {
        !           195:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Internal pcre_fullinfo() error %d", rc);
        !           196:                efree(subpat_names);
        !           197:                return NULL;
        !           198:        }
        !           199:        if (name_cnt > 0) {
        !           200:                int rc1, rc2;
        !           201: 
        !           202:                rc1 = pcre_fullinfo(pce->re, extra, PCRE_INFO_NAMETABLE, &name_table);
        !           203:                rc2 = pcre_fullinfo(pce->re, extra, PCRE_INFO_NAMEENTRYSIZE, &name_size);
        !           204:                rc = rc2 ? rc2 : rc1;
        !           205:                if (rc < 0) {
        !           206:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Internal pcre_fullinfo() error %d", rc);
        !           207:                        efree(subpat_names);
        !           208:                        return NULL;
        !           209:                }
        !           210: 
        !           211:                while (ni++ < name_cnt) {
        !           212:                        name_idx = 0xff * (unsigned char)name_table[0] + (unsigned char)name_table[1];
        !           213:                        subpat_names[name_idx] = name_table + 2;
        !           214:                        if (is_numeric_string(subpat_names[name_idx], strlen(subpat_names[name_idx]), NULL, NULL, 0) > 0) {
        !           215:                                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Numeric named subpatterns are not allowed");
        !           216:                                efree(subpat_names);
        !           217:                                return NULL;
        !           218:                        }
        !           219:                        name_table += name_size;
        !           220:                }
        !           221:        }
        !           222: 
        !           223:        return subpat_names;
        !           224: }
        !           225: /* }}} */
        !           226: 
        !           227: /* {{{ pcre_get_compiled_regex_cache
        !           228:  */
        !           229: PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(char *regex, int regex_len TSRMLS_DC)
        !           230: {
        !           231:        pcre                            *re = NULL;
        !           232:        pcre_extra                      *extra;
        !           233:        int                                      coptions = 0;
        !           234:        int                                      soptions = 0;
        !           235:        const char                      *error;
        !           236:        int                                      erroffset;
        !           237:        char                             delimiter;
        !           238:        char                             start_delimiter;
        !           239:        char                             end_delimiter;
        !           240:        char                            *p, *pp;
        !           241:        char                            *pattern;
        !           242:        int                                      do_study = 0;
        !           243:        int                                      poptions = 0;
        !           244:        int                             count = 0;
        !           245:        unsigned const char *tables = NULL;
        !           246: #if HAVE_SETLOCALE
        !           247:        char                            *locale = setlocale(LC_CTYPE, NULL);
        !           248: #endif
        !           249:        pcre_cache_entry        *pce;
        !           250:        pcre_cache_entry         new_entry;
        !           251: 
        !           252:        /* Try to lookup the cached regex entry, and if successful, just pass
        !           253:           back the compiled pattern, otherwise go on and compile it. */
        !           254:        if (zend_hash_find(&PCRE_G(pcre_cache), regex, regex_len+1, (void **)&pce) == SUCCESS) {
        !           255:                /*
        !           256:                 * We use a quick pcre_fullinfo() check to see whether cache is corrupted, and if it
        !           257:                 * is, we flush it and compile the pattern from scratch.
        !           258:                 */
        !           259:                if (pcre_fullinfo(pce->re, NULL, PCRE_INFO_CAPTURECOUNT, &count) == PCRE_ERROR_BADMAGIC) {
        !           260:                        zend_hash_clean(&PCRE_G(pcre_cache));
        !           261:                } else {
        !           262: #if HAVE_SETLOCALE
        !           263:                        if (!strcmp(pce->locale, locale)) {
        !           264: #endif
        !           265:                                return pce;
        !           266: #if HAVE_SETLOCALE
        !           267:                        }
        !           268: #endif
        !           269:                }
        !           270:        }
        !           271:        
        !           272:        p = regex;
        !           273:        
        !           274:        /* Parse through the leading whitespace, and display a warning if we
        !           275:           get to the end without encountering a delimiter. */
        !           276:        while (isspace((int)*(unsigned char *)p)) p++;
        !           277:        if (*p == 0) {
        !           278:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty regular expression");
        !           279:                return NULL;
        !           280:        }
        !           281:        
        !           282:        /* Get the delimiter and display a warning if it is alphanumeric
        !           283:           or a backslash. */
        !           284:        delimiter = *p++;
        !           285:        if (isalnum((int)*(unsigned char *)&delimiter) || delimiter == '\\') {
        !           286:                php_error_docref(NULL TSRMLS_CC,E_WARNING, "Delimiter must not be alphanumeric or backslash");
        !           287:                return NULL;
        !           288:        }
        !           289: 
        !           290:        start_delimiter = delimiter;
        !           291:        if ((pp = strchr("([{< )]}> )]}>", delimiter)))
        !           292:                delimiter = pp[5];
        !           293:        end_delimiter = delimiter;
        !           294: 
        !           295:        if (start_delimiter == end_delimiter) {
        !           296:                /* We need to iterate through the pattern, searching for the ending delimiter,
        !           297:                   but skipping the backslashed delimiters.  If the ending delimiter is not
        !           298:                   found, display a warning. */
        !           299:                pp = p;
        !           300:                while (*pp != 0) {
        !           301:                        if (*pp == '\\' && pp[1] != 0) pp++;
        !           302:                        else if (*pp == delimiter)
        !           303:                                break;
        !           304:                        pp++;
        !           305:                }
        !           306:                if (*pp == 0) {
        !           307:                        php_error_docref(NULL TSRMLS_CC,E_WARNING, "No ending delimiter '%c' found", delimiter);
        !           308:                        return NULL;
        !           309:                }
        !           310:        } else {
        !           311:                /* We iterate through the pattern, searching for the matching ending
        !           312:                 * delimiter. For each matching starting delimiter, we increment nesting
        !           313:                 * level, and decrement it for each matching ending delimiter. If we
        !           314:                 * reach the end of the pattern without matching, display a warning.
        !           315:                 */
        !           316:                int brackets = 1;       /* brackets nesting level */
        !           317:                pp = p;
        !           318:                while (*pp != 0) {
        !           319:                        if (*pp == '\\' && pp[1] != 0) pp++;
        !           320:                        else if (*pp == end_delimiter && --brackets <= 0)
        !           321:                                break;
        !           322:                        else if (*pp == start_delimiter)
        !           323:                                brackets++;
        !           324:                        pp++;
        !           325:                }
        !           326:                if (*pp == 0) {
        !           327:                        php_error_docref(NULL TSRMLS_CC,E_WARNING, "No ending matching delimiter '%c' found", end_delimiter);
        !           328:                        return NULL;
        !           329:                }
        !           330:        }
        !           331:        
        !           332:        /* Make a copy of the actual pattern. */
        !           333:        pattern = estrndup(p, pp-p);
        !           334: 
        !           335:        /* Move on to the options */
        !           336:        pp++;
        !           337: 
        !           338:        /* Parse through the options, setting appropriate flags.  Display
        !           339:           a warning if we encounter an unknown modifier. */    
        !           340:        while (*pp != 0) {
        !           341:                switch (*pp++) {
        !           342:                        /* Perl compatible options */
        !           343:                        case 'i':       coptions |= PCRE_CASELESS;              break;
        !           344:                        case 'm':       coptions |= PCRE_MULTILINE;             break;
        !           345:                        case 's':       coptions |= PCRE_DOTALL;                break;
        !           346:                        case 'x':       coptions |= PCRE_EXTENDED;              break;
        !           347:                        
        !           348:                        /* PCRE specific options */
        !           349:                        case 'A':       coptions |= PCRE_ANCHORED;              break;
        !           350:                        case 'D':       coptions |= PCRE_DOLLAR_ENDONLY;break;
        !           351:                        case 'S':       do_study  = 1;                                  break;
        !           352:                        case 'U':       coptions |= PCRE_UNGREEDY;              break;
        !           353:                        case 'X':       coptions |= PCRE_EXTRA;                 break;
        !           354:                        case 'u':       coptions |= PCRE_UTF8;
        !           355:        /* In  PCRE,  by  default, \d, \D, \s, \S, \w, and \W recognize only ASCII
        !           356:        characters, even in UTF-8 mode. However, this can be changed by setting
        !           357:        the PCRE_UCP option. */
        !           358: #ifdef PCRE_UCP
        !           359:                                                coptions |= PCRE_UCP;
        !           360: #endif                 
        !           361:                                break;
        !           362: 
        !           363:                        /* Custom preg options */
        !           364:                        case 'e':       poptions |= PREG_REPLACE_EVAL;  break;
        !           365:                        
        !           366:                        case ' ':
        !           367:                        case '\n':
        !           368:                                break;
        !           369: 
        !           370:                        default:
        !           371:                                php_error_docref(NULL TSRMLS_CC,E_WARNING, "Unknown modifier '%c'", pp[-1]);
        !           372:                                efree(pattern);
        !           373:                                return NULL;
        !           374:                }
        !           375:        }
        !           376: 
        !           377: #if HAVE_SETLOCALE
        !           378:        if (strcmp(locale, "C"))
        !           379:                tables = pcre_maketables();
        !           380: #endif
        !           381: 
        !           382:        /* Compile pattern and display a warning if compilation failed. */
        !           383:        re = pcre_compile(pattern,
        !           384:                                          coptions,
        !           385:                                          &error,
        !           386:                                          &erroffset,
        !           387:                                          tables);
        !           388: 
        !           389:        if (re == NULL) {
        !           390:                php_error_docref(NULL TSRMLS_CC,E_WARNING, "Compilation failed: %s at offset %d", error, erroffset);
        !           391:                efree(pattern);
        !           392:                if (tables) {
        !           393:                        pefree((void*)tables, 1);
        !           394:                }
        !           395:                return NULL;
        !           396:        }
        !           397: 
        !           398:        /* If study option was specified, study the pattern and
        !           399:           store the result in extra for passing to pcre_exec. */
        !           400:        if (do_study) {
        !           401:                extra = pcre_study(re, soptions, &error);
        !           402:                if (extra) {
        !           403:                        extra->flags |= PCRE_EXTRA_MATCH_LIMIT | PCRE_EXTRA_MATCH_LIMIT_RECURSION;
        !           404:                }
        !           405:                if (error != NULL) {
        !           406:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error while studying pattern");
        !           407:                }
        !           408:        } else {
        !           409:                extra = NULL;
        !           410:        }
        !           411: 
        !           412:        efree(pattern);
        !           413: 
        !           414:        /*
        !           415:         * If we reached cache limit, clean out the items from the head of the list;
        !           416:         * these are supposedly the oldest ones (but not necessarily the least used
        !           417:         * ones).
        !           418:         */
        !           419:        if (zend_hash_num_elements(&PCRE_G(pcre_cache)) == PCRE_CACHE_SIZE) {
        !           420:                int num_clean = PCRE_CACHE_SIZE / 8;
        !           421:                zend_hash_apply_with_argument(&PCRE_G(pcre_cache), pcre_clean_cache, &num_clean TSRMLS_CC);
        !           422:        }
        !           423: 
        !           424:        /* Store the compiled pattern and extra info in the cache. */
        !           425:        new_entry.re = re;
        !           426:        new_entry.extra = extra;
        !           427:        new_entry.preg_options = poptions;
        !           428:        new_entry.compile_options = coptions;
        !           429: #if HAVE_SETLOCALE
        !           430:        new_entry.locale = pestrdup(locale, 1);
        !           431:        new_entry.tables = tables;
        !           432: #endif
        !           433:        zend_hash_update(&PCRE_G(pcre_cache), regex, regex_len+1, (void *)&new_entry,
        !           434:                                                sizeof(pcre_cache_entry), (void**)&pce);
        !           435: 
        !           436:        return pce;
        !           437: }
        !           438: /* }}} */
        !           439: 
        !           440: /* {{{ pcre_get_compiled_regex
        !           441:  */
        !           442: PHPAPI pcre* pcre_get_compiled_regex(char *regex, pcre_extra **extra, int *preg_options TSRMLS_DC)
        !           443: {
        !           444:        pcre_cache_entry * pce = pcre_get_compiled_regex_cache(regex, strlen(regex) TSRMLS_CC);
        !           445: 
        !           446:        if (extra) {
        !           447:                *extra = pce ? pce->extra : NULL;
        !           448:        }
        !           449:        if (preg_options) {
        !           450:                *preg_options = pce ? pce->preg_options : 0;
        !           451:        }
        !           452:        
        !           453:        return pce ? pce->re : NULL;
        !           454: }
        !           455: /* }}} */
        !           456: 
        !           457: /* {{{ pcre_get_compiled_regex_ex
        !           458:  */
        !           459: PHPAPI pcre* pcre_get_compiled_regex_ex(char *regex, pcre_extra **extra, int *preg_options, int *compile_options TSRMLS_DC)
        !           460: {
        !           461:        pcre_cache_entry * pce = pcre_get_compiled_regex_cache(regex, strlen(regex) TSRMLS_CC);
        !           462:        
        !           463:        if (extra) {
        !           464:                *extra = pce ? pce->extra : NULL;
        !           465:        }
        !           466:        if (preg_options) {
        !           467:                *preg_options = pce ? pce->preg_options : 0;
        !           468:        }
        !           469:        if (compile_options) {
        !           470:                *compile_options = pce ? pce->compile_options : 0;
        !           471:        }
        !           472:        
        !           473:        return pce ? pce->re : NULL;
        !           474: }
        !           475: /* }}} */
        !           476: 
        !           477: /* {{{ add_offset_pair */
        !           478: static inline void add_offset_pair(zval *result, char *str, int len, int offset, char *name)
        !           479: {
        !           480:        zval *match_pair;
        !           481: 
        !           482:        ALLOC_ZVAL(match_pair);
        !           483:        array_init(match_pair);
        !           484:        INIT_PZVAL(match_pair);
        !           485: 
        !           486:        /* Add (match, offset) to the return value */
        !           487:        add_next_index_stringl(match_pair, str, len, 1);
        !           488:        add_next_index_long(match_pair, offset);
        !           489:        
        !           490:        if (name) {
        !           491:                zval_add_ref(&match_pair);
        !           492:                zend_hash_update(Z_ARRVAL_P(result), name, strlen(name)+1, &match_pair, sizeof(zval *), NULL);
        !           493:        }
        !           494:        zend_hash_next_index_insert(Z_ARRVAL_P(result), &match_pair, sizeof(zval *), NULL);
        !           495: }
        !           496: /* }}} */
        !           497: 
        !           498: static void php_do_pcre_match(INTERNAL_FUNCTION_PARAMETERS, int global) /* {{{ */
        !           499: {
        !           500:        /* parameters */
        !           501:        char                     *regex;                        /* Regular expression */
        !           502:        char                     *subject;                      /* String to match against */
        !           503:        int                               regex_len;
        !           504:        int                               subject_len;
        !           505:        pcre_cache_entry *pce;                          /* Compiled regular expression */
        !           506:        zval                     *subpats = NULL;       /* Array for subpatterns */
        !           507:        long                      flags = 0;            /* Match control flags */
        !           508:        long                      start_offset = 0;     /* Where the new search starts */
        !           509: 
        !           510:        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, ((global) ? "ssz|ll" : "ss|zll"), &regex, &regex_len,
        !           511:                                                          &subject, &subject_len, &subpats, &flags, &start_offset) == FAILURE) {
        !           512:                RETURN_FALSE;
        !           513:        }
        !           514:        
        !           515:        /* Compile regex or get it from cache. */
        !           516:        if ((pce = pcre_get_compiled_regex_cache(regex, regex_len TSRMLS_CC)) == NULL) {
        !           517:                RETURN_FALSE;
        !           518:        }
        !           519: 
        !           520:        php_pcre_match_impl(pce, subject, subject_len, return_value, subpats, 
        !           521:                global, ZEND_NUM_ARGS() >= 4, flags, start_offset TSRMLS_CC);
        !           522: }
        !           523: /* }}} */
        !           524: 
        !           525: /* {{{ php_pcre_match_impl() */
        !           526: PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, char *subject, int subject_len, zval *return_value,
        !           527:        zval *subpats, int global, int use_flags, long flags, long start_offset TSRMLS_DC)
        !           528: {
        !           529:        zval                    *result_set,            /* Holds a set of subpatterns after
        !           530:                                                                                   a global match */
        !           531:                                   **match_sets = NULL; /* An array of sets of matches for each
        !           532:                                                                                   subpattern after a global match */
        !           533:        pcre_extra              *extra = pce->extra;/* Holds results of studying */
        !           534:        pcre_extra               extra_data;            /* Used locally for exec options */
        !           535:        int                              exoptions = 0;         /* Execution options */
        !           536:        int                              count = 0;                     /* Count of matched subpatterns */
        !           537:        int                             *offsets;                       /* Array of subpattern offsets */
        !           538:        int                              num_subpats;           /* Number of captured subpatterns */
        !           539:        int                              size_offsets;          /* Size of the offsets array */
        !           540:        int                              matched;                       /* Has anything matched */
        !           541:        int                              g_notempty = 0;        /* If the match should not be empty */
        !           542:        const char         **stringlist;                /* Holds list of subpatterns */
        !           543:        char               **subpat_names;              /* Array for named subpatterns */
        !           544:        int                              i, rc;
        !           545:        int                              subpats_order;         /* Order of subpattern matches */
        !           546:        int                              offset_capture;    /* Capture match offsets: yes/no */
        !           547: 
        !           548:        /* Overwrite the passed-in value for subpatterns with an empty array. */
        !           549:        if (subpats != NULL) {
        !           550:                zval_dtor(subpats);
        !           551:                array_init(subpats);
        !           552:        }
        !           553: 
        !           554:        subpats_order = global ? PREG_PATTERN_ORDER : 0;
        !           555: 
        !           556:        if (use_flags) {
        !           557:                offset_capture = flags & PREG_OFFSET_CAPTURE;
        !           558: 
        !           559:                /*
        !           560:                 * subpats_order is pre-set to pattern mode so we change it only if
        !           561:                 * necessary.
        !           562:                 */
        !           563:                if (flags & 0xff) {
        !           564:                        subpats_order = flags & 0xff;
        !           565:                }
        !           566:                if ((global && (subpats_order < PREG_PATTERN_ORDER || subpats_order > PREG_SET_ORDER)) ||
        !           567:                        (!global && subpats_order != 0)) {
        !           568:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid flags specified");
        !           569:                        return;
        !           570:                }
        !           571:        } else {
        !           572:                offset_capture = 0;
        !           573:        }
        !           574: 
        !           575:        /* Negative offset counts from the end of the string. */
        !           576:        if (start_offset < 0) {
        !           577:                start_offset = subject_len + start_offset;
        !           578:                if (start_offset < 0) {
        !           579:                        start_offset = 0;
        !           580:                }
        !           581:        }
        !           582: 
        !           583:        if (extra == NULL) {
        !           584:                extra_data.flags = PCRE_EXTRA_MATCH_LIMIT | PCRE_EXTRA_MATCH_LIMIT_RECURSION;
        !           585:                extra = &extra_data;
        !           586:        }
        !           587:        extra->match_limit = PCRE_G(backtrack_limit);
        !           588:        extra->match_limit_recursion = PCRE_G(recursion_limit);
        !           589: 
        !           590:        /* Calculate the size of the offsets array, and allocate memory for it. */
        !           591:        rc = pcre_fullinfo(pce->re, extra, PCRE_INFO_CAPTURECOUNT, &num_subpats);
        !           592:        if (rc < 0) {
        !           593:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Internal pcre_fullinfo() error %d", rc);
        !           594:                RETURN_FALSE;
        !           595:        }
        !           596:        num_subpats++;
        !           597:        size_offsets = num_subpats * 3;
        !           598: 
        !           599:        /*
        !           600:         * Build a mapping from subpattern numbers to their names. We will always
        !           601:         * allocate the table, even though there may be no named subpatterns. This
        !           602:         * avoids somewhat more complicated logic in the inner loops.
        !           603:         */
        !           604:        subpat_names = make_subpats_table(num_subpats, pce TSRMLS_CC);
        !           605:        if (!subpat_names) {
        !           606:                RETURN_FALSE;
        !           607:        }
        !           608: 
        !           609:        offsets = (int *)safe_emalloc(size_offsets, sizeof(int), 0);
        !           610: 
        !           611:        /* Allocate match sets array and initialize the values. */
        !           612:        if (global && subpats_order == PREG_PATTERN_ORDER) {
        !           613:                match_sets = (zval **)safe_emalloc(num_subpats, sizeof(zval *), 0);
        !           614:                for (i=0; i<num_subpats; i++) {
        !           615:                        ALLOC_ZVAL(match_sets[i]);
        !           616:                        array_init(match_sets[i]);
        !           617:                        INIT_PZVAL(match_sets[i]);
        !           618:                }
        !           619:        }
        !           620: 
        !           621:        matched = 0;
        !           622:        PCRE_G(error_code) = PHP_PCRE_NO_ERROR;
        !           623:        
        !           624:        do {
        !           625:                /* Execute the regular expression. */
        !           626:                count = pcre_exec(pce->re, extra, subject, subject_len, start_offset,
        !           627:                                                  exoptions|g_notempty, offsets, size_offsets);
        !           628: 
        !           629:                /* the string was already proved to be valid UTF-8 */
        !           630:                exoptions |= PCRE_NO_UTF8_CHECK;
        !           631: 
        !           632:                /* Check for too many substrings condition. */
        !           633:                if (count == 0) {
        !           634:                        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Matched, but too many substrings");
        !           635:                        count = size_offsets/3;
        !           636:                }
        !           637: 
        !           638:                /* If something has matched */
        !           639:                if (count > 0) {
        !           640:                        matched++;
        !           641: 
        !           642:                        /* If subpatterns array has been passed, fill it in with values. */
        !           643:                        if (subpats != NULL) {
        !           644:                                /* Try to get the list of substrings and display a warning if failed. */
        !           645:                                if (pcre_get_substring_list(subject, offsets, count, &stringlist) < 0) {
        !           646:                                        efree(subpat_names);
        !           647:                                        efree(offsets);
        !           648:                                        if (match_sets) efree(match_sets);
        !           649:                                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Get subpatterns list failed");
        !           650:                                        RETURN_FALSE;
        !           651:                                }
        !           652: 
        !           653:                                if (global) {   /* global pattern matching */
        !           654:                                        if (subpats_order == PREG_PATTERN_ORDER) {
        !           655:                                                /* For each subpattern, insert it into the appropriate array. */
        !           656:                                                for (i = 0; i < count; i++) {
        !           657:                                                        if (offset_capture) {
        !           658:                                                                add_offset_pair(match_sets[i], (char *)stringlist[i],
        !           659:                                                                                                offsets[(i<<1)+1] - offsets[i<<1], offsets[i<<1], NULL);
        !           660:                                                        } else {
        !           661:                                                                add_next_index_stringl(match_sets[i], (char *)stringlist[i],
        !           662:                                                                                                           offsets[(i<<1)+1] - offsets[i<<1], 1);
        !           663:                                                        }
        !           664:                                                }
        !           665:                                                /*
        !           666:                                                 * If the number of captured subpatterns on this run is
        !           667:                                                 * less than the total possible number, pad the result
        !           668:                                                 * arrays with empty strings.
        !           669:                                                 */
        !           670:                                                if (count < num_subpats) {
        !           671:                                                        for (; i < num_subpats; i++) {
        !           672:                                                                add_next_index_string(match_sets[i], "", 1);
        !           673:                                                        }
        !           674:                                                }
        !           675:                                        } else {
        !           676:                                                /* Allocate the result set array */
        !           677:                                                ALLOC_ZVAL(result_set);
        !           678:                                                array_init(result_set);
        !           679:                                                INIT_PZVAL(result_set);
        !           680:                                                
        !           681:                                                /* Add all the subpatterns to it */
        !           682:                                                for (i = 0; i < count; i++) {
        !           683:                                                        if (offset_capture) {
        !           684:                                                                add_offset_pair(result_set, (char *)stringlist[i],
        !           685:                                                                                                offsets[(i<<1)+1] - offsets[i<<1], offsets[i<<1], subpat_names[i]);
        !           686:                                                        } else {
        !           687:                                                                if (subpat_names[i]) {
        !           688:                                                                        add_assoc_stringl(result_set, subpat_names[i], (char *)stringlist[i],
        !           689:                                                                                                                   offsets[(i<<1)+1] - offsets[i<<1], 1);
        !           690:                                                                }
        !           691:                                                                add_next_index_stringl(result_set, (char *)stringlist[i],
        !           692:                                                                                                           offsets[(i<<1)+1] - offsets[i<<1], 1);
        !           693:                                                        }
        !           694:                                                }
        !           695:                                                /* And add it to the output array */
        !           696:                                                zend_hash_next_index_insert(Z_ARRVAL_P(subpats), &result_set, sizeof(zval *), NULL);
        !           697:                                        }
        !           698:                                } else {                        /* single pattern matching */
        !           699:                                        /* For each subpattern, insert it into the subpatterns array. */
        !           700:                                        for (i = 0; i < count; i++) {
        !           701:                                                if (offset_capture) {
        !           702:                                                        add_offset_pair(subpats, (char *)stringlist[i],
        !           703:                                                                                        offsets[(i<<1)+1] - offsets[i<<1],
        !           704:                                                                                        offsets[i<<1], subpat_names[i]);
        !           705:                                                } else {
        !           706:                                                        if (subpat_names[i]) {
        !           707:                                                                add_assoc_stringl(subpats, subpat_names[i], (char *)stringlist[i],
        !           708:                                                                                                  offsets[(i<<1)+1] - offsets[i<<1], 1);
        !           709:                                                        }
        !           710:                                                        add_next_index_stringl(subpats, (char *)stringlist[i],
        !           711:                                                                                                   offsets[(i<<1)+1] - offsets[i<<1], 1);
        !           712:                                                }
        !           713:                                        }
        !           714:                                }
        !           715: 
        !           716:                                pcre_free((void *) stringlist);
        !           717:                        }
        !           718:                } else if (count == PCRE_ERROR_NOMATCH) {
        !           719:                        /* If we previously set PCRE_NOTEMPTY after a null match,
        !           720:                           this is not necessarily the end. We need to advance
        !           721:                           the start offset, and continue. Fudge the offset values
        !           722:                           to achieve this, unless we're already at the end of the string. */
        !           723:                        if (g_notempty != 0 && start_offset < subject_len) {
        !           724:                                offsets[0] = start_offset;
        !           725:                                offsets[1] = start_offset + 1;
        !           726:                        } else
        !           727:                                break;
        !           728:                } else {
        !           729:                        pcre_handle_exec_error(count TSRMLS_CC);
        !           730:                        break;
        !           731:                }
        !           732:                
        !           733:                /* If we have matched an empty string, mimic what Perl's /g options does.
        !           734:                   This turns out to be rather cunning. First we set PCRE_NOTEMPTY and try
        !           735:                   the match again at the same point. If this fails (picked up above) we
        !           736:                   advance to the next character. */
        !           737:                g_notempty = (offsets[1] == offsets[0])? PCRE_NOTEMPTY | PCRE_ANCHORED : 0;
        !           738:                
        !           739:                /* Advance to the position right after the last full match */
        !           740:                start_offset = offsets[1];
        !           741:        } while (global);
        !           742: 
        !           743:        /* Add the match sets to the output array and clean up */
        !           744:        if (global && subpats_order == PREG_PATTERN_ORDER) {
        !           745:                for (i = 0; i < num_subpats; i++) {
        !           746:                        if (subpat_names[i]) {
        !           747:                                zend_hash_update(Z_ARRVAL_P(subpats), subpat_names[i],
        !           748:                                                                 strlen(subpat_names[i])+1, &match_sets[i], sizeof(zval *), NULL);
        !           749:                                Z_ADDREF_P(match_sets[i]);
        !           750:                        }
        !           751:                        zend_hash_next_index_insert(Z_ARRVAL_P(subpats), &match_sets[i], sizeof(zval *), NULL);
        !           752:                }
        !           753:                efree(match_sets);
        !           754:        }
        !           755:        
        !           756:        efree(offsets);
        !           757:        efree(subpat_names);
        !           758: 
        !           759:        /* Did we encounter an error? */
        !           760:        if (PCRE_G(error_code) == PHP_PCRE_NO_ERROR) {
        !           761:                RETVAL_LONG(matched);
        !           762:        } else {
        !           763:                RETVAL_FALSE;
        !           764:        }
        !           765: }
        !           766: /* }}} */
        !           767: 
        !           768: /* {{{ proto int preg_match(string pattern, string subject [, array &subpatterns [, int flags [, int offset]]])
        !           769:    Perform a Perl-style regular expression match */
        !           770: static PHP_FUNCTION(preg_match)
        !           771: {
        !           772:        php_do_pcre_match(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
        !           773: }
        !           774: /* }}} */
        !           775: 
        !           776: /* {{{ proto int preg_match_all(string pattern, string subject, array &subpatterns [, int flags [, int offset]])
        !           777:    Perform a Perl-style global regular expression match */
        !           778: static PHP_FUNCTION(preg_match_all)
        !           779: {
        !           780:        php_do_pcre_match(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
        !           781: }
        !           782: /* }}} */
        !           783: 
        !           784: /* {{{ preg_get_backref
        !           785:  */
        !           786: static int preg_get_backref(char **str, int *backref)
        !           787: {
        !           788:        register char in_brace = 0;
        !           789:        register char *walk = *str;
        !           790: 
        !           791:        if (walk[1] == 0)
        !           792:                return 0;
        !           793: 
        !           794:        if (*walk == '$' && walk[1] == '{') {
        !           795:                in_brace = 1;
        !           796:                walk++;
        !           797:        }
        !           798:        walk++;
        !           799: 
        !           800:        if (*walk >= '0' && *walk <= '9') {
        !           801:                *backref = *walk - '0';
        !           802:                walk++;
        !           803:        } else
        !           804:                return 0;
        !           805:        
        !           806:        if (*walk && *walk >= '0' && *walk <= '9') {
        !           807:                *backref = *backref * 10 + *walk - '0';
        !           808:                walk++;
        !           809:        }
        !           810: 
        !           811:        if (in_brace) {
        !           812:                if (*walk == 0 || *walk != '}')
        !           813:                        return 0;
        !           814:                else
        !           815:                        walk++;
        !           816:        }
        !           817:        
        !           818:        *str = walk;
        !           819:        return 1;       
        !           820: }
        !           821: /* }}} */
        !           822: 
        !           823: /* {{{ preg_do_repl_func
        !           824:  */
        !           825: static int preg_do_repl_func(zval *function, char *subject, int *offsets, char **subpat_names, int count, char **result TSRMLS_DC)
        !           826: {
        !           827:        zval            *retval_ptr;            /* Function return value */
        !           828:        zval       **args[1];                   /* Argument to pass to function */
        !           829:        zval            *subpats;                       /* Captured subpatterns */ 
        !           830:        int                      result_len;            /* Return value length */
        !           831:        int                      i;
        !           832: 
        !           833:        MAKE_STD_ZVAL(subpats);
        !           834:        array_init(subpats);
        !           835:        for (i = 0; i < count; i++) {
        !           836:                if (subpat_names[i]) {
        !           837:                        add_assoc_stringl(subpats, subpat_names[i], &subject[offsets[i<<1]] , offsets[(i<<1)+1] - offsets[i<<1], 1);
        !           838:                }
        !           839:                add_next_index_stringl(subpats, &subject[offsets[i<<1]], offsets[(i<<1)+1] - offsets[i<<1], 1);
        !           840:        }
        !           841:        args[0] = &subpats;
        !           842: 
        !           843:        if (call_user_function_ex(EG(function_table), NULL, function, &retval_ptr, 1, args, 0, NULL TSRMLS_CC) == SUCCESS && retval_ptr) {
        !           844:                convert_to_string_ex(&retval_ptr);
        !           845:                *result = estrndup(Z_STRVAL_P(retval_ptr), Z_STRLEN_P(retval_ptr));
        !           846:                result_len = Z_STRLEN_P(retval_ptr);
        !           847:                zval_ptr_dtor(&retval_ptr);
        !           848:        } else {
        !           849:                if (!EG(exception)) {
        !           850:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to call custom replacement function");
        !           851:                }
        !           852:                result_len = offsets[1] - offsets[0];
        !           853:                *result = estrndup(&subject[offsets[0]], result_len);
        !           854:        }
        !           855: 
        !           856:        zval_ptr_dtor(&subpats);
        !           857: 
        !           858:        return result_len;
        !           859: }
        !           860: /* }}} */
        !           861: 
        !           862: /* {{{ preg_do_eval
        !           863:  */
        !           864: static int preg_do_eval(char *eval_str, int eval_str_len, char *subject,
        !           865:                                                int *offsets, int count, char **result TSRMLS_DC)
        !           866: {
        !           867:        zval             retval;                        /* Return value from evaluation */
        !           868:        char            *eval_str_end,          /* End of eval string */
        !           869:                                *match,                         /* Current match for a backref */
        !           870:                                *esc_match,                     /* Quote-escaped match */
        !           871:                                *walk,                          /* Used to walk the code string */
        !           872:                                *segment,                       /* Start of segment to append while walking */
        !           873:                                 walk_last;                     /* Last walked character */
        !           874:        int                      match_len;                     /* Length of the match */
        !           875:        int                      esc_match_len;         /* Length of the quote-escaped match */
        !           876:        int                      result_len;            /* Length of the result of the evaluation */
        !           877:        int                      backref;                       /* Current backref */
        !           878:        char        *compiled_string_description;
        !           879:        smart_str    code = {0};
        !           880:        
        !           881:        eval_str_end = eval_str + eval_str_len;
        !           882:        walk = segment = eval_str;
        !           883:        walk_last = 0;
        !           884:        
        !           885:        while (walk < eval_str_end) {
        !           886:                /* If found a backreference.. */
        !           887:                if ('\\' == *walk || '$' == *walk) {
        !           888:                        smart_str_appendl(&code, segment, walk - segment);
        !           889:                        if (walk_last == '\\') {
        !           890:                                code.c[code.len-1] = *walk++;
        !           891:                                segment = walk;
        !           892:                                walk_last = 0;
        !           893:                                continue;
        !           894:                        }
        !           895:                        segment = walk;
        !           896:                        if (preg_get_backref(&walk, &backref)) {
        !           897:                                if (backref < count) {
        !           898:                                        /* Find the corresponding string match and substitute it
        !           899:                                           in instead of the backref */
        !           900:                                        match = subject + offsets[backref<<1];
        !           901:                                        match_len = offsets[(backref<<1)+1] - offsets[backref<<1];
        !           902:                                        if (match_len) {
        !           903:                                                esc_match = php_addslashes_ex(match, match_len, &esc_match_len, 0, 1 TSRMLS_CC);
        !           904:                                        } else {
        !           905:                                                esc_match = match;
        !           906:                                                esc_match_len = 0;
        !           907:                                        }
        !           908:                                } else {
        !           909:                                        esc_match = "";
        !           910:                                        esc_match_len = 0;
        !           911:                                }
        !           912:                                smart_str_appendl(&code, esc_match, esc_match_len);
        !           913: 
        !           914:                                segment = walk;
        !           915: 
        !           916:                                /* Clean up and reassign */
        !           917:                                if (esc_match_len)
        !           918:                                        efree(esc_match);
        !           919:                                continue;
        !           920:                        }
        !           921:                }
        !           922:                walk++;
        !           923:                walk_last = walk[-1];
        !           924:        }
        !           925:        smart_str_appendl(&code, segment, walk - segment);
        !           926:        smart_str_0(&code);
        !           927: 
        !           928:        compiled_string_description = zend_make_compiled_string_description("regexp code" TSRMLS_CC);
        !           929:        /* Run the code */
        !           930:        if (zend_eval_stringl(code.c, code.len, &retval, compiled_string_description TSRMLS_CC) == FAILURE) {
        !           931:                efree(compiled_string_description);
        !           932:                php_error_docref(NULL TSRMLS_CC,E_ERROR, "Failed evaluating code: %s%s", PHP_EOL, code.c);
        !           933:                /* zend_error() does not return in this case */
        !           934:        }
        !           935:        efree(compiled_string_description);
        !           936:        convert_to_string(&retval);
        !           937:        
        !           938:        /* Save the return value and its length */
        !           939:        *result = estrndup(Z_STRVAL(retval), Z_STRLEN(retval));
        !           940:        result_len = Z_STRLEN(retval);
        !           941:        
        !           942:        /* Clean up */
        !           943:        zval_dtor(&retval);
        !           944:        smart_str_free(&code);
        !           945:        
        !           946:        return result_len;
        !           947: }
        !           948: /* }}} */
        !           949: 
        !           950: /* {{{ php_pcre_replace
        !           951:  */
        !           952: PHPAPI char *php_pcre_replace(char *regex,   int regex_len,
        !           953:                                                          char *subject, int subject_len,
        !           954:                                                          zval *replace_val, int is_callable_replace,
        !           955:                                                          int *result_len, int limit, int *replace_count TSRMLS_DC)
        !           956: {
        !           957:        pcre_cache_entry        *pce;                       /* Compiled regular expression */
        !           958: 
        !           959:        /* Compile regex or get it from cache. */
        !           960:        if ((pce = pcre_get_compiled_regex_cache(regex, regex_len TSRMLS_CC)) == NULL) {
        !           961:                return NULL;
        !           962:        }
        !           963: 
        !           964:        return php_pcre_replace_impl(pce, subject, subject_len, replace_val, 
        !           965:                is_callable_replace, result_len, limit, replace_count TSRMLS_CC);
        !           966: }
        !           967: /* }}} */
        !           968: 
        !           969: /* {{{ php_pcre_replace_impl() */
        !           970: PHPAPI char *php_pcre_replace_impl(pcre_cache_entry *pce, char *subject, int subject_len, zval *replace_val, 
        !           971:        int is_callable_replace, int *result_len, int limit, int *replace_count TSRMLS_DC)
        !           972: {
        !           973:        pcre_extra              *extra = pce->extra;/* Holds results of studying */
        !           974:        pcre_extra               extra_data;            /* Used locally for exec options */
        !           975:        int                              exoptions = 0;         /* Execution options */
        !           976:        int                              count = 0;                     /* Count of matched subpatterns */
        !           977:        int                             *offsets;                       /* Array of subpattern offsets */
        !           978:        char                    **subpat_names;         /* Array for named subpatterns */
        !           979:        int                              num_subpats;           /* Number of captured subpatterns */
        !           980:        int                              size_offsets;          /* Size of the offsets array */
        !           981:        int                              new_len;                       /* Length of needed storage */
        !           982:        int                              alloc_len;                     /* Actual allocated length */
        !           983:        int                              eval_result_len=0;     /* Length of the eval'ed or
        !           984:                                                                                   function-returned string */
        !           985:        int                              match_len;                     /* Length of the current match */
        !           986:        int                              backref;                       /* Backreference number */
        !           987:        int                              eval;                          /* If the replacement string should be eval'ed */
        !           988:        int                              start_offset;          /* Where the new search starts */
        !           989:        int                              g_notempty=0;          /* If the match should not be empty */
        !           990:        int                              replace_len=0;         /* Length of replacement string */
        !           991:        char                    *result,                        /* Result of replacement */
        !           992:                                        *replace=NULL,          /* Replacement string */
        !           993:                                        *new_buf,                       /* Temporary buffer for re-allocation */
        !           994:                                        *walkbuf,                       /* Location of current replacement in the result */
        !           995:                                        *walk,                          /* Used to walk the replacement string */
        !           996:                                        *match,                         /* The current match */
        !           997:                                        *piece,                         /* The current piece of subject */
        !           998:                                        *replace_end=NULL,      /* End of replacement string */
        !           999:                                        *eval_result,           /* Result of eval or custom function */
        !          1000:                                         walk_last;                     /* Last walked character */
        !          1001:        int                              rc;
        !          1002: 
        !          1003:        if (extra == NULL) {
        !          1004:                extra_data.flags = PCRE_EXTRA_MATCH_LIMIT | PCRE_EXTRA_MATCH_LIMIT_RECURSION;
        !          1005:                extra = &extra_data;
        !          1006:        }
        !          1007:        extra->match_limit = PCRE_G(backtrack_limit);
        !          1008:        extra->match_limit_recursion = PCRE_G(recursion_limit);
        !          1009: 
        !          1010:        eval = pce->preg_options & PREG_REPLACE_EVAL;
        !          1011:        if (is_callable_replace) {
        !          1012:                if (eval) {
        !          1013:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Modifier /e cannot be used with replacement callback");
        !          1014:                        return NULL;
        !          1015:                }
        !          1016:        } else {
        !          1017:                replace = Z_STRVAL_P(replace_val);
        !          1018:                replace_len = Z_STRLEN_P(replace_val);
        !          1019:                replace_end = replace + replace_len;
        !          1020:        }
        !          1021: 
        !          1022:        /* Calculate the size of the offsets array, and allocate memory for it. */
        !          1023:        rc = pcre_fullinfo(pce->re, extra, PCRE_INFO_CAPTURECOUNT, &num_subpats);
        !          1024:        if (rc < 0) {
        !          1025:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Internal pcre_fullinfo() error %d", rc);
        !          1026:                return NULL;
        !          1027:        }
        !          1028:        num_subpats++;
        !          1029:        size_offsets = num_subpats * 3;
        !          1030: 
        !          1031:        /*
        !          1032:         * Build a mapping from subpattern numbers to their names. We will always
        !          1033:         * allocate the table, even though there may be no named subpatterns. This
        !          1034:         * avoids somewhat more complicated logic in the inner loops.
        !          1035:         */
        !          1036:        subpat_names = make_subpats_table(num_subpats, pce TSRMLS_CC);
        !          1037:        if (!subpat_names) {
        !          1038:                return NULL;
        !          1039:        }
        !          1040: 
        !          1041:        offsets = (int *)safe_emalloc(size_offsets, sizeof(int), 0);
        !          1042:        
        !          1043:        alloc_len = 2 * subject_len + 1;
        !          1044:        result = safe_emalloc(alloc_len, sizeof(char), 0);
        !          1045: 
        !          1046:        /* Initialize */
        !          1047:        match = NULL;
        !          1048:        *result_len = 0;
        !          1049:        start_offset = 0;
        !          1050:        PCRE_G(error_code) = PHP_PCRE_NO_ERROR;
        !          1051:        
        !          1052:        while (1) {
        !          1053:                /* Execute the regular expression. */
        !          1054:                count = pcre_exec(pce->re, extra, subject, subject_len, start_offset,
        !          1055:                                                  exoptions|g_notempty, offsets, size_offsets);
        !          1056: 
        !          1057:                /* the string was already proved to be valid UTF-8 */
        !          1058:                exoptions |= PCRE_NO_UTF8_CHECK;
        !          1059: 
        !          1060:                /* Check for too many substrings condition. */
        !          1061:                if (count == 0) {
        !          1062:                        php_error_docref(NULL TSRMLS_CC,E_NOTICE, "Matched, but too many substrings");
        !          1063:                        count = size_offsets/3;
        !          1064:                }
        !          1065: 
        !          1066:                piece = subject + start_offset;
        !          1067: 
        !          1068:                if (count > 0 && (limit == -1 || limit > 0)) {
        !          1069:                        if (replace_count) {
        !          1070:                                ++*replace_count;
        !          1071:                        }
        !          1072:                        /* Set the match location in subject */
        !          1073:                        match = subject + offsets[0];
        !          1074: 
        !          1075:                        new_len = *result_len + offsets[0] - start_offset; /* part before the match */
        !          1076:                        
        !          1077:                        /* If evaluating, do it and add the return string's length */
        !          1078:                        if (eval) {
        !          1079:                                eval_result_len = preg_do_eval(replace, replace_len, subject,
        !          1080:                                                                                           offsets, count, &eval_result TSRMLS_CC);
        !          1081:                                new_len += eval_result_len;
        !          1082:                        } else if (is_callable_replace) {
        !          1083:                                /* Use custom function to get replacement string and its length. */
        !          1084:                                eval_result_len = preg_do_repl_func(replace_val, subject, offsets, subpat_names, count, &eval_result TSRMLS_CC);
        !          1085:                                new_len += eval_result_len;
        !          1086:                        } else { /* do regular substitution */
        !          1087:                                walk = replace;
        !          1088:                                walk_last = 0;
        !          1089:                                while (walk < replace_end) {
        !          1090:                                        if ('\\' == *walk || '$' == *walk) {
        !          1091:                                                if (walk_last == '\\') {
        !          1092:                                                        walk++;
        !          1093:                                                        walk_last = 0;
        !          1094:                                                        continue;
        !          1095:                                                }
        !          1096:                                                if (preg_get_backref(&walk, &backref)) {
        !          1097:                                                        if (backref < count)
        !          1098:                                                                new_len += offsets[(backref<<1)+1] - offsets[backref<<1];
        !          1099:                                                        continue;
        !          1100:                                                }
        !          1101:                                        }
        !          1102:                                        new_len++;
        !          1103:                                        walk++;
        !          1104:                                        walk_last = walk[-1];
        !          1105:                                }
        !          1106:                        }
        !          1107: 
        !          1108:                        if (new_len + 1 > alloc_len) {
        !          1109:                                alloc_len = 1 + alloc_len + 2 * new_len;
        !          1110:                                new_buf = emalloc(alloc_len);
        !          1111:                                memcpy(new_buf, result, *result_len);
        !          1112:                                efree(result);
        !          1113:                                result = new_buf;
        !          1114:                        }
        !          1115:                        /* copy the part of the string before the match */
        !          1116:                        memcpy(&result[*result_len], piece, match-piece);
        !          1117:                        *result_len += match-piece;
        !          1118: 
        !          1119:                        /* copy replacement and backrefs */
        !          1120:                        walkbuf = result + *result_len;
        !          1121:                        
        !          1122:                        /* If evaluating or using custom function, copy result to the buffer
        !          1123:                         * and clean up. */
        !          1124:                        if (eval || is_callable_replace) {
        !          1125:                                memcpy(walkbuf, eval_result, eval_result_len);
        !          1126:                                *result_len += eval_result_len;
        !          1127:                                STR_FREE(eval_result);
        !          1128:                        } else { /* do regular backreference copying */
        !          1129:                                walk = replace;
        !          1130:                                walk_last = 0;
        !          1131:                                while (walk < replace_end) {
        !          1132:                                        if ('\\' == *walk || '$' == *walk) {
        !          1133:                                                if (walk_last == '\\') {
        !          1134:                                                        *(walkbuf-1) = *walk++;
        !          1135:                                                        walk_last = 0;
        !          1136:                                                        continue;
        !          1137:                                                }
        !          1138:                                                if (preg_get_backref(&walk, &backref)) {
        !          1139:                                                        if (backref < count) {
        !          1140:                                                                match_len = offsets[(backref<<1)+1] - offsets[backref<<1];
        !          1141:                                                                memcpy(walkbuf, subject + offsets[backref<<1], match_len);
        !          1142:                                                                walkbuf += match_len;
        !          1143:                                                        }
        !          1144:                                                        continue;
        !          1145:                                                }
        !          1146:                                        }
        !          1147:                                        *walkbuf++ = *walk++;
        !          1148:                                        walk_last = walk[-1];
        !          1149:                                }
        !          1150:                                *walkbuf = '\0';
        !          1151:                                /* increment the result length by how much we've added to the string */
        !          1152:                                *result_len += walkbuf - (result + *result_len);
        !          1153:                        }
        !          1154: 
        !          1155:                        if (limit != -1)
        !          1156:                                limit--;
        !          1157: 
        !          1158:                } else if (count == PCRE_ERROR_NOMATCH || limit == 0) {
        !          1159:                        /* If we previously set PCRE_NOTEMPTY after a null match,
        !          1160:                           this is not necessarily the end. We need to advance
        !          1161:                           the start offset, and continue. Fudge the offset values
        !          1162:                           to achieve this, unless we're already at the end of the string. */
        !          1163:                        if (g_notempty != 0 && start_offset < subject_len) {
        !          1164:                                offsets[0] = start_offset;
        !          1165:                                offsets[1] = start_offset + 1;
        !          1166:                                memcpy(&result[*result_len], piece, 1);
        !          1167:                                (*result_len)++;
        !          1168:                        } else {
        !          1169:                                new_len = *result_len + subject_len - start_offset;
        !          1170:                                if (new_len + 1 > alloc_len) {
        !          1171:                                        alloc_len = new_len + 1; /* now we know exactly how long it is */
        !          1172:                                        new_buf = safe_emalloc(alloc_len, sizeof(char), 0);
        !          1173:                                        memcpy(new_buf, result, *result_len);
        !          1174:                                        efree(result);
        !          1175:                                        result = new_buf;
        !          1176:                                }
        !          1177:                                /* stick that last bit of string on our output */
        !          1178:                                memcpy(&result[*result_len], piece, subject_len - start_offset);
        !          1179:                                *result_len += subject_len - start_offset;
        !          1180:                                result[*result_len] = '\0';
        !          1181:                                break;
        !          1182:                        }
        !          1183:                } else {
        !          1184:                        pcre_handle_exec_error(count TSRMLS_CC);
        !          1185:                        efree(result);
        !          1186:                        result = NULL;
        !          1187:                        break;
        !          1188:                }
        !          1189:                        
        !          1190:                /* If we have matched an empty string, mimic what Perl's /g options does.
        !          1191:                   This turns out to be rather cunning. First we set PCRE_NOTEMPTY and try
        !          1192:                   the match again at the same point. If this fails (picked up above) we
        !          1193:                   advance to the next character. */
        !          1194:                g_notempty = (offsets[1] == offsets[0])? PCRE_NOTEMPTY | PCRE_ANCHORED : 0;
        !          1195:                
        !          1196:                /* Advance to the next piece. */
        !          1197:                start_offset = offsets[1];
        !          1198:        }
        !          1199: 
        !          1200:        efree(offsets);
        !          1201:        efree(subpat_names);
        !          1202: 
        !          1203:        return result;
        !          1204: }
        !          1205: /* }}} */
        !          1206: 
        !          1207: /* {{{ php_replace_in_subject
        !          1208:  */
        !          1209: static char *php_replace_in_subject(zval *regex, zval *replace, zval **subject, int *result_len, int limit, int is_callable_replace, int *replace_count TSRMLS_DC)
        !          1210: {
        !          1211:        zval            **regex_entry,
        !          1212:                                **replace_entry = NULL,
        !          1213:                                 *replace_value,
        !          1214:                                  empty_replace;
        !          1215:        char            *subject_value,
        !          1216:                                *result;
        !          1217:        int                      subject_len;
        !          1218: 
        !          1219:        /* Make sure we're dealing with strings. */     
        !          1220:        convert_to_string_ex(subject);
        !          1221:        /* FIXME: This might need to be changed to STR_EMPTY_ALLOC(). Check if this zval could be dtor()'ed somehow */
        !          1222:        ZVAL_STRINGL(&empty_replace, "", 0, 0);
        !          1223:        
        !          1224:        /* If regex is an array */
        !          1225:        if (Z_TYPE_P(regex) == IS_ARRAY) {
        !          1226:                /* Duplicate subject string for repeated replacement */
        !          1227:                subject_value = estrndup(Z_STRVAL_PP(subject), Z_STRLEN_PP(subject));
        !          1228:                subject_len = Z_STRLEN_PP(subject);
        !          1229:                *result_len = subject_len;
        !          1230:                
        !          1231:                zend_hash_internal_pointer_reset(Z_ARRVAL_P(regex));
        !          1232: 
        !          1233:                replace_value = replace;
        !          1234:                if (Z_TYPE_P(replace) == IS_ARRAY && !is_callable_replace)
        !          1235:                        zend_hash_internal_pointer_reset(Z_ARRVAL_P(replace));
        !          1236: 
        !          1237:                /* For each entry in the regex array, get the entry */
        !          1238:                while (zend_hash_get_current_data(Z_ARRVAL_P(regex), (void **)&regex_entry) == SUCCESS) {
        !          1239:                        /* Make sure we're dealing with strings. */     
        !          1240:                        convert_to_string_ex(regex_entry);
        !          1241:                
        !          1242:                        /* If replace is an array and not a callable construct */
        !          1243:                        if (Z_TYPE_P(replace) == IS_ARRAY && !is_callable_replace) {
        !          1244:                                /* Get current entry */
        !          1245:                                if (zend_hash_get_current_data(Z_ARRVAL_P(replace), (void **)&replace_entry) == SUCCESS) {
        !          1246:                                        if (!is_callable_replace) {
        !          1247:                                                convert_to_string_ex(replace_entry);
        !          1248:                                        }
        !          1249:                                        replace_value = *replace_entry;
        !          1250:                                        zend_hash_move_forward(Z_ARRVAL_P(replace));
        !          1251:                                } else {
        !          1252:                                        /* We've run out of replacement strings, so use an empty one */
        !          1253:                                        replace_value = &empty_replace;
        !          1254:                                }
        !          1255:                        }
        !          1256:                        
        !          1257:                        /* Do the actual replacement and put the result back into subject_value
        !          1258:                           for further replacements. */
        !          1259:                        if ((result = php_pcre_replace(Z_STRVAL_PP(regex_entry),
        !          1260:                                                                                   Z_STRLEN_PP(regex_entry),
        !          1261:                                                                                   subject_value,
        !          1262:                                                                                   subject_len,
        !          1263:                                                                                   replace_value,
        !          1264:                                                                                   is_callable_replace,
        !          1265:                                                                                   result_len,
        !          1266:                                                                                   limit,
        !          1267:                                                                                   replace_count TSRMLS_CC)) != NULL) {
        !          1268:                                efree(subject_value);
        !          1269:                                subject_value = result;
        !          1270:                                subject_len = *result_len;
        !          1271:                        } else {
        !          1272:                                efree(subject_value);
        !          1273:                                return NULL;
        !          1274:                        }
        !          1275: 
        !          1276:                        zend_hash_move_forward(Z_ARRVAL_P(regex));
        !          1277:                }
        !          1278: 
        !          1279:                return subject_value;
        !          1280:        } else {
        !          1281:                result = php_pcre_replace(Z_STRVAL_P(regex),
        !          1282:                                                                  Z_STRLEN_P(regex),
        !          1283:                                                                  Z_STRVAL_PP(subject),
        !          1284:                                                                  Z_STRLEN_PP(subject),
        !          1285:                                                                  replace,
        !          1286:                                                                  is_callable_replace,
        !          1287:                                                                  result_len,
        !          1288:                                                                  limit,
        !          1289:                                                                  replace_count TSRMLS_CC);
        !          1290:                return result;
        !          1291:        }
        !          1292: }
        !          1293: /* }}} */
        !          1294: 
        !          1295: /* {{{ preg_replace_impl
        !          1296:  */
        !          1297: static void preg_replace_impl(INTERNAL_FUNCTION_PARAMETERS, int is_callable_replace, int is_filter)
        !          1298: {
        !          1299:        zval               **regex,
        !          1300:                                   **replace,
        !          1301:                                   **subject,
        !          1302:                                   **subject_entry,
        !          1303:                                   **zcount = NULL;
        !          1304:        char                    *result;
        !          1305:        int                              result_len;
        !          1306:        int                              limit_val = -1;
        !          1307:        long                    limit = -1;
        !          1308:        char                    *string_key;
        !          1309:        ulong                    num_key;
        !          1310:        char                    *callback_name;
        !          1311:        int                              replace_count=0, old_replace_count;
        !          1312:        
        !          1313:        /* Get function parameters and do error-checking. */
        !          1314:        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZZ|lZ", &regex, &replace, &subject, &limit, &zcount) == FAILURE) {
        !          1315:                return;
        !          1316:        }
        !          1317:        
        !          1318:        if (!is_callable_replace && Z_TYPE_PP(replace) == IS_ARRAY && Z_TYPE_PP(regex) != IS_ARRAY) {
        !          1319:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Parameter mismatch, pattern is a string while replacement is an array");
        !          1320:                RETURN_FALSE;
        !          1321:        }
        !          1322: 
        !          1323:        SEPARATE_ZVAL(replace);
        !          1324:        if (Z_TYPE_PP(replace) != IS_ARRAY && (Z_TYPE_PP(replace) != IS_OBJECT || !is_callable_replace)) {
        !          1325:                convert_to_string_ex(replace);
        !          1326:        }
        !          1327:        if (is_callable_replace) {
        !          1328:                if (!zend_is_callable(*replace, 0, &callback_name TSRMLS_CC)) {
        !          1329:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Requires argument 2, '%s', to be a valid callback", callback_name);
        !          1330:                        efree(callback_name);
        !          1331:                        MAKE_COPY_ZVAL(subject, return_value);
        !          1332:                        return;
        !          1333:                }
        !          1334:                efree(callback_name);
        !          1335:        }
        !          1336: 
        !          1337:        SEPARATE_ZVAL(regex);
        !          1338:        SEPARATE_ZVAL(subject);
        !          1339: 
        !          1340:        if (ZEND_NUM_ARGS() > 3) {
        !          1341:                limit_val = limit;
        !          1342:        }
        !          1343:                
        !          1344:        if (Z_TYPE_PP(regex) != IS_ARRAY)
        !          1345:                convert_to_string_ex(regex);
        !          1346:        
        !          1347:        /* if subject is an array */
        !          1348:        if (Z_TYPE_PP(subject) == IS_ARRAY) {
        !          1349:                array_init(return_value);
        !          1350:                zend_hash_internal_pointer_reset(Z_ARRVAL_PP(subject));
        !          1351: 
        !          1352:                /* For each subject entry, convert it to string, then perform replacement
        !          1353:                   and add the result to the return_value array. */
        !          1354:                while (zend_hash_get_current_data(Z_ARRVAL_PP(subject), (void **)&subject_entry) == SUCCESS) {
        !          1355:                        SEPARATE_ZVAL(subject_entry);
        !          1356:                        old_replace_count = replace_count;
        !          1357:                        if ((result = php_replace_in_subject(*regex, *replace, subject_entry, &result_len, limit_val, is_callable_replace, &replace_count TSRMLS_CC)) != NULL) {
        !          1358:                                if (!is_filter || replace_count > old_replace_count) {
        !          1359:                                        /* Add to return array */
        !          1360:                                        switch(zend_hash_get_current_key(Z_ARRVAL_PP(subject), &string_key, &num_key, 0))
        !          1361:                                        {
        !          1362:                                        case HASH_KEY_IS_STRING:
        !          1363:                                                add_assoc_stringl(return_value, string_key, result, result_len, 0);
        !          1364:                                                break;
        !          1365: 
        !          1366:                                        case HASH_KEY_IS_LONG:
        !          1367:                                                add_index_stringl(return_value, num_key, result, result_len, 0);
        !          1368:                                                break;
        !          1369:                                        }
        !          1370:                                } else {
        !          1371:                                        efree(result);
        !          1372:                                }
        !          1373:                        }
        !          1374:                
        !          1375:                        zend_hash_move_forward(Z_ARRVAL_PP(subject));
        !          1376:                }
        !          1377:        } else {        /* if subject is not an array */
        !          1378:                old_replace_count = replace_count;
        !          1379:                if ((result = php_replace_in_subject(*regex, *replace, subject, &result_len, limit_val, is_callable_replace, &replace_count TSRMLS_CC)) != NULL) {
        !          1380:                        if (!is_filter || replace_count > old_replace_count) {
        !          1381:                                RETVAL_STRINGL(result, result_len, 0);
        !          1382:                        } else {
        !          1383:                                efree(result);
        !          1384:                        }
        !          1385:                }
        !          1386:        }
        !          1387:        if (ZEND_NUM_ARGS() > 4) {
        !          1388:                zval_dtor(*zcount);
        !          1389:                ZVAL_LONG(*zcount, replace_count);
        !          1390:        }
        !          1391:        
        !          1392: }
        !          1393: /* }}} */
        !          1394: 
        !          1395: /* {{{ proto mixed preg_replace(mixed regex, mixed replace, mixed subject [, int limit [, int &count]])
        !          1396:    Perform Perl-style regular expression replacement. */
        !          1397: static PHP_FUNCTION(preg_replace)
        !          1398: {
        !          1399:        preg_replace_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
        !          1400: }
        !          1401: /* }}} */
        !          1402: 
        !          1403: /* {{{ proto mixed preg_replace_callback(mixed regex, mixed callback, mixed subject [, int limit [, int &count]])
        !          1404:    Perform Perl-style regular expression replacement using replacement callback. */
        !          1405: static PHP_FUNCTION(preg_replace_callback)
        !          1406: {
        !          1407:        preg_replace_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1, 0);
        !          1408: }
        !          1409: /* }}} */
        !          1410: 
        !          1411: /* {{{ proto mixed preg_filter(mixed regex, mixed replace, mixed subject [, int limit [, int &count]])
        !          1412:    Perform Perl-style regular expression replacement and only return matches. */
        !          1413: static PHP_FUNCTION(preg_filter)
        !          1414: {
        !          1415:        preg_replace_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 1);
        !          1416: }
        !          1417: /* }}} */
        !          1418: 
        !          1419: /* {{{ proto array preg_split(string pattern, string subject [, int limit [, int flags]]) 
        !          1420:    Split string into an array using a perl-style regular expression as a delimiter */
        !          1421: static PHP_FUNCTION(preg_split)
        !          1422: {
        !          1423:        char                            *regex;                 /* Regular expression */
        !          1424:        char                            *subject;               /* String to match against */
        !          1425:        int                                      regex_len;
        !          1426:        int                                      subject_len;
        !          1427:        long                             limit_val = -1;/* Integer value of limit */
        !          1428:        long                             flags = 0;             /* Match control flags */
        !          1429:        pcre_cache_entry        *pce;                   /* Compiled regular expression */
        !          1430: 
        !          1431:        /* Get function parameters and do error checking */     
        !          1432:        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|ll", &regex, &regex_len,
        !          1433:                                                          &subject, &subject_len, &limit_val, &flags) == FAILURE) {
        !          1434:                RETURN_FALSE;
        !          1435:        }
        !          1436:        
        !          1437:        /* Compile regex or get it from cache. */
        !          1438:        if ((pce = pcre_get_compiled_regex_cache(regex, regex_len TSRMLS_CC)) == NULL) {
        !          1439:                RETURN_FALSE;
        !          1440:        }
        !          1441: 
        !          1442:        php_pcre_split_impl(pce, subject, subject_len, return_value, limit_val, flags TSRMLS_CC);
        !          1443: }
        !          1444: /* }}} */
        !          1445: 
        !          1446: /* {{{ php_pcre_split
        !          1447:  */
        !          1448: PHPAPI void php_pcre_split_impl(pcre_cache_entry *pce, char *subject, int subject_len, zval *return_value,
        !          1449:        long limit_val, long flags TSRMLS_DC)
        !          1450: {
        !          1451:        pcre_extra              *extra = NULL;          /* Holds results of studying */
        !          1452:        pcre                    *re_bump = NULL;        /* Regex instance for empty matches */
        !          1453:        pcre_extra              *extra_bump = NULL;     /* Almost dummy */
        !          1454:        pcre_extra               extra_data;            /* Used locally for exec options */
        !          1455:        int                             *offsets;                       /* Array of subpattern offsets */
        !          1456:        int                              size_offsets;          /* Size of the offsets array */
        !          1457:        int                              exoptions = 0;         /* Execution options */
        !          1458:        int                              count = 0;                     /* Count of matched subpatterns */
        !          1459:        int                              start_offset;          /* Where the new search starts */
        !          1460:        int                              next_offset;           /* End of the last delimiter match + 1 */
        !          1461:        int                              g_notempty = 0;        /* If the match should not be empty */
        !          1462:        char                    *last_match;            /* Location of last match */
        !          1463:        int                              rc;
        !          1464:        int                              no_empty;                      /* If NO_EMPTY flag is set */
        !          1465:        int                              delim_capture;         /* If delimiters should be captured */
        !          1466:        int                              offset_capture;        /* If offsets should be captured */
        !          1467: 
        !          1468:        no_empty = flags & PREG_SPLIT_NO_EMPTY;
        !          1469:        delim_capture = flags & PREG_SPLIT_DELIM_CAPTURE;
        !          1470:        offset_capture = flags & PREG_SPLIT_OFFSET_CAPTURE;
        !          1471:        
        !          1472:        if (limit_val == 0) {
        !          1473:                limit_val = -1;
        !          1474:        }
        !          1475: 
        !          1476:        if (extra == NULL) {
        !          1477:                extra_data.flags = PCRE_EXTRA_MATCH_LIMIT | PCRE_EXTRA_MATCH_LIMIT_RECURSION;
        !          1478:                extra = &extra_data;
        !          1479:        }
        !          1480:        extra->match_limit = PCRE_G(backtrack_limit);
        !          1481:        extra->match_limit_recursion = PCRE_G(recursion_limit);
        !          1482:        
        !          1483:        /* Initialize return value */
        !          1484:        array_init(return_value);
        !          1485: 
        !          1486:        /* Calculate the size of the offsets array, and allocate memory for it. */
        !          1487:        rc = pcre_fullinfo(pce->re, extra, PCRE_INFO_CAPTURECOUNT, &size_offsets);
        !          1488:        if (rc < 0) {
        !          1489:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Internal pcre_fullinfo() error %d", rc);
        !          1490:                RETURN_FALSE;
        !          1491:        }
        !          1492:        size_offsets = (size_offsets + 1) * 3;
        !          1493:        offsets = (int *)safe_emalloc(size_offsets, sizeof(int), 0);
        !          1494:        
        !          1495:        /* Start at the beginning of the string */
        !          1496:        start_offset = 0;
        !          1497:        next_offset = 0;
        !          1498:        last_match = subject;
        !          1499:        PCRE_G(error_code) = PHP_PCRE_NO_ERROR;
        !          1500:        
        !          1501:        /* Get next piece if no limit or limit not yet reached and something matched*/
        !          1502:        while ((limit_val == -1 || limit_val > 1)) {
        !          1503:                count = pcre_exec(pce->re, extra, subject,
        !          1504:                                                  subject_len, start_offset,
        !          1505:                                                  exoptions|g_notempty, offsets, size_offsets);
        !          1506: 
        !          1507:                /* the string was already proved to be valid UTF-8 */
        !          1508:                exoptions |= PCRE_NO_UTF8_CHECK;
        !          1509: 
        !          1510:                /* Check for too many substrings condition. */
        !          1511:                if (count == 0) {
        !          1512:                        php_error_docref(NULL TSRMLS_CC,E_NOTICE, "Matched, but too many substrings");
        !          1513:                        count = size_offsets/3;
        !          1514:                }
        !          1515:                                
        !          1516:                /* If something matched */
        !          1517:                if (count > 0) {
        !          1518:                        if (!no_empty || &subject[offsets[0]] != last_match) {
        !          1519: 
        !          1520:                                if (offset_capture) {
        !          1521:                                        /* Add (match, offset) pair to the return value */
        !          1522:                                        add_offset_pair(return_value, last_match, &subject[offsets[0]]-last_match, next_offset, NULL);
        !          1523:                                } else {
        !          1524:                                        /* Add the piece to the return value */
        !          1525:                                        add_next_index_stringl(return_value, last_match,
        !          1526:                                                                           &subject[offsets[0]]-last_match, 1);
        !          1527:                                }
        !          1528: 
        !          1529:                                /* One less left to do */
        !          1530:                                if (limit_val != -1)
        !          1531:                                        limit_val--;
        !          1532:                        }
        !          1533:                        
        !          1534:                        last_match = &subject[offsets[1]];
        !          1535:                        next_offset = offsets[1];
        !          1536: 
        !          1537:                        if (delim_capture) {
        !          1538:                                int i, match_len;
        !          1539:                                for (i = 1; i < count; i++) {
        !          1540:                                        match_len = offsets[(i<<1)+1] - offsets[i<<1];
        !          1541:                                        /* If we have matched a delimiter */
        !          1542:                                        if (!no_empty || match_len > 0) {
        !          1543:                                                if (offset_capture) {
        !          1544:                                                        add_offset_pair(return_value, &subject[offsets[i<<1]], match_len, offsets[i<<1], NULL);
        !          1545:                                                } else {
        !          1546:                                                        add_next_index_stringl(return_value,
        !          1547:                                                                                                   &subject[offsets[i<<1]],
        !          1548:                                                                                                   match_len, 1);
        !          1549:                                                }
        !          1550:                                        }
        !          1551:                                }
        !          1552:                        }
        !          1553:                } else if (count == PCRE_ERROR_NOMATCH) {
        !          1554:                        /* If we previously set PCRE_NOTEMPTY after a null match,
        !          1555:                           this is not necessarily the end. We need to advance
        !          1556:                           the start offset, and continue. Fudge the offset values
        !          1557:                           to achieve this, unless we're already at the end of the string. */
        !          1558:                        if (g_notempty != 0 && start_offset < subject_len) {
        !          1559:                                if (pce->compile_options & PCRE_UTF8) {
        !          1560:                                        if (re_bump == NULL) {
        !          1561:                                                int dummy;
        !          1562: 
        !          1563:                                                if ((re_bump = pcre_get_compiled_regex("/./us", &extra_bump, &dummy TSRMLS_CC)) == NULL) {
        !          1564:                                                        RETURN_FALSE;
        !          1565:                                                }
        !          1566:                                        }
        !          1567:                                        count = pcre_exec(re_bump, extra_bump, subject,
        !          1568:                                                          subject_len, start_offset,
        !          1569:                                                          exoptions, offsets, size_offsets);
        !          1570:                                        if (count < 1) {
        !          1571:                                                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown error");
        !          1572:                                                RETURN_FALSE;
        !          1573:                                        }
        !          1574:                                } else {
        !          1575:                                        offsets[0] = start_offset;
        !          1576:                                        offsets[1] = start_offset + 1;
        !          1577:                                }
        !          1578:                        } else
        !          1579:                                break;
        !          1580:                } else {
        !          1581:                        pcre_handle_exec_error(count TSRMLS_CC);
        !          1582:                        break;
        !          1583:                }
        !          1584: 
        !          1585:                /* If we have matched an empty string, mimic what Perl's /g options does.
        !          1586:                   This turns out to be rather cunning. First we set PCRE_NOTEMPTY and try
        !          1587:                   the match again at the same point. If this fails (picked up above) we
        !          1588:                   advance to the next character. */
        !          1589:                g_notempty = (offsets[1] == offsets[0])? PCRE_NOTEMPTY | PCRE_ANCHORED : 0;
        !          1590:                
        !          1591:                /* Advance to the position right after the last full match */
        !          1592:                start_offset = offsets[1];
        !          1593:        }
        !          1594: 
        !          1595: 
        !          1596:        start_offset = last_match - subject; /* the offset might have been incremented, but without further successful matches */
        !          1597: 
        !          1598:        if (!no_empty || start_offset < subject_len)
        !          1599:        {
        !          1600:                if (offset_capture) {
        !          1601:                        /* Add the last (match, offset) pair to the return value */
        !          1602:                        add_offset_pair(return_value, &subject[start_offset], subject_len - start_offset, start_offset, NULL);
        !          1603:                } else {
        !          1604:                        /* Add the last piece to the return value */
        !          1605:                        add_next_index_stringl(return_value, last_match, subject + subject_len - last_match, 1);
        !          1606:                }
        !          1607:        }
        !          1608: 
        !          1609:        
        !          1610:        /* Clean up */
        !          1611:        efree(offsets);
        !          1612: }
        !          1613: /* }}} */
        !          1614: 
        !          1615: /* {{{ proto string preg_quote(string str [, string delim_char])
        !          1616:    Quote regular expression characters plus an optional character */
        !          1617: static PHP_FUNCTION(preg_quote)
        !          1618: {
        !          1619:        int              in_str_len;
        !          1620:        char    *in_str;                /* Input string argument */
        !          1621:        char    *in_str_end;    /* End of the input string */
        !          1622:        int              delim_len = 0;
        !          1623:        char    *delim = NULL;  /* Additional delimiter argument */
        !          1624:        char    *out_str,               /* Output string with quoted characters */
        !          1625:                        *p,                             /* Iterator for input string */
        !          1626:                        *q,                             /* Iterator for output string */
        !          1627:                         delim_char=0,  /* Delimiter character to be quoted */
        !          1628:                         c;                             /* Current character */
        !          1629:        zend_bool quote_delim = 0; /* Whether to quote additional delim char */
        !          1630:        
        !          1631:        /* Get the arguments and check for errors */
        !          1632:        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &in_str, &in_str_len,
        !          1633:                                                          &delim, &delim_len) == FAILURE) {
        !          1634:                return;
        !          1635:        }
        !          1636:        
        !          1637:        in_str_end = in_str + in_str_len;
        !          1638: 
        !          1639:        /* Nothing to do if we got an empty string */
        !          1640:        if (in_str == in_str_end) {
        !          1641:                RETURN_EMPTY_STRING();
        !          1642:        }
        !          1643: 
        !          1644:        if (delim && *delim) {
        !          1645:                delim_char = delim[0];
        !          1646:                quote_delim = 1;
        !          1647:        }
        !          1648:        
        !          1649:        /* Allocate enough memory so that even if each character
        !          1650:           is quoted, we won't run out of room */
        !          1651:        out_str = safe_emalloc(4, in_str_len, 1);
        !          1652:        
        !          1653:        /* Go through the string and quote necessary characters */
        !          1654:        for(p = in_str, q = out_str; p != in_str_end; p++) {
        !          1655:                c = *p;
        !          1656:                switch(c) {
        !          1657:                        case '.':
        !          1658:                        case '\\':
        !          1659:                        case '+':
        !          1660:                        case '*':
        !          1661:                        case '?':
        !          1662:                        case '[':
        !          1663:                        case '^':
        !          1664:                        case ']':
        !          1665:                        case '$':
        !          1666:                        case '(':
        !          1667:                        case ')':
        !          1668:                        case '{':
        !          1669:                        case '}':
        !          1670:                        case '=':
        !          1671:                        case '!':
        !          1672:                        case '>':
        !          1673:                        case '<':
        !          1674:                        case '|':
        !          1675:                        case ':':
        !          1676:                        case '-':
        !          1677:                                *q++ = '\\';
        !          1678:                                *q++ = c;
        !          1679:                                break;
        !          1680: 
        !          1681:                        case '\0':
        !          1682:                                *q++ = '\\';
        !          1683:                                *q++ = '0';
        !          1684:                                *q++ = '0';
        !          1685:                                *q++ = '0';
        !          1686:                                break;
        !          1687: 
        !          1688:                        default:
        !          1689:                                if (quote_delim && c == delim_char)
        !          1690:                                        *q++ = '\\';
        !          1691:                                *q++ = c;
        !          1692:                                break;
        !          1693:                }
        !          1694:        }
        !          1695:        *q = '\0';
        !          1696:        
        !          1697:        /* Reallocate string and return it */
        !          1698:        RETVAL_STRINGL(erealloc(out_str, q - out_str + 1), q - out_str, 0);
        !          1699: }
        !          1700: /* }}} */
        !          1701: 
        !          1702: /* {{{ proto array preg_grep(string regex, array input [, int flags])
        !          1703:    Searches array and returns entries which match regex */
        !          1704: static PHP_FUNCTION(preg_grep)
        !          1705: {
        !          1706:        char                            *regex;                 /* Regular expression */
        !          1707:        int                                      regex_len;
        !          1708:        zval                            *input;                 /* Input array */
        !          1709:        long                             flags = 0;             /* Match control flags */
        !          1710:        pcre_cache_entry        *pce;                   /* Compiled regular expression */
        !          1711: 
        !          1712:        /* Get arguments and do error checking */
        !          1713:        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|l", &regex, &regex_len,
        !          1714:                                                          &input, &flags) == FAILURE) {
        !          1715:                return;
        !          1716:        }
        !          1717:        
        !          1718:        /* Compile regex or get it from cache. */
        !          1719:        if ((pce = pcre_get_compiled_regex_cache(regex, regex_len TSRMLS_CC)) == NULL) {
        !          1720:                RETURN_FALSE;
        !          1721:        }
        !          1722:        
        !          1723:        php_pcre_grep_impl(pce, input, return_value, flags TSRMLS_CC);
        !          1724: }
        !          1725: /* }}} */
        !          1726: 
        !          1727: PHPAPI void  php_pcre_grep_impl(pcre_cache_entry *pce, zval *input, zval *return_value, long flags TSRMLS_DC) /* {{{ */
        !          1728: {
        !          1729:        zval               **entry;                             /* An entry in the input array */
        !          1730:        pcre_extra              *extra = pce->extra;/* Holds results of studying */
        !          1731:        pcre_extra               extra_data;            /* Used locally for exec options */
        !          1732:        int                             *offsets;                       /* Array of subpattern offsets */
        !          1733:        int                              size_offsets;          /* Size of the offsets array */
        !          1734:        int                              count = 0;                     /* Count of matched subpatterns */
        !          1735:        char                    *string_key;
        !          1736:        ulong                    num_key;
        !          1737:        zend_bool                invert;                        /* Whether to return non-matching
        !          1738:                                                                                   entries */
        !          1739:        int                              rc;
        !          1740:        
        !          1741:        invert = flags & PREG_GREP_INVERT ? 1 : 0;
        !          1742:        
        !          1743:        if (extra == NULL) {
        !          1744:                extra_data.flags = PCRE_EXTRA_MATCH_LIMIT | PCRE_EXTRA_MATCH_LIMIT_RECURSION;
        !          1745:                extra = &extra_data;
        !          1746:        }
        !          1747:        extra->match_limit = PCRE_G(backtrack_limit);
        !          1748:        extra->match_limit_recursion = PCRE_G(recursion_limit);
        !          1749: 
        !          1750:        /* Calculate the size of the offsets array, and allocate memory for it. */
        !          1751:        rc = pcre_fullinfo(pce->re, extra, PCRE_INFO_CAPTURECOUNT, &size_offsets);
        !          1752:        if (rc < 0) {
        !          1753:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Internal pcre_fullinfo() error %d", rc);
        !          1754:                RETURN_FALSE;
        !          1755:        }
        !          1756:        size_offsets = (size_offsets + 1) * 3;
        !          1757:        offsets = (int *)safe_emalloc(size_offsets, sizeof(int), 0);
        !          1758:        
        !          1759:        /* Initialize return array */
        !          1760:        array_init(return_value);
        !          1761: 
        !          1762:        PCRE_G(error_code) = PHP_PCRE_NO_ERROR;
        !          1763: 
        !          1764:        /* Go through the input array */
        !          1765:        zend_hash_internal_pointer_reset(Z_ARRVAL_P(input));
        !          1766:        while (zend_hash_get_current_data(Z_ARRVAL_P(input), (void **)&entry) == SUCCESS) {
        !          1767:                zval subject = **entry;
        !          1768: 
        !          1769:                if (Z_TYPE_PP(entry) != IS_STRING) {
        !          1770:                        zval_copy_ctor(&subject);
        !          1771:                        convert_to_string(&subject);
        !          1772:                }
        !          1773: 
        !          1774:                /* Perform the match */
        !          1775:                count = pcre_exec(pce->re, extra, Z_STRVAL(subject),
        !          1776:                                                  Z_STRLEN(subject), 0,
        !          1777:                                                  0, offsets, size_offsets);
        !          1778: 
        !          1779:                /* Check for too many substrings condition. */
        !          1780:                if (count == 0) {
        !          1781:                        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Matched, but too many substrings");
        !          1782:                        count = size_offsets/3;
        !          1783:                } else if (count < 0 && count != PCRE_ERROR_NOMATCH) {
        !          1784:                        pcre_handle_exec_error(count TSRMLS_CC);
        !          1785:                        break;
        !          1786:                }
        !          1787: 
        !          1788:                /* If the entry fits our requirements */
        !          1789:                if ((count > 0 && !invert) || (count == PCRE_ERROR_NOMATCH && invert)) {
        !          1790: 
        !          1791:                        Z_ADDREF_PP(entry);
        !          1792: 
        !          1793:                        /* Add to return array */
        !          1794:                        switch (zend_hash_get_current_key(Z_ARRVAL_P(input), &string_key, &num_key, 0))
        !          1795:                        {
        !          1796:                                case HASH_KEY_IS_STRING:
        !          1797:                                        zend_hash_update(Z_ARRVAL_P(return_value), string_key,
        !          1798:                                                                         strlen(string_key)+1, entry, sizeof(zval *), NULL);
        !          1799:                                        break;
        !          1800: 
        !          1801:                                case HASH_KEY_IS_LONG:
        !          1802:                                        zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry,
        !          1803:                                                                                   sizeof(zval *), NULL);
        !          1804:                                        break;
        !          1805:                        }
        !          1806:                }
        !          1807: 
        !          1808:                if (Z_TYPE_PP(entry) != IS_STRING) {
        !          1809:                        zval_dtor(&subject);
        !          1810:                }
        !          1811: 
        !          1812:                zend_hash_move_forward(Z_ARRVAL_P(input));
        !          1813:        }
        !          1814:        zend_hash_internal_pointer_reset(Z_ARRVAL_P(input));
        !          1815:        /* Clean up */
        !          1816:        efree(offsets);
        !          1817: }
        !          1818: /* }}} */
        !          1819: 
        !          1820: /* {{{ proto int preg_last_error()
        !          1821:    Returns the error code of the last regexp execution. */
        !          1822: static PHP_FUNCTION(preg_last_error)
        !          1823: {
        !          1824:        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") == FAILURE) {
        !          1825:                return;
        !          1826:        }
        !          1827: 
        !          1828:        RETURN_LONG(PCRE_G(error_code));
        !          1829: }
        !          1830: /* }}} */
        !          1831: 
        !          1832: /* {{{ module definition structures */
        !          1833: 
        !          1834: /* {{{ arginfo */
        !          1835: ZEND_BEGIN_ARG_INFO_EX(arginfo_preg_match, 0, 0, 2)
        !          1836:     ZEND_ARG_INFO(0, pattern)
        !          1837:     ZEND_ARG_INFO(0, subject)
        !          1838:     ZEND_ARG_INFO(1, subpatterns) /* array */
        !          1839:     ZEND_ARG_INFO(0, flags)
        !          1840:     ZEND_ARG_INFO(0, offset)
        !          1841: ZEND_END_ARG_INFO()
        !          1842: 
        !          1843: ZEND_BEGIN_ARG_INFO_EX(arginfo_preg_match_all, 0, 0, 3)
        !          1844:     ZEND_ARG_INFO(0, pattern)
        !          1845:     ZEND_ARG_INFO(0, subject)
        !          1846:     ZEND_ARG_INFO(1, subpatterns) /* array */
        !          1847:     ZEND_ARG_INFO(0, flags)
        !          1848:     ZEND_ARG_INFO(0, offset)
        !          1849: ZEND_END_ARG_INFO()
        !          1850: 
        !          1851: ZEND_BEGIN_ARG_INFO_EX(arginfo_preg_replace, 0, 0, 3)
        !          1852:     ZEND_ARG_INFO(0, regex)
        !          1853:     ZEND_ARG_INFO(0, replace)
        !          1854:     ZEND_ARG_INFO(0, subject)
        !          1855:     ZEND_ARG_INFO(0, limit)
        !          1856:     ZEND_ARG_INFO(1, count)
        !          1857: ZEND_END_ARG_INFO()
        !          1858: 
        !          1859: ZEND_BEGIN_ARG_INFO_EX(arginfo_preg_replace_callback, 0, 0, 3)
        !          1860:     ZEND_ARG_INFO(0, regex)
        !          1861:     ZEND_ARG_INFO(0, callback)
        !          1862:     ZEND_ARG_INFO(0, subject)
        !          1863:     ZEND_ARG_INFO(0, limit)
        !          1864:     ZEND_ARG_INFO(1, count)
        !          1865: ZEND_END_ARG_INFO()
        !          1866: 
        !          1867: ZEND_BEGIN_ARG_INFO_EX(arginfo_preg_split, 0, 0, 2)
        !          1868:     ZEND_ARG_INFO(0, pattern)
        !          1869:     ZEND_ARG_INFO(0, subject)
        !          1870:     ZEND_ARG_INFO(0, limit)
        !          1871:     ZEND_ARG_INFO(0, flags) 
        !          1872: ZEND_END_ARG_INFO()
        !          1873: 
        !          1874: ZEND_BEGIN_ARG_INFO_EX(arginfo_preg_quote, 0, 0, 1)
        !          1875:     ZEND_ARG_INFO(0, str)
        !          1876:     ZEND_ARG_INFO(0, delim_char)
        !          1877: ZEND_END_ARG_INFO()
        !          1878: 
        !          1879: ZEND_BEGIN_ARG_INFO_EX(arginfo_preg_grep, 0, 0, 2)
        !          1880:     ZEND_ARG_INFO(0, regex)
        !          1881:     ZEND_ARG_INFO(0, input) /* array */
        !          1882:     ZEND_ARG_INFO(0, flags)
        !          1883: ZEND_END_ARG_INFO()
        !          1884: 
        !          1885: ZEND_BEGIN_ARG_INFO(arginfo_preg_last_error, 0)
        !          1886: ZEND_END_ARG_INFO()
        !          1887: /* }}} */
        !          1888: 
        !          1889: static const zend_function_entry pcre_functions[] = {
        !          1890:        PHP_FE(preg_match,                              arginfo_preg_match)
        !          1891:        PHP_FE(preg_match_all,                  arginfo_preg_match_all)
        !          1892:        PHP_FE(preg_replace,                    arginfo_preg_replace)
        !          1893:        PHP_FE(preg_replace_callback,   arginfo_preg_replace_callback)
        !          1894:        PHP_FE(preg_filter,                             arginfo_preg_replace)
        !          1895:        PHP_FE(preg_split,                              arginfo_preg_split)
        !          1896:        PHP_FE(preg_quote,                              arginfo_preg_quote)
        !          1897:        PHP_FE(preg_grep,                               arginfo_preg_grep)
        !          1898:        PHP_FE(preg_last_error,                 arginfo_preg_last_error)
        !          1899:        PHP_FE_END
        !          1900: };
        !          1901: 
        !          1902: zend_module_entry pcre_module_entry = {
        !          1903:        STANDARD_MODULE_HEADER,
        !          1904:    "pcre",
        !          1905:        pcre_functions,
        !          1906:        PHP_MINIT(pcre),
        !          1907:        PHP_MSHUTDOWN(pcre),
        !          1908:        NULL,
        !          1909:        NULL,
        !          1910:        PHP_MINFO(pcre),
        !          1911:        NO_VERSION_YET,
        !          1912:        PHP_MODULE_GLOBALS(pcre),
        !          1913:        PHP_GINIT(pcre),
        !          1914:        PHP_GSHUTDOWN(pcre),
        !          1915:        NULL,
        !          1916:        STANDARD_MODULE_PROPERTIES_EX
        !          1917: };
        !          1918: 
        !          1919: #ifdef COMPILE_DL_PCRE
        !          1920: ZEND_GET_MODULE(pcre)
        !          1921: #endif
        !          1922: 
        !          1923: /* }}} */
        !          1924: 
        !          1925: #endif /* HAVE_PCRE || HAVE_BUNDLED_PCRE */
        !          1926: 
        !          1927: /*
        !          1928:  * Local variables:
        !          1929:  * tab-width: 4
        !          1930:  * c-basic-offset: 4
        !          1931:  * End:
        !          1932:  * vim600: sw=4 ts=4 fdm=marker
        !          1933:  * vim<600: sw=4 ts=4
        !          1934:  */

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