Coverage Report

Created: 2025-08-28 06:07

/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
11.4k
exsltDynEvaluateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
48
11.4k
  xmlChar *str = NULL;
49
11.4k
  xmlXPathObjectPtr ret = NULL;
50
51
11.4k
  if (ctxt == NULL)
52
0
    return;
53
11.4k
  if (nargs != 1) {
54
2
    xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
55
2
        xsltGenericError(xsltGenericErrorContext,
56
2
      "dyn:evalute() : invalid number of args %d\n", nargs);
57
2
    ctxt->error = XPATH_INVALID_ARITY;
58
2
    return;
59
2
  }
60
11.4k
  str = xmlXPathPopString(ctxt);
61
  /* return an empty node-set if an empty string is passed in */
62
11.4k
  if (!str||!xmlStrlen(str)) {
63
231
    if (str) xmlFree(str);
64
231
    valuePush(ctxt,xmlXPathNewNodeSet(NULL));
65
231
    return;
66
231
  }
67
11.1k
#if LIBXML_VERSION >= 20911
68
        /*
69
         * Recursive evaluation can grow the call stack quickly.
70
         */
71
11.1k
        ctxt->context->depth += 5;
72
11.1k
#endif
73
11.1k
  ret = xmlXPathEval(str,ctxt->context);
74
11.1k
#if LIBXML_VERSION >= 20911
75
11.1k
        ctxt->context->depth -= 5;
76
11.1k
#endif
77
11.1k
  if (ret)
78
3.24k
    valuePush(ctxt,ret);
79
7.95k
  else {
80
7.95k
    xsltGenericError(xsltGenericErrorContext,
81
7.95k
      "dyn:evaluate() : unable to evaluate expression '%s'\n",str);
82
7.95k
    valuePush(ctxt,xmlXPathNewNodeSet(NULL));
83
7.95k
  }
84
11.1k
  xmlFree(str);
85
11.1k
  return;
86
11.4k
}
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
1.12M
{
101
1.12M
    xmlChar *str = NULL;
102
1.12M
    xmlNodeSetPtr nodeset = NULL;
103
1.12M
    xsltTransformContextPtr tctxt;
104
1.12M
    xmlXPathCompExprPtr comp = NULL;
105
1.12M
    xmlXPathObjectPtr ret = NULL;
106
1.12M
    xmlDocPtr oldDoc, container = NULL;
107
1.12M
    xmlNodePtr oldNode;
108
1.12M
    int oldContextSize;
109
1.12M
    int oldProximityPosition;
110
1.12M
    int i, j;
111
112
113
1.12M
    if (nargs != 2) {
114
5.33k
        xmlXPathSetArityError(ctxt);
115
5.33k
        return;
116
5.33k
    }
117
1.12M
    str = xmlXPathPopString(ctxt);
118
1.12M
    if (xmlXPathCheckError(ctxt))
119
126
        goto cleanup;
120
121
1.12M
    nodeset = xmlXPathPopNodeSet(ctxt);
122
1.12M
    if (xmlXPathCheckError(ctxt))
123
1.74k
        goto cleanup;
124
125
1.12M
    ret = xmlXPathNewNodeSet(NULL);
126
1.12M
    if (ret == NULL) {
127
49
        xsltGenericError(xsltGenericErrorContext,
128
49
                         "exsltDynMapFunction: ret == NULL\n");
129
49
        goto cleanup;
130
49
    }
131
132
1.12M
    tctxt = xsltXPathGetTransformContext(ctxt);
133
1.12M
    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
1.12M
    if (str == NULL || !xmlStrlen(str) ||
140
1.12M
        !(comp = xmlXPathCtxtCompile(tctxt->xpathCtxt, str)))
141
793k
        goto cleanup;
142
143
327k
    oldDoc = ctxt->context->doc;
144
327k
    oldNode = ctxt->context->node;
145
327k
    oldContextSize = ctxt->context->contextSize;
146
327k
    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
327k
    container = xsltCreateRVT(tctxt);
153
327k
    if (container == NULL) {
154
36
  xsltTransformError(tctxt, NULL, NULL,
155
36
        "dyn:map : internal error container == NULL\n");
156
36
  goto cleanup;
157
36
    }
158
327k
    xsltRegisterLocalRVT(tctxt, container);
159
327k
    if (nodeset && nodeset->nodeNr > 0) {
160
322k
        xmlXPathNodeSetSort(nodeset);
161
322k
        ctxt->context->contextSize = nodeset->nodeNr;
162
322k
        ctxt->context->proximityPosition = 0;
163
5.57M
        for (i = 0; i < nodeset->nodeNr; i++) {
164
5.24M
            xmlXPathObjectPtr subResult = NULL;
165
5.24M
            xmlNodePtr cur = nodeset->nodeTab[i];
166
167
5.24M
            ctxt->context->proximityPosition++;
168
5.24M
            ctxt->context->node = cur;
169
170
5.24M
            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
43.1k
                cur = (xmlNodePtr) ((xmlNsPtr) cur)->next;
176
43.1k
                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
43.1k
                ctxt->context->doc = cur->doc;
183
5.20M
            } else {
184
5.20M
                ctxt->context->doc = cur->doc;
185
5.20M
            }
186
187
5.24M
            subResult = xmlXPathCompiledEval(comp, ctxt->context);
188
5.24M
            if (subResult != NULL) {
189
3.80M
                switch (subResult->type) {
190
2.34M
                    case XPATH_NODESET:
191
2.34M
                        if (subResult->nodesetval != NULL)
192
33.5M
                            for (j = 0; j < subResult->nodesetval->nodeNr;
193
31.1M
                                 j++)
194
31.1M
                                xmlXPathNodeSetAdd(ret->nodesetval,
195
31.1M
                                                   subResult->nodesetval->
196
31.1M
                                                   nodeTab[j]);
197
2.34M
                        break;
198
776k
                    case XPATH_BOOLEAN:
199
776k
                        if (container != NULL) {
200
776k
                            xmlNodePtr newChildNode =
201
776k
                                xmlNewTextChild((xmlNodePtr) container, NULL,
202
776k
                                                BAD_CAST "boolean",
203
776k
                                                BAD_CAST (subResult->
204
776k
                                                boolval ? "true" : ""));
205
776k
                            if (newChildNode != NULL) {
206
774k
                                newChildNode->ns =
207
774k
                                    xmlNewNs(newChildNode,
208
774k
                                             BAD_CAST
209
774k
                                             "http://exslt.org/common",
210
774k
                                             BAD_CAST "exsl");
211
774k
                                xmlXPathNodeSetAddUnique(ret->nodesetval,
212
774k
                                                         newChildNode);
213
774k
                            }
214
776k
                        }
215
776k
                        break;
216
663k
                    case XPATH_NUMBER:
217
663k
                        if (container != NULL) {
218
663k
                            xmlChar *val =
219
663k
                                xmlXPathCastNumberToString(subResult->
220
663k
                                                           floatval);
221
663k
                            xmlNodePtr newChildNode =
222
663k
                                xmlNewTextChild((xmlNodePtr) container, NULL,
223
663k
                                                BAD_CAST "number", val);
224
663k
                            if (val != NULL)
225
663k
                                xmlFree(val);
226
227
663k
                            if (newChildNode != NULL) {
228
663k
                                newChildNode->ns =
229
663k
                                    xmlNewNs(newChildNode,
230
663k
                                             BAD_CAST
231
663k
                                             "http://exslt.org/common",
232
663k
                                             BAD_CAST "exsl");
233
663k
                                xmlXPathNodeSetAddUnique(ret->nodesetval,
234
663k
                                                         newChildNode);
235
663k
                            }
236
663k
                        }
237
663k
                        break;
238
12.9k
                    case XPATH_STRING:
239
12.9k
                        if (container != NULL) {
240
12.9k
                            xmlNodePtr newChildNode =
241
12.9k
                                xmlNewTextChild((xmlNodePtr) container, NULL,
242
12.9k
                                                BAD_CAST "string",
243
12.9k
                                                subResult->stringval);
244
12.9k
                            if (newChildNode != NULL) {
245
12.9k
                                newChildNode->ns =
246
12.9k
                                    xmlNewNs(newChildNode,
247
12.9k
                                             BAD_CAST
248
12.9k
                                             "http://exslt.org/common",
249
12.9k
                                             BAD_CAST "exsl");
250
12.9k
                                xmlXPathNodeSetAddUnique(ret->nodesetval,
251
12.9k
                                                         newChildNode);
252
12.9k
                            }
253
12.9k
                        }
254
12.9k
                        break;
255
0
        default:
256
0
                        break;
257
3.80M
                }
258
3.80M
                xmlXPathFreeObject(subResult);
259
3.80M
            }
260
5.24M
        }
261
322k
    }
262
327k
    ctxt->context->doc = oldDoc;
263
327k
    ctxt->context->node = oldNode;
264
327k
    ctxt->context->contextSize = oldContextSize;
265
327k
    ctxt->context->proximityPosition = oldProximityPosition;
266
267
268
1.12M
  cleanup:
269
    /* restore the xpath context */
270
1.12M
    if (comp != NULL)
271
327k
        xmlXPathFreeCompExpr(comp);
272
1.12M
    if (nodeset != NULL)
273
1.12M
        xmlXPathFreeNodeSet(nodeset);
274
1.12M
    if (str != NULL)
275
1.12M
        xmlFree(str);
276
1.12M
    valuePush(ctxt, ret);
277
1.12M
    return;
278
327k
}
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
}