1 /*
2 * Copyright (c) 2015, Masatake YAMATO
3 * Copyright (c) 2015, Red Hat, Inc.
4 *
5 * This source code is released for free distribution under the terms of the
6 * GNU General Public License version 2 or (at your option) any later version.
7 *
8 * This module contains functions for xpath meta parser.
9 */
10
11 #include "general.h" /* must always come first */
12 #include "debug.h"
13 #include "entry.h"
14 #include "options.h"
15 #include "parse_p.h"
16 #include "read.h"
17 #include "read_p.h"
18 #include "routines.h"
19 #include "xtag.h"
20
21 #ifdef HAVE_LIBXML
22 #include <libxml/xpath.h>
23 #include <libxml/tree.h>
24
simpleXpathMakeTag(xmlNode * node,const char * xpath,const tagXpathMakeTagSpec * spec,void * userData)25 static void simpleXpathMakeTag (xmlNode *node,
26 const char *xpath,
27 const tagXpathMakeTagSpec *spec,
28 void *userData)
29 {
30 tagEntryInfo tag;
31 xmlChar* str;
32 char *path;
33 int kind;
34
35 str = xmlNodeGetContent(node);
36 if (str == NULL)
37 return;
38
39 if (spec->kind == KIND_GHOST_INDEX && spec->decideKind)
40 kind = spec->decideKind (node, xpath, spec, userData);
41 else
42 kind = spec->kind;
43 Assert (kind != KIND_GHOST_INDEX);
44
45 if (spec->role == ROLE_DEFINITION_INDEX)
46 initTagEntry (&tag, (char *)str, kind);
47 else if (isXtagEnabled(XTAG_REFERENCE_TAGS))
48 initRefTagEntry (&tag, (char *)str,
49 kind,
50 spec->role);
51 else
52 goto out;
53
54
55 tag.lineNumber = XML_GET_LINE (node);
56 tag.filePosition = getInputFilePositionForLine (tag.lineNumber);
57
58 path = (char *)xmlGetNodePath (node);
59 tag.extensionFields.xpath = path;
60
61 if (spec->make)
62 spec->make (node, xpath, spec, &tag, userData);
63 else
64 makeTagEntry (&tag);
65
66 if (path)
67 xmlFree (path);
68 out:
69 xmlFree (str);
70 }
71
addTagXpath(const langType language CTAGS_ATTR_UNUSED,tagXpathTable * xpathTable)72 extern void addTagXpath (const langType language CTAGS_ATTR_UNUSED, tagXpathTable *xpathTable)
73 {
74 Assert (xpathTable->xpath);
75 Assert (!xpathTable->xpathCompiled);
76
77 verbose ("compile a xpath expression: %s\n", (xmlChar *)xpathTable->xpath);
78 xpathTable->xpathCompiled = xmlXPathCompile ((xmlChar *)xpathTable->xpath);
79 if (!xpathTable->xpathCompiled)
80 error (WARNING, "Failed to compile the Xpath expression: %s", xpathTable->xpath);
81 }
82
removeTagXpath(const langType language CTAGS_ATTR_UNUSED,tagXpathTable * xpathTable)83 extern void removeTagXpath (const langType language CTAGS_ATTR_UNUSED, tagXpathTable *xpathTable)
84 {
85 if (xpathTable->xpathCompiled)
86 {
87 xmlXPathFreeCompExpr (xpathTable->xpathCompiled);
88 xpathTable->xpathCompiled = NULL;
89 }
90 }
91
findXMLTagsCore(xmlXPathContext * ctx,xmlNode * root,const tagXpathTableTable * xpathTableTable,void * userData)92 static void findXMLTagsCore (xmlXPathContext *ctx, xmlNode *root,
93 const tagXpathTableTable *xpathTableTable,
94 void *userData)
95 {
96 unsigned int i;
97 int j;
98 xmlNode * node;
99
100 Assert (root);
101 Assert (xpathTableTable);
102
103 for (i = 0; i < xpathTableTable->count; ++i)
104 {
105 xmlXPathObject *object;
106 xmlNodeSet *set;
107 const tagXpathTable *elt = xpathTableTable->table + i;
108
109 if (! elt->xpathCompiled)
110 continue;
111
112 #if 0
113 /* Older version of libxml2 doesn't have xmlXPathSetContextNode. */
114 if (xmlXPathSetContextNode (root, ctx) != 0)
115 {
116 error (WARNING, "Failed to set node to XpathContext");
117 return;
118 }
119 #else
120 ctx->node = root;
121 #endif
122
123 object = xmlXPathCompiledEval (elt->xpathCompiled, ctx);
124 if (!object)
125 continue;
126
127 set = object->nodesetval;
128
129 if (set)
130 {
131 for (j = 0; j < xmlXPathNodeSetGetLength (set); ++j)
132 {
133 node = xmlXPathNodeSetItem(set, j);
134 if (elt->specType == LXPATH_TABLE_DO_MAKE)
135 simpleXpathMakeTag (node, elt->xpath, &(elt->spec.makeTagSpec), userData);
136 else
137 elt->spec.recurSpec.enter (node, elt->xpath, &(elt->spec.recurSpec), ctx, userData);
138 }
139 }
140 xmlXPathFreeObject (object);
141 }
142 }
143
suppressWarning(void * ctx CTAGS_ATTR_UNUSED,const char * msg CTAGS_ATTR_UNUSED,...)144 static void suppressWarning (void *ctx CTAGS_ATTR_UNUSED, const char *msg CTAGS_ATTR_UNUSED, ...)
145 {
146 }
147
makeXMLDoc(void)148 static xmlDocPtr makeXMLDoc (void)
149 {
150 const unsigned char* data;
151 size_t size;
152 xmlDocPtr doc;
153
154 doc = getInputFileUserData ();
155 if (doc)
156 {
157 verbose ("reuse xml doc data\n");
158 return doc;
159 }
160
161 data = getInputFileData (&size);
162 if (data)
163 {
164 xmlSetGenericErrorFunc (NULL, suppressWarning);
165 xmlLineNumbersDefault (1);
166 doc = xmlParseMemory((const char*)data, size);
167 }
168
169 return doc;
170 }
171
findXMLTagsFull(xmlXPathContext * ctx,xmlNode * root,int tableTableIndex,void (* runAfter)(xmlXPathContext *,xmlNode *,void *),void * userData)172 extern void findXMLTagsFull (xmlXPathContext *ctx, xmlNode *root,
173 int tableTableIndex,
174 void (* runAfter) (xmlXPathContext *, xmlNode *, void *),
175 void *userData)
176 {
177 bool usedAsEntryPoint = false;
178 xmlDocPtr doc = NULL;
179
180 const langType lang = getInputLanguage();
181 const tagXpathTableTable *xpathTableTable
182 = getXpathTableTable (lang, tableTableIndex);
183
184 if (ctx == NULL)
185 {
186 usedAsEntryPoint = true;
187
188 findRegexTags ();
189
190 doc = makeXMLDoc ();
191
192 if (doc == NULL)
193 {
194 verbose ("could not parse %s as a XML file\n", getInputFileName());
195 return;
196 }
197
198 ctx = xmlXPathNewContext (doc);
199 if (ctx == NULL)
200 error (FATAL, "failed to make a new xpath context for %s", getInputFileName());
201
202 root = xmlDocGetRootElement(doc);
203 if (root == NULL)
204 {
205 verbose ("could not get the root node for %s\n", getInputFileName());
206 goto out;
207 }
208 }
209
210 findXMLTagsCore (ctx, root, xpathTableTable, userData);
211 if (runAfter)
212 (* runAfter) (ctx, root, userData);
213
214 out:
215 if (usedAsEntryPoint)
216 {
217 xmlXPathFreeContext (ctx);
218
219 if (doc != getInputFileUserData ())
220 xmlFreeDoc (doc);
221 }
222 }
223
224 #else
225
addTagXpath(const langType language,tagXpathTable * xpathTable)226 extern void addTagXpath (const langType language, tagXpathTable *xpathTable)
227 {
228 xpathTable->xpathCompiled = NULL;
229 }
230
removeTagXpath(const langType language CTAGS_ATTR_UNUSED,tagXpathTable * xpathTable CTAGS_ATTR_UNUSED)231 extern void removeTagXpath (const langType language CTAGS_ATTR_UNUSED, tagXpathTable *xpathTable CTAGS_ATTR_UNUSED)
232 {
233 }
234
findXMLTagsFull(xmlXPathContext * ctx,xmlNode * root,int tableTableIndex,void (* runAfter)(xmlXPathContext *,xmlNode *,void *),void * userData)235 extern void findXMLTagsFull (xmlXPathContext *ctx, xmlNode *root,
236 int tableTableIndex,
237 void (* runAfter) (xmlXPathContext *, xmlNode *, void *),
238 void *userData)
239 {
240 }
241
242 #endif
243
findXMLTags(xmlXPathContext * ctx,xmlNode * root,int tableTableIndex,void * userData)244 extern void findXMLTags (xmlXPathContext *ctx, xmlNode *root,
245 int tableTableIndex,
246 void *userData)
247 {
248 findXMLTagsFull (ctx, root, tableTableIndex, NULL, userData);
249 }
250