+ /* Generate open and close pattern for tag with child */
+ if (hasChild) {
+ /* Child array and value */
+ zval *child, *ccval;
+
+ /* Close pattern length */
+ int len2, cpos2;
+
+ /* Close pattern buf */
+ char *buf2;
+
+ /* Retrieve child array */
+ /* XXX: child should exists here and be an array */
+ ZEND_ASSERT(((child = zend_hash_str_find(Z_ARR_P(val), "child", strlen("child"))) != NULL) && Z_TYPE_P(child) == IS_ARRAY);
+
+ /* Init open pattern buffer and length */
+ /* XXX: lagest bbcode tag length seems to be 7, we guess that 10 is enough to contain every tag and pipe */
+ buf = (char *)malloc((len = strlen("/\\[()(?:=([^\\]]*))?\\]/") + zend_array_count(Z_ARR_P(child))*10)*sizeof(char));
+
+ /* Init close pattern buffer and length */
+ /* XXX: lagest bbcode tag length seems to be 7, we guess that 10 is enough to contain every tag and pipe */
+ buf2 = (char *)malloc((len2 = strlen("/\\[\\/()\\]/") + zend_array_count(Z_ARR_P(child))*10)*sizeof(char));
+
+ /* Init cpos and cpos2 */
+ cpos = 4; cpos2 = 6;
+
+ /* Assign open pattern begin */
+ ZEND_ASSERT(memcpy(buf, &"/\\[(", 4) != NULL);
+
+ /* Assign close pattern begin */
+ ZEND_ASSERT(memcpy(buf2, &"/\\[\\/(", 6) != NULL);
+
+ /* Iterate on each val until hash end is reached */
+ ZEND_HASH_FOREACH_VAL(Z_ARR_P(child), ccval) {
+ /* Check that child val is a string */
+ /* XXX: ccval should be an array here */
+ ZEND_ASSERT(Z_TYPE_P(ccval) == IS_STRING);
+
+ /* Init zval node and type */
+ zval *znode, *ztype;
+
+ /* Init zend_long type */
+ zend_long ltype;
+
+ /* Retrieve the node */
+ ZEND_ASSERT((znode = zend_hash_find(tag, Z_STR_P(ccval))) != NULL && Z_TYPE_P(znode) == IS_ARRAY);
+
+ /* Try to retrieve the type */
+ //ZEND_ASSERT((ztype = zend_hash_str_find(Z_ARR_P(znode), "type", strlen("type"))) != NULL && Z_TYPE_P(ztype) == IS_LONG);
+ /* XXX: the type may not yet available here, it is fixed when checking the tag itself later */
+ if ((ztype = zend_hash_str_find(Z_ARR_P(znode), "type", strlen("type"))) == NULL || Z_TYPE_P(ztype) != IS_LONG) {
+ /* Check if empty tag */
+ if (Z_STRLEN_P(ccval) == 0) {
+ /* Set as root */
+ ltype = BBCODE_TYPE_ROOT;
+ } else if (strcmp("img", Z_STRVAL_P(ccval)) == 0) {
+ /* Set as single */
+ ltype = BBCODE_TYPE_SINGLE;
+ } else {
+ /* Set as multi */
+ ltype = BBCODE_TYPE_MULTI;
+ }
+ /* Type zval available and usable */
+ } else {
+ /* Set type from zval value */
+ ltype = Z_LVAL_P(ztype);
+ }
+
+ /* Check if not first string to add */
+ if (cpos > 4) {
+ /* Set pipe before next tag */
+ *(buf+cpos) = '|';
+ /* Move position */
+ cpos++;
+ /* Check if not single type */
+ if (ltype != BBCODE_TYPE_SINGLE) {
+ /* Set pipe before next tag */
+ *(buf2+cpos2) = '|';
+ /* Move position */
+ cpos2++;
+ }
+ }
+
+ /* Check if not single type */
+ if (ltype != BBCODE_TYPE_SINGLE) {
+ /* Grow buf if necessary */
+ if (cpos + Z_STRLEN_P(ccval) + 18 > len) {
+ /* We resize buf to current length(cpos), the string size plus eighteen char for trailing pattern */
+ buf = (char *)realloc(buf, (len = cpos + Z_STRLEN_P(ccval) + 18)*sizeof(char));
+ }
+
+ /* Copy string in buf */
+ ZEND_ASSERT(memcpy(buf+cpos, Z_STRVAL_P(ccval), Z_STRLEN_P(ccval)) != NULL);
+
+ /* Move cpos */
+ cpos += Z_STRLEN_P(ccval);
+
+ /* Grow buf2 if necessary */
+ if (cpos2 + Z_STRLEN_P(ccval) + 4 > len2) {
+ /* We resize buf2 to current length(cpos2), the string size plus four char for trailing pattern */
+ buf2 = (char *)realloc(buf2, (len2 = cpos2 + Z_STRLEN_P(ccval) + 4)*sizeof(char));
+ }
+
+ /* Copy string in buf2 */
+ ZEND_ASSERT(memcpy(buf2+cpos2, Z_STRVAL_P(ccval), Z_STRLEN_P(ccval)) != NULL);
+
+ /* Move cpos2 */
+ cpos2 += Z_STRLEN_P(ccval);
+ /* Single type case */
+ } else {
+ /* Grow buf if necessary */
+ if (cpos + Z_STRLEN_P(ccval) + strlen("(?:\\/)?") + 18 > len) {
+ /* We resize buf to current length(cpos), the string size plus three plus eighteen char for trailing pattern */
+ buf = (char *)realloc(buf, (len = cpos + Z_STRLEN_P(ccval) + strlen("(?:\\/)?") + 18)*sizeof(char));
+ }
+
+ /* Copy string in buf */
+ ZEND_ASSERT(memcpy(buf+cpos, Z_STRVAL_P(ccval), Z_STRLEN_P(ccval)) != NULL);
+
+ /* Append pattern in buf */
+ ZEND_ASSERT(memcpy(buf+cpos+Z_STRLEN_P(ccval), &"(?:\\/)?", strlen("(?:\\/)?")) != NULL);
+
+ /* Move cpos */
+ cpos += Z_STRLEN_P(ccval) + strlen("(?:\\/)?");
+ }
+ } ZEND_HASH_FOREACH_END();
+
+ /* Assign open pattern begin */
+ ZEND_ASSERT(memcpy(buf+cpos, &")(?:=([^\\]]*))?\\]/", 18) != NULL);
+
+ /* Assign open pattern begin */
+ ZEND_ASSERT(memcpy(buf2+cpos2, &")\\]/", 4) != NULL);
+
+ /* Open key string */
+ zend_string *okey = zend_string_init("opattern", strlen("opattern"), 0);
+
+ /* Close key string */
+ zend_string *ckey = zend_string_init("cpattern", strlen("cpattern"), 0);
+
+ /* Open and close value */
+ zval oval, cval;
+
+ /* Set open value */
+ ZVAL_STRINGL(&oval, buf, cpos + 18);
+
+ /* Set close value */
+ ZVAL_STRINGL(&cval, buf2, cpos2 + 4);
+
+ /* Free buffers */
+ free(buf); free(buf2);
+
+ /* Add open key */
+ ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), okey, &oval) != NULL);
+
+ /* Add close key */
+ ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), ckey, &cval) != NULL);
+
+ /* Free open key */
+ zend_string_release(okey);
+
+ /* Free close key */
+ zend_string_release(ckey);
+ }