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 §ionCorkIndex);
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 §ionCorkIndex);
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