xref: /Universal-ctags/parsers/xml.c (revision aa4def1793bf3cadfdb89c325d093e28cb67adc1)
1 /*
2 *
3 *   Copyright (c) 2019, Masatake YAMATO
4 *   Copyright (c) 2019, Red Hat, K.K.
5 *
6 *   This source code is released for free distribution under the terms of the
7 *   GNU General Public License version 2 or (at your option) any later version.
8 *
9 *   This module contains functions for generating tags for id="..." in a XML file
10 */
11 
12 #include "general.h"	/* must always come first */
13 #include "entry.h"
14 #include "options.h"
15 #include "parse.h"
16 #include "read.h"
17 #include "routines.h"
18 #include "selectors.h"
19 #include "subparser.h"
20 #include "xml.h"
21 
22 static void makeTagWithNotification (xmlNode *node,
23 									 const char *xpath,
24 									 const tagXpathMakeTagSpec *spec,
25 									 tagEntryInfo *tag,
26 									 void *userData);
27 
28 typedef enum {
29 	K_ID,
30 	K_NSPREFIX,
31 	K_ROOT,
32 } xmlKind;
33 
34 #define ROOT_ELT_LETTER 'r'
35 static const char ROOT_ELT_LETTER_STR[2] = {ROOT_ELT_LETTER, '\0'};
36 static kindDefinition XmlKinds [] = {
37 	{ true, 'i', "id", "id attributes" },
38 	{ true, 'n', "nsprefix", "namespace prefixes" },
39 	{ true, ROOT_ELT_LETTER, "root", "root elements" },
40 };
41 
42 enum xmlXpathTables {
43 	TABLE_MAIN,
44 	TABLE_ID,
45 };
46 
47 typedef enum {
48 	F_NS_URI,
49 } xmlField;
50 
51 static fieldDefinition XmlFields [] = {
52 	{
53 		.name = "uri",
54 		.description = "uri associated with name prefix",
55 		.enabled = true,
56 	}
57 };
58 
59 static void findNsPrefix (xmlNode *node,
60 					   const char *xpath,
61 					   const struct sTagXpathRecurSpec *spec,
62 					   xmlXPathContext *ctx,
63 					   void *userData);
64 
65 static tagXpathTable XmlXpathMainTable [] = {
66 	{ "//*",
67 	  LXPATH_TABLE_DO_RECUR,
68 	  { .recurSpec = { .enter = findNsPrefix, .nextTable = TABLE_ID } }
69 	},
70 };
71 
72 static tagXpathTable XmlXpathIdTable [] = {
73 	{ "./@id",
74 	  LXPATH_TABLE_DO_MAKE,
75 	  { .makeTagSpec = { K_ID, ROLE_DEFINITION_INDEX,
76 						 .make = makeTagWithNotification,} }
77 	},
78 };
79 
80 static tagXpathTableTable xmlXpathTableTable[] = {
81 	[TABLE_MAIN] = { ARRAY_AND_SIZE (XmlXpathMainTable) },
82 	[TABLE_ID]   = { ARRAY_AND_SIZE (XmlXpathIdTable) },
83 };
84 
85 /* Pick up the root element specifier from "<!DOCTYPE...", and
86  * run DTD parser for the "[" ... "]>" area.
87  *
88  * optlib2c translates the following .ctags elements:
89  * ==========================================================
90  * --langdef=Xml
91  * --kinddef-Xml=r,root,root elements
92  * --mline-regex-Xml=/<!DOCTYPE[[:space:]]+([a-zA-Z0-9]+)[[:space:]]+[^[]+\[((.|[\n])+)\]>/\1/r/{mgroup=1}{_guest=DTD,2start,2end}
93  * ==========================================================
94  */
95 static tagRegexTable XmlTagRegexTable [] = {
96 	{"<!DOCTYPE[[:space:]]+([a-zA-Z0-9]+)[[:space:]]+[^[]*\\[((.|[\n])+)\\]>", "\\1",
97 	 ROOT_ELT_LETTER_STR, "{mgroup=1}{_guest=DTD,2start,2end}", NULL, true},
98 };
99 
makeTagWithNotificationCommon(tagEntryInfo * tag,xmlNode * node)100 static int makeTagWithNotificationCommon (tagEntryInfo *tag,
101 										   xmlNode *node)
102 {
103 	int n = makeTagEntry (tag);
104 
105 	subparser *sub;
106 	foreachSubparser (sub, false)
107 	{
108 		xmlSubparser *xmlsub = (xmlSubparser *)sub;
109 
110 		if (xmlsub->makeTagEntryWithNodeNotify)
111 		{
112 			enterSubparser(sub);
113 			xmlsub->makeTagEntryWithNodeNotify (xmlsub, node, tag);
114 			leaveSubparser();
115 		}
116 	}
117 	return n;
118 }
119 
makeTagWithNotification(xmlNode * node,const char * xpath CTAGS_ATTR_UNUSED,const tagXpathMakeTagSpec * spec CTAGS_ATTR_UNUSED,tagEntryInfo * tag,void * userData CTAGS_ATTR_UNUSED)120 static void makeTagWithNotification (xmlNode *node,
121 									 const char *xpath CTAGS_ATTR_UNUSED,
122 									 const tagXpathMakeTagSpec *spec CTAGS_ATTR_UNUSED,
123 									 tagEntryInfo *tag,
124 									 void *userData CTAGS_ATTR_UNUSED)
125 {
126 	makeTagWithNotificationCommon (tag, node);
127 }
128 
makeNsPrefixTag(const char * name,xmlNode * node,xmlNsPtr ns)129 static int makeNsPrefixTag (const char *name, xmlNode *node, xmlNsPtr ns)
130 {
131 	int n = CORK_NIL;
132 	tagEntryInfo tag;
133 	vString *anon = NULL;
134 
135 	if (name)
136 		initTagEntry (&tag, name, K_NSPREFIX);
137 	else
138 	{
139 		anon = anonGenerateNew ("ns", K_NSPREFIX);
140 		initTagEntry (&tag, vStringValue (anon), K_NSPREFIX);
141 		markTagExtraBit (&tag, XTAG_ANONYMOUS);
142 	}
143 	/* TODO
144 	 * - move this code block to lxpath.c.
145 	 * - adjust the line number for nsprefixes forward. */
146 	tag.lineNumber = XML_GET_LINE (node);
147 	tag.filePosition = getInputFilePositionForLine (tag.lineNumber);
148 	char *p = (char *)xmlGetNodePath (node);
149 	if (ns->href && *ns->href)
150 		attachParserField (&tag, false, XmlFields [F_NS_URI].ftype, (char *)ns->href);
151 
152 	n = makeTagWithNotificationCommon (&tag, node);
153 	if (p)
154 		xmlFree (p);
155 	if (anon)
156 		vStringDelete (anon);
157 
158 	return n;
159 }
160 
findNsPrefix(xmlNode * node,const char * xpath,const struct sTagXpathRecurSpec * spec,xmlXPathContext * ctx,void * userData)161 static void findNsPrefix (xmlNode *node,
162 					   const char *xpath,
163 					   const struct sTagXpathRecurSpec *spec,
164 					   xmlXPathContext *ctx,
165 					   void *userData)
166 {
167 	for (xmlNsPtr ns = node->nsDef; ns; ns = ns->next)
168 		makeNsPrefixTag ((char *)ns->prefix, node, ns);
169 
170 	findXMLTags (ctx, node, spec->nextTable, userData);
171 }
172 
runAfter(xmlXPathContext * ctx,xmlNode * root,void * user_data)173 static void runAfter (xmlXPathContext *ctx, xmlNode *root, void *user_data)
174 {
175 	subparser *sub;
176 	foreachSubparser (sub, false)
177 	{
178 		xmlSubparser *xmlsub = (xmlSubparser *)sub;
179 		if (xmlsub->runXPathEngine)
180 		{
181 			enterSubparser(sub);
182 			xmlsub->runXPathEngine (xmlsub, ctx, root);
183 			leaveSubparser();
184 		}
185 	}
186 }
187 
188 static void
findXmlTags(void)189 findXmlTags (void)
190 {
191 	findXMLTagsFull (NULL, NULL, TABLE_MAIN, runAfter, NULL);
192 }
193 
194 extern parserDefinition*
XmlParser(void)195 XmlParser (void)
196 {
197 	static const char *const extensions [] = { "xml", NULL };
198 	parserDefinition* const def = parserNew ("XML");
199 	static selectLanguage selectors[] = { selectByXpathFileSpec, NULL };
200 
201 	def->kindTable     = XmlKinds;
202 	def->kindCount     = ARRAY_SIZE (XmlKinds);
203 	def->extensions    = extensions;
204 	def->parser        = findXmlTags;
205 	def->tagXpathTableTable = xmlXpathTableTable;
206 	def->tagXpathTableCount = ARRAY_SIZE (xmlXpathTableTable);
207 	def->selectLanguage = selectors;
208 	def->fieldTable = XmlFields;
209 	def->fieldCount = ARRAY_SIZE (XmlFields);
210 	def->tagRegexTable = XmlTagRegexTable;
211 	def->tagRegexCount = ARRAY_SIZE(XmlTagRegexTable);
212 
213 	return def;
214 }
215