Coverage Report

Created: 2025-07-11 06:13

/src/libxslt/libexslt/dynamic.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * dynamic.c: Implementation of the EXSLT -- Dynamic module
3
 *
4
 * References:
5
 *   http://www.exslt.org/dyn/dyn.html
6
 *
7
 * See Copyright for the status of this software.
8
 *
9
 * Authors:
10
 *   Mark Vakoc <mark_vakoc@jdedwards.com>
11
 *   Thomas Broyer <tbroyer@ltgt.net>
12
 *
13
 * TODO:
14
 * elements:
15
 * functions:
16
 *    min
17
 *    max
18
 *    sum
19
 *    map
20
 *    closure
21
 */
22
23
#define IN_LIBEXSLT
24
#include "libexslt/libexslt.h"
25
26
#include <libxml/tree.h>
27
#include <libxml/xpath.h>
28
#include <libxml/xpathInternals.h>
29
30
#include <libxslt/xsltutils.h>
31
#include <libxslt/xsltInternals.h>
32
#include <libxslt/extensions.h>
33
34
#include "exslt.h"
35
36
/**
37
 * exsltDynEvaluateFunction:
38
 * @ctxt:  an XPath parser context
39
 * @nargs:  the number of arguments
40
 *
41
 * Evaluates the string as an XPath expression and returns the result
42
 * value, which may be a boolean, number, string, node set, result tree
43
 * fragment or external object.
44
 */
45
46
static void
47
20.5k
exsltDynEvaluateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
48
20.5k
  xmlChar *str = NULL;
49
20.5k
  xmlXPathObjectPtr ret = NULL;
50
51
20.5k
  if (ctxt == NULL)
52
0
    return;
53
20.5k
  if (nargs != 1) {
54
3
    xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
55
3
        xsltGenericError(xsltGenericErrorContext,
56
3
      "dyn:evalute() : invalid number of args %d\n", nargs);
57
3
    ctxt->error = XPATH_INVALID_ARITY;
58
3
    return;
59
3
  }
60
20.5k
  str = xmlXPathPopString(ctxt);
61
  /* return an empty node-set if an empty string is passed in */
62
20.5k
  if (!str||!xmlStrlen(str)) {
63
1.61k
    if (str) xmlFree(str);
64
1.61k
    valuePush(ctxt,xmlXPathNewNodeSet(NULL));
65
1.61k
    return;
66
1.61k
  }
67
18.8k
#if LIBXML_VERSION >= 20911
68
        /*
69
         * Recursive evaluation can grow the call stack quickly.
70
         */
71
18.8k
        ctxt->context->depth += 5;
72
18.8k
#endif
73
18.8k
  ret = xmlXPathEval(str,ctxt->context);
74
18.8k
#if LIBXML_VERSION >= 20911
75
18.8k
        ctxt->context->depth -= 5;
76
18.8k
#endif
77
18.8k
  if (ret)
78
2.30k
    valuePush(ctxt,ret);
79
16.5k
  else {
80
16.5k
    xsltGenericError(xsltGenericErrorContext,
81
16.5k
      "dyn:evaluate() : unable to evaluate expression '%s'\n",str);
82
16.5k
    valuePush(ctxt,xmlXPathNewNodeSet(NULL));
83
16.5k
  }
84
18.8k
  xmlFree(str);
85
18.8k
  return;
86
20.5k
}
87
88
/**
89
 * exsltDynMapFunction:
90
 * @ctxt:  an XPath parser context
91
 * @nargs:  the number of arguments
92
 *
93
 * Evaluates the string as an XPath expression and returns the result
94
 * value, which may be a boolean, number, string, node set, result tree
95
 * fragment or external object.
96
 */
97
98
static void
99
exsltDynMapFunction(xmlXPathParserContextPtr ctxt, int nargs)
100
831k
{
101
831k
    xmlChar *str = NULL;
102
831k
    xmlNodeSetPtr nodeset = NULL;
103
831k
    xsltTransformContextPtr tctxt;
104
831k
    xmlXPathCompExprPtr comp = NULL;
105
831k
    xmlXPathObjectPtr ret = NULL;
106
831k
    xmlDocPtr oldDoc, container = NULL;
107
831k
    xmlNodePtr oldNode;
108
831k
    int oldContextSize;
109
831k
    int oldProximityPosition;
110
831k
    int i, j;
111
112
113
831k
    if (nargs != 2) {
114
11.1k
        xmlXPathSetArityError(ctxt);
115
11.1k
        return;
116
11.1k
    }
117
820k
    str = xmlXPathPopString(ctxt);
118
820k
    if (xmlXPathCheckError(ctxt))
119
131
        goto cleanup;
120
121
820k
    nodeset = xmlXPathPopNodeSet(ctxt);
122
820k
    if (xmlXPathCheckError(ctxt))
123
1.74k
        goto cleanup;
124
125
818k
    ret = xmlXPathNewNodeSet(NULL);
126
818k
    if (ret == NULL) {
127
44
        xsltGenericError(xsltGenericErrorContext,
128
44
                         "exsltDynMapFunction: ret == NULL\n");
129
44
        goto cleanup;
130
44
    }
131
132
818k
    tctxt = xsltXPathGetTransformContext(ctxt);
133
818k
    if (tctxt == NULL) {
134
0
  xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
135
0
        "dyn:map : internal error tctxt == NULL\n");
136
0
  goto cleanup;
137
0
    }
138
139
818k
    if (str == NULL || !xmlStrlen(str) ||
140
818k
        !(comp = xmlXPathCtxtCompile(tctxt->xpathCtxt, str)))
141
555k
        goto cleanup;
142
143
263k
    oldDoc = ctxt->context->doc;
144
263k
    oldNode = ctxt->context->node;
145
263k
    oldContextSize = ctxt->context->contextSize;
146
263k
    oldProximityPosition = ctxt->context->proximityPosition;
147
148
        /**
149
   * since we really don't know we're going to be adding node(s)
150
   * down the road we create the RVT regardless
151
   */
152
263k
    container = xsltCreateRVT(tctxt);
153
263k
    if (container == NULL) {
154
34
  xsltTransformError(tctxt, NULL, NULL,
155
34
        "dyn:map : internal error container == NULL\n");
156
34
  goto cleanup;
157
34
    }
158
263k
    xsltRegisterLocalRVT(tctxt, container);
159
263k
    if (nodeset && nodeset->nodeNr > 0) {
160
256k
        xmlXPathNodeSetSort(nodeset);
161
256k
        ctxt->context->contextSize = nodeset->nodeNr;
162
256k
        ctxt->context->proximityPosition = 0;
163
4.28M
        for (i = 0; i < nodeset->nodeNr; i++) {
164
4.02M
            xmlXPathObjectPtr subResult = NULL;
165
4.02M
            xmlNodePtr cur = nodeset->nodeTab[i];
166
167
4.02M
            ctxt->context->proximityPosition++;
168
4.02M
            ctxt->context->node = cur;
169
170
4.02M
            if (cur->type == XML_NAMESPACE_DECL) {
171
                /*
172
                * The XPath module sets the owner element of a ns-node on
173
                * the ns->next field.
174
                */
175
75.0k
                cur = (xmlNodePtr) ((xmlNsPtr) cur)->next;
176
75.0k
                if ((cur == NULL) || (cur->type != XML_ELEMENT_NODE)) {
177
0
                    xsltGenericError(xsltGenericErrorContext,
178
0
                        "Internal error in exsltDynMapFunction: "
179
0
                        "Cannot retrieve the doc of a namespace node.\n");
180
0
                    continue;
181
0
                }
182
75.0k
                ctxt->context->doc = cur->doc;
183
3.95M
            } else {
184
3.95M
                ctxt->context->doc = cur->doc;
185
3.95M
            }
186
187
4.02M
            subResult = xmlXPathCompiledEval(comp, ctxt->context);
188
4.02M
            if (subResult != NULL) {
189
2.78M
                switch (subResult->type) {
190
1.76M
                    case XPATH_NODESET:
191
1.76M
                        if (subResult->nodesetval != NULL)
192
25.0M
                            for (j = 0; j < subResult->nodesetval->nodeNr;
193
23.3M
                                 j++)
194
23.3M
                                xmlXPathNodeSetAdd(ret->nodesetval,
195
23.3M
                                                   subResult->nodesetval->
196
23.3M
                                                   nodeTab[j]);
197
1.76M
                        break;
198
658k
                    case XPATH_BOOLEAN:
199
658k
                        if (container != NULL) {
200
658k
                            xmlNodePtr newChildNode =
201
658k
                                xmlNewTextChild((xmlNodePtr) container, NULL,
202
658k
                                                BAD_CAST "boolean",
203
658k
                                                BAD_CAST (subResult->
204
658k
                                                boolval ? "true" : ""));
205
658k
                            if (newChildNode != NULL) {
206
657k
                                newChildNode->ns =
207
657k
                                    xmlNewNs(newChildNode,
208
657k
                                             BAD_CAST
209
657k
                                             "http://exslt.org/common",
210
657k
                                             BAD_CAST "exsl");
211
657k
                                xmlXPathNodeSetAddUnique(ret->nodesetval,
212
657k
                                                         newChildNode);
213
657k
                            }
214
658k
                        }
215
658k
                        break;
216
363k
                    case XPATH_NUMBER:
217
363k
                        if (container != NULL) {
218
363k
                            xmlChar *val =
219
363k
                                xmlXPathCastNumberToString(subResult->
220
363k
                                                           floatval);
221
363k
                            xmlNodePtr newChildNode =
222
363k
                                xmlNewTextChild((xmlNodePtr) container, NULL,
223
363k
                                                BAD_CAST "number", val);
224
363k
                            if (val != NULL)
225
363k
                                xmlFree(val);
226
227
363k
                            if (newChildNode != NULL) {
228
363k
                                newChildNode->ns =
229
363k
                                    xmlNewNs(newChildNode,
230
363k
                                             BAD_CAST
231
363k
                                             "http://exslt.org/common",
232
363k
                                             BAD_CAST "exsl");
233
363k
                                xmlXPathNodeSetAddUnique(ret->nodesetval,
234
363k
                                                         newChildNode);
235
363k
                            }
236
363k
                        }
237
363k
                        break;
238
3.19k
                    case XPATH_STRING:
239
3.19k
                        if (container != NULL) {
240
3.19k
                            xmlNodePtr newChildNode =
241
3.19k
                                xmlNewTextChild((xmlNodePtr) container, NULL,
242
3.19k
                                                BAD_CAST "string",
243
3.19k
                                                subResult->stringval);
244
3.19k
                            if (newChildNode != NULL) {
245
3.19k
                                newChildNode->ns =
246
3.19k
                                    xmlNewNs(newChildNode,
247
3.19k
                                             BAD_CAST
248
3.19k
                                             "http://exslt.org/common",
249
3.19k
                                             BAD_CAST "exsl");
250
3.19k
                                xmlXPathNodeSetAddUnique(ret->nodesetval,
251
3.19k
                                                         newChildNode);
252
3.19k
                            }
253
3.19k
                        }
254
3.19k
                        break;
255
0
        default:
256
0
                        break;
257
2.78M
                }
258
2.78M
                xmlXPathFreeObject(subResult);
259
2.78M
            }
260
4.02M
        }
261
256k
    }
262
263k
    ctxt->context->doc = oldDoc;
263
263k
    ctxt->context->node = oldNode;
264
263k
    ctxt->context->contextSize = oldContextSize;
265
263k
    ctxt->context->proximityPosition = oldProximityPosition;
266
267
268
820k
  cleanup:
269
    /* restore the xpath context */
270
820k
    if (comp != NULL)
271
263k
        xmlXPathFreeCompExpr(comp);
272
820k
    if (nodeset != NULL)
273
818k
        xmlXPathFreeNodeSet(nodeset);
274
820k
    if (str != NULL)
275
820k
        xmlFree(str);
276
820k
    valuePush(ctxt, ret);
277
820k
    return;
278
263k
}
279
280
281
/**
282
 * exsltDynRegister:
283
 *
284
 * Registers the EXSLT - Dynamic module
285
 */
286
287
void
288
4
exsltDynRegister (void) {
289
4
    xsltRegisterExtModuleFunction ((const xmlChar *) "evaluate",
290
4
           EXSLT_DYNAMIC_NAMESPACE,
291
4
           exsltDynEvaluateFunction);
292
4
  xsltRegisterExtModuleFunction ((const xmlChar *) "map",
293
4
           EXSLT_DYNAMIC_NAMESPACE,
294
4
           exsltDynMapFunction);
295
296
4
}