xref: /Universal-ctags/parsers/plist.c (revision 436fe7fa55f54367a8fe33f0040e3c5cdef6c679)
1 /*
2 *
3 *   Copyright (c) 2016, Masatake YAMATO
4 *   Copyright (c) 2016, 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 property list defined
10 *   in http://www.apple.com/DTDs/PropertyList-1.0.dtd.
11 */
12 
13 #include "general.h"	/* must always come first */
14 
15 #include <string.h>
16 
17 #include "entry.h"
18 #include "parse.h"
19 #include "read.h"
20 #include "routines.h"
21 #include "strlist.h"
22 #include "xml.h"
23 
24 
25 typedef enum {
26 	K_KEY,
27 } plistKind;
28 
29 static kindDefinition PlistKinds [] = {
30 	{ true,  'k', "key",	  "keys" },
31 };
32 
33 static void plistFindTagsUnderKey (xmlNode *node,
34 				   const char *xpath,
35 				   const struct sTagXpathRecurSpec *spec,
36 				   xmlXPathContext *ctx,
37 				   void *userData);
38 
39 static void makeTagWithScope (xmlNode *node,
40 			      const char *xpath,
41 			      const struct sTagXpathMakeTagSpec *spec,
42 			      struct sTagEntryInfo *tag,
43 			      void *userData);
44 
45 static tagXpathTable plistXpathMainTable[] = {
46 	{ "///plist//dict/key",
47 	  LXPATH_TABLE_DO_RECUR,
48 	  .spec.recurSpec = {
49 			plistFindTagsUnderKey
50 		}
51 	},
52 };
53 
54 static tagXpathTable plistXpathTextTable[] = {
55 	{ "text()",
56 	  LXPATH_TABLE_DO_MAKE,
57 	  .spec.makeTagSpec = {
58 			K_KEY,  ROLE_DEFINITION_INDEX,
59 			makeTagWithScope
60 		}
61 	},
62 };
63 
64 enum plistXpathTable {
65 	TABLE_MAIN,
66 	TABLE_TEXT,
67 };
68 
69 static tagXpathTableTable plistXpathTableTable[] = {
70 	[TABLE_MAIN] = { ARRAY_AND_SIZE(plistXpathMainTable) },
71 	[TABLE_TEXT] = { ARRAY_AND_SIZE(plistXpathTextTable) },
72 };
73 
isCompoundElement(xmlNode * node)74 static bool isCompoundElement (xmlNode *node)
75 {
76 	return !! (node->name
77 		   && ((strcmp ((char *)(node->name), "dict") == 0)
78 		       || (strcmp ((char *)(node->name), "array") == 0)));
79 }
80 
getPrevKeyElement(xmlNode * node)81 static xmlNode *getPrevKeyElement (xmlNode *node)
82 {
83 	xmlNode *prev;
84 
85 	prev = xmlPreviousElementSibling (node);
86 	if (prev)
87 	{
88 		if (strcmp ((char *)prev->name, "key") == 0)
89 			return prev;
90 		else
91 			prev = NULL;
92 	}
93 	return prev;
94 }
95 
plistFindTagsUnderKey(xmlNode * node,const char * xpath CTAGS_ATTR_UNUSED,const struct sTagXpathRecurSpec * spec CTAGS_ATTR_UNUSED,xmlXPathContext * ctx,void * userData CTAGS_ATTR_UNUSED)96 static void plistFindTagsUnderKey (xmlNode *node,
97 				   const char *xpath CTAGS_ATTR_UNUSED,
98 				   const struct sTagXpathRecurSpec *spec CTAGS_ATTR_UNUSED,
99 				   xmlXPathContext *ctx,
100 				   void *userData CTAGS_ATTR_UNUSED)
101 {
102 	xmlNode *current;
103 	xmlNode *prev;
104 	stringList *queue;
105 	vString* path;
106 	vString* v;
107 	int c;
108 
109 	queue = stringListNew ();
110 	current = node;
111 	for (current = node; current; current = current->parent)
112 	{
113 		if (isCompoundElement (current)
114 		    && (prev = getPrevKeyElement (current)))
115 		{
116 			char* parent = (char *)xmlNodeGetContent (prev);
117 			if (parent)
118 			{
119 				v = vStringNewInit (parent);
120 				stringListAdd (queue, v);
121 				xmlFree (parent);
122 			}
123 		}
124 	}
125 
126 	path = vStringNew ();
127 	while ((c = stringListCount (queue)) > 0)
128 	{
129 		v = stringListLast (queue);
130 		vStringCat (path, v);
131 		vStringDelete (v);
132 		stringListRemoveLast (queue);
133 		if (c != 1)
134 			vStringPut (path, '.');
135 	}
136 	stringListDelete (queue);
137 
138 	findXMLTags (ctx, node,
139 				 TABLE_TEXT, (vStringLength (path) > 0)? vStringValue (path): NULL);
140 
141 	vStringDelete (path);
142 }
143 
makeTagWithScope(xmlNode * node CTAGS_ATTR_UNUSED,const char * xpath CTAGS_ATTR_UNUSED,const struct sTagXpathMakeTagSpec * spec CTAGS_ATTR_UNUSED,struct sTagEntryInfo * tag,void * userData)144 static void makeTagWithScope (xmlNode *node CTAGS_ATTR_UNUSED,
145 			      const char *xpath CTAGS_ATTR_UNUSED,
146 			      const struct sTagXpathMakeTagSpec *spec CTAGS_ATTR_UNUSED,
147 			      struct sTagEntryInfo *tag,
148 			      void *userData)
149 {
150 	tag->extensionFields.scopeKindIndex = userData? K_KEY: KIND_GHOST_INDEX;
151 	tag->extensionFields.scopeName  = userData;
152 	makeTagEntry (tag);
153 }
154 
155 static void
findPlistTags(void)156 findPlistTags (void)
157 {
158 	scheduleRunningBaseparser (RUN_DEFAULT_SUBPARSERS);
159 }
160 
161 static void
runXPathEngine(xmlSubparser * s,xmlXPathContext * ctx,xmlNode * root)162 runXPathEngine(xmlSubparser *s,
163 			   xmlXPathContext *ctx, xmlNode *root)
164 {
165 	findXMLTags (ctx, root, TABLE_MAIN, NULL);
166 }
167 
168 static xmlSubparser plistSubparser = {
169 	.subparser = {
170 		.direction = SUBPARSER_BI_DIRECTION,
171 	},
172 	.runXPathEngine = runXPathEngine,
173 };
174 
175 extern parserDefinition*
PlistXMLParser(void)176 PlistXMLParser (void)
177 {
178 	static const char *const extensions [] = { "plist", NULL };
179 	parserDefinition* const def = parserNew ("PlistXML");
180 	static parserDependency dependencies [] = {
181 		[0] = { DEPTYPE_SUBPARSER, "XML", &plistSubparser },
182 	};
183 
184 	def->kindTable         = PlistKinds;
185 	def->kindCount     = ARRAY_SIZE (PlistKinds);
186 	def->extensions    = extensions;
187 	def->parser        = findPlistTags;
188 	def->tagXpathTableTable  = plistXpathTableTable;
189 	def->tagXpathTableCount  = ARRAY_SIZE (plistXpathTableTable);
190 	def->dependencies = dependencies;
191 	def->dependencyCount = ARRAY_SIZE (dependencies);
192 
193 	return def;
194 }
195