X-Git-Url: https://git.rapsys.eu/bbcode/blobdiff_plain/bf6b128379b87fb3e0e38faa4f3ce4eef74563bf..95abbe987216e9edabfde3709d0e1a4e99179815:/bbcode.c?ds=sidebyside diff --git a/bbcode.c b/bbcode.c index 7916d1c..9271a96 100644 --- a/bbcode.c +++ b/bbcode.c @@ -9,7 +9,11 @@ #include "Zend/zend_exceptions.h" #include "php_bbcode.h" -#include +/* XXX: only required for php_debug_zval_dump */ +#include "ext/standard/php_var.h" + +/* XXX: required for pcre_cache_entry */ +#include "ext/pcre/php_pcre.h" /* If you declare any globals in php_bbcode.h uncomment this: ZEND_DECLARE_MODULE_GLOBALS(bbcode) @@ -18,7 +22,8 @@ ZEND_DECLARE_MODULE_GLOBALS(bbcode) typedef struct _bbcode_object { HashTable *tag; HashTable *smiley; - zval flag; + zend_string *root; + zend_long flag; zend_object std; } bbcode_object; @@ -44,7 +49,9 @@ zend_class_entry *bbcode_exception_ce; GEN(TYPE_ROOT) /*BBCODE_TYPE_ROOT*/\ GEN(TYPE_MULTI) /*BBCODE_TYPE_ARG|BBCODE_TYPE_OPTARG|BBCODE_TYPE_NOARG*/\ GEN(TYPE_SINGLE) /*BBCODE_TYPE_SINGLE*/\ - \ + +/* TODO: add again each feature when implemented (before beta) */ +#if 0 /* Quotes */ \ GEN(QUOTE_DOUBLE) /*BBCODE_ARG_DOUBLE_QUOTE*/\ GEN(QUOTE_SIMPLE) /*BBCODE_ARG_SINGLE_QUOTE*/\ @@ -65,6 +72,7 @@ zend_class_entry *bbcode_exception_ce; GEN(REMOVE_EMPTY) /*BBCODE_FLAGS_REMOVE_IF_EMPTY*/\ GEN(REMOVE_CONTENT) /*BBCODE_FLAGS_CDATA_NOT_ALLOWED*/\ GEN(REMOVE_REOPEN) /*BBCODE_FLAGS_DENY_REOPEN_CHILD*/ +#endif /* Generate enum version */ @@ -104,12 +112,18 @@ enum { }; /* Set default flag - * TODO: Set it in an ini ? */ + * TODO: Implement it in an ini file ? (later maybe) */ +#define BBCODE_DEFAULT_FLAG 0 +#if 0 #define BBCODE_DEFAULT_FLAG BBCODE_QUOTE_DOUBLE|BBCODE_QUOTE_SIMPLE|BBCODE_QUOTE_ESCAPE|BBCODE_CORRECT_AUTO|BBCODE_CORRECT_REOPEN|BBCODE_SMILEY_ON|BBCODE_CLOSE_AUTO|BBCODE_REMOVE_EMPTY +#endif + +/* Set debug flag */ +#define BBCODE_DEBUG 0 /* {{{ Function prototypes (bbcode_destroy|bbcode_free|bbcode_clone|bbcode_create) */ -static void bbcode_destroy(bbcode_object *obj TSRMLS_DC); -static void bbcode_free(bbcode_object *obj TSRMLS_DC); +static void bbcode_destroy(zend_object *obj TSRMLS_DC); +static void bbcode_free(zend_object *obj TSRMLS_DC); static zend_object *bbcode_clone(zval *obj TSRMLS_DC); static zend_object *bbcode_create(zend_class_entry *ce TSRMLS_DC); /* }}} */ @@ -126,80 +140,137 @@ static inline bbcode_object* bbcode_fetch(zend_object *obj TSRMLS_DC) { #define Z_BBCODE_P(zv) bbcode_fetch(Z_OBJ_P((zv))) /* }}} */ -/* {{{ static void bbcode_destroy(bbcode_object *obj TSRMLS_DC) { +/* {{{ static void bbcode_destroy(zend_object *obj TSRMLS_DC) { * BBCode object destroy call */ -static void bbcode_destroy(bbcode_object *obj TSRMLS_DC) { - //zend_objects_destroy_object(&obj->std); - zend_objects_destroy_object((zend_object *)obj); - - //zend_object_std_dtor(&obj->std TSRMLS_CC); - - /*if (obj->flag) { - efree(obj->flag); - }*/ - - //efree(obj); +static void bbcode_destroy(zend_object *obj TSRMLS_DC) { + /* Trigger user land destructor on obj */ + zend_objects_destroy_object(obj); } /* }}} */ -/* {{{ static void bbcode_free(bbcode_object *obj TSRMLS_DC) { +/* {{{ static void bbcode_free(zend_object *obj TSRMLS_DC) { * BBCode object free call */ -static void bbcode_free(bbcode_object *obj TSRMLS_DC) { +static void bbcode_free(zend_object *obj TSRMLS_DC) { + /* Retrieve bbcode object from zend object */ + bbcode_object *bbobj = bbcode_fetch(obj); + + /* Check if root is present */ + if (bbobj->root) { + /* Release the root string */ + zend_string_release(bbobj->root); + } - /*if (obj->flag) { - efree(obj->flag); - }*/ + /* Check if tag is present */ + if (bbobj->tag) { + /* Release the tag hash */ + zend_array_destroy(bbobj->tag); + } + + /* Check if smiley is present */ + if (bbobj->smiley) { + /* Release the smiley hash */ + zend_array_destroy(bbobj->smiley); + } - //efree(obj); - zend_object_std_dtor((zend_object *)obj); + /* Trigger zend object std destructor on obj */ + zend_object_std_dtor(obj); + + /* Free the bbobj */ + efree(bbobj); } /* }}} */ /* {{{ static zend_object *bbcode_clone(zval *obj TSRMLS_DC) { * BBCode object clone call */ static zend_object *bbcode_clone(zval *obj TSRMLS_DC) { + /* Fetch pointer to old bbcode object */ + bbcode_object *oldbbobj = Z_BBCODE_P(obj); - /* Fetch pointer on old obj */ - zend_object *oldobj = Z_OBJ_P(obj); - /* Create pointer on nex obj */ - zend_object *newobj = bbcode_create(oldobj->ce); + /* Create pointer on new obj */ + //bbcode_object *newbbobj = bbcode_fetch(bbcode_create(oldbbobj->std.ce)); + bbcode_object *newbbobj = bbcode_fetch(bbcode_create(Z_OBJ_P(obj)->ce)); /* Call members cloning function */ - zend_objects_clone_members(newobj, oldobj); + zend_objects_clone_members(&newbbobj->std, &oldbbobj->std); + + /* Duplicate flag value */ + newbbobj->flag = oldbbobj->flag; + + /* Set root as null */ + newbbobj->root = NULL; + + /* Check if root is available */ + if (oldbbobj->root) { + /* Duplicate root zend string */ + newbbobj->root = zend_string_copy(oldbbobj->root); + } + + /* Set tag as null */ + newbbobj->tag = NULL; - /* Retrieve from zend object old bbcode */ - bbcode_object *bboldobj = Z_BBCODE_P(obj); + /* Check if tag is available */ + if (oldbbobj->tag) { + /* Duplicate tag zend array */ + newbbobj->tag = zend_array_dup(oldbbobj->tag); + } - /* Retrieve from zend object new bbcode */ - bbcode_object *bbnewobj = bbcode_fetch(newobj); + /* Set smiley as null */ + newbbobj->smiley = NULL; - /* Restore object member values */ - //TODO: deal with the other one (tag|smiley) - Z_LVAL(bbnewobj->flag) = Z_LVAL(bboldobj->flag); + /* Check if smiley is available */ + if (oldbbobj->smiley) { + /* Duplicate smiley zend array */ + newbbobj->smiley = zend_array_dup(oldbbobj->smiley); + } /* Return the new object */ - return newobj; + return &newbbobj->std; } /* }}} */ /* {{{ static zend_object *bbcode_create(zend_class_entry *ce TSRMLS_DC) { * BBCode object create call */ static zend_object *bbcode_create(zend_class_entry *ce TSRMLS_DC) { - bbcode_object *obj = (bbcode_object *)ecalloc(1, sizeof(bbcode_object) + zend_object_properties_size(ce));//emalloc(sizeof(bbcode_object)); + /* Allocate object */ + bbcode_object *obj = (bbcode_object *)ecalloc(1, sizeof(bbcode_object) + zend_object_properties_size(ce)); + /* Init std member */ zend_object_std_init(&obj->std, ce TSRMLS_CC); + + /* Set object properties */ object_properties_init(&obj->std, ce TSRMLS_CC); + /* Duplicate standard object handler */ memcpy(&bbcode_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + + /* Set object offset */ bbcode_handlers.offset = XtOffsetOf(bbcode_object, std); + + /* Set destructor function */ bbcode_handlers.dtor_obj = (zend_object_dtor_obj_t) bbcode_destroy; + + /* Set free function */ bbcode_handlers.free_obj = (zend_object_free_obj_t) bbcode_free; + + /* Set clone function */ bbcode_handlers.clone_obj = (zend_object_clone_obj_t) bbcode_clone; + /* Assign custom handler */ obj->std.handlers = &bbcode_handlers; - Z_LVAL(obj->flag) = 0; + /* Init tag */ + obj->tag = NULL; + + /* Init smiley */ + obj->smiley = NULL; + + /* Init root */ + obj->root = NULL; + + /* Init flag */ + obj->flag = BBCODE_DEFAULT_FLAG; + /* Return the std member address */ return &obj->std; } /* }}} */ @@ -209,7 +280,7 @@ static zend_object *bbcode_create(zend_class_entry *ce TSRMLS_DC) { ZEND_BEGIN_ARG_INFO_EX(bbcode_construct_arginfo, 0, 0, 1) // ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) ZEND_ARG_ARRAY_INFO(0, tag, 0) - ZEND_ARG_ARRAY_INFO(0, smiley, 0) + ZEND_ARG_ARRAY_INFO(0, smiley, 1) // ZEND_ARG_INFO(pass_by_ref, name) ZEND_ARG_INFO(0, flag) ZEND_END_ARG_INFO() @@ -223,13 +294,157 @@ ZEND_BEGIN_ARG_INFO_EX(bbcode_parse_arginfo, 0, 0, 1) ZEND_END_ARG_INFO() /* }}} */ -/* { { { static zend_bool *bbcode_check_tag(HashTable *tag TSRMLS_DC) { +/* {{{ static HashTable *bbcode_gen_child(HashTable *ret, HashTable *tag, zend_string *cur TSRMLS_DC) { */ +static HashTable *bbcode_gen_child(HashTable *ret, HashTable *tag, zend_string *cur TSRMLS_DC) { + /* Key string */ + zend_string *key; + /* Value */ + zval *val; + + /* Iterate on each key val until hash end is reached */ + ZEND_HASH_FOREACH_STR_KEY_VAL(tag, key, val) { + /* Check if key is not a string, value is null or not an array */ + if (key == NULL || val == NULL || Z_TYPE_P(val) != IS_ARRAY) { + /* Return failure, useless to generate a hash when we will trigger an error later */ + return NULL; + } + + /* Init type val */ + zval *type; + + /* Check that we have no type or that it's not root type */ + /* XXX: it's not possible to have in child the root tag */ + if ((type = zend_hash_str_find(Z_ARR_P(val), "type", strlen("type"))) == NULL || Z_LVAL_P(type) != BBCODE_TYPE_ROOT) { + /* Check that we are not on cur key */ + /* XXX: not really required, but it's stupid to allow same tag in child */ + //if (strcmp(ZSTR_VAL(cur), ZSTR_VAL(key)) != 0) { + /* Init parent val */ + zval *parent; + + /* Init toInsert bool */ + unsigned char toInsert = 0; + + /* Check if we find parent key */ + if ((parent = zend_hash_str_find(Z_ARR_P(val), "parent", strlen("parent"))) != NULL) { + /* Parent value */ + zval *pval; + + /* Iterate on each val until hash end is reached */ + ZEND_HASH_FOREACH_VAL(Z_ARR_P(parent), pval) { + /* Check that parent val is a string */ + if (Z_TYPE_P(pval) != IS_STRING) { + return NULL; + } + + /* Check that tkey is a string and is identical */ + if (strcmp(Z_STRVAL_P(pval), ZSTR_VAL(cur)) == 0) { + toInsert = 1; + } + } ZEND_HASH_FOREACH_END(); + /* No parent key */ + } else { + toInsert = 1; + } + + /* Add the key */ + if (toInsert) { + /* Init child val */ + zval cval; + + /* Set child val */ + ZVAL_STR(&cval, key); + + /* Insert the child val in return array */ + ZEND_ASSERT(zend_hash_next_index_insert_new(ret, &cval) != NULL); + } + //} + } + } ZEND_HASH_FOREACH_END(); + + /* Return value */ + return ret; +} +/* }}} */ + +/* {{{ static HashTable *bbcode_gen_parent(HashTable *ret, HashTable *tag, zend_string *cur TSRMLS_DC) { */ +static HashTable *bbcode_gen_parent(HashTable *ret, HashTable *tag, zend_string *cur TSRMLS_DC) { + /* Key string */ + zend_string *key; + /* Value */ + zval *val; + + /* Iterate on each key val until hash end is reached */ + ZEND_HASH_FOREACH_STR_KEY_VAL(tag, key, val) { + /* Check if key is not a string, value is null or not an array */ + if (key == NULL || val == NULL || Z_TYPE_P(val) != IS_ARRAY) { + /* Return failure, useless to generate a hash when we will trigger an error later */ + return NULL; + } + + /* Init type val */ + zval *type; + + /* Check that we have no type or that it's not single type */ + /* XXX: it's not possible to have in child the root tag */ + if ((type = zend_hash_str_find(Z_ARR_P(val), "type", strlen("type"))) == NULL || Z_LVAL_P(type) != BBCODE_TYPE_SINGLE) { + /* Check that we are not on cur key */ + /* XXX: not really required, but it's stupid to allow same tag in child */ + //if (strcmp(ZSTR_VAL(cur), ZSTR_VAL(key)) != 0) { + /* Init child val */ + zval *child; + + /* Init toInsert bool */ + unsigned char toInsert = 0; + + /* Check if we find child key */ + if ((child = zend_hash_str_find(Z_ARR_P(val), "child", strlen("child"))) != NULL) { + /* Child value */ + zval *cval; + + /* Iterate on each val until hash end is reached */ + ZEND_HASH_FOREACH_VAL(Z_ARR_P(child), cval) { + /* Check that child val is a string */ + if (Z_TYPE_P(cval) != IS_STRING) { + return NULL; + } + + /* Check that tkey is a string and is identical */ + if (strcmp(Z_STRVAL_P(cval), ZSTR_VAL(cur)) == 0) { + toInsert = 1; + } + } ZEND_HASH_FOREACH_END(); + /* No child key */ + } else { + toInsert = 1; + } + + /* Add the key */ + if (toInsert) { + /* Init parent val */ + zval pval; + + /* Set parent val */ + ZVAL_STR(&pval, key); + + /* Insert the parent val in return array */ + ZEND_ASSERT(zend_hash_next_index_insert_new(ret, &pval) != NULL); + } + //} + } + } ZEND_HASH_FOREACH_END(); + + /* Return value */ + return ret; +} +/* }}} */ + +/* {{{ static zend_bool *bbcode_check_tag(HashTable *tag TSRMLS_DC) { * Check that tag hash is valid */ -static zend_bool bbcode_check_tag(HashTable *tag TSRMLS_DC) { +static zend_string *bbcode_check_tag(HashTable *tag TSRMLS_DC) { /* Key numeric value */ zend_long idx; - /* Key string */ - zend_string *key; + /* Key and root string */ + zend_string *key, *root = NULL; /* Value */ zval *val; @@ -241,30 +456,36 @@ static zend_bool bbcode_check_tag(HashTable *tag TSRMLS_DC) { /* Iterate on each key val until hash end is reached */ ZEND_HASH_FOREACH_KEY_VAL(tag, idx, key, val) { - /* Check if key is a string */ - if (!key) { + /* Check if key is not a string */ + if (key == NULL) { /* Display error about long key */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key %ld[%d] from argument tag is not an expected string", idx, pos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Key %ld[%d] from argument tag is not an expected string", idx, pos); /* Return failure */ - return 0; + return NULL; + /* Check that key do not contains a special character */ + } else if (strchr(ZSTR_VAL(key), '=') != NULL || strchr(ZSTR_VAL(key), '[') != NULL || strchr(ZSTR_VAL(key), ']') != NULL || strchr(ZSTR_VAL(key), '(') != NULL || strchr(ZSTR_VAL(key), ')') != NULL || strchr(ZSTR_VAL(key), '/') != NULL || strchr(ZSTR_VAL(key), '\\') != NULL) { + /* Display error about key containing a special char */ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Key %s[%d] from argument tag contains a special forbidden character: =[]()/\\", ZSTR_VAL(key), pos); + /* Return failure */ + return NULL; } /* Check that value exists */ if (val == NULL) { /* Display error about long key */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Value for key %s[%d] from argument tag is NULL instead of expected array", ZSTR_VAL(key), pos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Value for key %s[%d] from argument tag is NULL instead of expected array", ZSTR_VAL(key), pos); /* Return failure */ - return 0; + return NULL; /* Check that value is an array */ } else if (Z_TYPE_P(val) != IS_ARRAY) { /* Display error about long key */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Value for key %s[%d] from argument tag is %s[%d] instead of expected array", ZSTR_VAL(key), pos, BBCODE_GET_TYPE(Z_TYPE_P(val)), Z_TYPE_P(val)); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Value for key %s[%d] from argument tag is %s[%d] instead of expected array", ZSTR_VAL(key), pos, BBCODE_GET_TYPE(Z_TYPE_P(val)), Z_TYPE_P(val)); /* Return failure */ - return 0; + return NULL; } /* Store child tag array */ - HashTable *ctag = Z_ARRVAL_P(val); + HashTable *ctag = Z_ARR_P(val); /* Child key numeric value */ zend_long cidx; /* Child key string */ @@ -272,28 +493,31 @@ static zend_bool bbcode_check_tag(HashTable *tag TSRMLS_DC) { /* Child value */ zval *cval; + /* Buffer */ + char *buf; + /* Child key position */ - int cpos = 0, type; + int cpos = 0, type, len; /* Detect if entry has a type, parent, child, open, close, default or arg key to display error if mandatory field is missing */ - bool hasType = false, hasParent = false, hasChild = false, hasOpen = false, hasClose = false, hasDefault = false, hasArg = false; + unsigned char hasType = 0, hasParent = 0, hasChild = 0, hasOpen = 0, hasClose = 0, hasDefault = 0, hasArg = 0; /* Iterate on each ckey cval until hash end is reached */ ZEND_HASH_FOREACH_KEY_VAL(ctag, cidx, ckey, cval) { /* Check if ckey is a string */ if (!ckey) { /* Display error about long ckey */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key %s[%d]/%ld[%d] from argument tag is not an expected string", ZSTR_VAL(key), pos, cidx, cpos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Key %s[%d]/%ld[%d] from argument tag is not an expected string", ZSTR_VAL(key), pos, cidx, cpos); /* Return failure */ - return 0; + return NULL; } /* Check if ckey is empty */ if (ZSTR_LEN(ckey) == 0) { /* Display error about long key */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key %s[%d]/%s[%d] from argument tag is empty not one of expected type|parent|child|open|close|default string", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Key %s[%d]/%s[%d] from argument tag is empty not one of expected type|parent|child|open|close|default string", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); /* Return failure */ - return 0; + return NULL; /* Check if current ckey is type */ } else if (strcmp("type", ZSTR_VAL(ckey)) == 0) { /* Extract type */ @@ -306,9 +530,9 @@ static zend_bool bbcode_check_tag(HashTable *tag TSRMLS_DC) { type != BBCODE_TYPE_SINGLE ) { /* Display error about unexpected type value */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Value '%d' for key %s[%d]/%s[%d] from argument tag is not one of expected BBCODE::TYPE_(ROOT|MULTI|SINGLE) constant", type, ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Value '%d' for key %s[%d]/%s[%d] from argument tag is not one of expected BBCODE::TYPE_(ROOT|MULTI|SINGLE) constant", type, ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); /* Return failure */ - return 0; + return NULL; /* Check if root */ } else if (type == BBCODE_TYPE_ROOT) { /* Grow hasRoot */ @@ -316,21 +540,27 @@ static zend_bool bbcode_check_tag(HashTable *tag TSRMLS_DC) { } /* Set hasType */ - hasType = true; + hasType = 1; /* Check if current key is parent */ } else if (strcmp("parent", ZSTR_VAL(ckey)) == 0) { /* Check that value exists */ if (cval == NULL) { /* Display error about long key */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Value for key %s[%d]/%s[%d] from argument tag is NULL instead of expected array", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Value for key %s[%d]/%s[%d] from argument tag is NULL instead of expected array", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); /* Return failure */ - return 0; + return NULL; /* Check that value is an array */ } else if (Z_TYPE_P(cval) != IS_ARRAY) { /* Display error about long key */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Value for key %s[%d]/%s[%d] from argument tag is %s[%d] instead of expected array", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos, BBCODE_GET_TYPE(Z_TYPE_P(cval)), Z_TYPE_P(cval)); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Value for key %s[%d]/%s[%d] from argument tag is %s[%d] instead of expected array", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos, BBCODE_GET_TYPE(Z_TYPE_P(cval)), Z_TYPE_P(cval)); + /* Return failure */ + return NULL; + /* Check that value is an non empty array */ + } else if (zend_array_count(Z_ARR_P(cval)) == 0) { + /* Display error about empty parent array */ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Value for key %s[%d]/%s[%d] from argument tag is an empty array instead of expected filled array", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); /* Return failure */ - return 0; + return NULL; } /* Parent value */ @@ -340,36 +570,37 @@ static zend_bool bbcode_check_tag(HashTable *tag TSRMLS_DC) { int ppos = 0; /* Iterate on each val until hash end is reached */ - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(cval), pval) { + ZEND_HASH_FOREACH_VAL(Z_ARR_P(cval), pval) { /* Check that parent val is a string */ + /* XXX: pval == NULL case is catched here too */ if (Z_TYPE_P(pval) != IS_STRING) { /* Display error about not string */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Parent value %s[%d] for key %s[%d]/%s[%d] is not an expected string", Z_STRVAL_P(pval), ppos, ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Value for key %s[%d]/%s[%d]/[%d] from argument tag is %s[%d] instead of expected string", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos, ppos, BBCODE_GET_TYPE(Z_TYPE_P(pval)), Z_TYPE_P(pval)); /* Return failure */ - return 0; + return NULL; } /* Tag key */ zend_string *tkey; /* Reset found flag */ - bool found = false; + unsigned char found = 0; /* Iterate on each key until hash end is reached */ ZEND_HASH_FOREACH_STR_KEY(tag, tkey) { /* Check that tkey is a string and is identical */ if (tkey != NULL && strcmp(Z_STRVAL_P(pval), ZSTR_VAL(tkey)) == 0) { /* We found parent value in tag keys*/ - found = true; + found = 1; } } ZEND_HASH_FOREACH_END(); /* Check if we found the key */ if (!found) { /* Display error about long key */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Parent value %s for key %s[%d]/%s[%d] is not present in tag keys", Z_STRVAL_P(pval), ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Value for key %s[%d]/%s[%d]/%s[%d] from argument tag is not present in tag keys", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos, Z_STRVAL_P(pval), ppos); /* Return failure */ - return 0; + return NULL; } /* Grow ppos */ @@ -377,21 +608,21 @@ static zend_bool bbcode_check_tag(HashTable *tag TSRMLS_DC) { } ZEND_HASH_FOREACH_END(); /* Set hasParent */ - hasParent = true; + hasParent = 1; /* Check if current key is child */ } else if (strcmp("child", ZSTR_VAL(ckey)) == 0) { /* Check that value exists */ if (cval == NULL) { /* Display error about long key */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Value for key %s[%d]/%s[%d] from argument tag is NULL instead of expected array", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Value for key %s[%d]/%s[%d] from argument tag is NULL instead of expected array", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); /* Return failure */ - return 0; + return NULL; /* Check that value is an array */ } else if (Z_TYPE_P(cval) != IS_ARRAY) { /* Display error about long key */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Value for key %s[%d]/%s[%d] from argument tag is %s[%d] instead of expected array", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos, BBCODE_GET_TYPE(Z_TYPE_P(cval)), Z_TYPE_P(cval)); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Value for key %s[%d]/%s[%d] from argument tag is %s[%d] instead of expected array", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos, BBCODE_GET_TYPE(Z_TYPE_P(cval)), Z_TYPE_P(cval)); /* Return failure */ - return 0; + return NULL; } /* Child value */ @@ -401,36 +632,37 @@ static zend_bool bbcode_check_tag(HashTable *tag TSRMLS_DC) { int ccpos = 0; /* Iterate on each val until hash end is reached */ - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(cval), ccval) { + ZEND_HASH_FOREACH_VAL(Z_ARR_P(cval), ccval) { /* Check that child val is a string */ + /* XXX: ccval == NULL case is catched here too */ if (Z_TYPE_P(ccval) != IS_STRING) { /* Display error about not string */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Child value %s[%d] for key %s[%d]/%s[%d] is not an expected string", Z_STRVAL_P(ccval), ccpos, ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Value for key %s[%d]/%s[%d]/[%d] from argument tag is %s[%d] instead of expected string", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos, ccpos, BBCODE_GET_TYPE(Z_TYPE_P(ccval)), Z_TYPE_P(ccval)); /* Return failure */ - return 0; + return NULL; } /* Tag key */ zend_string *tkey; /* Reset found flag */ - bool found = false; + unsigned char found = 0; /* Iterate on each key until hash end is reached */ ZEND_HASH_FOREACH_STR_KEY(tag, tkey) { /* Check that tkey is a string and is identical */ if (tkey != NULL && strcmp(Z_STRVAL_P(ccval), ZSTR_VAL(tkey)) == 0) { /* We found child value in tag keys*/ - found = true; + found = 1; } } ZEND_HASH_FOREACH_END(); /* Check if we found the key */ if (!found) { /* Display error about long key */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Child value %s for key %s[%d]/%s[%d] is not present in tag keys", Z_STRVAL_P(ccval), ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Child value %s for key %s[%d]/%s[%d] is not present in tag keys", Z_STRVAL_P(ccval), ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); /* Return failure */ - return 0; + return NULL; } /* Grow ccpos */ @@ -438,54 +670,59 @@ static zend_bool bbcode_check_tag(HashTable *tag TSRMLS_DC) { } ZEND_HASH_FOREACH_END(); /* Set hasChild */ - hasChild = true; + hasChild = 1; /* Check if current key is open */ } else if (strcmp("open", ZSTR_VAL(ckey)) == 0) { /* Set hasOpen */ - hasOpen = true; + hasOpen = 1; /* Check if current key is close */ } else if (strcmp("close", ZSTR_VAL(ckey)) == 0) { /* Set hasClose */ - hasClose = true; + hasClose = 1; /* Check if current key is default */ } else if (strcmp("default", ZSTR_VAL(ckey)) == 0) { /* Set hasDefault */ - hasDefault = true; + hasDefault = 1; /* Check if current key is arg */ } else if (strcmp("arg", ZSTR_VAL(ckey)) == 0) { /* Set hasArg */ - hasArg = true; + hasArg = 1; /* Check if current key is unknown */ } else { /* Display error about long key */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key %s[%d]/%s[%d] from argument tag is not one of expected type|parent|child|open|close|default string", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Key %s[%d]/%s[%d] from argument tag is not one of expected type|parent|child|open|close|default string", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); /* Return failure */ - return 0; + return NULL; } /* Grow cpos */ cpos++; } ZEND_HASH_FOREACH_END(); - /* Check if val array is initialized */ - /*XXX: this step is required to workaround zend engine half initialized hashtable when val array is empty that trigger a segfault*/ - if ((GC_FLAGS(Z_ARR_P(val)) & HASH_FLAG_INITIALIZED) != HASH_FLAG_INITIALIZED) { + /* Check if val array is immutable */ + /* XXX: this strange case happen with empty val array, the zend engine don't initialize a usable hashtable, to avoid triggering a segfault later when using the array, we fix it here */ + if ((Z_GC_FLAGS_P(val) & GC_IMMUTABLE) == GC_IMMUTABLE) { /* Allocate hash table */ - /* XXX: this is required to avoid segfault in zend_hash_add when the array is empty */ + /* XXX: this is required to avoid segfault in zend_hash_add when the array is immutable */ ALLOC_HASHTABLE(Z_ARR_P(val)); + /* Init hash table */ zend_hash_init(Z_ARR_P(val), 0, NULL, ZVAL_PTR_DTOR, 0); - zend_hash_copy(Z_ARR_P(val), ctag, (copy_ctor_func_t) zval_add_ref); + + /* Copy old values in new array */ + //XXX: disabled for now, array shoudln't need to be copied, immutable flag seems only present on empty arrays + //zend_hash_copy(Z_ARR_P(val), ctag, (copy_ctor_func_t) zval_add_ref); } /* Check if entry has no type */ + /* TODO: make depend this code on a parse flag ? */ if (!hasType) { - /* Type zval */ - zval tval; - - /* Type key string */ + /* Type key */ zend_string *tkey = zend_string_init("type", strlen("type"), 0); + /* Type value */ + zval tval; + /* Check if key is '' */ if (ZSTR_LEN(key) == 0) { /* Set tval value to root */ @@ -510,25 +747,148 @@ static zend_bool bbcode_check_tag(HashTable *tag TSRMLS_DC) { zend_string_release(tkey); /* Set hasType */ - hasType = true; + hasType = 1; } /* Check for root type */ if (type == BBCODE_TYPE_ROOT) { + /* Set root */ + root = key; + /* Check if has parent */ if (hasParent) { /* Display error about parent entry */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key %s[%d] from argument tag of type BBCODE::TYPE_ROOT cannot have a parent entry", ZSTR_VAL(key), pos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Key %s[%d] from argument tag of type BBCODE::TYPE_ROOT cannot have a parent entry", ZSTR_VAL(key), pos); /* Return failure */ - return 0; + return NULL; + } + + /* Check if has open entry */ + if (!hasOpen) { + /* Key string */ + zend_string *mkey = zend_string_init("open", strlen("open"), 0); + + /* Value */ + zval mval; + + /* Check if tag is empty */ + if (ZSTR_LEN(key) == 0) { + /* Init new value */ + ZVAL_EMPTY_STRING(&mval); + /* Non empty tag */ + } else { + /* Init buffer and length */ + buf = (char *)malloc((len = strlen("<>") + ZSTR_LEN(key) + (hasArg?strlen("%s"):0))*sizeof(char)); + + /* Generate string in */ + ZEND_ASSERT(sprintf(buf, "<%s%s>", ZSTR_VAL(key), hasArg?"%s":"") == len); + + /* Init new value */ + ZVAL_STRINGL(&mval, buf, len); + + /* Free buffer */ + free(buf); + } + + /* Add new key */ + ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), mkey, &mval) != NULL); + + /* Free key */ + zend_string_release(mkey); + + /* Set hasOpen */ + hasOpen = 1; + } + + /* Check if has close entry */ + if (!hasClose) { + /* Key string */ + zend_string *mkey = zend_string_init("close", strlen("close"), 0); + + /* Value */ + zval mval; + + /* Check if tag is empty */ + if (ZSTR_LEN(key) == 0) { + /* Init new value */ + ZVAL_EMPTY_STRING(&mval); + /* Non empty tag */ + } else { + /* Init buffer and length */ + buf = (char *)malloc((len = strlen("") + ZSTR_LEN(key))*sizeof(char)); + + /* Generate string in */ + ZEND_ASSERT(sprintf(buf, "", ZSTR_VAL(key)) == len); + + /* Init new value */ + ZVAL_STRINGL(&mval, buf, len); + + /* Free buffer */ + free(buf); + } + + /* Add new key */ + ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), mkey, &mval) != NULL); + + /* Free key */ + zend_string_release(mkey); + + /* Set hasClose */ + hasClose = 1; + } + + /* Check if has not default */ + if (!hasDefault) { + /* Key string */ + zend_string *mkey = zend_string_init("default", strlen("default"), 0); + + /* Value */ + zval mval; + + /* Set value */ + ZVAL_STRING(&mval, "%s"); + + /* Add new key */ + ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), mkey, &mval) != NULL); + + /* Free key */ + zend_string_release(mkey); + + /* Set hasDefault */ + hasDefault = 1; } - /* TODO: Compute child here */ - //TODO: set child if not provided for ROOT (has to loop on each possible type and check if a parent array is present with this tag inside, if none set to all possible tag except '' one) - //TODO: display error if type=root and child was not provided| XXX: we can compute that shit here for lazy morons + /* Compute child here */ if (!hasChild) { - //TODO compute child by looping on each non root element and element with no parent or with root in parent - //XXX: it seems that a better approach would be to save root element en push each element one by one ? + /* Child value */ + zval cval; + + /* Init new child val array */ + ZVAL_NEW_ARR(&cval); + + /* Init hash table */ + zend_hash_init(Z_ARR(cval), 0, NULL, ZVAL_PTR_DTOR, 0); + + /* Compute and set child here */ + /* XXX: we don't check or trigger error inside this function as these tests are done in current loop */ + if (bbcode_gen_child(Z_ARR(cval), tag, key) != NULL) { + /* Child key string */ + zend_string *ckey = zend_string_init("child", strlen("child"), 0); + + /* Add new child key */ + ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), ckey, &cval) != NULL); + + /* Free type key */ + zend_string_release(ckey); + + /* Set hasChild */ + hasChild = 1; + /* Child generation reached an error case */ + /* XXX: it will trigger error later */ + } else { + /* Release hash table */ + zend_array_destroy(Z_ARR(cval)); + } } /* Check for multi type */ } else if (type == BBCODE_TYPE_MULTI) { @@ -540,26 +900,33 @@ static zend_bool bbcode_check_tag(HashTable *tag TSRMLS_DC) { /* Value */ zval mval; - /* Tag */ - zend_string *mtag = zend_string_init("<>", strlen("<>") + ZSTR_LEN(key) + (hasArg?strlen("%s"):0), 0); - memcpy(ZSTR_VAL(mtag)+sizeof(char), ZSTR_VAL(key), ZSTR_LEN(key)); - if (hasArg) { - memcpy(ZSTR_VAL(mtag)+(1+ZSTR_LEN(key))*sizeof(char), "%s>", strlen("%s>")); + /* Check if tag is empty */ + if (ZSTR_LEN(key) == 0) { + /* Init new value */ + ZVAL_EMPTY_STRING(&mval); + /* Non empty tag */ } else { - memcpy(ZSTR_VAL(mtag)+(1+ZSTR_LEN(key))*sizeof(char), ">", strlen(">")); - } + /* Init buffer and length */ + buf = (char *)malloc((len = strlen("<>") + ZSTR_LEN(key) + (hasArg?strlen("%s"):0))*sizeof(char)); - /* Set value */ - ZVAL_STRING(&mval, ZSTR_VAL(mtag)); + /* Generate string in */ + ZEND_ASSERT(sprintf(buf, "<%s%s>", ZSTR_VAL(key), hasArg?"%s":"") == len); + + /* Init new value */ + ZVAL_STRINGL(&mval, buf, len); + + /* Free buffer */ + free(buf); + } /* Add new key */ ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), mkey, &mval) != NULL); - /* Free tag */ - zend_string_release(mtag); - /* Free key */ zend_string_release(mkey); + + /* Set hasOpen */ + hasOpen = 1; } /* Check if has close entry */ @@ -570,22 +937,33 @@ static zend_bool bbcode_check_tag(HashTable *tag TSRMLS_DC) { /* Value */ zval mval; - /* Tag */ - zend_string *mtag = zend_string_init("", strlen("") + ZSTR_LEN(key), 0); - memcpy(ZSTR_VAL(mtag)+2*sizeof(char), ZSTR_VAL(key), ZSTR_LEN(key)); - memcpy(ZSTR_VAL(mtag)+(2+ZSTR_LEN(key))*sizeof(char), ">", strlen(">")); + /* Check if tag is empty */ + if (ZSTR_LEN(key) == 0) { + /* Init new value */ + ZVAL_EMPTY_STRING(&mval); + /* Non empty tag */ + } else { + /* Init buffer and length */ + buf = (char *)malloc((len = strlen("") + ZSTR_LEN(key))*sizeof(char)); - /* Set value */ - ZVAL_STRING(&mval, ZSTR_VAL(mtag)); + /* Generate string in */ + ZEND_ASSERT(sprintf(buf, "", ZSTR_VAL(key)) == len); + + /* Init new value */ + ZVAL_STRINGL(&mval, buf, len); + + /* Free buffer */ + free(buf); + } /* Add new key */ ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), mkey, &mval) != NULL); - /* Free tag */ - zend_string_release(mtag); - /* Free key */ zend_string_release(mkey); + + /* Set hasClose */ + hasClose = 1; } /* Check if has not default */ @@ -604,26 +982,88 @@ static zend_bool bbcode_check_tag(HashTable *tag TSRMLS_DC) { /* Free key */ zend_string_release(mkey); + + /* Set hasDefault */ + hasDefault = 1; } - //TODO: set parent&child if not provided for ARG|OPTARG|NOARG|SINGLE (has to loop on each possible type and check if no child array or present in child array if available) + /* Compute parent here */ + if (!hasParent) { + /* Parent key string */ + zend_string *pkey = zend_string_init("parent", strlen("parent"), 0); + + /* Parent value */ + zval pval; + + /* Init new parent val array */ + ZVAL_NEW_ARR(&pval); + + /* Init hash table */ + zend_hash_init(Z_ARR(pval), 0, NULL, ZVAL_PTR_DTOR, 0); + + /* Compute and set parent here */ + /* XXX: we don't check or trigger error inside this function as these tests are done in current loop */ + /* TODO: change this assert to a if() and free pval on error, error will trigger in later loop (code coverage tests to do) */ + ZEND_ASSERT(bbcode_gen_parent(Z_ARR(pval), tag, key) != NULL); + + /* Add new parent key */ + ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), pkey, &pval) != NULL); + + /* Free type key */ + zend_string_release(pkey); + + /* Set hasParent */ + hasParent = 1; + } + + /* Compute child here */ + if (!hasChild) { + /* Child value */ + zval cval; + + /* Init new child val array */ + ZVAL_NEW_ARR(&cval); + + /* Init hash table */ + zend_hash_init(Z_ARR(cval), 0, NULL, ZVAL_PTR_DTOR, 0); + + /* Compute and set child here */ + /* XXX: we don't check or trigger error inside this function as these tests are done in current loop */ + if (bbcode_gen_child(Z_ARR(cval), tag, key) != NULL) { + /* Child key string */ + zend_string *ckey = zend_string_init("child", strlen("child"), 0); + /* Add new child key */ + ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), ckey, &cval) != NULL); + + /* Free type key */ + zend_string_release(ckey); + + /* Set hasChild */ + hasChild = 1; + /* Child generation reached an error case */ + /* XXX: it will trigger error later */ + } else { + /* Release hash table */ + zend_array_destroy(Z_ARR(cval)); + } + } /* Check for single type */ } else if (type == BBCODE_TYPE_SINGLE) { /* Check if has open entry */ if (hasOpen) { /* Display error about open entry */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key %s[%d] from argument tag of type BBCODE::TYPE_SINGLE cannot have an open entry", ZSTR_VAL(key), pos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Key %s[%d] from argument tag of type BBCODE::TYPE_SINGLE cannot have an open entry", ZSTR_VAL(key), pos); /* Return failure */ - return 0; + return NULL; } /* Check if has close entry */ if (hasClose) { /* Display error about close entry */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key %s[%d] from argument tag of type BBCODE::TYPE_SINGLE cannot have a close entry", ZSTR_VAL(key), pos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Key %s[%d] from argument tag of type BBCODE::TYPE_SINGLE cannot have a close entry", ZSTR_VAL(key), pos); /* Return failure */ - return 0; + return NULL; } /* Check if has not default */ @@ -634,57 +1074,238 @@ static zend_bool bbcode_check_tag(HashTable *tag TSRMLS_DC) { /* Value */ zval sval; - /* Tag */ - zend_string *stag; - - /* Check if jey is img */ + /* Check if key is img */ if (strcmp("img", ZSTR_VAL(key)) == 0) { - stag = zend_string_init("", strlen("") + (hasArg?strlen("%s"):0), 0); - if (hasArg) { - memcpy(ZSTR_VAL(stag)+strlen("", strlen("%s/>")); - } else { - memcpy(ZSTR_VAL(stag)+strlen("", strlen("/>")); - } + /* Init buffer and length */ + buf = (char *)malloc((len = strlen("") + (hasArg?strlen("%s"):0))*sizeof(char)); + + /* Generate string in */ + ZEND_ASSERT(sprintf(buf, "", hasArg?"%s":"") == len); } else { - stag = zend_string_init("", strlen("") + ZSTR_LEN(key) + (hasArg?strlen("%s"):0), 0); - memcpy(ZSTR_VAL(stag)+sizeof(char), ZSTR_VAL(key), ZSTR_LEN(key)); - if (hasArg) { - memcpy(ZSTR_VAL(stag)+(1+ZSTR_LEN(key))*sizeof(char), "%s/>", strlen("%s/>")); - } else { - memcpy(ZSTR_VAL(stag)+(1+ZSTR_LEN(key))*sizeof(char), "/>", strlen("/>")); - } + /* Init buffer and length */ + buf = (char *)malloc((len = strlen("") + ZSTR_LEN(key) + (hasArg?strlen("%s"):0))*sizeof(char)); + + /* Generate string in */ + ZEND_ASSERT(sprintf(buf, "<%s%s/>", ZSTR_VAL(key), hasArg?"%s":"") == len); } - /* Set value */ - ZVAL_STRING(&sval, ZSTR_VAL(stag)); + /* Init new value */ + ZVAL_STRINGL(&sval, buf, len); + + /* Free buffer */ + free(buf); /* Add new key */ ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), skey, &sval) != NULL); - /* Free tag */ - zend_string_release(stag); - /* Free key */ zend_string_release(skey); + + /* Set hasDefault */ + hasDefault = 1; } /* Check if has child */ if (hasChild) { /* Display error about child entry */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key %s[%d] from argument tag of type BBCODE::TYPE_SINGLE cannot have a child entry", ZSTR_VAL(key), pos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Key %s[%d] from argument tag of type BBCODE::TYPE_SINGLE cannot have a child entry", ZSTR_VAL(key), pos); /* Return failure */ - return 0; + return NULL; } - //TODO: set parent if not provided for ARG|OPTARG|NOARG|SINGLE (has to loop on each possible type and check if no child array or present in child array if available) + /* Compute parent here */ + if (!hasParent) { + /* Parent key string */ + zend_string *pkey = zend_string_init("parent", strlen("parent"), 0); + + /* Parent value */ + zval pval; + + /* Init new parent val array */ + ZVAL_NEW_ARR(&pval); + + /* Init hash table */ + zend_hash_init(Z_ARR(pval), 0, NULL, ZVAL_PTR_DTOR, 0); + + /* Compute and set parent here */ + /* XXX: we don't check or trigger error inside this function as these tests are done in current loop */ + /* TODO: change this asser to a if() and free pval on error, error will trigger in later loop (code coverage tests to do) */ + ZEND_ASSERT(bbcode_gen_parent(Z_ARR(pval), tag, key) != NULL); + + /* Add new parent key */ + ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), pkey, &pval) != NULL); + + /* Free type key */ + zend_string_release(pkey); + + /* Set hasParent */ + hasParent = 1; + } } -#ifndef DEBUG - printf("key=>%s\n", ZSTR_VAL(key)); - fflush(NULL); - php_debug_zval_dump(val, 0); - fflush(NULL); -#endif + /* 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); + } /* Grow pos */ pos++; @@ -693,13 +1314,19 @@ static zend_bool bbcode_check_tag(HashTable *tag TSRMLS_DC) { /* Check if not one unique root */ if (hasRoot != 1) { /* Display error about multiple root */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Has %d BBCODE::TYPE_ROOT entry, require one unique", hasRoot); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Tag array has %d BBCODE::TYPE_ROOT entry instead of expected unique one", hasRoot); /* Return failure */ - return 0; + return NULL; } + /* Duplicate root zend string to avoid it beeing freed */ + return zend_string_copy(root); + +#if 0 + /* Return success */ - return 1; + return root; +#endif } /* }}} */ @@ -721,7 +1348,7 @@ static zend_bool bbcode_check_smiley(HashTable *smiley TSRMLS_DC) { /* Check that key is a string */ if (key == NULL) { /* Display error about long key */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key %ld[%d] from argument smiley is not an expected string", idx, pos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Key %ld[%d] from argument smiley is not an expected string", idx, pos); /* Return failure */ return 0; } @@ -729,13 +1356,13 @@ static zend_bool bbcode_check_smiley(HashTable *smiley TSRMLS_DC) { /* Check that value exists */ if (val == NULL) { /* Display error about long key */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Value for key %s[%d] from argument smiley is NULL instead of expected array", ZSTR_VAL(key), pos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Value for key %s[%d] from argument smiley is NULL instead of expected array", ZSTR_VAL(key), pos); /* Return failure */ return 0; /* Check that value is not an array */ } else if (Z_TYPE_P(val) == IS_ARRAY) { /* Display error about long key */ - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Value for key %s[%d] from argument smiley is an array instead of expected string", ZSTR_VAL(key), pos); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Value for key %s[%d] from argument smiley is an array instead of expected string", ZSTR_VAL(key), pos); /* Return failure */ return 0; } @@ -749,109 +1376,420 @@ static zend_bool bbcode_check_smiley(HashTable *smiley TSRMLS_DC) { } /* }}} */ -/* {{{ PHP_METHOD(BBCode, __construct) { - */ +/* {{{ PHP_METHOD(BBCode, __construct) { */ PHP_METHOD(BBCode, __construct) { + /* Init tag hash */ HashTable *tag = NULL; + + /* Init smiley hash */ HashTable *smiley = NULL; - zend_long flag = BBCODE_DEFAULT_FLAG; -/*TODO: init tag with [ - '' => [ ? ] -]*/ -/* TODO: init smiley with default list - * TODO: use prefix in an ini parameter ? ini_setable ? - * TODO: make sure it's not really a global and per-app param ? - * */ + /* Init flag long */ + zend_long flag = BBCODE_DEFAULT_FLAG; - /* Retrieve the pointer to this object */ - bbcode_object *obj = Z_BBCODE_P(getThis()); + /* Zend error handling */ + zend_error_handling error_handling; - /* Verify that the pointer is leading somewhere */ - assert(obj != NULL); + /* TODO: set tag array to a default value like this in case of missing/empty tag array : + * [ '' => [ ? ] ] + * (maybe later) + */ - /* Set default values */ - obj->tag = tag; - obj->smiley = smiley; - Z_LVAL(obj->flag) = flag; + /* TODO: init smiley with default list ? what about img storage ? + * TODO: use prefix in an ini parameter ? ini_setable ? + * TODO: make sure it's not really a global and per-app param ? + * (maybe later) + */ + zend_replace_error_handling(EH_THROW, bbcode_exception_ce, &error_handling); /* Parse parameters */ + /*if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "h|hl", &tag, &smiley, &flag) == FAILURE) { return; }*/ ZEND_PARSE_PARAMETERS_START(1, 3) + /* Tag arg is required */ Z_PARAM_ARRAY_HT(tag) - Z_PARAM_ARRAY_HT(smiley) + /* We switch to optional args */ + Z_PARAM_OPTIONAL + /* Smiley arg is optional and may be null */ + Z_PARAM_ARRAY_HT_EX(smiley, 1, 0) + /* Flag arg is optional and may be null */ Z_PARAM_LONG(flag) + /* End parameters parse */ ZEND_PARSE_PARAMETERS_END(); - /*if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "h|hl", &tag, &smiley, &flag) == FAILURE) { - return; - }*/ + /* Retrieve the pointer to this object */ + bbcode_object *obj = Z_BBCODE_P(getThis()); + + /* Verify that the pointer is leading somewhere */ + assert(obj != NULL); + + /* Set flag value */ + obj->flag = flag; + + /* Duplicate hash to avoid modifications on original argument */ + obj->tag = zend_array_dup(tag); /* Save tag argument hash in current object */ - //TODO: check tag structure - if (bbcode_check_tag(tag)) { - obj->tag = tag; - } else { - zend_hash_destroy(tag); - zend_hash_destroy(smiley); + if ((obj->root = bbcode_check_tag(obj->tag)) == NULL) { + /* Restore error handler */ + zend_restore_error_handling(&error_handling); + + /* Release the tag hash */ + zend_array_destroy(obj->tag); + + /* Free the zend object */ + //XXX: seems useless, it don't seems possible to null the contructor return value... ZVAL_NULL(getThis() TSRMLS_CC); + + /* Free the object */ + efree(obj); + + /* Stop here */ return; } - /* Save smiley argument hash in current object if provided */ - //TODO: check smiley structure + /* Check if smiley argument is available */ if (smiley) { - if (bbcode_check_smiley(smiley)) { - obj->smiley = smiley; - } else { - zend_hash_destroy(tag); - zend_hash_destroy(smiley); + /* Duplicate hash to avoid modifications on original argument */ + obj->smiley = zend_array_dup(smiley); + + /* Save smiley argument hash in current object */ + if (!bbcode_check_smiley(obj->smiley)) { + /* Restore error handler */ + zend_restore_error_handling(&error_handling); + + /* Release the root string */ + zend_string_release(obj->root); + + /* Release the tag hash */ + zend_array_destroy(obj->tag); + + /* Release the smiley hash */ + zend_array_destroy(obj->smiley); + + /* Free the object */ + //XXX: seems useless, it don't seems possible to null the contructor return value... ZVAL_NULL(getThis() TSRMLS_CC); + + /* Free the object */ + efree(obj); + + /* Stop here */ return; } } - /* Save flag argument long in current object if provided */ - if (flag) { - Z_LVAL(obj->flag) = flag; - } + /* Restore error handler */ + zend_restore_error_handling(&error_handling); } /* }}} */ /* {{{ PHP_METHOD(BBCode, __destruct) { + * XXX: this function is called by bbcode_destroy function */ PHP_METHOD(BBCode, __destruct) { +//XXX: disable this code, we don't need to retrieve obj as we do nothing with userland destructor for now +#if 0 /* Retrieve the pointer to this object */ bbcode_object *obj = Z_BBCODE_P(getThis()); /* Verify that the pointer is leading somewhere */ assert(obj != NULL); +#endif +} +/* }}} */ + +/* */ +//[HashTable *ret][zend_string *current][char *str][int offset][int length] +static char *bbcode_parse(HashTable *tag, zend_string *cur, char *rs, int *ros, int *rlen, char *str, int len TSRMLS_DC) { + /* Check if return string is not initialized or shorter than wanted length */ + if (rs == NULL || *rlen - *ros <= 1024) { + /* Compute new length */ + *rlen = (rs == NULL) ? 2048 : *rlen + 1024; + + /* Grow the return string */ + ZEND_ASSERT((rs = (char *)realloc(rs, (*rlen)*sizeof(char))) != NULL); + } + + //DEBUG: for reminder + //str=skip0[r]string0[a=arg0]string1[/a]string2[b]string3[/b][/r]skip1 + //len=64 + + /* Init zval for current node and type */ + zval *znode, *ztype; + + /* Retrieve current tag hash as zval */ + ZEND_ASSERT((znode = zend_hash_find(tag, cur)) != NULL && Z_TYPE_P(znode) == IS_ARRAY); + + /* Retrieve current tag type */ + ZEND_ASSERT((ztype = zend_hash_str_find(Z_ARR_P(znode), "type", strlen("type"))) != NULL && Z_TYPE_P(ztype) == IS_LONG); + + /* Retrieve current tag open */ + /* XXX: may be NULL if not present */ + zval *zopen = zend_hash_str_find(Z_ARR_P(znode), "open", strlen("open")); + + /* Retrieve current tag close */ + /* XXX: may be NULL if not present */ + zval *zclose = zend_hash_str_find(Z_ARR_P(znode), "close", strlen("close")); + + /* Retrieve current tag default */ + /* XXX: may be NULL if not present */ + zval *zdefault = zend_hash_str_find(Z_ARR_P(znode), "default", strlen("default")); + + /* Retrieve current tag arg */ + /* XXX: may be NULL if not present */ + zval *zarg = zend_hash_str_find(Z_ARR_P(znode), "arg", strlen("arg")); + + /* The arg zend string */ + zend_string *argstr = NULL; + + /* Check if we have a not root and not empty tag */ + if (Z_LVAL_P(ztype) != BBCODE_TYPE_ROOT && strcmp(ZSTR_VAL(cur), "") != 0) { + /* Regular expression */ + zend_string *regex; + + /* Open and close pattern */ + char *openstr, *closestr, *found; + + /* Open and close pattern length */ + int openlen, closelen; + + /* Alloc open pattern buffer and set length */ + openstr = (char *)malloc(((openlen = strlen("/\\[\\]/") + (zarg == NULL ? strlen("(?:=[^\\]]*)?") : strlen("(?:=([^\\]]*))?")) + ZSTR_LEN(cur)) + 1)*sizeof(char)); + + /* Alloc close pattern buffer and set length */ + closestr = (char *)malloc(((closelen = strlen("[/]") + ZSTR_LEN(cur)) + 1)*sizeof(char)); + + /* Create open tag string */ + ZEND_ASSERT(snprintf(openstr, openlen + 1, "/\\[%s%s\\]/", ZSTR_VAL(cur), (zarg == NULL ? "(?:=[^\\]]*)?" : "(?:=([^\\]]*))?")) == openlen); + + /* Duplicate open tag in regex */ + regex = zend_string_init(openstr, openlen, 0); + + /* Free the close string */ + free(closestr); + + /* Compiled regular expression */ + pcre_cache_entry *pce; + + /* Array for subpatterns and return value */ + zval rv, subpats; + + /* Init subpats array */ + /* XXX: if we don't initialize the array first, some strange memory corruption happen inside php_pcre_match_impl call */ + array_init(&subpats); + + /* Compile regex or get it from cache. */ + /* TODO: see if we handle the error here ? (maybe later) + * XXX: it seems unlikely any error could be recoverable if pcre function throw something + * XXX: special char in tag is already protected earlier + * XXX: maybe just check that the pcre error trigger an exception or something uncatchable in all case + * XXX: see function pcre_get_compiled_regex_cache in ext/pcre/php_pcre.c +530 */ + ZEND_ASSERT((pce = pcre_get_compiled_regex_cache(regex)) != NULL); + + /* Increase pce refcount */ + /* XXX: we use this instead of pce->refcount++; to avoid dereferencing compile error */ + php_pcre_pce_incref(pce); + + /* Call zend pcre implementation */ + php_pcre_match_impl(pce, str, len, &rv, &subpats, 0, 0, 0, 0); + + /* Decrease pce refcount */ + /* XXX: we use this instead of pce->refcount--; to avoid dereferencing compile error */ + php_pcre_pce_decref(pce); + + /* Check that we found an occurence */ + if (Z_TYPE(rv) == IS_LONG && Z_LVAL(rv) == 1) { + /* Init match zval */ + zval *match; + + /* Init the matched tag string */ + //char *tstr; + + /* Test that subpats is an array */ + ZEND_ASSERT(Z_TYPE(subpats) == IS_ARRAY); + + /* Test that subpats members count is right */ + ZEND_ASSERT(zend_array_count(Z_ARR(subpats)) == (zarg == NULL ? 1 : 2)); - /* Free tag hashtable */ - if (obj->tag) { - efree(obj->tag); + /* Retrieve the matched full tag string */ + ZEND_ASSERT((match = zend_hash_index_find(Z_ARR(subpats), 0)) != NULL); + + /* Test that match is a string */ + ZEND_ASSERT(Z_TYPE_P(match) == IS_STRING); + + /* Find the tag in string */ + if ((found = (char *)zend_memnstr(str, Z_STRVAL_P(match), Z_STRLEN_P(match), str+len))) { + /* Move str to found position + matched tag string */ + str = found + Z_STRLEN_P(match); + } + + /* Check if have and arg to retrieve */ + if (zarg != NULL) { + /* Retrieve the matched arg string */ + ZEND_ASSERT((match = zend_hash_index_find(Z_ARR(subpats), 1)) != NULL); + + /* Test that match is a string */ + ZEND_ASSERT(Z_TYPE_P(match) == IS_STRING); + + /* Save arg string */ + argstr = Z_STR_P(match); + } + + //XXX: code unfinished, these todo are normal, work start here :) + //TODO: set the new search position to the current offset plus found subpats[0] string length + //TODO: save argument string for appending in return string if zarg is not NULL + //TODO: see what happen if we find start tag and not end + //TODO: see what happen if we find no start tag and no end + //TODO: see what happen if we find no start tag and no end + + /* No occurence found */ + } else { + /* XXX: nothing to do ? skip end tag search ??? return to parent level ? */ + } + + /* Destroy subpats array */ + zend_array_destroy(Z_ARR(subpats)); + + /* Create close tag string */ + //ZEND_ASSERT(snprintf(closestr, closelen + 1, "/\\[\\/%s\\]/", ZSTR_VAL(cur)) == closelen); + ZEND_ASSERT(snprintf(closestr, closelen + 1, "[/%s]", ZSTR_VAL(cur)) == closelen); + + /* Look for close tag */ + /* XXX: watch out, this is a maximum length to consider, the close tag may be present earlier for current leaf */ + if ((found = (char *)zend_memnrstr(str, closestr, closelen, str+len))) { + /* Reduce len by the size of trailing string since close tag */ + len -= len + str - found; + } + + /* Free openstr and closestr */ + free(openstr); + } + + printf("str=%.*s\n", len, str); + fflush(NULL); + + return NULL; + +#if 0 + fflush(NULL); + printf("str=%s\n", str); +#endif + + /* XXX: debug */ + zval ztag; + ZVAL_ARR(&ztag, tag); + php_debug_zval_dump(&ztag, 0); + fflush(NULL); + exit(1); + + php_debug_zval_dump(zopen, 0); + php_debug_zval_dump(zclose, 0); + php_debug_zval_dump(zdefault, 0); + fflush(NULL); + exit(1); + + if (argstr != NULL) { + printf("arg=%s[%ld]\n", ZSTR_VAL(argstr), ZSTR_LEN(argstr)); + fflush(NULL); + exit(1); } - /* Free smiley hashtable */ - if (obj->smiley) { - efree(obj->smiley); + //XXX: same here unfinished, code start here :) + //TODO: check what happen when we have something like that : [t]s[/t][t]p[/t] + //1: compute open and close tag + //2: extract end of open tag position and begin of close tag reverse searched in current space + //(if we don't find end tag, consider positionning to end of string or rise error ? parse flag involved here) + //3: append to return string open tag + //4: loop for child tags in subspace and trigger recursion on space between current open and close tags + //(loop involved here) + //5: duplicate plain strings between each child in return string + //6: append the close tag + +#if 0 + /* Check if non empty string (other tag that empty root) */ + if (strcmp(ZSTR_VAL(cur), "") != 0) { + //TODO: generate search patterns for opening tag and closing tag + //TODO: search position of opening tag + //TODO: search close tag + //TODO: search for child tag + /* Non root tag */ + } else { + //Nothing to do, search offset for child will be 0 and length of search to len + //TODO: search current end tag here ? } + + /* Search next child node */ + + /**/ + //bbcode_search_node(ret, +#endif + + /* Return string by default */ + return rs; } -/* }}} */ /* { { { PHP_METHOD(BBCode, parse) { */ PHP_METHOD(BBCode, parse) { + /* Init string to parse */ zend_string *str = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "S", &str) == FAILURE) { - return; - } + /* Return string */ + //TODO: voir si on crée pas une struct pour éviter de trainer 3 arguments + char *rs = NULL; + + /* Return offset and length */ + int ros = 0, rlen = 0; + + /* Parse parameters */ + /*if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "S", &str) == FAILURE) { return; }*/ + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(str) + ZEND_PARSE_PARAMETERS_END(); + + /* Retrieve the pointer to this object */ + bbcode_object *obj = Z_BBCODE_P(getThis()); + + /* Init return string and set length to string * 2 plus last null char */ + //ZEND_ASSERT((rs = (char *)malloc((rlen = (2*Z_STRLEN(str)) + 1)*sizeof(char)) != NULL); + + /* Go compute the tree */ + bbcode_parse(obj->tag, obj->root, rs, &ros, &rlen, ZSTR_VAL(str), ZSTR_LEN(str)); + + /* Free return string */ + free(rs); + +#if 0 + /* Retrieve root node */ + zval *root = zend_hash_find(obj->tag, obj->root); + + bbcode_ + + /* 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); +#endif + +#if BBCODE_DEBUG + /* Tag */ + zval tag; + + /* Init new child val array */ + ZVAL_ARR(&tag, obj->tag); + + php_debug_zval_dump(&tag, 0); + fflush(NULL); +#endif + + //printf("flag=%ld\n", obj->flag); + //printf("die ici: %s +%d\n", __FILE__, __LINE__); //TODO: create a tree for storing result //TODO: apply recursively search of next possible tags (or a closing parent tag ?) regexp and store result inside //TODO: d //TODO: apply the smiley parsing on string + /* TODO: the return string */ RETURN_STR(str); } /* }}} */ @@ -870,8 +1808,7 @@ const zend_function_entry bbcode_methods[] = { /* {{{ PHP_MINIT_FUNCTION */ -PHP_MINIT_FUNCTION(bbcode) -{ +PHP_MINIT_FUNCTION(bbcode) { /* If you have INI entries, uncomment these lines REGISTER_INI_ENTRIES(); */ @@ -895,14 +1832,14 @@ PHP_MINIT_FUNCTION(bbcode) /* Register BBCodeException class entry */ bbcode_exception_ce = zend_register_internal_class_ex(&tmp_ce, zend_exception_get_default(TSRMLS_C) TSRMLS_CC); + /* Send success */ return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ -PHP_MSHUTDOWN_FUNCTION(bbcode) -{ +PHP_MSHUTDOWN_FUNCTION(bbcode) { /* uncomment this line if you have INI entries UNREGISTER_INI_ENTRIES(); */ @@ -912,8 +1849,7 @@ PHP_MSHUTDOWN_FUNCTION(bbcode) /* {{{ PHP_MINFO_FUNCTION */ -PHP_MINFO_FUNCTION(bbcode) -{ +PHP_MINFO_FUNCTION(bbcode) { php_info_print_table_start(); php_info_print_table_header(2, PHP_BBCODE_NAME " support", "enabled"); php_info_print_table_row(2, "Extension version", PHP_BBCODE_VERSION); @@ -929,14 +1865,14 @@ PHP_MINFO_FUNCTION(bbcode) */ zend_module_entry bbcode_module_entry = { STANDARD_MODULE_HEADER, - PHP_BBCODE_NAME, - NULL, /*bbcode_functions,*/ - PHP_MINIT(bbcode), - PHP_MSHUTDOWN(bbcode), - NULL, - NULL, - PHP_MINFO(bbcode), - PHP_BBCODE_VERSION, + PHP_BBCODE_NAME, /* extension name */ + NULL, /* function list */ + PHP_MINIT(bbcode), /* process startup */ + PHP_MSHUTDOWN(bbcode), /* process shutdown */ + NULL, /* request startup */ + NULL, /* request shutdown */ + PHP_MINFO(bbcode), /* extension info */ + PHP_BBCODE_VERSION, /* extension version */ STANDARD_MODULE_PROPERTIES }; /* }}} */