Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygentemplates.c
Go to the documentation of this file.
00001 /* 00002 * templates.c: Implementation of the template processing 00003 * 00004 * Reference: 00005 * http://www.w3.org/TR/1999/REC-xslt-19991116 00006 * 00007 * See Copyright for the status of this software. 00008 * 00009 * daniel@veillard.com 00010 */ 00011 00012 #define IN_LIBXSLT 00013 #include "libxslt.h" 00014 00015 #include <string.h> 00016 00017 #include <libxml/xmlmemory.h> 00018 #include <libxml/globals.h> 00019 #include <libxml/xmlerror.h> 00020 #include <libxml/tree.h> 00021 #include <libxml/xpathInternals.h> 00022 #include <libxml/parserInternals.h> 00023 #include "xslt.h" 00024 #include "xsltInternals.h" 00025 #include "xsltutils.h" 00026 #include "variables.h" 00027 #include "functions.h" 00028 #include "templates.h" 00029 #include "transform.h" 00030 #include "namespaces.h" 00031 #include "attributes.h" 00032 00033 #ifdef WITH_XSLT_DEBUG 00034 #define WITH_XSLT_DEBUG_TEMPLATES 00035 #endif 00036 00037 /************************************************************************ 00038 * * 00039 * Module interfaces * 00040 * * 00041 ************************************************************************/ 00042 00055 int 00056 xsltEvalXPathPredicate(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp, 00057 xmlNsPtr *nsList, int nsNr) { 00058 int ret; 00059 xmlXPathObjectPtr res; 00060 int oldNsNr; 00061 xmlNsPtr *oldNamespaces; 00062 xmlNodePtr oldInst; 00063 int oldProximityPosition, oldContextSize; 00064 00065 oldContextSize = ctxt->xpathCtxt->contextSize; 00066 oldProximityPosition = ctxt->xpathCtxt->proximityPosition; 00067 oldNsNr = ctxt->xpathCtxt->nsNr; 00068 oldNamespaces = ctxt->xpathCtxt->namespaces; 00069 oldInst = ctxt->inst; 00070 00071 ctxt->xpathCtxt->node = ctxt->node; 00072 ctxt->xpathCtxt->namespaces = nsList; 00073 ctxt->xpathCtxt->nsNr = nsNr; 00074 00075 res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); 00076 00077 if (res != NULL) { 00078 ret = xmlXPathEvalPredicate(ctxt->xpathCtxt, res); 00079 xmlXPathFreeObject(res); 00080 #ifdef WITH_XSLT_DEBUG_TEMPLATES 00081 XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 00082 "xsltEvalXPathPredicate: returns %d\n", ret)); 00083 #endif 00084 } else { 00085 #ifdef WITH_XSLT_DEBUG_TEMPLATES 00086 XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 00087 "xsltEvalXPathPredicate: failed\n")); 00088 #endif 00089 ctxt->state = XSLT_STATE_STOPPED; 00090 ret = 0; 00091 } 00092 ctxt->xpathCtxt->nsNr = oldNsNr; 00093 00094 ctxt->xpathCtxt->namespaces = oldNamespaces; 00095 ctxt->inst = oldInst; 00096 ctxt->xpathCtxt->contextSize = oldContextSize; 00097 ctxt->xpathCtxt->proximityPosition = oldProximityPosition; 00098 00099 return(ret); 00100 } 00101 00115 xmlChar * 00116 xsltEvalXPathStringNs(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp, 00117 int nsNr, xmlNsPtr *nsList) { 00118 xmlChar *ret = NULL; 00119 xmlXPathObjectPtr res; 00120 xmlNodePtr oldInst; 00121 xmlNodePtr oldNode; 00122 int oldPos, oldSize; 00123 int oldNsNr; 00124 xmlNsPtr *oldNamespaces; 00125 00126 oldInst = ctxt->inst; 00127 oldNode = ctxt->node; 00128 oldPos = ctxt->xpathCtxt->proximityPosition; 00129 oldSize = ctxt->xpathCtxt->contextSize; 00130 oldNsNr = ctxt->xpathCtxt->nsNr; 00131 oldNamespaces = ctxt->xpathCtxt->namespaces; 00132 00133 ctxt->xpathCtxt->node = ctxt->node; 00134 /* TODO: do we need to propagate the namespaces here ? */ 00135 ctxt->xpathCtxt->namespaces = nsList; 00136 ctxt->xpathCtxt->nsNr = nsNr; 00137 res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); 00138 if (res != NULL) { 00139 if (res->type != XPATH_STRING) 00140 res = xmlXPathConvertString(res); 00141 if (res->type == XPATH_STRING) { 00142 ret = res->stringval; 00143 res->stringval = NULL; 00144 } else { 00145 xsltTransformError(ctxt, NULL, NULL, 00146 "xpath : string() function didn't return a String\n"); 00147 } 00148 xmlXPathFreeObject(res); 00149 } else { 00150 ctxt->state = XSLT_STATE_STOPPED; 00151 } 00152 #ifdef WITH_XSLT_DEBUG_TEMPLATES 00153 XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 00154 "xsltEvalXPathString: returns %s\n", ret)); 00155 #endif 00156 ctxt->inst = oldInst; 00157 ctxt->node = oldNode; 00158 ctxt->xpathCtxt->contextSize = oldSize; 00159 ctxt->xpathCtxt->proximityPosition = oldPos; 00160 ctxt->xpathCtxt->nsNr = oldNsNr; 00161 ctxt->xpathCtxt->namespaces = oldNamespaces; 00162 return(ret); 00163 } 00164 00175 xmlChar * 00176 xsltEvalXPathString(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp) { 00177 return(xsltEvalXPathStringNs(ctxt, comp, 0, NULL)); 00178 } 00179 00193 xmlChar * 00194 xsltEvalTemplateString(xsltTransformContextPtr ctxt, 00195 xmlNodePtr contextNode, 00196 xmlNodePtr inst) 00197 { 00198 xmlNodePtr oldInsert, insert = NULL; 00199 xmlChar *ret; 00200 00201 if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) 00202 return(NULL); 00203 00204 if (inst->children == NULL) 00205 return(NULL); 00206 00207 /* 00208 * This creates a temporary element-node to add the resulting 00209 * text content to. 00210 * OPTIMIZE TODO: Keep such an element-node in the transformation 00211 * context to avoid creating it every time. 00212 */ 00213 insert = xmlNewDocNode(ctxt->output, NULL, 00214 (const xmlChar *)"fake", NULL); 00215 if (insert == NULL) { 00216 xsltTransformError(ctxt, NULL, contextNode, 00217 "Failed to create temporary node\n"); 00218 return(NULL); 00219 } 00220 oldInsert = ctxt->insert; 00221 ctxt->insert = insert; 00222 /* 00223 * OPTIMIZE TODO: if inst->children consists only of text-nodes. 00224 */ 00225 xsltApplyOneTemplate(ctxt, contextNode, inst->children, NULL, NULL); 00226 00227 ctxt->insert = oldInsert; 00228 00229 ret = xmlNodeGetContent(insert); 00230 if (insert != NULL) 00231 xmlFreeNode(insert); 00232 return(ret); 00233 } 00234 00255 xmlChar * 00256 xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt, 00257 const xmlChar *str, xmlNodePtr inst) 00258 { 00259 xmlChar *ret = NULL; 00260 const xmlChar *cur; 00261 xmlChar *expr, *val; 00262 xmlNsPtr *nsList = NULL; 00263 int nsNr = 0; 00264 00265 if (str == NULL) return(NULL); 00266 if (*str == 0) 00267 return(xmlStrndup((xmlChar *)"", 0)); 00268 00269 cur = str; 00270 while (*cur != 0) { 00271 if (*cur == '{') { 00272 if (*(cur+1) == '{') { /* escaped '{' */ 00273 cur++; 00274 ret = xmlStrncat(ret, str, cur - str); 00275 cur++; 00276 str = cur; 00277 continue; 00278 } 00279 ret = xmlStrncat(ret, str, cur - str); 00280 str = cur; 00281 cur++; 00282 while ((*cur != 0) && (*cur != '}')) cur++; 00283 if (*cur == 0) { 00284 xsltTransformError(ctxt, NULL, inst, 00285 "xsltAttrTemplateValueProcessNode: unmatched '{'\n"); 00286 ret = xmlStrncat(ret, str, cur - str); 00287 return(ret); 00288 } 00289 str++; 00290 expr = xmlStrndup(str, cur - str); 00291 if (expr == NULL) 00292 return(ret); 00293 else if (*expr == '{') { 00294 ret = xmlStrcat(ret, expr); 00295 xmlFree(expr); 00296 } else { 00297 xmlXPathCompExprPtr comp; 00298 /* 00299 * TODO: keep precompiled form around 00300 */ 00301 if ((nsList == NULL) && (inst != NULL)) { 00302 int i = 0; 00303 00304 nsList = xmlGetNsList(inst->doc, inst); 00305 if (nsList != NULL) { 00306 while (nsList[i] != NULL) 00307 i++; 00308 nsNr = i; 00309 } 00310 } 00311 comp = xmlXPathCompile(expr); 00312 val = xsltEvalXPathStringNs(ctxt, comp, nsNr, nsList); 00313 xmlXPathFreeCompExpr(comp); 00314 xmlFree(expr); 00315 if (val != NULL) { 00316 ret = xmlStrcat(ret, val); 00317 xmlFree(val); 00318 } 00319 } 00320 cur++; 00321 str = cur; 00322 } else if (*cur == '}') { 00323 cur++; 00324 if (*cur == '}') { /* escaped '}' */ 00325 ret = xmlStrncat(ret, str, cur - str); 00326 cur++; 00327 str = cur; 00328 continue; 00329 } else { 00330 xsltTransformError(ctxt, NULL, inst, 00331 "xsltAttrTemplateValueProcessNode: unmatched '}'\n"); 00332 } 00333 } else 00334 cur++; 00335 } 00336 if (cur != str) { 00337 ret = xmlStrncat(ret, str, cur - str); 00338 } 00339 00340 if (nsList != NULL) 00341 xmlFree(nsList); 00342 00343 return(ret); 00344 } 00345 00356 xmlChar * 00357 xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt, const xmlChar *str) { 00358 return(xsltAttrTemplateValueProcessNode(ctxt, str, NULL)); 00359 } 00360 00376 xmlChar * 00377 xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr inst, 00378 const xmlChar *name, const xmlChar *ns) 00379 { 00380 xmlChar *ret; 00381 xmlChar *expr; 00382 00383 if ((ctxt == NULL) || (inst == NULL) || (name == NULL)) 00384 return(NULL); 00385 00386 expr = xsltGetNsProp(inst, name, ns); 00387 if (expr == NULL) 00388 return(NULL); 00389 00390 /* 00391 * TODO: though now {} is detected ahead, it would still be good to 00392 * optimize both functions to keep the splitted value if the 00393 * attribute content and the XPath precompiled expressions around 00394 */ 00395 00396 ret = xsltAttrTemplateValueProcessNode(ctxt, expr, inst); 00397 #ifdef WITH_XSLT_DEBUG_TEMPLATES 00398 XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, 00399 "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret)); 00400 #endif 00401 if (expr != NULL) 00402 xmlFree(expr); 00403 return(ret); 00404 } 00405 00421 const xmlChar * 00422 xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr inst, 00423 const xmlChar *name, const xmlChar *ns, int *found) { 00424 const xmlChar *ret; 00425 xmlChar *expr; 00426 00427 if ((style == NULL) || (inst == NULL) || (name == NULL)) 00428 return(NULL); 00429 00430 expr = xsltGetNsProp(inst, name, ns); 00431 if (expr == NULL) { 00432 *found = 0; 00433 return(NULL); 00434 } 00435 *found = 1; 00436 00437 ret = xmlStrchr(expr, '{'); 00438 if (ret != NULL) { 00439 xmlFree(expr); 00440 return(NULL); 00441 } 00442 ret = xmlDictLookup(style->dict, expr, -1); 00443 xmlFree(expr); 00444 return(ret); 00445 } 00446 00461 xmlAttrPtr 00462 xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target, 00463 xmlAttrPtr attr) 00464 { 00465 const xmlChar *value; 00466 xmlAttrPtr ret; 00467 00468 if ((ctxt == NULL) || (attr == NULL) || (target == NULL)) 00469 return(NULL); 00470 00471 if (attr->type != XML_ATTRIBUTE_NODE) 00472 return(NULL); 00473 00474 /* 00475 * Skip all XSLT attributes. 00476 */ 00477 #ifdef XSLT_REFACTORED 00478 if (attr->psvi == xsltXSLTAttrMarker) 00479 return(NULL); 00480 #else 00481 if ((attr->ns != NULL) && xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) 00482 return(NULL); 00483 #endif 00484 /* 00485 * Get the value. 00486 */ 00487 if (attr->children != NULL) { 00488 if ((attr->children->type != XML_TEXT_NODE) || 00489 (attr->children->next != NULL)) 00490 { 00491 xsltTransformError(ctxt, NULL, attr->parent, 00492 "Internal error: The children of an attribute node of a " 00493 "literal result element are not in the expected form.\n"); 00494 return(NULL); 00495 } 00496 value = attr->children->content; 00497 if (value == NULL) 00498 value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); 00499 } else 00500 value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); 00501 /* 00502 * Overwrite duplicates. 00503 */ 00504 ret = target->properties; 00505 while (ret != NULL) { 00506 if (((attr->ns != NULL) == (ret->ns != NULL)) && 00507 xmlStrEqual(ret->name, attr->name) && 00508 ((attr->ns == NULL) || xmlStrEqual(ret->ns->href, attr->ns->href))) 00509 { 00510 break; 00511 } 00512 ret = ret->next; 00513 } 00514 if (ret != NULL) { 00515 /* free the existing value */ 00516 xmlFreeNodeList(ret->children); 00517 ret->children = ret->last = NULL; 00518 /* 00519 * Adjust ns-prefix if needed. 00520 */ 00521 if ((ret->ns != NULL) && 00522 (! xmlStrEqual(ret->ns->prefix, attr->ns->prefix))) 00523 { 00524 ret->ns = xsltGetNamespace(ctxt, attr->parent, attr->ns, target); 00525 } 00526 } else { 00527 /* create a new attribute */ 00528 if (attr->ns != NULL) 00529 ret = xmlNewNsProp(target, 00530 xsltGetNamespace(ctxt, attr->parent, attr->ns, target), 00531 attr->name, NULL); 00532 else 00533 ret = xmlNewNsProp(target, NULL, attr->name, NULL); 00534 } 00535 /* 00536 * Set the value. 00537 */ 00538 if (ret != NULL) { 00539 xmlNodePtr text; 00540 00541 text = xmlNewText(NULL); 00542 if (text != NULL) { 00543 ret->last = ret->children = text; 00544 text->parent = (xmlNodePtr) ret; 00545 text->doc = ret->doc; 00546 00547 if (attr->psvi != NULL) { 00548 /* 00549 * Evaluate the Attribute Value Template. 00550 */ 00551 xmlChar *val; 00552 val = xsltEvalAVT(ctxt, attr->psvi, attr->parent); 00553 if (val == NULL) { 00554 /* 00555 * TODO: Damn, we need an easy mechanism to report 00556 * qualified names! 00557 */ 00558 if (attr->ns) { 00559 xsltTransformError(ctxt, NULL, attr->parent, 00560 "Internal error: Failed to evaluate the AVT " 00561 "of attribute '{%s}%s'.\n", 00562 attr->ns->href, attr->name); 00563 } else { 00564 xsltTransformError(ctxt, NULL, attr->parent, 00565 "Internal error: Failed to evaluate the AVT " 00566 "of attribute '%s'.\n", 00567 attr->name); 00568 } 00569 text->content = xmlStrdup(BAD_CAST ""); 00570 } else { 00571 text->content = val; 00572 } 00573 } else if ((ctxt->internalized) && (target != NULL) && 00574 (target->doc != NULL) && 00575 (target->doc->dict == ctxt->dict)) { 00576 text->content = (xmlChar *) value; 00577 } else { 00578 text->content = xmlStrdup(value); 00579 } 00580 } 00581 } else { 00582 if (attr->ns) { 00583 xsltTransformError(ctxt, NULL, attr->parent, 00584 "Internal error: Failed to create attribute '{%s}%s'.\n", 00585 attr->ns->href, attr->name); 00586 } else { 00587 xsltTransformError(ctxt, NULL, attr->parent, 00588 "Internal error: Failed to create attribute '%s'.\n", 00589 attr->name); 00590 } 00591 } 00592 return(ret); 00593 } 00594 00595 00615 xmlAttrPtr 00616 xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt, 00617 xmlNodePtr target, xmlAttrPtr attrs) 00618 { 00619 xmlAttrPtr attr, copy, last; 00620 xmlNodePtr oldInsert, text; 00621 xmlNsPtr origNs = NULL, copyNs = NULL; 00622 const xmlChar *value; 00623 xmlChar *valueAVT; 00624 00625 if ((ctxt == NULL) || (target == NULL) || (attrs == NULL)) 00626 return(NULL); 00627 00628 oldInsert = ctxt->insert; 00629 ctxt->insert = target; 00630 00631 /* 00632 * Instantiate LRE-attributes. 00633 */ 00634 if (target->properties) { 00635 last = target->properties; 00636 while (last->next != NULL) 00637 last = last->next; 00638 } else { 00639 last = NULL; 00640 } 00641 attr = attrs; 00642 do { 00643 /* 00644 * Skip XSLT attributes. 00645 */ 00646 #ifdef XSLT_REFACTORED 00647 if (attr->psvi == xsltXSLTAttrMarker) { 00648 goto next_attribute; 00649 } 00650 #else 00651 if ((attr->ns != NULL) && 00652 xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) 00653 { 00654 goto next_attribute; 00655 } 00656 #endif 00657 /* 00658 * Get the value. 00659 */ 00660 if (attr->children != NULL) { 00661 if ((attr->children->type != XML_TEXT_NODE) || 00662 (attr->children->next != NULL)) 00663 { 00664 xsltTransformError(ctxt, NULL, attr->parent, 00665 "Internal error: The children of an attribute node of a " 00666 "literal result element are not in the expected form.\n"); 00667 goto error; 00668 } 00669 value = attr->children->content; 00670 if (value == NULL) 00671 value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); 00672 } else 00673 value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); 00674 00675 /* 00676 * Create a new attribute. 00677 */ 00678 copy = xmlNewDocProp(target->doc, attr->name, NULL); 00679 if (copy == NULL) { 00680 if (attr->ns) { 00681 xsltTransformError(ctxt, NULL, attr->parent, 00682 "Internal error: Failed to create attribute '{%s}%s'.\n", 00683 attr->ns->href, attr->name); 00684 } else { 00685 xsltTransformError(ctxt, NULL, attr->parent, 00686 "Internal error: Failed to create attribute '%s'.\n", 00687 attr->name); 00688 } 00689 goto error; 00690 } 00691 /* 00692 * Attach it to the target element. 00693 */ 00694 copy->parent = target; 00695 if (last == NULL) { 00696 target->properties = copy; 00697 last = copy; 00698 } else { 00699 last->next = copy; 00700 copy->prev = last; 00701 last = copy; 00702 } 00703 /* 00704 * Set the namespace. Avoid lookups of same namespaces. 00705 */ 00706 if (attr->ns != origNs) { 00707 origNs = attr->ns; 00708 if (attr->ns != NULL) { 00709 #ifdef XSLT_REFACTORED 00710 copyNs = xsltGetSpecialNamespace(ctxt, attr->parent, 00711 attr->ns->href, attr->ns->prefix, target); 00712 #else 00713 copyNs = xsltGetNamespace(ctxt, attr->parent, 00714 attr->ns, target); 00715 #endif 00716 if (copyNs == NULL) 00717 goto error; 00718 } else 00719 copyNs = NULL; 00720 } 00721 copy->ns = copyNs; 00722 00723 /* 00724 * Set the value. 00725 */ 00726 text = xmlNewText(NULL); 00727 if (text != NULL) { 00728 copy->last = copy->children = text; 00729 text->parent = (xmlNodePtr) copy; 00730 text->doc = copy->doc; 00731 00732 if (attr->psvi != NULL) { 00733 /* 00734 * Evaluate the Attribute Value Template. 00735 */ 00736 valueAVT = xsltEvalAVT(ctxt, attr->psvi, attr->parent); 00737 if (valueAVT == NULL) { 00738 /* 00739 * TODO: Damn, we need an easy mechanism to report 00740 * qualified names! 00741 */ 00742 if (attr->ns) { 00743 xsltTransformError(ctxt, NULL, attr->parent, 00744 "Internal error: Failed to evaluate the AVT " 00745 "of attribute '{%s}%s'.\n", 00746 attr->ns->href, attr->name); 00747 } else { 00748 xsltTransformError(ctxt, NULL, attr->parent, 00749 "Internal error: Failed to evaluate the AVT " 00750 "of attribute '%s'.\n", 00751 attr->name); 00752 } 00753 text->content = xmlStrdup(BAD_CAST ""); 00754 goto error; 00755 } else { 00756 text->content = valueAVT; 00757 } 00758 } else if ((ctxt->internalized) && 00759 (target->doc != NULL) && 00760 (target->doc->dict == ctxt->dict)) 00761 { 00762 text->content = (xmlChar *) value; 00763 } else { 00764 text->content = xmlStrdup(value); 00765 } 00766 if ((copy != NULL) && (text != NULL) && 00767 (xmlIsID(copy->doc, copy->parent, copy))) 00768 xmlAddID(NULL, copy->doc, text->content, copy); 00769 } 00770 00771 next_attribute: 00772 attr = attr->next; 00773 } while (attr != NULL); 00774 00775 /* 00776 * Apply attribute-sets. 00777 * The creation of such attributes will not overwrite any existing 00778 * attribute. 00779 */ 00780 attr = attrs; 00781 do { 00782 #ifdef XSLT_REFACTORED 00783 if ((attr->psvi == xsltXSLTAttrMarker) && 00784 xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets")) 00785 { 00786 xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL); 00787 } 00788 #else 00789 if ((attr->ns != NULL) && 00790 xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets") && 00791 xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) 00792 { 00793 xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL); 00794 } 00795 #endif 00796 attr = attr->next; 00797 } while (attr != NULL); 00798 00799 ctxt->insert = oldInsert; 00800 return(target->properties); 00801 00802 error: 00803 ctxt->insert = oldInsert; 00804 return(NULL); 00805 } 00806 00807 00817 xmlNodePtr * 00818 xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr node) { 00819 if (node == NULL) 00820 return(NULL); 00821 00822 return(0); 00823 } 00824 00825 Generated on Mon May 28 2012 04:19:32 for ReactOS by
1.7.6.1
|