xref: /Universal-ctags/parsers/iniconf.c (revision 3671ad7255885a0c8f6ff4979d80c70f201ea411)
1 /*
2 *
3 *   Copyright (c) 2000-2001, Darren Hiebert
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 generating tags for ini/config files.
9 */
10 
11 /*
12  *  This is based on geany's conf.c:
13  * --------------------------------
14  * commit 3af538fa65f8b17897259080db8144b1edc43470
15  * Author: Enrico Tröger <enrico.troeger@uvena.de>
16  * Date:   Sun Nov 27 20:39:57 2005 +0000
17  *
18  * added tag support for filetype Conf
19  *
20  *
21  * git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@15 ea778897-0a13-0410-b9d1-a72fbfd435f5
22  *
23  */
24 
25 #include "general.h"  /* must always come first */
26 
27 #include "entry.h"
28 #include "htable.h"
29 #include "iniconf.h"
30 #include "parse.h"
31 #include "read.h"
32 #include "subparser.h"
33 #include "vstring.h"
34 
isIdentifier(int c)35 static bool isIdentifier (int c)
36 {
37     /* allow whitespace within keys and sections */
38     return (bool)(isalnum (c) || isspace (c) ||  c == '_');
39 }
40 
isValue(int c)41 static bool isValue (int c)
42 {
43 	return (c != '\0');
44 }
45 
maySwitchLanguage(const char * section,const char * key,const char * value)46 static iniconfSubparser *maySwitchLanguage (const char *section, const char *key, const char *value)
47 {
48 	iniconfSubparser *iniconf_subparser = NULL;
49 	subparser *sub;
50 
51 	foreachSubparser (sub, false)
52 	{
53 		iniconfSubparser *s = (iniconfSubparser *)sub;
54 		if ((sub->direction & SUBPARSER_BASE_RUNS_SUB)
55 			&& s->probeLanguage)
56 		{
57 			bool r;
58 
59 			enterSubparser ((subparser *)s);
60 			r = s->probeLanguage(section, key, value);
61 			leaveSubparser ();
62 			if (r)
63 			{
64 				iniconf_subparser = s;
65 				chooseExclusiveSubparser (sub, NULL);
66 				break;
67 			}
68 		}
69 	}
70 
71 	return iniconf_subparser;
72 }
73 
74 typedef enum {
75 	K_SECTION,
76 	K_KEY,
77 } makeKind;
78 
79 static kindDefinition IniconfKinds [] = {
80 	{ true, 's', "section",  "sections"},
81 	{ true, 'k', "key",      "keys"},
82 };
83 
makeIniconfTagMaybe(const char * section,const char * key,const char * value CTAGS_ATTR_UNUSED,int * index)84 static void makeIniconfTagMaybe (const char *section, const char *key, const char *value CTAGS_ATTR_UNUSED, int *index)
85 {
86 	tagEntryInfo e;
87 
88 	if (!isLanguageEnabled (getInputLanguage ()))
89 		return;
90 
91 	if (key)
92 	{
93 		initTagEntry (&e, key, K_KEY);
94 		e.extensionFields.scopeIndex = *index;
95 		makeTagEntry (&e);
96 	}
97 	else
98 	{
99 		tagEntryInfo *last = getEntryInCorkQueue (*index);
100 		if (last)
101 			last->extensionFields.endLine = getInputLineNumber ();
102 
103 		initTagEntry (&e, section, K_SECTION);
104 		*index = makeTagEntry (&e);
105 	}
106 }
107 
findIniconfTags(void)108 static void findIniconfTags (void)
109 {
110 	const unsigned char *line;
111 	vString *val   = vStringNew ();
112 	vString *name  = vStringNew ();
113 	vString *scope = vStringNew ();
114 	iniconfSubparser *sub;
115 	int sectionCorkIndex = CORK_NIL;
116 
117 
118 	sub = (iniconfSubparser *)getSubparserRunningBaseparser();
119 	if (sub)
120 		chooseExclusiveSubparser ((subparser *)sub, NULL);
121 
122 	while ((line = readLineFromInputFile ()) != NULL)
123 	{
124 		const unsigned char* cp = line;
125 		bool possible = true;
126 
127 		if (isspace ((int) *cp) || *cp == '#' || *cp == ';' || *cp == '\0'
128 		    || (*cp == '/' && *(cp+1) == '/'))
129 			continue;
130 
131 		/* look for a section */
132 		if (*cp == '[')
133 		{
134 			++cp;
135 			while (*cp != '\0' && *cp != ']')
136 			{
137 				vStringPut (name, (int) *cp);
138 				++cp;
139 			}
140 
141 			makeIniconfTagMaybe (vStringValue (name), NULL, NULL,
142 								 &sectionCorkIndex);
143 
144 
145 			if (!sub)
146 				sub = maySwitchLanguage (vStringValue (name), NULL, NULL);
147 
148 			if (sub)
149 			{
150 				enterSubparser((subparser *)sub);
151 				sub->newDataNotify (sub, vStringValue (name), NULL, NULL);
152 				leaveSubparser ();
153 			}
154 
155 			vStringCopy (scope, name);
156 			vStringClear (name);
157 			continue;
158 		}
159 
160 		while (*cp != '\0')
161 		{
162 			/*  We look for any sequence of identifier characters following a white space */
163 			if (possible && isIdentifier ((int) *cp))
164 			{
165 				while (isIdentifier ((int) *cp))
166 				{
167 					vStringPut (name, (int) *cp);
168 					++cp;
169 				}
170 				vStringStripTrailing (name);
171 				while (isspace ((int) *cp))
172 					++cp;
173 				if (*cp == '=' || *cp == ':')
174 				{
175 
176 					cp++;
177 					while (isspace ((int) *cp))
178 						++cp;
179 					while (isValue ((int) *cp))
180 					{
181 						vStringPut (val, (int) *cp);
182 						++cp;
183 					}
184 					vStringStripTrailing (val);
185 
186 					makeIniconfTagMaybe ((vStringLength (scope) > 0)
187 										 ? vStringValue (scope)
188 										 : NULL,
189 										 vStringValue (name),
190 										 vStringValue (val),
191 										 &sectionCorkIndex);
192 					if (!sub)
193 						sub = maySwitchLanguage ((vStringLength (scope) > 0)
194 												 ? vStringValue (scope)
195 												 : NULL,
196 												 vStringValue (name),
197 												 vStringValue (val));
198 					if (sub)
199 					{
200 						enterSubparser ((subparser *)sub);
201 						sub->newDataNotify (sub,
202 											(vStringLength (scope) > 0)
203 											? vStringValue (scope)
204 											: NULL,
205 											vStringValue (name),
206 											vStringValue (val));
207 						leaveSubparser ();
208 					}
209 					vStringClear (val);
210 				}
211 				vStringClear (name);
212 			}
213 			else
214 				possible = !!(isspace ((int) *cp));
215 
216 			if (*cp != '\0')
217 				++cp;
218 		}
219 	}
220 
221 	vStringDelete (name);
222 	vStringDelete (scope);
223 	vStringDelete (val);
224 }
225 
IniconfParser(void)226 extern parserDefinition* IniconfParser (void)
227 {
228 	static const char *const extensions [] = { "ini", "conf", NULL };
229 	parserDefinition* const def = parserNew ("Iniconf");
230 
231 	def->kindTable      = IniconfKinds;
232 	def->kindCount  = ARRAY_SIZE (IniconfKinds);
233 	def->extensions = extensions;
234 	def->parser     = findIniconfTags;
235 	def->useCork   = CORK_QUEUE;
236 
237 	return def;
238 }
239