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