1 /* The DOM tree navigation interface */
13 #include "dom/stack.h"
14 #include "dom/string.h"
15 #include "util/memory.h"
18 /* Navigator states */
20 #define DOM_STACK_STATE_GRANULARITY 0x7
21 #define DOM_STACK_CALLBACKS_SIZE (sizeof(dom_stack_callback_T) * DOM_NODES)
23 static inline struct dom_stack_state
*
24 realloc_dom_stack_states(struct dom_stack_state
**states
, size_t size
)
26 return mem_align_alloc(states
, size
, size
+ 1,
27 struct dom_stack_state
,
28 DOM_STACK_STATE_GRANULARITY
);
31 static inline struct dom_stack_state
*
32 realloc_dom_stack_context(struct dom_stack_context
***contexts
, size_t size
)
34 return mem_align_alloc(contexts
, size
, size
+ 1,
35 struct dom_stack_context
*,
36 DOM_STACK_STATE_GRANULARITY
);
39 static inline unsigned char *
40 realloc_dom_stack_state_objects(struct dom_stack_context
*context
, size_t depth
)
43 return mem_align_alloc__(__FILE__
, __LINE__
, (void **) &context
->state_objects
,
45 context
->info
->object_size
,
46 DOM_STACK_STATE_GRANULARITY
);
48 return mem_align_alloc__((void **) &context
->state_objects
,
50 context
->info
->object_size
,
51 DOM_STACK_STATE_GRANULARITY
);
56 init_dom_stack(struct dom_stack
*stack
, enum dom_stack_flag flags
)
60 memset(stack
, 0, sizeof(*stack
));
66 done_dom_stack(struct dom_stack
*stack
)
72 for (i
= 0; i
< stack
->contexts_size
; i
++) {
73 mem_free_if(stack
->contexts
[i
]->state_objects
);
74 mem_free(stack
->contexts
[i
]);
77 mem_free_if(stack
->contexts
);
78 mem_free_if(stack
->states
);
80 memset(stack
, 0, sizeof(*stack
));
83 struct dom_stack_context
*
84 add_dom_stack_context(struct dom_stack
*stack
, void *data
,
85 struct dom_stack_context_info
*context_info
)
87 struct dom_stack_context
*context
;
89 if (!realloc_dom_stack_context(&stack
->contexts
, stack
->contexts_size
))
92 context
= mem_calloc(1, sizeof(*context
));
96 stack
->contexts
[stack
->contexts_size
++] = context
;
97 context
->info
= context_info
;
104 done_dom_stack_context(struct dom_stack
*stack
, struct dom_stack_context
*context
)
108 mem_free_if(context
->state_objects
);
111 /* Handle the trivial case of temporary contexts optimally by iteration last added first. */
112 for (i
= stack
->contexts_size
- 1; i
>= 0; i
--) {
113 if (stack
->contexts
[i
] != context
)
116 stack
->contexts_size
--;
117 if (i
< stack
->contexts_size
) {
118 struct dom_stack_context
**pos
= &stack
->contexts
[i
];
119 size_t size
= stack
->contexts_size
- i
;
121 memmove(pos
, pos
+ 1, size
* sizeof(*pos
));
127 enum dom_stack_action
{
133 call_dom_stack_callbacks(struct dom_stack
*stack
, struct dom_stack_state
*state
,
134 enum dom_stack_action action
)
138 for (i
= 0; i
< stack
->contexts_size
; i
++) {
139 struct dom_stack_context
*context
= stack
->contexts
[i
];
140 dom_stack_callback_T callback
;
142 if (action
== DOM_STACK_PUSH
)
143 callback
= context
->info
->push
[state
->node
->type
];
145 callback
= context
->info
->pop
[state
->node
->type
];
148 void *data
= get_dom_stack_state_data(context
, state
);
150 stack
->current
= context
;
151 callback(stack
, state
->node
, data
);
152 stack
->current
= NULL
;
158 push_dom_node(struct dom_stack
*stack
, struct dom_node
*node
)
160 struct dom_stack_state
*state
;
163 assert(stack
&& node
);
164 assert(0 < node
->type
&& node
->type
< DOM_NODES
);
166 if (stack
->depth
> DOM_STACK_MAX_DEPTH
) {
170 state
= realloc_dom_stack_states(&stack
->states
, stack
->depth
);
176 state
+= stack
->depth
;
178 for (i
= 0; i
< stack
->contexts_size
; i
++) {
179 struct dom_stack_context
*context
= stack
->contexts
[i
];
181 if (context
->info
->object_size
182 && !realloc_dom_stack_state_objects(context
, stack
->depth
)) {
188 state
->depth
= stack
->depth
;
191 /* Grow the state array to the new depth so the state accessors work
192 * in the callbacks */
194 call_dom_stack_callbacks(stack
, state
, DOM_STACK_PUSH
);
200 pop_dom_node(struct dom_stack
*stack
)
202 struct dom_stack_state
*state
;
207 if (dom_stack_is_empty(stack
))
210 state
= get_dom_stack_top(stack
);
211 if (state
->immutable
)
214 call_dom_stack_callbacks(stack
, state
, DOM_STACK_POP
);
216 if (!(stack
->flags
& DOM_STACK_KEEP_NODES
))
217 done_dom_node(state
->node
);
220 assert(stack
->depth
>= 0);
222 for (i
= 0; i
< stack
->contexts_size
; i
++) {
223 struct dom_stack_context
*context
= stack
->contexts
[i
];
225 if (context
->info
->object_size
) {
226 void *state_data
= get_dom_stack_state_data(context
, state
);
228 memset(state_data
, 0, context
->info
->object_size
);
232 memset(state
, 0, sizeof(*state
));
236 pop_dom_nodes(struct dom_stack
*stack
, enum dom_node_type type
,
237 struct dom_string
*string
)
239 struct dom_stack_state
*state
;
243 if (dom_stack_is_empty(stack
)) return;
245 state
= search_dom_stack(stack
, type
, string
);
247 pop_dom_state(stack
, state
);
251 pop_dom_state(struct dom_stack
*stack
, struct dom_stack_state
*target
)
253 struct dom_stack_state
*state
;
260 if (dom_stack_is_empty(stack
)) return;
262 foreachback_dom_stack_state (stack
, state
, pos
) {
263 /* Don't pop past states marked immutable. */
264 if (state
->immutable
)
267 /* Pop until the target state is reached. */
274 struct dom_stack_state
*
275 search_dom_stack(struct dom_stack
*stack
, enum dom_node_type type
,
276 struct dom_string
*string
)
278 struct dom_stack_state
*state
;
281 /* FIXME: Take node subtype and compare if non-zero or something. */
282 foreachback_dom_stack_state (stack
, state
, pos
) {
283 struct dom_node
*parent
= state
->node
;
285 if (parent
->type
== type
286 && !dom_string_casecmp(&parent
->string
, string
))
294 struct dom_stack_walk_state
{
295 /* Used for recording which node list are currently being 'decended'
296 * into. E.g. whether we are iterating all child elements or attributes
298 struct dom_node_list
*list
;
299 /* The index (in the list above) which are currently being handled. */
303 static struct dom_stack_context_info dom_stack_walk_context_info
= {
304 /* Object size: */ sizeof(struct dom_stack_walk_state
),
308 /* DOM_NODE_ELEMENT */ NULL
,
309 /* DOM_NODE_ATTRIBUTE */ NULL
,
310 /* DOM_NODE_TEXT */ NULL
,
311 /* DOM_NODE_CDATA_SECTION */ NULL
,
312 /* DOM_NODE_ENTITY_REFERENCE */ NULL
,
313 /* DOM_NODE_ENTITY */ NULL
,
314 /* DOM_NODE_PROC_INSTRUCTION */ NULL
,
315 /* DOM_NODE_COMMENT */ NULL
,
316 /* DOM_NODE_DOCUMENT */ NULL
,
317 /* DOM_NODE_DOCUMENT_TYPE */ NULL
,
318 /* DOM_NODE_DOCUMENT_FRAGMENT */ NULL
,
319 /* DOM_NODE_NOTATION */ NULL
,
324 /* DOM_NODE_ELEMENT */ NULL
,
325 /* DOM_NODE_ATTRIBUTE */ NULL
,
326 /* DOM_NODE_TEXT */ NULL
,
327 /* DOM_NODE_CDATA_SECTION */ NULL
,
328 /* DOM_NODE_ENTITY_REFERENCE */ NULL
,
329 /* DOM_NODE_ENTITY */ NULL
,
330 /* DOM_NODE_PROC_INSTRUCTION */ NULL
,
331 /* DOM_NODE_COMMENT */ NULL
,
332 /* DOM_NODE_DOCUMENT */ NULL
,
333 /* DOM_NODE_DOCUMENT_TYPE */ NULL
,
334 /* DOM_NODE_DOCUMENT_FRAGMENT */ NULL
,
335 /* DOM_NODE_NOTATION */ NULL
,
339 /* FIXME: Instead of walking all nodes in the tree only visit those which are
340 * of actual interest to the contexts on the stack. */
342 walk_dom_nodes(struct dom_stack
*stack
, struct dom_node
*root
)
344 struct dom_stack_context
*context
;
346 assert(root
&& stack
);
348 context
= add_dom_stack_context(stack
, NULL
, &dom_stack_walk_context_info
);
352 push_dom_node(stack
, root
);
354 while (!dom_stack_is_empty(stack
)) {
355 struct dom_stack_state
*state
= get_dom_stack_top(stack
);
356 struct dom_stack_walk_state
*wstate
= get_dom_stack_state_data(context
, state
);
357 struct dom_node_list
*list
= wstate
->list
;
358 struct dom_node
*node
= state
->node
;
360 switch (node
->type
) {
361 case DOM_NODE_DOCUMENT
:
362 if (!list
) list
= node
->data
.document
.children
;
365 case DOM_NODE_ELEMENT
:
366 if (!list
) list
= node
->data
.element
.map
;
368 if (list
== node
->data
.element
.children
) break;
369 if (is_dom_node_list_member(list
, wstate
->index
)
370 && list
== node
->data
.element
.map
)
373 list
= node
->data
.element
.children
;
376 case DOM_NODE_PROCESSING_INSTRUCTION
:
377 if (!list
) list
= node
->data
.proc_instruction
.map
;
380 case DOM_NODE_DOCUMENT_TYPE
:
381 if (!list
) list
= node
->data
.document_type
.entities
;
383 if (list
== node
->data
.document_type
.notations
) break;
384 if (is_dom_node_list_member(list
, wstate
->index
)
385 && list
== node
->data
.document_type
.entities
)
388 list
= node
->data
.document_type
.notations
;
391 case DOM_NODE_ATTRIBUTE
:
393 case DOM_NODE_CDATA_SECTION
:
394 case DOM_NODE_COMMENT
:
395 case DOM_NODE_NOTATION
:
396 case DOM_NODE_DOCUMENT_FRAGMENT
:
397 case DOM_NODE_ENTITY_REFERENCE
:
398 case DOM_NODE_ENTITY
:
403 /* Reset list state if it is a new list */
404 if (list
!= wstate
->list
) {
409 /* If we have next child node */
410 if (is_dom_node_list_member(list
, wstate
->index
)) {
411 struct dom_node
*child
= list
->entries
[wstate
->index
++];
413 if (push_dom_node(stack
, child
))
420 done_dom_stack_context(stack
, context
);
424 /* DOM Stack Tracing: */
426 #ifdef DOM_STACK_TRACE
428 #include "util/string.h"
430 /* Compress a string to a single line with newlines etc. replaced with "\\n"
432 static inline unsigned char *
433 compress_string(unsigned char *string
, unsigned int length
)
435 struct string buffer
;
436 unsigned char escape
[2] = "\\";
438 if (!init_string(&buffer
)) return NULL
;
440 for (; length
> 0; string
++, length
--) {
441 unsigned char *bytes
= string
;
443 if (*string
== '\n' || *string
== '\r' || *string
== '\t') {
445 escape
[1] = *string
== '\n' ? 'n'
446 : (*string
== '\r' ? 'r' : 't');
449 add_bytes_to_string(&buffer
, bytes
, bytes
== escape
? 2 : 1);
452 return buffer
.source
;
455 /* Set @string to the value of the given @node, however, with strings
456 * compressed and entity references 'expanded'. */
458 set_enhanced_dom_node_value(struct dom_string
*string
, struct dom_node
*node
)
460 struct dom_string
*value
;
464 memset(string
, 0, sizeof(*string
));
466 switch (node
->type
) {
467 case DOM_NODE_ENTITY_REFERENCE
:
468 /* FIXME: Set to the entity value. */
469 string
->string
= null_or_stracpy(string
->string
);
473 value
= get_dom_node_value(node
);
475 set_dom_string(string
, NULL
, 0);
479 string
->string
= compress_string(value
->string
, value
->length
);
482 string
->length
= string
->string
? strlen(string
->string
) : 0;
485 static unsigned char indent_string
[] =
488 #define get_indent_offset(stack) \
489 ((stack)->depth < sizeof(indent_string)/2 ? (stack)->depth * 2 : sizeof(indent_string))
492 dom_stack_trace_tree(struct dom_stack
*stack
, struct dom_node
*node
, void *data
)
494 struct dom_string
*value
= &node
->string
;
495 struct dom_string
*name
= get_dom_node_name(node
);
497 LOG_INFO("%s%.*s %.*s: %.*s",
498 empty_string_or_(stack
->current
->data
),
499 get_indent_offset(stack
), indent_string
,
500 name
->length
, name
->string
,
501 value
->length
, value
->string
);
505 dom_stack_trace_id_leaf(struct dom_stack
*stack
, struct dom_node
*node
, void *data
)
507 struct dom_string value
;
508 struct dom_string
*name
;
509 struct dom_string
*id
;
513 name
= get_dom_node_name(node
);
514 id
= get_dom_node_type_name(node
->type
);
515 set_enhanced_dom_node_value(&value
, node
);
517 LOG_INFO("%s%.*s %.*s: %.*s -> %.*s",
518 empty_string_or_(stack
->current
->data
),
519 get_indent_offset(stack
), indent_string
,
520 id
->length
, id
->string
, name
->length
, name
->string
,
521 value
.length
, value
.string
);
523 if (is_dom_string_set(&value
))
524 done_dom_string(&value
);
528 dom_stack_trace_leaf(struct dom_stack
*stack
, struct dom_node
*node
, void *data
)
530 struct dom_string
*name
;
531 struct dom_string value
;
535 name
= get_dom_node_name(node
);
536 set_enhanced_dom_node_value(&value
, node
);
538 LOG_INFO("%s%.*s %.*s: %.*s",
539 empty_string_or_(stack
->current
->data
),
540 get_indent_offset(stack
), indent_string
,
541 name
->length
, name
->string
,
542 value
.length
, value
.string
);
544 if (is_dom_string_set(&value
))
545 done_dom_string(&value
);
549 dom_stack_trace_branch(struct dom_stack
*stack
, struct dom_node
*node
, void *data
)
551 struct dom_string
*name
;
552 struct dom_string
*id
;
556 name
= get_dom_node_name(node
);
557 id
= get_dom_node_type_name(node
->type
);
559 LOG_INFO("%s%.*s %.*s: %.*s",
560 empty_string_or_(stack
->current
->data
),
561 get_indent_offset(stack
), indent_string
,
562 id
->length
, id
->string
, name
->length
, name
->string
);
565 struct dom_stack_context_info dom_stack_trace_context_info
= {
566 /* Object size: */ 0,
570 /* DOM_NODE_ELEMENT */ dom_stack_trace_branch
,
571 /* DOM_NODE_ATTRIBUTE */ dom_stack_trace_id_leaf
,
572 /* DOM_NODE_TEXT */ dom_stack_trace_leaf
,
573 /* DOM_NODE_CDATA_SECTION */ dom_stack_trace_id_leaf
,
574 /* DOM_NODE_ENTITY_REFERENCE */ dom_stack_trace_id_leaf
,
575 /* DOM_NODE_ENTITY */ dom_stack_trace_id_leaf
,
576 /* DOM_NODE_PROC_INSTRUCTION */ dom_stack_trace_id_leaf
,
577 /* DOM_NODE_COMMENT */ dom_stack_trace_leaf
,
578 /* DOM_NODE_DOCUMENT */ dom_stack_trace_tree
,
579 /* DOM_NODE_DOCUMENT_TYPE */ dom_stack_trace_id_leaf
,
580 /* DOM_NODE_DOCUMENT_FRAGMENT */ dom_stack_trace_id_leaf
,
581 /* DOM_NODE_NOTATION */ dom_stack_trace_id_leaf
,
586 /* DOM_NODE_ELEMENT */ NULL
,
587 /* DOM_NODE_ATTRIBUTE */ NULL
,
588 /* DOM_NODE_TEXT */ NULL
,
589 /* DOM_NODE_CDATA_SECTION */ NULL
,
590 /* DOM_NODE_ENTITY_REFERENCE */ NULL
,
591 /* DOM_NODE_ENTITY */ NULL
,
592 /* DOM_NODE_PROC_INSTRUCTION */ NULL
,
593 /* DOM_NODE_COMMENT */ NULL
,
594 /* DOM_NODE_DOCUMENT */ NULL
,
595 /* DOM_NODE_DOCUMENT_TYPE */ NULL
,
596 /* DOM_NODE_DOCUMENT_FRAGMENT */ NULL
,
597 /* DOM_NODE_NOTATION */ NULL
,
601 #endif /* DOM_STACK_TRACE */