Update Russian translation (#3918)
[geany-mirror.git] / ctags / parsers / make.c
blob57e19dec416f2faf131cf92f21c776ce276c3e06
1 /*
2 * Copyright (c) 2000-2005, Darren Hiebert
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License version 2 or (at your option) any later version.
7 * This module contains functions for generating tags for makefiles.
8 */
11 * INCLUDE FILES
13 #include "general.h" /* must always come first */
15 #include <string.h>
16 #include <ctype.h>
18 #include "make.h"
20 #include "entry.h"
21 #include "kind.h"
22 #include "numarray.h"
23 #include "parse.h"
24 #include "read.h"
25 #include "routines.h"
26 #include "strlist.h"
27 #include "vstring.h"
28 #include "xtag.h"
32 * DATA DEFINITIONS
34 typedef enum {
35 K_MACRO, K_TARGET, K_INCLUDE,
36 } makeKind;
38 typedef enum {
39 R_INCLUDE_GENERIC,
40 R_INCLUDE_OPTIONAL,
41 } makeMakefileRole;
43 static roleDefinition MakeMakefileRoles [] = {
44 { true, "included", "included" },
45 { true, "optional", "optionally included"},
48 static kindDefinition MakeKinds [] = {
49 { true, 'm', "macro", "macros"},
50 { true, 't', "target", "targets"},
51 { true, 'I', "makefile", "makefiles",
52 .referenceOnly = true, ATTACH_ROLES(MakeMakefileRoles)},
57 * FUNCTION DEFINITIONS
60 static int nextChar (void)
62 int c = getcFromInputFile ();
63 if (c == '\\')
65 c = getcFromInputFile ();
66 if (c == '\n')
67 c = nextChar ();
69 return c;
72 static void skipLine (void)
74 int c;
76 c = nextChar ();
77 while (c != EOF && c != '\n');
78 if (c == '\n')
79 ungetcToInputFile (c);
82 static int skipToNonWhite (int c)
84 while (c != '\n' && isspace (c))
85 c = nextChar ();
86 return c;
89 static bool isIdentifier (int c)
91 return (bool)(c != '\0' && (isalnum (c) || strchr (".-_/$(){}%", c) != NULL));
94 static bool isSpecialTarget (vString *const name)
96 size_t i = 0;
97 /* All special targets begin with '.'. */
98 if (vStringLength (name) < 1 || vStringChar (name, i++) != '.') {
99 return false;
101 while (i < vStringLength (name)) {
102 char ch = vStringChar (name, i++);
103 if (ch != '_' && !isupper ((unsigned char) ch))
105 return false;
108 return true;
111 static int makeSimpleMakeTag (vString *const name, makeKind kind)
113 if (!isLanguageEnabled (getInputLanguage ()))
114 return CORK_NIL;
116 return makeSimpleTag (name, kind);
119 static void makeSimpleMakeRefTag (const vString* const name, const int kind,
120 int roleIndex)
122 if (!isLanguageEnabled (getInputLanguage ()))
123 return;
125 makeSimpleRefTag (name, kind, roleIndex);
128 static int newTarget (vString *const name)
130 /* Ignore GNU Make's "special targets". */
131 if (isSpecialTarget (name))
133 return CORK_NIL;
135 return makeSimpleMakeTag (name, K_TARGET);
138 static int newMacro (vString *const name, bool with_define_directive, bool appending)
140 int r = CORK_NIL;
141 subparser *s;
143 if (!appending)
144 r = makeSimpleMakeTag (name, K_MACRO);
146 foreachSubparser(s, false)
148 makeSubparser *m = (makeSubparser *)s;
149 enterSubparser(s);
150 if (m->newMacroNotify)
151 m->newMacroNotify (m, vStringValue(name), with_define_directive, appending);
152 leaveSubparser();
155 return r;
158 static void valueFound (vString *const name)
160 subparser *s;
161 foreachSubparser(s, false)
163 makeSubparser *m = (makeSubparser *)s;
164 enterSubparser(s);
165 if (m->valueNotify)
166 m->valueNotify (m, vStringValue (name));
167 leaveSubparser();
171 static void directiveFound (vString *const name)
173 subparser *s;
174 foreachSubparser (s, false)
176 makeSubparser *m = (makeSubparser *)s;
177 enterSubparser(s);
178 if (m->directiveNotify)
179 m->directiveNotify (m, vStringValue (name));
180 leaveSubparser();
184 static void newInclude (vString *const name, bool optional)
186 makeSimpleMakeRefTag (name, K_INCLUDE,
187 optional? R_INCLUDE_OPTIONAL: R_INCLUDE_GENERIC);
190 static bool isAcceptableAsInclude (vString *const name)
192 if (strcmp (vStringValue (name), "$") == 0)
193 return false;
194 return true;
197 static void readIdentifier (const int first, vString *const id)
199 int depth = 0;
200 int c = first;
201 vStringClear (id);
202 while (isIdentifier (c) || (depth > 0 && c != EOF && c != '\n'))
204 if (c == '(' || c == '{')
205 depth++;
206 else if (depth > 0 && (c == ')' || c == '}'))
207 depth--;
208 vStringPut (id, c);
209 c = nextChar ();
211 ungetcToInputFile (c);
214 static void endTargets (intArray *targets, unsigned long lnum)
216 for (unsigned int i = 0; i < intArrayCount (targets); i++)
218 int cork_index = intArrayItem (targets, i);
219 setTagEndLineToCorkEntry (cork_index, lnum);
221 intArrayClear (targets);
224 static bool isTheLastTargetOnTheSameLine (intArray *current_targets,
225 unsigned long line)
227 if (!intArrayIsEmpty (current_targets))
229 int r = intArrayLast (current_targets);
230 tagEntryInfo *e = getEntryInCorkQueue (r);
231 if (e && e->lineNumber == line)
232 return true;
235 return false;
238 static void findMakeTags (void)
240 stringList *identifiers = stringListNew ();
241 bool newline = true;
242 int current_macro = CORK_NIL;
243 bool in_value = false;
244 intArray *current_targets = intArrayNew ();
245 bool variable_possible = true;
246 bool appending = false;
247 int c;
248 subparser *sub;
250 sub = getSubparserRunningBaseparser();
251 if (sub)
252 chooseExclusiveSubparser (sub, NULL);
254 while ((c = nextChar ()) != EOF)
256 if (newline)
258 if (!intArrayIsEmpty (current_targets))
260 if (c == '\t' || (c = skipToNonWhite (c)) == '#')
262 skipLine (); /* skip rule or comment */
263 c = nextChar ();
265 else if (c != '\n')
266 endTargets (current_targets, getInputLineNumber () - 1);
268 else if (in_value)
269 in_value = false;
271 stringListClear (identifiers);
272 variable_possible = intArrayIsEmpty (current_targets);
273 newline = false;
275 if (c == '\n')
276 newline = true;
277 else if (isspace (c))
278 continue;
279 else if (c == '#')
280 skipLine ();
281 else if (variable_possible && c == '?')
283 c = nextChar ();
284 ungetcToInputFile (c);
285 variable_possible = (c == '=');
287 else if (variable_possible && c == '+')
289 c = nextChar ();
290 ungetcToInputFile (c);
291 variable_possible = (c == '=');
292 appending = true;
294 else if ((! in_value) && variable_possible && c == ':' &&
295 stringListCount (identifiers) > 0)
297 c = nextChar ();
298 ungetcToInputFile (c);
299 if (c != '=')
301 unsigned int i;
302 for (i = 0; i < stringListCount (identifiers); i++)
304 int r = newTarget (stringListItem (identifiers, i));
305 if (r != CORK_NIL)
306 intArrayAdd (current_targets, r);
308 stringListClear (identifiers);
311 else if (variable_possible && c == '=' &&
312 stringListCount (identifiers) == 1)
314 newMacro (stringListItem (identifiers, 0), false, appending);
316 in_value = true;
317 unsigned long curline = getInputLineNumber ();
318 unsigned long adj = isTheLastTargetOnTheSameLine (current_targets,
319 curline)? 0: 1;
320 endTargets (current_targets, curline - adj);
321 appending = false;
323 else if (variable_possible && isIdentifier (c))
325 vString *name = vStringNew ();
326 readIdentifier (c, name);
327 stringListAdd (identifiers, name);
329 if (in_value)
330 valueFound(name);
332 if (stringListCount (identifiers) == 1)
334 if ((current_macro != CORK_NIL) && ! strcmp (vStringValue (name), "endef"))
336 setTagEndLineToCorkEntry (current_macro, getInputLineNumber ());
337 current_macro = CORK_NIL;
339 else if (current_macro != CORK_NIL)
340 skipLine ();
341 else if (! strcmp (vStringValue (name), "define"))
343 c = skipToNonWhite (nextChar ());
344 vStringClear (name);
345 /* all remaining characters on the line are the name -- even spaces */
346 while (c != EOF && c != '\n')
348 vStringPut (name, c);
349 c = nextChar ();
351 if (c == '\n')
352 ungetcToInputFile (c);
353 vStringStripTrailing (name);
355 current_macro = newMacro (name, true, false);
357 else if (! strcmp (vStringValue (name), "export"))
358 stringListClear (identifiers);
359 else if (! strcmp (vStringValue (name), "include")
360 || ! strcmp (vStringValue (name), "sinclude")
361 || ! strcmp (vStringValue (name), "-include"))
363 bool optional = (vStringValue (name)[0] == 'i')? false: true;
364 while (1)
366 c = skipToNonWhite (nextChar ());
367 readIdentifier (c, name);
368 vStringStripTrailing (name);
369 if (isAcceptableAsInclude(name))
370 newInclude (name, optional);
372 /* non-space characters after readIdentifier() may
373 * be rejected by the function:
374 * e.g.
375 * include $*
377 * Here, remove such characters from input stream.
380 c = nextChar ();
381 while (c != EOF && c != '\n' && (!isspace (c)));
382 if (c == '\n')
383 ungetcToInputFile (c);
385 if (c == EOF || c == '\n')
386 break;
389 else
390 directiveFound (name);
393 else
394 variable_possible = false;
397 endTargets (current_targets, getInputLineNumber ());
399 intArrayDelete (current_targets);
400 stringListDelete (identifiers);
404 extern parserDefinition* MakefileParser (void)
406 static const char *const patterns [] = { "[Mm]akefile", "GNUmakefile", NULL };
407 static const char *const extensions [] = { "mak", "mk", NULL };
408 static const char *const aliases [] = {
409 /* the mode name in emacs */
410 "makefile",
411 NULL };
412 parserDefinition* const def = parserNew ("Make");
413 def->kindTable = MakeKinds;
414 def->kindCount = ARRAY_SIZE (MakeKinds);
415 def->patterns = patterns;
416 def->extensions = extensions;
417 def->aliases = aliases;
418 def->parser = findMakeTags;
419 def->useCork = CORK_QUEUE;
420 return def;