Merge pull request #3196 from techee/anon_rename_fix2
[geany-mirror.git] / ctags / parsers / nsis.c
blob8e64196b3047b4b59d2b492b4b8f0e36b2bf3c5f
1 /*
2 * Copyright (c) 2000-2002, Darren Hiebert
3 * Copyright (c) 2009-2011, Enrico Tröger
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.
8 * This module contains functions for generating tags for NSIS scripts
9 * (https://en.wikipedia.org/wiki/Nullsoft_Scriptable_Install_System).
11 * Based on sh.c.
15 * INCLUDE FILES
17 #include "general.h" /* must always come first */
19 #include <string.h>
21 #include "entry.h"
22 #include "parse.h"
23 #include "read.h"
24 #include "vstring.h"
25 #include "routines.h"
28 * DATA DEFINITIONS
30 typedef enum {
31 K_SECTION,
32 K_FUNCTION,
33 K_VARIABLE,
34 K_DEFINITION,
35 K_MACRO,
36 K_SECTION_GROUP,
37 K_MACRO_PARAM,
38 K_LANGSTR,
39 K_SCRIPT,
40 } NsisKind;
42 typedef enum {
43 NSIS_SCRIPT_INCLUDED,
44 } nsisScriptRole;
46 static roleDefinition NsisScriptRoles [] = {
47 { true, "included", "included with !include" },
50 static kindDefinition NsisKinds [] = {
51 { true, 's', "section", "sections"},
52 { true, 'f', "function", "functions"},
53 { true, 'v', "variable", "variables"},
54 { true, 'd', "definition", "definitions"},
55 { true, 'm', "macro", "macros"},
56 { true, 'S', "sectionGroup", "section groups"},
57 { false, 'p', "macroparam", "macro parameters"},
58 { true, 'l', "langstr", "language strings"},
59 { true, 'i', "script", "NSIS scripts",
60 .referenceOnly = true, ATTACH_ROLES(NsisScriptRoles)},
63 typedef enum {
64 F_LANGID,
65 } nsisField;
67 static fieldDefinition NsisFields[] = {
68 { .name = "langid",
69 .description = "language identifier specified in (License)LangString commands",
70 .enabled = true },
74 * FUNCTION DEFINITIONS
77 static const unsigned char* skipWhitespace (const unsigned char* cp)
79 while (isspace ((int) *cp))
80 ++cp;
81 return cp;
84 static const unsigned char* skipFlags (const unsigned char* cp)
86 while (*cp == '/')
88 ++cp;
89 while (! isspace ((int) *cp))
90 ++cp;
91 while (isspace ((int) *cp))
92 ++cp;
94 return cp;
97 static int makeSimpleTagWithScope(vString *name, int kindIndex, int parentCorkIndex)
99 tagEntryInfo e;
100 initTagEntry (&e, vStringValue (name), kindIndex);
101 e.extensionFields.scopeIndex = parentCorkIndex;
102 return makeTagEntry (&e);
105 #define lineStartingWith(CP,EXPECTED,EOL) \
106 (strncasecmp ((const char*) CP, EXPECTED, strlen(EXPECTED)) == 0 \
107 && (EOL ? (isspace ((int) CP [strlen(EXPECTED)]) || CP [strlen(EXPECTED)] == '\0') \
108 : isspace ((int) CP [strlen(EXPECTED)])))
110 #define fillName(NAME,CP,CONDITION) \
111 while (CONDITION) \
113 vStringPut ((NAME), (int) *(CP)); \
114 ++(CP); \
116 do {} while (0)
118 static const unsigned char* parseSection (const unsigned char* cp, vString *name,
119 int kindIndex, int scopeIndex, int *corkIndex)
121 cp = skipWhitespace (cp);
122 cp = skipFlags (cp);
123 cp = skipWhitespace (cp);
125 if (corkIndex)
126 *corkIndex = CORK_NIL;
128 if (strpbrk((const char *)cp, "'`\""))
130 const unsigned char terminator = *cp;
132 cp++;
133 if (*cp == terminator)
135 /* An empty section.
136 * See https://nsis.sourceforge.io/Docs/Chapter4.html#sectionsettext
138 anonGenerate (name,
139 (kindIndex == K_SECTION
140 ? "AnonymousSection"
141 : "AnonymousSectionGroup"),
142 kindIndex);
143 cp++;
145 else if (*cp == '\0')
146 return cp;
147 else
149 int in_escape = 0;
152 vStringPut (name, (int) *cp);
153 ++cp;
155 if (*cp == '\0')
156 break;
159 * Ignore `"' in `$\"' as the terminator of quotation.
161 if (*cp == '$' && in_escape == 0)
162 in_escape++;
163 else if (*cp == '\\' && in_escape == 1)
164 in_escape++;
165 else if (*cp == terminator && in_escape == 2)
167 * This `"' is not a terminator of quotation;
168 * set in_escape to 3.
170 in_escape++;
171 else
172 in_escape = 0;
174 if ((in_escape != 3) && *cp == terminator)
176 ++cp;
177 break;
180 while (1);
183 else
185 while (isalnum ((int) *cp)
186 || *cp == '_' || *cp == '-' || *cp == '.' || *cp == '!'
187 || *cp == '$' || *cp == '{' || *cp == '}' || *cp == '(' || *cp == ')')
189 vStringPut (name, (int) *cp);
190 ++cp;
193 int r = makeSimpleTagWithScope (name, kindIndex, scopeIndex);
194 if (corkIndex)
195 *corkIndex = r;
196 if (vStringLength (name) > 0)
199 * Try to capture section_index_output.
201 vStringClear (name);
202 cp = skipWhitespace (cp);
204 fillName (name, cp, (isalnum ((int) *cp) || *cp == '_'));
206 if (vStringLength (name) > 0)
208 makeSimpleTag (name, K_DEFINITION);
209 vStringClear (name);
212 return cp;
215 static const unsigned char* parseLangString (const unsigned char* cp, vString *name)
217 cp = skipWhitespace (cp);
219 /* `^' is not explained in the nsis reference manual. However, it is used
220 * in gvim.
221 * e.g.
222 * https://github.com/vim/vim/blob/3dabd718f4b2d8e09de9e2ec73832620b91c2f79/nsis/lang/english.nsi
224 fillName (name, cp, (isalnum ((int) *cp) || *cp == '_' || *cp == '^'));
226 if (vStringLength (name) > 0)
228 int r = makeSimpleTag (name, K_LANGSTR);
229 if (r == CORK_NIL)
230 goto out;
231 vStringClear (name);
233 cp = skipWhitespace (cp);
234 fillName (name, cp, ((*cp != '\0') && (!isspace ((int) *cp))));
235 if (vStringLength (name) > 0)
237 attachParserFieldToCorkEntry (r, NsisFields[F_LANGID].ftype,
238 vStringValue (name));
239 vStringClear (name);
242 out:
243 return cp;
246 static void findNsisTags (void)
248 int sectionGroupIndex = CORK_NIL;
249 vString *name = vStringNew ();
250 const unsigned char *line;
252 while ((line = readLineFromInputFile ()) != NULL)
254 const unsigned char* cp = line;
256 while (isspace (*cp))
257 cp++;
259 if (*cp == '#' || *cp == ';')
260 continue;
262 /* functions */
263 if (lineStartingWith (cp, "function", false))
265 cp += 8;
266 cp = skipWhitespace (cp);
268 fillName (name, cp,
269 (isalnum ((int) *cp) || *cp == '_' || *cp == '-' || *cp == '.' || *cp == '!'));
271 makeSimpleTag (name, K_FUNCTION);
272 vStringClear (name);
274 /* variables */
275 else if (lineStartingWith (cp, "var", false))
277 cp += 3;
278 cp = skipWhitespace (cp);
279 cp = skipFlags (cp);
281 fillName (name, cp, (isalnum ((int) *cp) || *cp == '_'));
283 makeSimpleTag (name, K_VARIABLE);
284 vStringClear (name);
286 /* section groups */
287 else if (lineStartingWith (cp, "sectiongroup", false))
289 cp += 12;
290 cp = parseSection (cp, name, K_SECTION_GROUP, CORK_NIL, &sectionGroupIndex);
292 else if (lineStartingWith (cp, "sectiongroupend", true))
294 cp += 15;
295 sectionGroupIndex = CORK_NIL;
297 /* sections */
298 else if (lineStartingWith (cp, "section", false))
300 cp += 7;
301 cp = parseSection (cp, name, K_SECTION, sectionGroupIndex, NULL);
303 /* LangString */
304 else if (lineStartingWith (cp, "langstring", false))
306 cp += 10;
307 cp = parseLangString (cp, name);
309 /* LicenseLangString */
310 else if (lineStartingWith (cp, "licenselangstring", false))
312 cp += 17;
313 cp = parseLangString (cp, name);
315 /* definitions */
316 else if (lineStartingWith (cp, "!define", false))
318 cp += 7;
319 cp = skipWhitespace (cp);
320 cp = skipFlags (cp);
322 fillName (name, cp, (isalnum ((int) *cp) || *cp == '_'));
324 makeSimpleTag (name, K_DEFINITION);
325 vStringClear (name);
327 /* macro */
328 else if (lineStartingWith (cp, "!macro", false))
330 cp += 6;
331 cp = skipWhitespace (cp);
332 cp = skipFlags (cp);
334 fillName (name, cp, (isalnum ((int) *cp) || *cp == '_'));
336 int index = makeSimpleTag (name, K_MACRO);
337 if (vStringLength (name) > 0)
339 while (1)
341 vStringClear (name);
342 cp = skipWhitespace (cp);
343 fillName (name, cp, (isalnum ((int) *cp) || *cp == '_'));
344 if (vStringLength (name) == 0)
345 break;
346 makeSimpleTagWithScope (name, K_MACRO_PARAM, index);
350 /* include */
351 else if (lineStartingWith (cp, "!include", false))
353 cp += 8;
355 /* !include [/NONFATAL] [/CHARSET=ACP|OEM|CP#|UTF8|UTF16LE|UTF16BE] file */
356 cp = skipWhitespace (cp);
358 /* /NONFATAL */
359 cp = skipFlags (cp);
360 cp = skipWhitespace (cp);
362 /* /CHARSET */
363 cp = skipFlags (cp);
364 cp = skipWhitespace (cp);
366 fillName (name, cp, (*cp != '\0' && *cp != ';' && *cp != '#'));
367 vStringStripTrailing (name);
369 if (vStringLength (name) > 0)
371 makeSimpleRefTag (name, K_SCRIPT, NSIS_SCRIPT_INCLUDED);
372 vStringClear (name);
374 /* TODO: capture !addincludedir */
377 vStringDelete (name);
380 extern parserDefinition* NsisParser (void)
382 static const char *const extensions [] = {
383 "nsi", "nsh", NULL
385 parserDefinition* def = parserNew ("NSIS");
386 def->kindTable = NsisKinds;
387 def->kindCount = ARRAY_SIZE (NsisKinds);
388 def->extensions = extensions;
389 def->fieldTable = NsisFields;
390 def->fieldCount = ARRAY_SIZE (NsisFields);
391 def->parser = findNsisTags;
392 def->useCork = CORK_QUEUE;
393 return def;