xref: /Universal-ctags/main/lxpath.c (revision 27650e323c732d2c8fcc323adfb389577da29d3c)
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