Remove stdbool, seems a deprecaded c99 feature
[bbcode] / bbcode.c
1
2 #ifdef HAVE_CONFIG_H
3 #include "config.h"
4 #endif
5
6 #include "php.h"
7 #include "php_ini.h"
8 #include "ext/standard/info.h"
9 #include "Zend/zend_exceptions.h"
10 #include "php_bbcode.h"
11
12 /* XXX: only required for php_debug_zval_dump */
13 #include "ext/standard/php_var.h"
14
15 /* XXX: required for pcre_cache_entry */
16 #include "ext/pcre/php_pcre.h"
17
18 /* If you declare any globals in php_bbcode.h uncomment this:
19 ZEND_DECLARE_MODULE_GLOBALS(bbcode)
20 */
21
22 typedef struct _bbcode_object {
23 HashTable *tag;
24 HashTable *smiley;
25 zend_string *root;
26 zend_long flag;
27 zend_object std;
28 } bbcode_object;
29
30 /* True global resources - no need for thread safety here */
31 zend_object_handlers bbcode_handlers;
32
33 /* BBCode object */
34 zend_class_entry *bbcode_ce;
35
36 /* BBCode exception object */
37 zend_class_entry *bbcode_exception_ce;
38
39 /* Define all constants */
40 #ifdef old_code_dropped
41 GEN(TYPE_ARG) /*BBCODE_TYPE_ARG*/\
42 GEN(TYPE_NOARG) /*BBCODE_TYPE_NOARG*/\
43 GEN(TYPE_OPTARG) /*BBCODE_TYPE_OPTARG*/\
44
45 #endif
46
47 #define BBCODE_DEF(GEN) \
48 /* Types */ \
49 GEN(TYPE_ROOT) /*BBCODE_TYPE_ROOT*/\
50 GEN(TYPE_MULTI) /*BBCODE_TYPE_ARG|BBCODE_TYPE_OPTARG|BBCODE_TYPE_NOARG*/\
51 GEN(TYPE_SINGLE) /*BBCODE_TYPE_SINGLE*/\
52
53 /* TODO: add again each feature when implemented (before beta) */
54 #if 0
55 /* Quotes */ \
56 GEN(QUOTE_DOUBLE) /*BBCODE_ARG_DOUBLE_QUOTE*/\
57 GEN(QUOTE_SIMPLE) /*BBCODE_ARG_SINGLE_QUOTE*/\
58 GEN(QUOTE_HTML) /*BBCODE_ARG_HTML_QUOTE*/\
59 GEN(QUOTE_ESCAPE) /*BBCODE_ARG_QUOTE_ESCAPING*/\
60 \
61 /* Corrections */ \
62 GEN(CORRECT_AUTO) /*BBCODE_AUTO_CORRECT*/\
63 GEN(CORRECT_REOPEN) /*BBCODE_CORRECT_REOPEN_TAGS*/\
64 GEN(CORRECT_NOTREE) /*BBCODE_DISABLE_TREE_BUILD*/\
65 \
66 /* Smileys */ \
67 GEN(SMILEY_ON) /*BBCODE_DEFAULT_SMILEYS_ON*/\
68 GEN(SMILEY_CI) /*BBCODE_SMILEYS_CASE_INSENSITIVE*/\
69 \
70 /* Flags */ \
71 GEN(CLOSE_AUTO) /*BBCODE_FLAGS_ONE_OPEN_PER_LEVEL*/\
72 GEN(REMOVE_EMPTY) /*BBCODE_FLAGS_REMOVE_IF_EMPTY*/\
73 GEN(REMOVE_CONTENT) /*BBCODE_FLAGS_CDATA_NOT_ALLOWED*/\
74 GEN(REMOVE_REOPEN) /*BBCODE_FLAGS_DENY_REOPEN_CHILD*/
75 #endif
76
77
78 /* Generate enum version */
79 #define BBCODE_GEN_ENUM(NAME) BBCODE_ ## NAME,
80
81 /* Generate zend const version */
82 #define BBCODE_GEN_CONST(NAME) zend_declare_class_constant_long(bbcode_ce, #NAME, sizeof(#NAME) - 1, BBCODE_ ## NAME TSRMLS_CC);
83
84 /* Defined in /usr/include/php/Zend/zend_types.h +382 */
85 #define BBCODE_GET_TYPE(TYPE) (TYPE==IS_UNDEF)?"undef":( \
86 (TYPE==IS_NULL)?"null":( \
87 (TYPE==IS_FALSE)?"false":( \
88 (TYPE==IS_TRUE)?"true":( \
89 (TYPE==IS_LONG)?"long":( \
90 (TYPE==IS_DOUBLE)?"double":( \
91 (TYPE==IS_STRING)?"string":( \
92 (TYPE==IS_ARRAY)?"array":( \
93 (TYPE==IS_OBJECT)?"object":( \
94 (TYPE==IS_RESOURCE)?"resource":( \
95 (TYPE==IS_REFERENCE)?"reference":( \
96 (TYPE==IS_CONSTANT_AST)?"constant":"unknown"\
97 ) \
98 ) \
99 ) \
100 ) \
101 ) \
102 ) \
103 ) \
104 ) \
105 ) \
106 ) \
107 )
108
109 /* All flags enum set */
110 enum {
111 BBCODE_DEF(BBCODE_GEN_ENUM)
112 };
113
114 /* Set default flag
115 * TODO: Implement it in an ini file ? (later maybe) */
116 #define BBCODE_DEFAULT_FLAG 0
117 #if 0
118 #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
119 #endif
120
121 /* Set debug flag */
122 #define BBCODE_DEBUG 0
123
124 /* {{{ Function prototypes (bbcode_destroy|bbcode_free|bbcode_clone|bbcode_create) */
125 static void bbcode_destroy(zend_object *obj TSRMLS_DC);
126 static void bbcode_free(zend_object *obj TSRMLS_DC);
127 static zend_object *bbcode_clone(zval *obj TSRMLS_DC);
128 static zend_object *bbcode_create(zend_class_entry *ce TSRMLS_DC);
129 /* }}} */
130
131 /* {{{ static inline bbcode_object* bbcode_fetch(zend_object *obj TSRMLS_DC) {
132 * BBCode object fetch function */
133 static inline bbcode_object* bbcode_fetch(zend_object *obj TSRMLS_DC) {
134 return (bbcode_object *)((char *)obj - XtOffsetOf(bbcode_object, std));
135 }
136 /* }}} */
137
138 /* {{{ Z_BBCODE_P(zv) bbcode_fetch(Z_OBJ_P((zv)))
139 * BBCode object pointer fetch macro */
140 #define Z_BBCODE_P(zv) bbcode_fetch(Z_OBJ_P((zv)))
141 /* }}} */
142
143 /* {{{ static void bbcode_destroy(zend_object *obj TSRMLS_DC) {
144 * BBCode object destroy call */
145 static void bbcode_destroy(zend_object *obj TSRMLS_DC) {
146 /* Trigger user land destructor on obj */
147 zend_objects_destroy_object(obj);
148 }
149 /* }}} */
150
151 /* {{{ static void bbcode_free(zend_object *obj TSRMLS_DC) {
152 * BBCode object free call */
153 static void bbcode_free(zend_object *obj TSRMLS_DC) {
154 /* Retrieve bbcode object from zend object */
155 bbcode_object *bbobj = bbcode_fetch(obj);
156
157 /* Check if root is present */
158 if (bbobj->root) {
159 /* Release the root string */
160 zend_string_release(bbobj->root);
161 }
162
163 /* Check if tag is present */
164 if (bbobj->tag) {
165 /* Release the tag hash */
166 zend_array_destroy(bbobj->tag);
167 }
168
169 /* Check if smiley is present */
170 if (bbobj->smiley) {
171 /* Release the smiley hash */
172 zend_array_destroy(bbobj->smiley);
173 }
174
175 /* Trigger zend object std destructor on obj */
176 zend_object_std_dtor(obj);
177
178 /* Free the bbobj */
179 efree(bbobj);
180 }
181 /* }}} */
182
183 /* {{{ static zend_object *bbcode_clone(zval *obj TSRMLS_DC) {
184 * BBCode object clone call */
185 static zend_object *bbcode_clone(zval *obj TSRMLS_DC) {
186 /* Fetch pointer to old bbcode object */
187 bbcode_object *oldbbobj = Z_BBCODE_P(obj);
188
189 /* Create pointer on new obj */
190 //bbcode_object *newbbobj = bbcode_fetch(bbcode_create(oldbbobj->std.ce));
191 bbcode_object *newbbobj = bbcode_fetch(bbcode_create(Z_OBJ_P(obj)->ce));
192
193 /* Call members cloning function */
194 zend_objects_clone_members(&newbbobj->std, &oldbbobj->std);
195
196 /* Duplicate flag value */
197 newbbobj->flag = oldbbobj->flag;
198
199 /* Set root as null */
200 newbbobj->root = NULL;
201
202 /* Check if root is available */
203 if (oldbbobj->root) {
204 /* Duplicate root zend string */
205 newbbobj->root = zend_string_copy(oldbbobj->root);
206 }
207
208 /* Set tag as null */
209 newbbobj->tag = NULL;
210
211 /* Check if tag is available */
212 if (oldbbobj->tag) {
213 /* Duplicate tag zend array */
214 newbbobj->tag = zend_array_dup(oldbbobj->tag);
215 }
216
217 /* Set smiley as null */
218 newbbobj->smiley = NULL;
219
220 /* Check if smiley is available */
221 if (oldbbobj->smiley) {
222 /* Duplicate smiley zend array */
223 newbbobj->smiley = zend_array_dup(oldbbobj->smiley);
224 }
225
226 /* Return the new object */
227 return &newbbobj->std;
228 }
229 /* }}} */
230
231 /* {{{ static zend_object *bbcode_create(zend_class_entry *ce TSRMLS_DC) {
232 * BBCode object create call */
233 static zend_object *bbcode_create(zend_class_entry *ce TSRMLS_DC) {
234 /* Allocate object */
235 bbcode_object *obj = (bbcode_object *)ecalloc(1, sizeof(bbcode_object) + zend_object_properties_size(ce));
236
237 /* Init std member */
238 zend_object_std_init(&obj->std, ce TSRMLS_CC);
239
240 /* Set object properties */
241 object_properties_init(&obj->std, ce TSRMLS_CC);
242
243 /* Duplicate standard object handler */
244 memcpy(&bbcode_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
245
246 /* Set object offset */
247 bbcode_handlers.offset = XtOffsetOf(bbcode_object, std);
248
249 /* Set destructor function */
250 bbcode_handlers.dtor_obj = (zend_object_dtor_obj_t) bbcode_destroy;
251
252 /* Set free function */
253 bbcode_handlers.free_obj = (zend_object_free_obj_t) bbcode_free;
254
255 /* Set clone function */
256 bbcode_handlers.clone_obj = (zend_object_clone_obj_t) bbcode_clone;
257
258 /* Assign custom handler */
259 obj->std.handlers = &bbcode_handlers;
260
261 /* Init tag */
262 obj->tag = NULL;
263
264 /* Init smiley */
265 obj->smiley = NULL;
266
267 /* Init root */
268 obj->root = NULL;
269
270 /* Init flag */
271 obj->flag = BBCODE_DEFAULT_FLAG;
272
273 /* Return the std member address */
274 return &obj->std;
275 }
276 /* }}} */
277
278 /* {{{ ZEND_BEGIN_ARG_INFO_EX(bbcode_construct_arginfo) */
279 // ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args)
280 ZEND_BEGIN_ARG_INFO_EX(bbcode_construct_arginfo, 0, 0, 1)
281 // ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null)
282 ZEND_ARG_ARRAY_INFO(0, tag, 0)
283 ZEND_ARG_ARRAY_INFO(0, smiley, 1)
284 // ZEND_ARG_INFO(pass_by_ref, name)
285 ZEND_ARG_INFO(0, flag)
286 ZEND_END_ARG_INFO()
287 /* }}} */
288
289 /* {{{ ZEND_BEGIN_ARG_INFO_EX(bbcode_parse_arginfo) */
290 // ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args)
291 ZEND_BEGIN_ARG_INFO_EX(bbcode_parse_arginfo, 0, 0, 1)
292 // ZEND_ARG_INFO(pass_by_ref, name)
293 ZEND_ARG_INFO(0, str)
294 ZEND_END_ARG_INFO()
295 /* }}} */
296
297 /* {{{ static HashTable *bbcode_gen_child(HashTable *ret, HashTable *tag, zend_string *cur TSRMLS_DC) { */
298 static HashTable *bbcode_gen_child(HashTable *ret, HashTable *tag, zend_string *cur TSRMLS_DC) {
299 /* Key string */
300 zend_string *key;
301 /* Value */
302 zval *val;
303
304 /* Iterate on each key val until hash end is reached */
305 ZEND_HASH_FOREACH_STR_KEY_VAL(tag, key, val) {
306 /* Check if key is not a string, value is null or not an array */
307 if (key == NULL || val == NULL || Z_TYPE_P(val) != IS_ARRAY) {
308 /* Return failure, useless to generate a hash when we will trigger an error later */
309 return NULL;
310 }
311
312 /* Init type val */
313 zval *type;
314
315 /* Check that we have no type or that it's not root type */
316 /* XXX: it's not possible to have in child the root tag */
317 if ((type = zend_hash_str_find(Z_ARR_P(val), "type", strlen("type"))) == NULL || Z_LVAL_P(type) != BBCODE_TYPE_ROOT) {
318 /* Check that we are not on cur key */
319 /* XXX: not really required, but it's stupid to allow same tag in child */
320 //if (strcmp(ZSTR_VAL(cur), ZSTR_VAL(key)) != 0) {
321 /* Init parent val */
322 zval *parent;
323
324 /* Init toInsert bool */
325 unsigned char toInsert = 0;
326
327 /* Check if we find parent key */
328 if ((parent = zend_hash_str_find(Z_ARR_P(val), "parent", strlen("parent"))) != NULL) {
329 /* Parent value */
330 zval *pval;
331
332 /* Iterate on each val until hash end is reached */
333 ZEND_HASH_FOREACH_VAL(Z_ARR_P(parent), pval) {
334 /* Check that parent val is a string */
335 if (Z_TYPE_P(pval) != IS_STRING) {
336 return NULL;
337 }
338
339 /* Check that tkey is a string and is identical */
340 if (strcmp(Z_STRVAL_P(pval), ZSTR_VAL(cur)) == 0) {
341 toInsert = 1;
342 }
343 } ZEND_HASH_FOREACH_END();
344 /* No parent key */
345 } else {
346 toInsert = 1;
347 }
348
349 /* Add the key */
350 if (toInsert) {
351 /* Init child val */
352 zval cval;
353
354 /* Set child val */
355 ZVAL_STR(&cval, key);
356
357 /* Insert the child val in return array */
358 ZEND_ASSERT(zend_hash_next_index_insert_new(ret, &cval) != NULL);
359 }
360 //}
361 }
362 } ZEND_HASH_FOREACH_END();
363
364 /* Return value */
365 return ret;
366 }
367 /* }}} */
368
369 /* {{{ static HashTable *bbcode_gen_parent(HashTable *ret, HashTable *tag, zend_string *cur TSRMLS_DC) { */
370 static HashTable *bbcode_gen_parent(HashTable *ret, HashTable *tag, zend_string *cur TSRMLS_DC) {
371 /* Key string */
372 zend_string *key;
373 /* Value */
374 zval *val;
375
376 /* Iterate on each key val until hash end is reached */
377 ZEND_HASH_FOREACH_STR_KEY_VAL(tag, key, val) {
378 /* Check if key is not a string, value is null or not an array */
379 if (key == NULL || val == NULL || Z_TYPE_P(val) != IS_ARRAY) {
380 /* Return failure, useless to generate a hash when we will trigger an error later */
381 return NULL;
382 }
383
384 /* Init type val */
385 zval *type;
386
387 /* Check that we have no type or that it's not single type */
388 /* XXX: it's not possible to have in child the root tag */
389 if ((type = zend_hash_str_find(Z_ARR_P(val), "type", strlen("type"))) == NULL || Z_LVAL_P(type) != BBCODE_TYPE_SINGLE) {
390 /* Check that we are not on cur key */
391 /* XXX: not really required, but it's stupid to allow same tag in child */
392 //if (strcmp(ZSTR_VAL(cur), ZSTR_VAL(key)) != 0) {
393 /* Init child val */
394 zval *child;
395
396 /* Init toInsert bool */
397 unsigned char toInsert = 0;
398
399 /* Check if we find child key */
400 if ((child = zend_hash_str_find(Z_ARR_P(val), "child", strlen("child"))) != NULL) {
401 /* Child value */
402 zval *cval;
403
404 /* Iterate on each val until hash end is reached */
405 ZEND_HASH_FOREACH_VAL(Z_ARR_P(child), cval) {
406 /* Check that child val is a string */
407 if (Z_TYPE_P(cval) != IS_STRING) {
408 return NULL;
409 }
410
411 /* Check that tkey is a string and is identical */
412 if (strcmp(Z_STRVAL_P(cval), ZSTR_VAL(cur)) == 0) {
413 toInsert = 1;
414 }
415 } ZEND_HASH_FOREACH_END();
416 /* No child key */
417 } else {
418 toInsert = 1;
419 }
420
421 /* Add the key */
422 if (toInsert) {
423 /* Init parent val */
424 zval pval;
425
426 /* Set parent val */
427 ZVAL_STR(&pval, key);
428
429 /* Insert the parent val in return array */
430 ZEND_ASSERT(zend_hash_next_index_insert_new(ret, &pval) != NULL);
431 }
432 //}
433 }
434 } ZEND_HASH_FOREACH_END();
435
436 /* Return value */
437 return ret;
438 }
439 /* }}} */
440
441 /* {{{ static zend_bool *bbcode_check_tag(HashTable *tag TSRMLS_DC) {
442 * Check that tag hash is valid */
443 static zend_string *bbcode_check_tag(HashTable *tag TSRMLS_DC) {
444 /* Key numeric value */
445 zend_long idx;
446 /* Key and root string */
447 zend_string *key, *root = NULL;
448 /* Value */
449 zval *val;
450
451 /* Key position */
452 int pos = 0;
453
454 /* Has a root */
455 int hasRoot = 0;
456
457 /* Iterate on each key val until hash end is reached */
458 ZEND_HASH_FOREACH_KEY_VAL(tag, idx, key, val) {
459 /* Check if key is not a string */
460 if (key == NULL) {
461 /* Display error about long key */
462 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Key %ld[%d] from argument tag is not an expected string", idx, pos);
463 /* Return failure */
464 return NULL;
465 /* Check that key do not contains a special character */
466 } 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) {
467 /* Display error about key containing a special char */
468 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Key %s[%d] from argument tag contains a special forbidden character: =[]()/\\", ZSTR_VAL(key), pos);
469 /* Return failure */
470 return NULL;
471 }
472
473 /* Check that value exists */
474 if (val == NULL) {
475 /* Display error about long key */
476 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);
477 /* Return failure */
478 return NULL;
479 /* Check that value is an array */
480 } else if (Z_TYPE_P(val) != IS_ARRAY) {
481 /* Display error about long key */
482 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));
483 /* Return failure */
484 return NULL;
485 }
486
487 /* Store child tag array */
488 HashTable *ctag = Z_ARR_P(val);
489 /* Child key numeric value */
490 zend_long cidx;
491 /* Child key string */
492 zend_string *ckey;
493 /* Child value */
494 zval *cval;
495
496 /* Buffer */
497 char *buf;
498
499 /* Child key position */
500 int cpos = 0, type, len;
501
502 /* Detect if entry has a type, parent, child, open, close, default or arg key to display error if mandatory field is missing */
503 unsigned char hasType = 0, hasParent = 0, hasChild = 0, hasOpen = 0, hasClose = 0, hasDefault = 0, hasArg = 0;
504
505 /* Iterate on each ckey cval until hash end is reached */
506 ZEND_HASH_FOREACH_KEY_VAL(ctag, cidx, ckey, cval) {
507 /* Check if ckey is a string */
508 if (!ckey) {
509 /* Display error about long ckey */
510 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);
511 /* Return failure */
512 return NULL;
513 }
514
515 /* Check if ckey is empty */
516 if (ZSTR_LEN(ckey) == 0) {
517 /* Display error about long key */
518 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);
519 /* Return failure */
520 return NULL;
521 /* Check if current ckey is type */
522 } else if (strcmp("type", ZSTR_VAL(ckey)) == 0) {
523 /* Extract type */
524 type = Z_LVAL_P(cval);
525
526 /* Check that current type is valid */
527 if (
528 type != BBCODE_TYPE_ROOT &&
529 type != BBCODE_TYPE_MULTI &&
530 type != BBCODE_TYPE_SINGLE
531 ) {
532 /* Display error about unexpected type value */
533 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);
534 /* Return failure */
535 return NULL;
536 /* Check if root */
537 } else if (type == BBCODE_TYPE_ROOT) {
538 /* Grow hasRoot */
539 hasRoot++;
540 }
541
542 /* Set hasType */
543 hasType = 1;
544 /* Check if current key is parent */
545 } else if (strcmp("parent", ZSTR_VAL(ckey)) == 0) {
546 /* Check that value exists */
547 if (cval == NULL) {
548 /* Display error about long key */
549 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);
550 /* Return failure */
551 return NULL;
552 /* Check that value is an array */
553 } else if (Z_TYPE_P(cval) != IS_ARRAY) {
554 /* Display error about long key */
555 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));
556 /* Return failure */
557 return NULL;
558 /* Check that value is an non empty array */
559 } else if (zend_array_count(Z_ARR_P(cval)) == 0) {
560 /* Display error about empty parent array */
561 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);
562 /* Return failure */
563 return NULL;
564 }
565
566 /* Parent value */
567 zval *pval;
568
569 /* Child key position */
570 int ppos = 0;
571
572 /* Iterate on each val until hash end is reached */
573 ZEND_HASH_FOREACH_VAL(Z_ARR_P(cval), pval) {
574 /* Check that parent val is a string */
575 /* XXX: pval == NULL case is catched here too */
576 if (Z_TYPE_P(pval) != IS_STRING) {
577 /* Display error about not string */
578 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));
579 /* Return failure */
580 return NULL;
581 }
582
583 /* Tag key */
584 zend_string *tkey;
585
586 /* Reset found flag */
587 unsigned char found = 0;
588
589 /* Iterate on each key until hash end is reached */
590 ZEND_HASH_FOREACH_STR_KEY(tag, tkey) {
591 /* Check that tkey is a string and is identical */
592 if (tkey != NULL && strcmp(Z_STRVAL_P(pval), ZSTR_VAL(tkey)) == 0) {
593 /* We found parent value in tag keys*/
594 found = 1;
595 }
596 } ZEND_HASH_FOREACH_END();
597
598 /* Check if we found the key */
599 if (!found) {
600 /* Display error about long key */
601 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);
602 /* Return failure */
603 return NULL;
604 }
605
606 /* Grow ppos */
607 ppos++;
608 } ZEND_HASH_FOREACH_END();
609
610 /* Set hasParent */
611 hasParent = 1;
612 /* Check if current key is child */
613 } else if (strcmp("child", ZSTR_VAL(ckey)) == 0) {
614 /* Check that value exists */
615 if (cval == NULL) {
616 /* Display error about long key */
617 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);
618 /* Return failure */
619 return NULL;
620 /* Check that value is an array */
621 } else if (Z_TYPE_P(cval) != IS_ARRAY) {
622 /* Display error about long key */
623 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));
624 /* Return failure */
625 return NULL;
626 }
627
628 /* Child value */
629 zval *ccval;
630
631 /* Child key position */
632 int ccpos = 0;
633
634 /* Iterate on each val until hash end is reached */
635 ZEND_HASH_FOREACH_VAL(Z_ARR_P(cval), ccval) {
636 /* Check that child val is a string */
637 /* XXX: ccval == NULL case is catched here too */
638 if (Z_TYPE_P(ccval) != IS_STRING) {
639 /* Display error about not string */
640 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));
641 /* Return failure */
642 return NULL;
643 }
644
645 /* Tag key */
646 zend_string *tkey;
647
648 /* Reset found flag */
649 unsigned char found = 0;
650
651 /* Iterate on each key until hash end is reached */
652 ZEND_HASH_FOREACH_STR_KEY(tag, tkey) {
653 /* Check that tkey is a string and is identical */
654 if (tkey != NULL && strcmp(Z_STRVAL_P(ccval), ZSTR_VAL(tkey)) == 0) {
655 /* We found child value in tag keys*/
656 found = 1;
657 }
658 } ZEND_HASH_FOREACH_END();
659
660 /* Check if we found the key */
661 if (!found) {
662 /* Display error about long key */
663 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);
664 /* Return failure */
665 return NULL;
666 }
667
668 /* Grow ccpos */
669 ccpos++;
670 } ZEND_HASH_FOREACH_END();
671
672 /* Set hasChild */
673 hasChild = 1;
674 /* Check if current key is open */
675 } else if (strcmp("open", ZSTR_VAL(ckey)) == 0) {
676 /* Set hasOpen */
677 hasOpen = 1;
678 /* Check if current key is close */
679 } else if (strcmp("close", ZSTR_VAL(ckey)) == 0) {
680 /* Set hasClose */
681 hasClose = 1;
682 /* Check if current key is default */
683 } else if (strcmp("default", ZSTR_VAL(ckey)) == 0) {
684 /* Set hasDefault */
685 hasDefault = 1;
686 /* Check if current key is arg */
687 } else if (strcmp("arg", ZSTR_VAL(ckey)) == 0) {
688 /* Set hasArg */
689 hasArg = 1;
690 /* Check if current key is unknown */
691 } else {
692 /* Display error about long key */
693 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);
694 /* Return failure */
695 return NULL;
696 }
697
698 /* Grow cpos */
699 cpos++;
700 } ZEND_HASH_FOREACH_END();
701
702 /* Check if val array is immutable */
703 /* 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 */
704 if ((Z_GC_FLAGS_P(val) & GC_IMMUTABLE) == GC_IMMUTABLE) {
705 /* Allocate hash table */
706 /* XXX: this is required to avoid segfault in zend_hash_add when the array is immutable */
707 ALLOC_HASHTABLE(Z_ARR_P(val));
708
709 /* Init hash table */
710 zend_hash_init(Z_ARR_P(val), 0, NULL, ZVAL_PTR_DTOR, 0);
711
712 /* Copy old values in new array */
713 //XXX: disabled for now, array shoudln't need to be copied, immutable flag seems only present on empty arrays
714 //zend_hash_copy(Z_ARR_P(val), ctag, (copy_ctor_func_t) zval_add_ref);
715 }
716
717 /* Check if entry has no type */
718 /* TODO: make depend this code on a parse flag ? */
719 if (!hasType) {
720 /* Type key */
721 zend_string *tkey = zend_string_init("type", strlen("type"), 0);
722
723 /* Type value */
724 zval tval;
725
726 /* Check if key is '' */
727 if (ZSTR_LEN(key) == 0) {
728 /* Set tval value to root */
729 ZVAL_LONG(&tval, (type = BBCODE_TYPE_ROOT));
730
731 /* Grow hasRoot */
732 hasRoot++;
733 /* Check if key is img */
734 } else if (strcmp("img", ZSTR_VAL(key)) == 0) {
735 /* Set tval value to single */
736 ZVAL_LONG(&tval, (type = BBCODE_TYPE_SINGLE));
737 /* Handle other key as multi */
738 } else {
739 /* Set tval value to multi */
740 ZVAL_LONG(&tval, (type = BBCODE_TYPE_MULTI));
741 }
742
743 /* Add new type key */
744 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), tkey, &tval) != NULL);
745
746 /* Free type key */
747 zend_string_release(tkey);
748
749 /* Set hasType */
750 hasType = 1;
751 }
752
753 /* Check for root type */
754 if (type == BBCODE_TYPE_ROOT) {
755 /* Set root */
756 root = key;
757
758 /* Check if has parent */
759 if (hasParent) {
760 /* Display error about parent entry */
761 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);
762 /* Return failure */
763 return NULL;
764 }
765
766 /* Check if has open entry */
767 if (!hasOpen) {
768 /* Key string */
769 zend_string *mkey = zend_string_init("open", strlen("open"), 0);
770
771 /* Value */
772 zval mval;
773
774 /* Check if tag is empty */
775 if (ZSTR_LEN(key) == 0) {
776 /* Init new value */
777 ZVAL_EMPTY_STRING(&mval);
778 /* Non empty tag */
779 } else {
780 /* Init buffer and length */
781 buf = (char *)malloc((len = strlen("<>") + ZSTR_LEN(key) + (hasArg?strlen("%s"):0))*sizeof(char));
782
783 /* Generate string in */
784 ZEND_ASSERT(sprintf(buf, "<%s%s>", ZSTR_VAL(key), hasArg?"%s":"") == len);
785
786 /* Init new value */
787 ZVAL_STRINGL(&mval, buf, len);
788
789 /* Free buffer */
790 free(buf);
791 }
792
793 /* Add new key */
794 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), mkey, &mval) != NULL);
795
796 /* Free key */
797 zend_string_release(mkey);
798
799 /* Set hasOpen */
800 hasOpen = 1;
801 }
802
803 /* Check if has close entry */
804 if (!hasClose) {
805 /* Key string */
806 zend_string *mkey = zend_string_init("close", strlen("close"), 0);
807
808 /* Value */
809 zval mval;
810
811 /* Check if tag is empty */
812 if (ZSTR_LEN(key) == 0) {
813 /* Init new value */
814 ZVAL_EMPTY_STRING(&mval);
815 /* Non empty tag */
816 } else {
817 /* Init buffer and length */
818 buf = (char *)malloc((len = strlen("</>") + ZSTR_LEN(key))*sizeof(char));
819
820 /* Generate string in */
821 ZEND_ASSERT(sprintf(buf, "</%s>", ZSTR_VAL(key)) == len);
822
823 /* Init new value */
824 ZVAL_STRINGL(&mval, buf, len);
825
826 /* Free buffer */
827 free(buf);
828 }
829
830 /* Add new key */
831 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), mkey, &mval) != NULL);
832
833 /* Free key */
834 zend_string_release(mkey);
835
836 /* Set hasClose */
837 hasClose = 1;
838 }
839
840 /* Check if has not default */
841 if (!hasDefault) {
842 /* Key string */
843 zend_string *mkey = zend_string_init("default", strlen("default"), 0);
844
845 /* Value */
846 zval mval;
847
848 /* Set value */
849 ZVAL_STRING(&mval, "%s");
850
851 /* Add new key */
852 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), mkey, &mval) != NULL);
853
854 /* Free key */
855 zend_string_release(mkey);
856
857 /* Set hasDefault */
858 hasDefault = 1;
859 }
860
861 /* Compute child here */
862 if (!hasChild) {
863 /* Child value */
864 zval cval;
865
866 /* Init new child val array */
867 ZVAL_NEW_ARR(&cval);
868
869 /* Init hash table */
870 zend_hash_init(Z_ARR(cval), 0, NULL, ZVAL_PTR_DTOR, 0);
871
872 /* Compute and set child here */
873 /* XXX: we don't check or trigger error inside this function as these tests are done in current loop */
874 if (bbcode_gen_child(Z_ARR(cval), tag, key) != NULL) {
875 /* Child key string */
876 zend_string *ckey = zend_string_init("child", strlen("child"), 0);
877
878 /* Add new child key */
879 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), ckey, &cval) != NULL);
880
881 /* Free type key */
882 zend_string_release(ckey);
883
884 /* Set hasChild */
885 hasChild = 1;
886 /* Child generation reached an error case */
887 /* XXX: it will trigger error later */
888 } else {
889 /* Release hash table */
890 zend_array_destroy(Z_ARR(cval));
891 }
892 }
893 /* Check for multi type */
894 } else if (type == BBCODE_TYPE_MULTI) {
895 /* Check if has open entry */
896 if (!hasOpen) {
897 /* Key string */
898 zend_string *mkey = zend_string_init("open", strlen("open"), 0);
899
900 /* Value */
901 zval mval;
902
903 /* Check if tag is empty */
904 if (ZSTR_LEN(key) == 0) {
905 /* Init new value */
906 ZVAL_EMPTY_STRING(&mval);
907 /* Non empty tag */
908 } else {
909 /* Init buffer and length */
910 buf = (char *)malloc((len = strlen("<>") + ZSTR_LEN(key) + (hasArg?strlen("%s"):0))*sizeof(char));
911
912 /* Generate string in */
913 ZEND_ASSERT(sprintf(buf, "<%s%s>", ZSTR_VAL(key), hasArg?"%s":"") == len);
914
915 /* Init new value */
916 ZVAL_STRINGL(&mval, buf, len);
917
918 /* Free buffer */
919 free(buf);
920 }
921
922 /* Add new key */
923 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), mkey, &mval) != NULL);
924
925 /* Free key */
926 zend_string_release(mkey);
927
928 /* Set hasOpen */
929 hasOpen = 1;
930 }
931
932 /* Check if has close entry */
933 if (!hasClose) {
934 /* Key string */
935 zend_string *mkey = zend_string_init("close", strlen("close"), 0);
936
937 /* Value */
938 zval mval;
939
940 /* Check if tag is empty */
941 if (ZSTR_LEN(key) == 0) {
942 /* Init new value */
943 ZVAL_EMPTY_STRING(&mval);
944 /* Non empty tag */
945 } else {
946 /* Init buffer and length */
947 buf = (char *)malloc((len = strlen("</>") + ZSTR_LEN(key))*sizeof(char));
948
949 /* Generate string in */
950 ZEND_ASSERT(sprintf(buf, "</%s>", ZSTR_VAL(key)) == len);
951
952 /* Init new value */
953 ZVAL_STRINGL(&mval, buf, len);
954
955 /* Free buffer */
956 free(buf);
957 }
958
959 /* Add new key */
960 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), mkey, &mval) != NULL);
961
962 /* Free key */
963 zend_string_release(mkey);
964
965 /* Set hasClose */
966 hasClose = 1;
967 }
968
969 /* Check if has not default */
970 if (!hasDefault) {
971 /* Key string */
972 zend_string *mkey = zend_string_init("default", strlen("default"), 0);
973
974 /* Value */
975 zval mval;
976
977 /* Set value */
978 ZVAL_STRING(&mval, "%s");
979
980 /* Add new key */
981 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), mkey, &mval) != NULL);
982
983 /* Free key */
984 zend_string_release(mkey);
985
986 /* Set hasDefault */
987 hasDefault = 1;
988 }
989
990 /* Compute parent here */
991 if (!hasParent) {
992 /* Parent key string */
993 zend_string *pkey = zend_string_init("parent", strlen("parent"), 0);
994
995 /* Parent value */
996 zval pval;
997
998 /* Init new parent val array */
999 ZVAL_NEW_ARR(&pval);
1000
1001 /* Init hash table */
1002 zend_hash_init(Z_ARR(pval), 0, NULL, ZVAL_PTR_DTOR, 0);
1003
1004 /* Compute and set parent here */
1005 /* XXX: we don't check or trigger error inside this function as these tests are done in current loop */
1006 /* TODO: change this assert to a if() and free pval on error, error will trigger in later loop (code coverage tests to do) */
1007 ZEND_ASSERT(bbcode_gen_parent(Z_ARR(pval), tag, key) != NULL);
1008
1009 /* Add new parent key */
1010 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), pkey, &pval) != NULL);
1011
1012 /* Free type key */
1013 zend_string_release(pkey);
1014
1015 /* Set hasParent */
1016 hasParent = 1;
1017 }
1018
1019 /* Compute child here */
1020 if (!hasChild) {
1021 /* Child value */
1022 zval cval;
1023
1024 /* Init new child val array */
1025 ZVAL_NEW_ARR(&cval);
1026
1027 /* Init hash table */
1028 zend_hash_init(Z_ARR(cval), 0, NULL, ZVAL_PTR_DTOR, 0);
1029
1030 /* Compute and set child here */
1031 /* XXX: we don't check or trigger error inside this function as these tests are done in current loop */
1032 if (bbcode_gen_child(Z_ARR(cval), tag, key) != NULL) {
1033 /* Child key string */
1034 zend_string *ckey = zend_string_init("child", strlen("child"), 0);
1035
1036 /* Add new child key */
1037 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), ckey, &cval) != NULL);
1038
1039 /* Free type key */
1040 zend_string_release(ckey);
1041
1042 /* Set hasChild */
1043 hasChild = 1;
1044 /* Child generation reached an error case */
1045 /* XXX: it will trigger error later */
1046 } else {
1047 /* Release hash table */
1048 zend_array_destroy(Z_ARR(cval));
1049 }
1050 }
1051 /* Check for single type */
1052 } else if (type == BBCODE_TYPE_SINGLE) {
1053 /* Check if has open entry */
1054 if (hasOpen) {
1055 /* Display error about open entry */
1056 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);
1057 /* Return failure */
1058 return NULL;
1059 }
1060
1061 /* Check if has close entry */
1062 if (hasClose) {
1063 /* Display error about close entry */
1064 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);
1065 /* Return failure */
1066 return NULL;
1067 }
1068
1069 /* Check if has not default */
1070 if (!hasDefault) {
1071 /* Key string */
1072 zend_string *skey = zend_string_init("default", strlen("default"), 0);
1073
1074 /* Value */
1075 zval sval;
1076
1077 /* Check if key is img */
1078 if (strcmp("img", ZSTR_VAL(key)) == 0) {
1079 /* Init buffer and length */
1080 buf = (char *)malloc((len = strlen("<img src=\"%s\"/>") + (hasArg?strlen("%s"):0))*sizeof(char));
1081
1082 /* Generate string in */
1083 ZEND_ASSERT(sprintf(buf, "<img src=\"%%s\"%s/>", hasArg?"%s":"") == len);
1084 } else {
1085 /* Init buffer and length */
1086 buf = (char *)malloc((len = strlen("</>") + ZSTR_LEN(key) + (hasArg?strlen("%s"):0))*sizeof(char));
1087
1088 /* Generate string in */
1089 ZEND_ASSERT(sprintf(buf, "<%s%s/>", ZSTR_VAL(key), hasArg?"%s":"") == len);
1090 }
1091
1092 /* Init new value */
1093 ZVAL_STRINGL(&sval, buf, len);
1094
1095 /* Free buffer */
1096 free(buf);
1097
1098 /* Add new key */
1099 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), skey, &sval) != NULL);
1100
1101 /* Free key */
1102 zend_string_release(skey);
1103
1104 /* Set hasDefault */
1105 hasDefault = 1;
1106 }
1107
1108 /* Check if has child */
1109 if (hasChild) {
1110 /* Display error about child entry */
1111 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);
1112 /* Return failure */
1113 return NULL;
1114 }
1115
1116 /* Compute parent here */
1117 if (!hasParent) {
1118 /* Parent key string */
1119 zend_string *pkey = zend_string_init("parent", strlen("parent"), 0);
1120
1121 /* Parent value */
1122 zval pval;
1123
1124 /* Init new parent val array */
1125 ZVAL_NEW_ARR(&pval);
1126
1127 /* Init hash table */
1128 zend_hash_init(Z_ARR(pval), 0, NULL, ZVAL_PTR_DTOR, 0);
1129
1130 /* Compute and set parent here */
1131 /* XXX: we don't check or trigger error inside this function as these tests are done in current loop */
1132 /* TODO: change this asser to a if() and free pval on error, error will trigger in later loop (code coverage tests to do) */
1133 ZEND_ASSERT(bbcode_gen_parent(Z_ARR(pval), tag, key) != NULL);
1134
1135 /* Add new parent key */
1136 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), pkey, &pval) != NULL);
1137
1138 /* Free type key */
1139 zend_string_release(pkey);
1140
1141 /* Set hasParent */
1142 hasParent = 1;
1143 }
1144 }
1145
1146 /* Generate open and close pattern for tag with child */
1147 if (hasChild) {
1148 /* Child array and value */
1149 zval *child, *ccval;
1150
1151 /* Close pattern length */
1152 int len2, cpos2;
1153
1154 /* Close pattern buf */
1155 char *buf2;
1156
1157 /* Retrieve child array */
1158 /* XXX: child should exists here and be an array */
1159 ZEND_ASSERT(((child = zend_hash_str_find(Z_ARR_P(val), "child", strlen("child"))) != NULL) && Z_TYPE_P(child) == IS_ARRAY);
1160
1161 /* Init open pattern buffer and length */
1162 /* XXX: lagest bbcode tag length seems to be 7, we guess that 10 is enough to contain every tag and pipe */
1163 buf = (char *)malloc((len = strlen("/\\[()(?:=([^\\]]*))?\\]/") + zend_array_count(Z_ARR_P(child))*10)*sizeof(char));
1164
1165 /* Init close pattern buffer and length */
1166 /* XXX: lagest bbcode tag length seems to be 7, we guess that 10 is enough to contain every tag and pipe */
1167 buf2 = (char *)malloc((len2 = strlen("/\\[\\/()\\]/") + zend_array_count(Z_ARR_P(child))*10)*sizeof(char));
1168
1169 /* Init cpos and cpos2 */
1170 cpos = 4; cpos2 = 6;
1171
1172 /* Assign open pattern begin */
1173 ZEND_ASSERT(memcpy(buf, &"/\\[(", 4) != NULL);
1174
1175 /* Assign close pattern begin */
1176 ZEND_ASSERT(memcpy(buf2, &"/\\[\\/(", 6) != NULL);
1177
1178 /* Iterate on each val until hash end is reached */
1179 ZEND_HASH_FOREACH_VAL(Z_ARR_P(child), ccval) {
1180 /* Check that child val is a string */
1181 /* XXX: ccval should be an array here */
1182 ZEND_ASSERT(Z_TYPE_P(ccval) == IS_STRING);
1183
1184 /* Init zval node and type */
1185 zval *znode, *ztype;
1186
1187 /* Init zend_long type */
1188 zend_long ltype;
1189
1190 /* Retrieve the node */
1191 ZEND_ASSERT((znode = zend_hash_find(tag, Z_STR_P(ccval))) != NULL && Z_TYPE_P(znode) == IS_ARRAY);
1192
1193 /* Try to retrieve the type */
1194 //ZEND_ASSERT((ztype = zend_hash_str_find(Z_ARR_P(znode), "type", strlen("type"))) != NULL && Z_TYPE_P(ztype) == IS_LONG);
1195 /* XXX: the type may not yet available here, it is fixed when checking the tag itself later */
1196 if ((ztype = zend_hash_str_find(Z_ARR_P(znode), "type", strlen("type"))) == NULL || Z_TYPE_P(ztype) != IS_LONG) {
1197 /* Check if empty tag */
1198 if (Z_STRLEN_P(ccval) == 0) {
1199 /* Set as root */
1200 ltype = BBCODE_TYPE_ROOT;
1201 } else if (strcmp("img", Z_STRVAL_P(ccval)) == 0) {
1202 /* Set as single */
1203 ltype = BBCODE_TYPE_SINGLE;
1204 } else {
1205 /* Set as multi */
1206 ltype = BBCODE_TYPE_MULTI;
1207 }
1208 /* Type zval available and usable */
1209 } else {
1210 /* Set type from zval value */
1211 ltype = Z_LVAL_P(ztype);
1212 }
1213
1214 /* Check if not first string to add */
1215 if (cpos > 4) {
1216 /* Set pipe before next tag */
1217 *(buf+cpos) = '|';
1218 /* Move position */
1219 cpos++;
1220 /* Check if not single type */
1221 if (ltype != BBCODE_TYPE_SINGLE) {
1222 /* Set pipe before next tag */
1223 *(buf2+cpos2) = '|';
1224 /* Move position */
1225 cpos2++;
1226 }
1227 }
1228
1229 /* Check if not single type */
1230 if (ltype != BBCODE_TYPE_SINGLE) {
1231 /* Grow buf if necessary */
1232 if (cpos + Z_STRLEN_P(ccval) + 18 > len) {
1233 /* We resize buf to current length(cpos), the string size plus eighteen char for trailing pattern */
1234 buf = (char *)realloc(buf, (len = cpos + Z_STRLEN_P(ccval) + 18)*sizeof(char));
1235 }
1236
1237 /* Copy string in buf */
1238 ZEND_ASSERT(memcpy(buf+cpos, Z_STRVAL_P(ccval), Z_STRLEN_P(ccval)) != NULL);
1239
1240 /* Move cpos */
1241 cpos += Z_STRLEN_P(ccval);
1242
1243 /* Grow buf2 if necessary */
1244 if (cpos2 + Z_STRLEN_P(ccval) + 4 > len2) {
1245 /* We resize buf2 to current length(cpos2), the string size plus four char for trailing pattern */
1246 buf2 = (char *)realloc(buf2, (len2 = cpos2 + Z_STRLEN_P(ccval) + 4)*sizeof(char));
1247 }
1248
1249 /* Copy string in buf2 */
1250 ZEND_ASSERT(memcpy(buf2+cpos2, Z_STRVAL_P(ccval), Z_STRLEN_P(ccval)) != NULL);
1251
1252 /* Move cpos2 */
1253 cpos2 += Z_STRLEN_P(ccval);
1254 /* Single type case */
1255 } else {
1256 /* Grow buf if necessary */
1257 if (cpos + Z_STRLEN_P(ccval) + strlen("(?:\\/)?") + 18 > len) {
1258 /* We resize buf to current length(cpos), the string size plus three plus eighteen char for trailing pattern */
1259 buf = (char *)realloc(buf, (len = cpos + Z_STRLEN_P(ccval) + strlen("(?:\\/)?") + 18)*sizeof(char));
1260 }
1261
1262 /* Copy string in buf */
1263 ZEND_ASSERT(memcpy(buf+cpos, Z_STRVAL_P(ccval), Z_STRLEN_P(ccval)) != NULL);
1264
1265 /* Append pattern in buf */
1266 ZEND_ASSERT(memcpy(buf+cpos+Z_STRLEN_P(ccval), &"(?:\\/)?", strlen("(?:\\/)?")) != NULL);
1267
1268 /* Move cpos */
1269 cpos += Z_STRLEN_P(ccval) + strlen("(?:\\/)?");
1270 }
1271 } ZEND_HASH_FOREACH_END();
1272
1273 /* Assign open pattern begin */
1274 ZEND_ASSERT(memcpy(buf+cpos, &")(?:=([^\\]]*))?\\]/", 18) != NULL);
1275
1276 /* Assign open pattern begin */
1277 ZEND_ASSERT(memcpy(buf2+cpos2, &")\\]/", 4) != NULL);
1278
1279 /* Open key string */
1280 zend_string *okey = zend_string_init("opattern", strlen("opattern"), 0);
1281
1282 /* Close key string */
1283 zend_string *ckey = zend_string_init("cpattern", strlen("cpattern"), 0);
1284
1285 /* Open and close value */
1286 zval oval, cval;
1287
1288 /* Set open value */
1289 ZVAL_STRINGL(&oval, buf, cpos + 18);
1290
1291 /* Set close value */
1292 ZVAL_STRINGL(&cval, buf2, cpos2 + 4);
1293
1294 /* Free buffers */
1295 free(buf); free(buf2);
1296
1297 /* Add open key */
1298 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), okey, &oval) != NULL);
1299
1300 /* Add close key */
1301 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), ckey, &cval) != NULL);
1302
1303 /* Free open key */
1304 zend_string_release(okey);
1305
1306 /* Free close key */
1307 zend_string_release(ckey);
1308 }
1309
1310 /* Grow pos */
1311 pos++;
1312 } ZEND_HASH_FOREACH_END();
1313
1314 /* Check if not one unique root */
1315 if (hasRoot != 1) {
1316 /* Display error about multiple root */
1317 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Tag array has %d BBCODE::TYPE_ROOT entry instead of expected unique one", hasRoot);
1318 /* Return failure */
1319 return NULL;
1320 }
1321
1322 /* Duplicate root zend string to avoid it beeing freed */
1323 return zend_string_copy(root);
1324
1325 #if 0
1326
1327 /* Return success */
1328 return root;
1329 #endif
1330 }
1331 /* }}} */
1332
1333 /* {{{ static zend_bool *bbcode_check_smiley(HashTable *smiley TSRMLS_DC) {
1334 * Check that smiley hash is valid */
1335 static zend_bool bbcode_check_smiley(HashTable *smiley TSRMLS_DC) {
1336 /* Key numeric value */
1337 zend_long idx;
1338 /* Key string */
1339 zend_string *key;
1340 /* Value */
1341 zval *val;
1342
1343 /* Key position */
1344 int pos = 0;
1345
1346 /* Iterate on each key val until hash end is reached */
1347 ZEND_HASH_FOREACH_KEY_VAL(smiley, idx, key, val) {
1348 /* Check that key is a string */
1349 if (key == NULL) {
1350 /* Display error about long key */
1351 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Key %ld[%d] from argument smiley is not an expected string", idx, pos);
1352 /* Return failure */
1353 return 0;
1354 }
1355
1356 /* Check that value exists */
1357 if (val == NULL) {
1358 /* Display error about long key */
1359 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);
1360 /* Return failure */
1361 return 0;
1362 /* Check that value is not an array */
1363 } else if (Z_TYPE_P(val) == IS_ARRAY) {
1364 /* Display error about long key */
1365 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);
1366 /* Return failure */
1367 return 0;
1368 }
1369
1370 /* Grow pos */
1371 pos++;
1372 } ZEND_HASH_FOREACH_END();
1373
1374 /* Return success */
1375 return 1;
1376 }
1377 /* }}} */
1378
1379 /* {{{ PHP_METHOD(BBCode, __construct) { */
1380 PHP_METHOD(BBCode, __construct) {
1381 /* Init tag hash */
1382 HashTable *tag = NULL;
1383
1384 /* Init smiley hash */
1385 HashTable *smiley = NULL;
1386
1387 /* Init flag long */
1388 zend_long flag = BBCODE_DEFAULT_FLAG;
1389
1390 /* Zend error handling */
1391 zend_error_handling error_handling;
1392
1393 /* TODO: set tag array to a default value like this in case of missing/empty tag array :
1394 * [ '' => [ ? ] ]
1395 * (maybe later)
1396 */
1397
1398 /* TODO: init smiley with default list ? what about img storage ?
1399 * TODO: use prefix in an ini parameter ? ini_setable ?
1400 * TODO: make sure it's not really a global and per-app param ?
1401 * (maybe later)
1402 */
1403
1404 zend_replace_error_handling(EH_THROW, bbcode_exception_ce, &error_handling);
1405 /* Parse parameters */
1406 /*if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "h|hl", &tag, &smiley, &flag) == FAILURE) { return; }*/
1407 ZEND_PARSE_PARAMETERS_START(1, 3)
1408 /* Tag arg is required */
1409 Z_PARAM_ARRAY_HT(tag)
1410 /* We switch to optional args */
1411 Z_PARAM_OPTIONAL
1412 /* Smiley arg is optional and may be null */
1413 Z_PARAM_ARRAY_HT_EX(smiley, 1, 0)
1414 /* Flag arg is optional and may be null */
1415 Z_PARAM_LONG(flag)
1416 /* End parameters parse */
1417 ZEND_PARSE_PARAMETERS_END();
1418
1419 /* Retrieve the pointer to this object */
1420 bbcode_object *obj = Z_BBCODE_P(getThis());
1421
1422 /* Verify that the pointer is leading somewhere */
1423 assert(obj != NULL);
1424
1425 /* Set flag value */
1426 obj->flag = flag;
1427
1428 /* Duplicate hash to avoid modifications on original argument */
1429 obj->tag = zend_array_dup(tag);
1430
1431 /* Save tag argument hash in current object */
1432 if ((obj->root = bbcode_check_tag(obj->tag)) == NULL) {
1433 /* Restore error handler */
1434 zend_restore_error_handling(&error_handling);
1435
1436 /* Release the tag hash */
1437 zend_array_destroy(obj->tag);
1438
1439 /* Free the zend object */
1440 //XXX: seems useless, it don't seems possible to null the contructor return value...
1441 ZVAL_NULL(getThis() TSRMLS_CC);
1442
1443 /* Free the object */
1444 efree(obj);
1445
1446 /* Stop here */
1447 return;
1448 }
1449
1450 /* Check if smiley argument is available */
1451 if (smiley) {
1452 /* Duplicate hash to avoid modifications on original argument */
1453 obj->smiley = zend_array_dup(smiley);
1454
1455 /* Save smiley argument hash in current object */
1456 if (!bbcode_check_smiley(obj->smiley)) {
1457 /* Restore error handler */
1458 zend_restore_error_handling(&error_handling);
1459
1460 /* Release the root string */
1461 zend_string_release(obj->root);
1462
1463 /* Release the tag hash */
1464 zend_array_destroy(obj->tag);
1465
1466 /* Release the smiley hash */
1467 zend_array_destroy(obj->smiley);
1468
1469 /* Free the object */
1470 //XXX: seems useless, it don't seems possible to null the contructor return value...
1471 ZVAL_NULL(getThis() TSRMLS_CC);
1472
1473 /* Free the object */
1474 efree(obj);
1475
1476 /* Stop here */
1477 return;
1478 }
1479 }
1480
1481 /* Restore error handler */
1482 zend_restore_error_handling(&error_handling);
1483 }
1484 /* }}} */
1485
1486 /* {{{ PHP_METHOD(BBCode, __destruct) {
1487 * XXX: this function is called by bbcode_destroy function
1488 */
1489 PHP_METHOD(BBCode, __destruct) {
1490 //XXX: disable this code, we don't need to retrieve obj as we do nothing with userland destructor for now
1491 #if 0
1492 /* Retrieve the pointer to this object */
1493 bbcode_object *obj = Z_BBCODE_P(getThis());
1494
1495 /* Verify that the pointer is leading somewhere */
1496 assert(obj != NULL);
1497 #endif
1498 }
1499 /* }}} */
1500
1501 /* */
1502 //[HashTable *ret][zend_string *current][char *str][int offset][int length]
1503 static char *bbcode_parse(HashTable *tag, zend_string *cur, char *rs, int *ros, int *rlen, char *str, int len TSRMLS_DC) {
1504 /* Check if return string is not initialized or shorter than wanted length */
1505 if (rs == NULL || *rlen - *ros <= 1024) {
1506 /* Compute new length */
1507 *rlen = (rs == NULL) ? 2048 : *rlen + 1024;
1508
1509 /* Grow the return string */
1510 ZEND_ASSERT((rs = (char *)realloc(rs, (*rlen)*sizeof(char))) != NULL);
1511 }
1512
1513 //DEBUG: for reminder
1514 //str=skip0[r]string0[a=arg0]string1[/a]string2[b]string3[/b][/r]skip1
1515 //len=64
1516
1517 /* Init zval for current node and type */
1518 zval *znode, *ztype;
1519
1520 /* Retrieve current tag hash as zval */
1521 ZEND_ASSERT((znode = zend_hash_find(tag, cur)) != NULL && Z_TYPE_P(znode) == IS_ARRAY);
1522
1523 /* Retrieve current tag type */
1524 ZEND_ASSERT((ztype = zend_hash_str_find(Z_ARR_P(znode), "type", strlen("type"))) != NULL && Z_TYPE_P(ztype) == IS_LONG);
1525
1526 /* Retrieve current tag open */
1527 /* XXX: may be NULL if not present */
1528 zval *zopen = zend_hash_str_find(Z_ARR_P(znode), "open", strlen("open"));
1529
1530 /* Retrieve current tag close */
1531 /* XXX: may be NULL if not present */
1532 zval *zclose = zend_hash_str_find(Z_ARR_P(znode), "close", strlen("close"));
1533
1534 /* Retrieve current tag default */
1535 /* XXX: may be NULL if not present */
1536 zval *zdefault = zend_hash_str_find(Z_ARR_P(znode), "default", strlen("default"));
1537
1538 /* Retrieve current tag arg */
1539 /* XXX: may be NULL if not present */
1540 zval *zarg = zend_hash_str_find(Z_ARR_P(znode), "arg", strlen("arg"));
1541
1542 /* The arg zend string */
1543 zend_string *argstr = NULL;
1544
1545 /* Check if we have a not root and not empty tag */
1546 if (Z_LVAL_P(ztype) != BBCODE_TYPE_ROOT && strcmp(ZSTR_VAL(cur), "") != 0) {
1547 /* Regular expression */
1548 zend_string *regex;
1549
1550 /* Open and close pattern */
1551 char *openstr, *closestr, *found;
1552
1553 /* Open and close pattern length */
1554 int openlen, closelen;
1555
1556 /* Alloc open pattern buffer and set length */
1557 openstr = (char *)malloc(((openlen = strlen("/\\[\\]/") + (zarg == NULL ? strlen("(?:=[^\\]]*)?") : strlen("(?:=([^\\]]*))?")) + ZSTR_LEN(cur)) + 1)*sizeof(char));
1558
1559 /* Alloc close pattern buffer and set length */
1560 closestr = (char *)malloc(((closelen = strlen("[/]") + ZSTR_LEN(cur)) + 1)*sizeof(char));
1561
1562 /* Create open tag string */
1563 ZEND_ASSERT(snprintf(openstr, openlen + 1, "/\\[%s%s\\]/", ZSTR_VAL(cur), (zarg == NULL ? "(?:=[^\\]]*)?" : "(?:=([^\\]]*))?")) == openlen);
1564
1565 /* Duplicate open tag in regex */
1566 regex = zend_string_init(openstr, openlen, 0);
1567
1568 /* Free the close string */
1569 free(closestr);
1570
1571 /* Compiled regular expression */
1572 pcre_cache_entry *pce;
1573
1574 /* Array for subpatterns and return value */
1575 zval rv, subpats;
1576
1577 /* Init subpats array */
1578 /* XXX: if we don't initialize the array first, some strange memory corruption happen inside php_pcre_match_impl call */
1579 array_init(&subpats);
1580
1581 /* Compile regex or get it from cache. */
1582 /* TODO: see if we handle the error here ? (maybe later)
1583 * XXX: it seems unlikely any error could be recoverable if pcre function throw something
1584 * XXX: special char in tag is already protected earlier
1585 * XXX: maybe just check that the pcre error trigger an exception or something uncatchable in all case
1586 * XXX: see function pcre_get_compiled_regex_cache in ext/pcre/php_pcre.c +530 */
1587 ZEND_ASSERT((pce = pcre_get_compiled_regex_cache(regex)) != NULL);
1588
1589 /* Increase pce refcount */
1590 /* XXX: we use this instead of pce->refcount++; to avoid dereferencing compile error */
1591 php_pcre_pce_incref(pce);
1592
1593 /* Call zend pcre implementation */
1594 php_pcre_match_impl(pce, str, len, &rv, &subpats, 0, 0, 0, 0);
1595
1596 /* Decrease pce refcount */
1597 /* XXX: we use this instead of pce->refcount--; to avoid dereferencing compile error */
1598 php_pcre_pce_decref(pce);
1599
1600 /* Check that we found an occurence */
1601 if (Z_TYPE(rv) == IS_LONG && Z_LVAL(rv) == 1) {
1602 /* Init match zval */
1603 zval *match;
1604
1605 /* Init the matched tag string */
1606 //char *tstr;
1607
1608 /* Test that subpats is an array */
1609 ZEND_ASSERT(Z_TYPE(subpats) == IS_ARRAY);
1610
1611 /* Test that subpats members count is right */
1612 ZEND_ASSERT(zend_array_count(Z_ARR(subpats)) == (zarg == NULL ? 1 : 2));
1613
1614 /* Retrieve the matched full tag string */
1615 ZEND_ASSERT((match = zend_hash_index_find(Z_ARR(subpats), 0)) != NULL);
1616
1617 /* Test that match is a string */
1618 ZEND_ASSERT(Z_TYPE_P(match) == IS_STRING);
1619
1620 /* Find the tag in string */
1621 if ((found = (char *)zend_memnstr(str, Z_STRVAL_P(match), Z_STRLEN_P(match), str+len))) {
1622 /* Move str to found position + matched tag string */
1623 str = found + Z_STRLEN_P(match);
1624 }
1625
1626 /* Check if have and arg to retrieve */
1627 if (zarg != NULL) {
1628 /* Retrieve the matched arg string */
1629 ZEND_ASSERT((match = zend_hash_index_find(Z_ARR(subpats), 1)) != NULL);
1630
1631 /* Test that match is a string */
1632 ZEND_ASSERT(Z_TYPE_P(match) == IS_STRING);
1633
1634 /* Save arg string */
1635 argstr = Z_STR_P(match);
1636 }
1637
1638 //XXX: code unfinished, these todo are normal, work start here :)
1639 //TODO: set the new search position to the current offset plus found subpats[0] string length
1640 //TODO: save argument string for appending in return string if zarg is not NULL
1641 //TODO: see what happen if we find start tag and not end
1642 //TODO: see what happen if we find no start tag and no end
1643 //TODO: see what happen if we find no start tag and no end
1644
1645 /* No occurence found */
1646 } else {
1647 /* XXX: nothing to do ? skip end tag search ??? return to parent level ? */
1648 }
1649
1650 /* Destroy subpats array */
1651 zend_array_destroy(Z_ARR(subpats));
1652
1653 /* Create close tag string */
1654 //ZEND_ASSERT(snprintf(closestr, closelen + 1, "/\\[\\/%s\\]/", ZSTR_VAL(cur)) == closelen);
1655 ZEND_ASSERT(snprintf(closestr, closelen + 1, "[/%s]", ZSTR_VAL(cur)) == closelen);
1656
1657 /* Look for close tag */
1658 /* XXX: watch out, this is a maximum length to consider, the close tag may be present earlier for current leaf */
1659 if ((found = (char *)zend_memnrstr(str, closestr, closelen, str+len))) {
1660 /* Reduce len by the size of trailing string since close tag */
1661 len -= len + str - found;
1662 }
1663
1664 /* Free openstr and closestr */
1665 free(openstr);
1666 }
1667
1668 printf("str=%.*s\n", len, str);
1669 fflush(NULL);
1670
1671 return NULL;
1672
1673 #if 0
1674 fflush(NULL);
1675 printf("str=%s\n", str);
1676 #endif
1677
1678 /* XXX: debug */
1679 zval ztag;
1680 ZVAL_ARR(&ztag, tag);
1681 php_debug_zval_dump(&ztag, 0);
1682 fflush(NULL);
1683 exit(1);
1684
1685 php_debug_zval_dump(zopen, 0);
1686 php_debug_zval_dump(zclose, 0);
1687 php_debug_zval_dump(zdefault, 0);
1688 fflush(NULL);
1689 exit(1);
1690
1691 if (argstr != NULL) {
1692 printf("arg=%s[%ld]\n", ZSTR_VAL(argstr), ZSTR_LEN(argstr));
1693 fflush(NULL);
1694 exit(1);
1695 }
1696
1697 //XXX: same here unfinished, code start here :)
1698 //TODO: check what happen when we have something like that : [t]s[/t][t]p[/t]
1699 //1: compute open and close tag
1700 //2: extract end of open tag position and begin of close tag reverse searched in current space
1701 //(if we don't find end tag, consider positionning to end of string or rise error ? parse flag involved here)
1702 //3: append to return string open tag
1703 //4: loop for child tags in subspace and trigger recursion on space between current open and close tags
1704 //(loop involved here)
1705 //5: duplicate plain strings between each child in return string
1706 //6: append the close tag
1707
1708 #if 0
1709 /* Check if non empty string (other tag that empty root) */
1710 if (strcmp(ZSTR_VAL(cur), "") != 0) {
1711 //TODO: generate search patterns for opening tag and closing tag
1712 //TODO: search position of opening tag
1713 //TODO: search close tag
1714 //TODO: search for child tag
1715 /* Non root tag */
1716 } else {
1717 //Nothing to do, search offset for child will be 0 and length of search to len
1718 //TODO: search current end tag here ?
1719 }
1720
1721 /* Search next child node */
1722
1723 /**/
1724 //bbcode_search_node(ret,
1725 #endif
1726
1727 /* Return string by default */
1728 return rs;
1729 }
1730
1731 /* { { { PHP_METHOD(BBCode, parse) {
1732 */
1733 PHP_METHOD(BBCode, parse) {
1734 /* Init string to parse */
1735 zend_string *str = NULL;
1736
1737 /* Return string */
1738 //TODO: voir si on crée pas une struct pour éviter de trainer 3 arguments
1739 char *rs = NULL;
1740
1741 /* Return offset and length */
1742 int ros = 0, rlen = 0;
1743
1744 /* Parse parameters */
1745 /*if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "S", &str) == FAILURE) { return; }*/
1746 ZEND_PARSE_PARAMETERS_START(1, 1)
1747 Z_PARAM_STR(str)
1748 ZEND_PARSE_PARAMETERS_END();
1749
1750 /* Retrieve the pointer to this object */
1751 bbcode_object *obj = Z_BBCODE_P(getThis());
1752
1753 /* Init return string and set length to string * 2 plus last null char */
1754 //ZEND_ASSERT((rs = (char *)malloc((rlen = (2*Z_STRLEN(str)) + 1)*sizeof(char)) != NULL);
1755
1756 /* Go compute the tree */
1757 bbcode_parse(obj->tag, obj->root, rs, &ros, &rlen, ZSTR_VAL(str), ZSTR_LEN(str));
1758
1759 /* Free return string */
1760 free(rs);
1761
1762 #if 0
1763 /* Retrieve root node */
1764 zval *root = zend_hash_find(obj->tag, obj->root);
1765
1766 bbcode_
1767
1768 /* Retrieve child array */
1769 /* XXX: child should exists here and be an array */
1770 ZEND_ASSERT(((child = zend_hash_str_find(Z_ARR_P(val), "child", strlen("child"))) != NULL) && Z_TYPE_P(child) == IS_ARRAY);
1771 #endif
1772
1773 #if BBCODE_DEBUG
1774 /* Tag */
1775 zval tag;
1776
1777 /* Init new child val array */
1778 ZVAL_ARR(&tag, obj->tag);
1779
1780 php_debug_zval_dump(&tag, 0);
1781 fflush(NULL);
1782 #endif
1783
1784 //printf("flag=%ld\n", obj->flag);
1785 //printf("die ici: %s +%d\n", __FILE__, __LINE__);
1786
1787 //TODO: create a tree for storing result
1788 //TODO: apply recursively search of next possible tags (or a closing parent tag ?) regexp and store result inside
1789 //TODO: d
1790 //TODO: apply the smiley parsing on string
1791
1792 /* TODO: the return string */
1793 RETURN_STR(str);
1794 }
1795 /* }}} */
1796
1797 /* {{{ bbcode_methods[]
1798 *
1799 * Every BBCode visible function must have an entry in bbcode_methods[].
1800 */
1801 const zend_function_entry bbcode_methods[] = {
1802 PHP_ME(BBCode, __construct, bbcode_construct_arginfo, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
1803 PHP_ME(BBCode, __destruct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR)
1804 PHP_ME(BBCode, parse, bbcode_parse_arginfo, ZEND_ACC_PUBLIC)
1805 PHP_FE_END
1806 };
1807 /* }}} */
1808
1809 /* {{{ PHP_MINIT_FUNCTION
1810 */
1811 PHP_MINIT_FUNCTION(bbcode) {
1812 /* If you have INI entries, uncomment these lines
1813 REGISTER_INI_ENTRIES();
1814 */
1815
1816 /* Tmp zend class entry*/
1817 zend_class_entry tmp_ce;
1818
1819 /* Init BBCode class entry */
1820 INIT_CLASS_ENTRY(tmp_ce, "BBCode", bbcode_methods);
1821
1822 /* Register BBCode class entry */
1823 bbcode_ce = zend_register_internal_class(&tmp_ce TSRMLS_CC);
1824 bbcode_ce->create_object = bbcode_create;
1825
1826 /* Generate const */
1827 BBCODE_DEF(BBCODE_GEN_CONST)
1828
1829 /* Init BBCodeException class entry */
1830 INIT_CLASS_ENTRY(tmp_ce, "BBCodeException", NULL);
1831
1832 /* Register BBCodeException class entry */
1833 bbcode_exception_ce = zend_register_internal_class_ex(&tmp_ce, zend_exception_get_default(TSRMLS_C) TSRMLS_CC);
1834
1835 /* Send success */
1836 return SUCCESS;
1837 }
1838 /* }}} */
1839
1840 /* {{{ PHP_MSHUTDOWN_FUNCTION
1841 */
1842 PHP_MSHUTDOWN_FUNCTION(bbcode) {
1843 /* uncomment this line if you have INI entries
1844 UNREGISTER_INI_ENTRIES();
1845 */
1846 return SUCCESS;
1847 }
1848 /* }}} */
1849
1850 /* {{{ PHP_MINFO_FUNCTION
1851 */
1852 PHP_MINFO_FUNCTION(bbcode) {
1853 php_info_print_table_start();
1854 php_info_print_table_header(2, PHP_BBCODE_NAME " support", "enabled");
1855 php_info_print_table_row(2, "Extension version", PHP_BBCODE_VERSION);
1856 php_info_print_table_end();
1857
1858 /* Remove comments if you have entries in php.ini
1859 DISPLAY_INI_ENTRIES();
1860 */
1861 }
1862 /* }}} */
1863
1864 /* {{{ bbcode_module_entry
1865 */
1866 zend_module_entry bbcode_module_entry = {
1867 STANDARD_MODULE_HEADER,
1868 PHP_BBCODE_NAME, /* extension name */
1869 NULL, /* function list */
1870 PHP_MINIT(bbcode), /* process startup */
1871 PHP_MSHUTDOWN(bbcode), /* process shutdown */
1872 NULL, /* request startup */
1873 NULL, /* request shutdown */
1874 PHP_MINFO(bbcode), /* extension info */
1875 PHP_BBCODE_VERSION, /* extension version */
1876 STANDARD_MODULE_PROPERTIES
1877 };
1878 /* }}} */
1879
1880 #ifdef COMPILE_DL_BBCODE
1881 #ifdef ZTS
1882 ZEND_TSRMLS_CACHE_DEFINE()
1883 #endif
1884 ZEND_GET_MODULE(bbcode)
1885 #endif
1886
1887 /*
1888 * Local variables:
1889 * tab-width: 4
1890 * c-basic-offset: 4
1891 * End:
1892 * vim600: noet sw=4 ts=4 fdm=marker
1893 * vim<600: noet sw=4 ts=4
1894 */