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 DOM_STACK_STATE_GRANULARITY
);
30 static inline struct dom_stack_state
*
31 realloc_dom_stack_context(struct dom_stack_context
***contexts
, size_t size
)
33 return mem_align_alloc(contexts
, size
, size
+ 1,
34 DOM_STACK_STATE_GRANULARITY
);
37 static inline unsigned char *
38 realloc_dom_stack_state_objects(struct dom_stack_context
*context
, size_t depth
)
41 return mem_align_alloc__(__FILE__
, __LINE__
, (void **) &context
->state_objects
,
43 context
->info
->object_size
,
44 DOM_STACK_STATE_GRANULARITY
);
46 return mem_align_alloc__((void **) &context
->state_objects
,
48 context
->info
->object_size
,
49 DOM_STACK_STATE_GRANULARITY
);
54 init_dom_stack(struct dom_stack
*stack
, enum dom_stack_flag flags
)
58 memset(stack
, 0, sizeof(*stack
));
64 done_dom_stack(struct dom_stack
*stack
)
70 for (i
= 0; i
< stack
->contexts_size
; i
++) {
71 mem_free_if(stack
->contexts
[i
]->state_objects
);
72 mem_free(stack
->contexts
[i
]);
75 mem_free_if(stack
->contexts
);
76 mem_free_if(stack
->states
);
78 memset(stack
, 0, sizeof(*stack
));
81 struct dom_stack_context
*
82 add_dom_stack_context(struct dom_stack
*stack
, void *data
,
83 struct dom_stack_context_info
*context_info
)
85 struct dom_stack_context
*context
;
87 if (!realloc_dom_stack_context(&stack
->contexts
, stack
->contexts_size
))
90 context
= mem_calloc(1, sizeof(*context
));
94 stack
->contexts
[stack
->contexts_size
++] = context
;
95 context
->info
= context_info
;
102 done_dom_stack_context(struct dom_stack
*stack
, struct dom_stack_context
*context
)
106 mem_free_if(context
->state_objects
);
109 /* Handle the trivial case of temporary contexts optimally by iteration last added first. */
110 for (i
= stack
->contexts_size
- 1; i
>= 0; i
--) {
111 if (stack
->contexts
[i
] != context
)
114 stack
->contexts_size
--;
115 if (i
< stack
->contexts_size
) {
116 struct dom_stack_context
**pos
= &stack
->contexts
[i
];
117 size_t size
= stack
->contexts_size
- i
;
119 memmove(pos
, pos
+ 1, size
* sizeof(*pos
));
125 enum dom_stack_action
{
130 /* Returns whether the node should be freed with done_dom_node(). */
132 call_dom_stack_callbacks(struct dom_stack
*stack
, struct dom_stack_state
*state
,
133 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 assert(state
->node
->type
< DOM_NODES
); /* unsigned comparison */
144 /* The node type is out of range for the
145 * callback arrays. The node may have been
146 * corrupted or already freed. Ignore
147 * free_node here because attempting to free
148 * the node would probably just corrupt things
153 if (action
== DOM_STACK_PUSH
)
154 callback
= context
->info
->push
[state
->node
->type
];
156 callback
= context
->info
->pop
[state
->node
->type
];
159 void *data
= get_dom_stack_state_data(context
, state
);
161 stack
->current
= context
;
162 switch (callback(stack
, state
->node
, data
)) {
163 case DOM_CODE_FREE_NODE
:
169 stack
->current
= NULL
;
177 push_dom_node(struct dom_stack
*stack
, struct dom_node
*node
)
179 struct dom_stack_state
*state
;
182 assert(stack
&& node
);
183 assert(0 < node
->type
&& node
->type
< DOM_NODES
);
185 if (stack
->depth
> DOM_STACK_MAX_DEPTH
) {
186 return DOM_CODE_MAX_DEPTH_ERR
;
189 state
= realloc_dom_stack_states(&stack
->states
, stack
->depth
);
192 return DOM_CODE_ALLOC_ERR
;
195 state
+= stack
->depth
;
197 for (i
= 0; i
< stack
->contexts_size
; i
++) {
198 struct dom_stack_context
*context
= stack
->contexts
[i
];
200 if (context
->info
->object_size
201 && !realloc_dom_stack_state_objects(context
, stack
->depth
)) {
203 return DOM_CODE_ALLOC_ERR
;
207 state
->depth
= stack
->depth
;
210 /* Grow the state array to the new depth so the state accessors work
211 * in the callbacks */
213 call_dom_stack_callbacks(stack
, state
, DOM_STACK_PUSH
);
219 pop_dom_node(struct dom_stack
*stack
)
221 struct dom_stack_state
*state
;
226 if (dom_stack_is_empty(stack
))
229 state
= get_dom_stack_top(stack
);
230 if (state
->immutable
)
233 if (call_dom_stack_callbacks(stack
, state
, DOM_STACK_POP
)
234 || (stack
->flags
& DOM_STACK_FLAG_FREE_NODES
))
235 done_dom_node(state
->node
);
238 assert(stack
->depth
>= 0);
240 for (i
= 0; i
< stack
->contexts_size
; i
++) {
241 struct dom_stack_context
*context
= stack
->contexts
[i
];
243 if (context
->info
->object_size
) {
244 void *state_data
= get_dom_stack_state_data(context
, state
);
246 memset(state_data
, 0, context
->info
->object_size
);
250 memset(state
, 0, sizeof(*state
));
254 pop_dom_nodes(struct dom_stack
*stack
, enum dom_node_type type
,
255 struct dom_string
*string
)
257 struct dom_stack_state
*state
;
261 if (dom_stack_is_empty(stack
)) return;
263 state
= search_dom_stack(stack
, type
, string
);
265 pop_dom_state(stack
, state
);
269 pop_dom_state(struct dom_stack
*stack
, struct dom_stack_state
*target
)
271 struct dom_stack_state
*state
;
278 if (dom_stack_is_empty(stack
)) return;
280 foreachback_dom_stack_state (stack
, state
, pos
) {
281 /* Don't pop past states marked immutable. */
282 if (state
->immutable
)
285 /* Pop until the target state is reached. */
292 struct dom_stack_state
*
293 search_dom_stack(struct dom_stack
*stack
, enum dom_node_type type
,
294 struct dom_string
*string
)
296 struct dom_stack_state
*state
;
299 /* FIXME: Take node subtype and compare if non-zero or something. */
300 foreachback_dom_stack_state (stack
, state
, pos
) {
301 struct dom_node
*parent
= state
->node
;
303 if (parent
->type
== type
304 && !dom_string_casecmp(&parent
->string
, string
))
312 struct dom_stack_walk_state
{
313 /* Used for recording which node list are currently being 'decended'
314 * into. E.g. whether we are iterating all child elements or attributes
316 struct dom_node_list
*list
;
317 /* The index (in the list above) which are currently being handled. */
321 static struct dom_stack_context_info dom_stack_walk_context_info
= {
322 /* Object size: */ sizeof(struct dom_stack_walk_state
),
326 /* DOM_NODE_ELEMENT */ NULL
,
327 /* DOM_NODE_ATTRIBUTE */ NULL
,
328 /* DOM_NODE_TEXT */ NULL
,
329 /* DOM_NODE_CDATA_SECTION */ NULL
,
330 /* DOM_NODE_ENTITY_REFERENCE */ NULL
,
331 /* DOM_NODE_ENTITY */ NULL
,
332 /* DOM_NODE_PROC_INSTRUCTION */ NULL
,
333 /* DOM_NODE_COMMENT */ NULL
,
334 /* DOM_NODE_DOCUMENT */ NULL
,
335 /* DOM_NODE_DOCUMENT_TYPE */ NULL
,
336 /* DOM_NODE_DOCUMENT_FRAGMENT */ NULL
,
337 /* DOM_NODE_NOTATION */ NULL
,
342 /* DOM_NODE_ELEMENT */ NULL
,
343 /* DOM_NODE_ATTRIBUTE */ NULL
,
344 /* DOM_NODE_TEXT */ NULL
,
345 /* DOM_NODE_CDATA_SECTION */ NULL
,
346 /* DOM_NODE_ENTITY_REFERENCE */ NULL
,
347 /* DOM_NODE_ENTITY */ NULL
,
348 /* DOM_NODE_PROC_INSTRUCTION */ NULL
,
349 /* DOM_NODE_COMMENT */ NULL
,
350 /* DOM_NODE_DOCUMENT */ NULL
,
351 /* DOM_NODE_DOCUMENT_TYPE */ NULL
,
352 /* DOM_NODE_DOCUMENT_FRAGMENT */ NULL
,
353 /* DOM_NODE_NOTATION */ NULL
,
357 /* FIXME: Instead of walking all nodes in the tree only visit those which are
358 * of actual interest to the contexts on the stack. */
360 walk_dom_nodes(struct dom_stack
*stack
, struct dom_node
*root
)
362 struct dom_stack_context
*context
;
364 assert(root
&& stack
);
366 context
= add_dom_stack_context(stack
, NULL
, &dom_stack_walk_context_info
);
370 if (push_dom_node(stack
, root
) != DOM_CODE_OK
)
373 while (!dom_stack_is_empty(stack
)) {
374 struct dom_stack_state
*state
= get_dom_stack_top(stack
);
375 struct dom_stack_walk_state
*wstate
= get_dom_stack_state_data(context
, state
);
376 struct dom_node_list
*list
= wstate
->list
;
377 struct dom_node
*node
= state
->node
;
379 switch (node
->type
) {
380 case DOM_NODE_DOCUMENT
:
381 if (!list
) list
= node
->data
.document
.children
;
384 case DOM_NODE_ELEMENT
:
385 if (!list
) list
= node
->data
.element
.map
;
387 if (list
== node
->data
.element
.children
) break;
388 if (is_dom_node_list_member(list
, wstate
->index
)
389 && list
== node
->data
.element
.map
)
392 list
= node
->data
.element
.children
;
395 case DOM_NODE_PROCESSING_INSTRUCTION
:
396 if (!list
) list
= node
->data
.proc_instruction
.map
;
399 case DOM_NODE_DOCUMENT_TYPE
:
400 if (!list
) list
= node
->data
.document_type
.entities
;
402 if (list
== node
->data
.document_type
.notations
) break;
403 if (is_dom_node_list_member(list
, wstate
->index
)
404 && list
== node
->data
.document_type
.entities
)
407 list
= node
->data
.document_type
.notations
;
410 case DOM_NODE_ATTRIBUTE
:
412 case DOM_NODE_CDATA_SECTION
:
413 case DOM_NODE_COMMENT
:
414 case DOM_NODE_NOTATION
:
415 case DOM_NODE_DOCUMENT_FRAGMENT
:
416 case DOM_NODE_ENTITY_REFERENCE
:
417 case DOM_NODE_ENTITY
:
422 /* Reset list state if it is a new list */
423 if (list
!= wstate
->list
) {
428 /* If we have next child node */
429 if (is_dom_node_list_member(list
, wstate
->index
)) {
430 struct dom_node
*child
= list
->entries
[wstate
->index
++];
432 if (push_dom_node(stack
, child
) == DOM_CODE_OK
)
439 done_dom_stack_context(stack
, context
);
443 /* DOM Stack Tracing: */
445 #ifdef DOM_STACK_TRACE
447 #include "util/string.h"
449 /* Compress a string to a single line with newlines etc. replaced with "\\n"
451 static inline unsigned char *
452 compress_string(unsigned char *string
, unsigned int length
)
454 struct string buffer
;
455 unsigned char escape
[2] = "\\";
457 if (!init_string(&buffer
)) return NULL
;
459 for (; length
> 0; string
++, length
--) {
460 unsigned char *bytes
= string
;
462 if (*string
== '\n' || *string
== '\r' || *string
== '\t') {
464 escape
[1] = *string
== '\n' ? 'n'
465 : (*string
== '\r' ? 'r' : 't');
468 add_bytes_to_string(&buffer
, bytes
, bytes
== escape
? 2 : 1);
471 return buffer
.source
;
474 /* Set @string to the value of the given @node, however, with strings
475 * compressed and entity references 'expanded'. */
477 set_enhanced_dom_node_value(struct dom_string
*string
, struct dom_node
*node
)
479 struct dom_string
*value
;
483 memset(string
, 0, sizeof(*string
));
485 switch (node
->type
) {
486 case DOM_NODE_ENTITY_REFERENCE
:
487 /* FIXME: Set to the entity value. */
488 string
->string
= null_or_stracpy(string
->string
);
492 value
= get_dom_node_value(node
);
494 set_dom_string(string
, NULL
, 0);
498 string
->string
= compress_string(value
->string
, value
->length
);
501 string
->length
= string
->string
? strlen(string
->string
) : 0;
504 static unsigned char indent_string
[] =
507 #define get_indent_offset(stack) \
508 ((stack)->depth < sizeof(indent_string)/2 ? (stack)->depth * 2 : sizeof(indent_string))
511 dom_stack_trace_tree(struct dom_stack
*stack
, struct dom_node
*node
, void *data
)
513 struct dom_string
*value
= &node
->string
;
514 struct dom_string
*name
= get_dom_node_name(node
);
516 LOG_INFO("%s%.*s %.*s: %.*s",
517 empty_string_or_(stack
->current
->data
),
518 get_indent_offset(stack
), indent_string
,
519 name
->length
, name
->string
,
520 value
->length
, value
->string
);
526 dom_stack_trace_id_leaf(struct dom_stack
*stack
, struct dom_node
*node
, void *data
)
528 struct dom_string value
;
529 struct dom_string
*name
;
530 struct dom_string
*id
;
534 name
= get_dom_node_name(node
);
535 id
= get_dom_node_type_name(node
->type
);
536 set_enhanced_dom_node_value(&value
, node
);
538 LOG_INFO("%s%.*s %.*s: %.*s -> %.*s",
539 empty_string_or_(stack
->current
->data
),
540 get_indent_offset(stack
), indent_string
,
541 id
->length
, id
->string
, name
->length
, name
->string
,
542 value
.length
, value
.string
);
544 done_dom_string(&value
);
550 dom_stack_trace_leaf(struct dom_stack
*stack
, struct dom_node
*node
, void *data
)
552 struct dom_string
*name
;
553 struct dom_string value
;
557 name
= get_dom_node_name(node
);
558 set_enhanced_dom_node_value(&value
, node
);
560 LOG_INFO("%s%.*s %.*s: %.*s",
561 empty_string_or_(stack
->current
->data
),
562 get_indent_offset(stack
), indent_string
,
563 name
->length
, name
->string
,
564 value
.length
, value
.string
);
566 done_dom_string(&value
);
572 dom_stack_trace_branch(struct dom_stack
*stack
, struct dom_node
*node
, void *data
)
574 struct dom_string
*name
;
575 struct dom_string
*id
;
579 name
= get_dom_node_name(node
);
580 id
= get_dom_node_type_name(node
->type
);
582 LOG_INFO("%s%.*s %.*s: %.*s",
583 empty_string_or_(stack
->current
->data
),
584 get_indent_offset(stack
), indent_string
,
585 id
->length
, id
->string
, name
->length
, name
->string
);
590 struct dom_stack_context_info dom_stack_trace_context_info
= {
591 /* Object size: */ 0,
595 /* DOM_NODE_ELEMENT */ dom_stack_trace_branch
,
596 /* DOM_NODE_ATTRIBUTE */ dom_stack_trace_id_leaf
,
597 /* DOM_NODE_TEXT */ dom_stack_trace_leaf
,
598 /* DOM_NODE_CDATA_SECTION */ dom_stack_trace_id_leaf
,
599 /* DOM_NODE_ENTITY_REFERENCE */ dom_stack_trace_id_leaf
,
600 /* DOM_NODE_ENTITY */ dom_stack_trace_id_leaf
,
601 /* DOM_NODE_PROC_INSTRUCTION */ dom_stack_trace_id_leaf
,
602 /* DOM_NODE_COMMENT */ dom_stack_trace_leaf
,
603 /* DOM_NODE_DOCUMENT */ dom_stack_trace_tree
,
604 /* DOM_NODE_DOCUMENT_TYPE */ dom_stack_trace_id_leaf
,
605 /* DOM_NODE_DOCUMENT_FRAGMENT */ dom_stack_trace_id_leaf
,
606 /* DOM_NODE_NOTATION */ dom_stack_trace_id_leaf
,
611 /* DOM_NODE_ELEMENT */ NULL
,
612 /* DOM_NODE_ATTRIBUTE */ NULL
,
613 /* DOM_NODE_TEXT */ NULL
,
614 /* DOM_NODE_CDATA_SECTION */ NULL
,
615 /* DOM_NODE_ENTITY_REFERENCE */ NULL
,
616 /* DOM_NODE_ENTITY */ NULL
,
617 /* DOM_NODE_PROC_INSTRUCTION */ NULL
,
618 /* DOM_NODE_COMMENT */ NULL
,
619 /* DOM_NODE_DOCUMENT */ NULL
,
620 /* DOM_NODE_DOCUMENT_TYPE */ NULL
,
621 /* DOM_NODE_DOCUMENT_FRAGMENT */ NULL
,
622 /* DOM_NODE_NOTATION */ NULL
,
626 #endif /* DOM_STACK_TRACE */