xref: /Universal-ctags/parsers/automake.c (revision be440f85f3708cf0f2deb40928355d50ccc05d40)
1 /*
2 *   Copyright (c) 2000-2005, Darren Hiebert
3 *
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.
6 *
7 *   This module contains functions for generating tags for makefiles.
8 */
9 
10 /*
11 *   INCLUDE FILES
12 */
13 #include "general.h"  /* must always come first */
14 
15 #include <string.h>
16 #include <ctype.h>
17 
18 #include "make.h"
19 
20 #include "entry.h"
21 #include "htable.h"
22 #include "kind.h"
23 #include "parse.h"
24 #include "read.h"
25 #include "subparser.h"
26 
27 
28 typedef enum {
29 	K_AM_DIR,
30 	K_AM_PROGRAM,
31 	K_AM_MAN,
32 	K_AM_LTLIBRARY,
33 	K_AM_LIBRARY,
34 	K_AM_SCRIPT,
35 	K_AM_DATA,
36 	K_AM_CONDITION,
37 	K_AM_SUBDIR,
38 } makeAMKind;
39 
40 typedef enum {
41 	R_AM_DIR_PROGRAMS,
42 	R_AM_DIR_MANS,
43 	R_AM_DIR_LTLIBRARIES,
44 	R_AM_DIR_LIBRARIES,
45 	R_AM_DIR_SCRIPTS,
46 	R_AM_DIR_DATA,
47 } makeAMDirectoryRole;
48 
49 static roleDefinition AutomakeDirectoryRoles [] = {
50 	{ true, "program",   "directory for PROGRAMS primary" },
51 	{ true, "man",       "directory for MANS primary" },
52 	{ true, "ltlibrary", "directory for LTLIBRARIES primary"},
53 	{ true, "library",   "directory for LIBRARIES primary"},
54 	{ true, "script",    "directory for SCRIPTS primary"},
55 	{ true, "data",      "directory for DATA primary"},
56 };
57 
58 typedef enum {
59 	R_AM_CONDITION_BRANCHED,
60 } makeAMConditionRole;
61 
62 static roleDefinition AutomakeConditionRoles [] = {
63 	{ true, "branched",  "used for branching" },
64 };
65 
66 static scopeSeparator AutomakeSeparators [] = {
67 	{ K_AM_DIR        , "/" },
68 };
69 
70 static kindDefinition AutomakeKinds [] = {
71 	{ true, 'd', "directory", "directories",
72 	  .referenceOnly = false, ATTACH_ROLES(AutomakeDirectoryRoles)},
73 	{ true, 'P', "program",   "programs",
74 	  ATTACH_SEPARATORS(AutomakeSeparators) },
75 	{ true, 'M', "man",       "manuals",
76 	  ATTACH_SEPARATORS(AutomakeSeparators) },
77 	{ true, 'T', "ltlibrary", "ltlibraries",
78 	  ATTACH_SEPARATORS(AutomakeSeparators) },
79 	{ true, 'L', "library",   "libraries",
80 	  ATTACH_SEPARATORS(AutomakeSeparators) },
81 	{ true, 'S', "script",    "scripts",
82 	  ATTACH_SEPARATORS(AutomakeSeparators) },
83 	{ true, 'D', "data",      "datum",
84 	  ATTACH_SEPARATORS(AutomakeSeparators) },
85 	{ true, 'c', "condition", "conditions",
86 	  .referenceOnly = true, ATTACH_ROLES(AutomakeConditionRoles) },
87 	{ true, 's', "subdir", "subdirs" },
88 };
89 
90 struct sBlacklist {
91 	enum { BL_END, BL_PREFIX } type;
92 	const char* substr;
93 	size_t len;
94 } am_blacklist [] = {
95 	{ BL_PREFIX, "EXTRA",  5 },
96 	{ BL_PREFIX, "noinst", 6 },
97 	{ BL_PREFIX, "check",  5 },
98 	{ BL_END,    NULL,     0 },
99 };
100 
101 struct sAutomakeSubparser {
102 	makeSubparser make;
103 
104 	hashTable* directories;
105 	int index;
106 	bool in_subdirs;
107 };
108 
109 
bl_check(const char * name,struct sBlacklist * blacklist)110 static bool bl_check (const char *name, struct sBlacklist *blacklist)
111 {
112 	if ((blacklist->type == BL_PREFIX) &&
113 	    (strncmp (blacklist->substr, name, blacklist->len) == 0))
114 		return false;
115 	else
116 		return true;
117 }
118 
lookupAutomakeDirectory(hashTable * directories,vString * const name)119 static int lookupAutomakeDirectory (hashTable* directories,  vString *const name)
120 {
121 	int *i = hashTableGetItem (directories,  vStringValue (name));
122 
123 	if (i)
124 		return *i;
125 	else
126 		return CORK_NIL;
127 }
128 
addAutomakeDirectory(hashTable * directories,vString * const name,int corkIndex)129 static void addAutomakeDirectory (hashTable* directories, vString *const name, int corkIndex)
130 {
131 	char * k = vStringStrdup (name);
132 	int  * i = xMalloc (1, int);
133 
134 	*i = corkIndex;
135 
136 	hashTablePutItem (directories, k, i);
137 }
138 
automakeMakeTag(struct sAutomakeSubparser * automake,char * name,const char * suffix,bool appending,int kindex,int rindex,struct sBlacklist * blacklist)139 static bool automakeMakeTag (struct sAutomakeSubparser* automake,
140 							 char* name, const char* suffix, bool appending,
141 							 int kindex, int rindex, struct sBlacklist *blacklist)
142 {
143 	size_t expected_len;
144 	size_t len;
145 	char* tail;
146 	vString *subname;
147 	int i;
148 
149 	len = strlen (name);
150 	expected_len = strlen (suffix);
151 
152 	if (len <= expected_len)
153 		return false;
154 
155 	for (i = 0; blacklist[i].type != BL_END; i++)
156 	{
157 		if (bl_check (name, blacklist + i) == false)
158 			return false;
159 	}
160 
161 	tail = name + len - expected_len;
162 	if (strcmp (tail, suffix))
163 		return false;
164 
165 	subname = vStringNew();
166 
167 	/* ??? dist, nodist, nobase, notrans,... */
168 	if (strncmp (name, "dist_", 5) == 0)
169 		vStringNCopyS(subname, name + 5, len - expected_len - 5);
170 	else
171 		vStringNCopyS(subname, name, len - expected_len);
172 
173 	if (rindex == ROLE_DEFINITION_INDEX)
174 	{
175 		automake->index = makeSimpleTag (subname, kindex);
176 		addAutomakeDirectory (automake->directories, subname, automake->index);
177 	}
178 	else
179 	{
180 		automake->index = CORK_NIL;
181 		if (appending)
182 			automake->index = lookupAutomakeDirectory (automake->directories, subname);
183 
184 		if ((!appending) || (automake->index == CORK_NIL))
185 			automake->index = makeSimpleRefTag (subname, kindex, rindex);
186 	}
187 
188 	vStringDelete (subname);
189 	return true;
190 }
191 
valueCallback(makeSubparser * make,char * name)192 static void valueCallback (makeSubparser *make, char *name)
193 {
194 	struct sAutomakeSubparser *automake = (struct sAutomakeSubparser *)make;
195 	int p = automake->index;
196 	tagEntryInfo *parent;
197 	int k;
198 	tagEntryInfo elt;
199 
200 	parent = getEntryInCorkQueue (p);
201 	if (parent && (parent->kindIndex == K_AM_DIR)
202 	    && (parent->extensionFields.roleBits))
203 	{
204 		int roleIndex;
205 		for (roleIndex = 0; roleIndex < ARRAY_SIZE(AutomakeDirectoryRoles); roleIndex++)
206 			if (parent->extensionFields.roleBits & ((roleBitsType)1) << roleIndex)
207 				break;
208 
209 		k = K_AM_PROGRAM + roleIndex;
210 		initTagEntry (&elt, name, k);
211 		elt.extensionFields.scopeIndex = p;
212 		makeTagEntry (&elt);
213 	}
214 	else if (automake->in_subdirs)
215 	{
216 		initTagEntry (&elt, name, K_AM_SUBDIR);
217 		makeTagEntry (&elt);
218 	}
219 }
220 
refCondtionAM(vString * directive)221 static void refCondtionAM (vString *directive)
222 {
223 	makeSimpleRefTag (directive,
224 			  K_AM_CONDITION, R_AM_CONDITION_BRANCHED);
225 }
226 
nextChar(void)227 static int nextChar (void)
228 {
229 	int c = getcFromInputFile ();
230 	if (c == '\\')
231 	{
232 		c = getcFromInputFile ();
233 		if (c == '\n')
234 			c = nextChar ();
235 	}
236 	return c;
237 }
238 
skipToNonWhite(int c)239 static int skipToNonWhite (int c)
240 {
241 	while (c != '\n' && isspace (c))
242 		c = nextChar ();
243 	return c;
244 }
245 
directiveCallback(makeSubparser * make CTAGS_ATTR_UNUSED,char * directive)246 static void directiveCallback (makeSubparser *make CTAGS_ATTR_UNUSED, char *directive)
247 {
248 	int c;
249 	if (! strcmp (directive, "if"))
250 	{
251 		vString *condition = vStringNew ();
252 
253 		c = skipToNonWhite (nextChar ());
254 		while (c != EOF && c != '\n')
255 		{
256 			/* the operator for negation should not be
257 			   part of the condition name. */
258 			if (c != '!')
259 				vStringPut (condition, c);
260 			c = nextChar ();
261 		}
262 		if (c == '\n')
263 			ungetcToInputFile (c);
264 		vStringStripTrailing (condition);
265 		if (vStringLength (condition) > 0 )
266 			refCondtionAM (condition);
267 		vStringDelete (condition);
268 	}
269 }
270 
newMacroCallback(makeSubparser * make,char * name,bool with_define_directive,bool appending)271 static void newMacroCallback (makeSubparser *make, char* name, bool with_define_directive,
272 							  bool appending)
273 {
274 	struct sAutomakeSubparser *automake = (struct sAutomakeSubparser *)make;
275 
276 	automake->index = CORK_NIL;
277 	automake->in_subdirs = false;
278 
279 	if (with_define_directive)
280 		return;
281 
282 	if (strcmp (name, "SUBDIRS") == 0)
283 	{
284 		automake->in_subdirs = true;
285 		return;
286 	}
287 
288 	(void)(0
289 	       || automakeMakeTag (automake,
290 							   name, "dir", appending,
291 							   K_AM_DIR, ROLE_DEFINITION_INDEX, am_blacklist)
292 	       || automakeMakeTag (automake,
293 							   name, "_PROGRAMS", appending,
294 							   K_AM_DIR, R_AM_DIR_PROGRAMS, am_blacklist)
295 	       || automakeMakeTag (automake,
296 							   name, "_MANS", appending,
297 							   K_AM_DIR, R_AM_DIR_MANS, am_blacklist)
298 	       || automakeMakeTag (automake,
299 							   name, "_LTLIBRARIES", appending,
300 							   K_AM_DIR, R_AM_DIR_LTLIBRARIES, am_blacklist)
301 	       || automakeMakeTag (automake,
302 							   name, "_LIBRARIES", appending,
303 							   K_AM_DIR, R_AM_DIR_LIBRARIES, am_blacklist)
304 	       || automakeMakeTag (automake,
305 							   name, "_SCRIPTS", appending,
306 							   K_AM_DIR, R_AM_DIR_SCRIPTS, am_blacklist)
307 	       || automakeMakeTag  (automake,
308 								name, "_DATA", appending,
309 								K_AM_DIR, R_AM_DIR_DATA, am_blacklist)
310 		);
311 }
312 
inputStart(subparser * s)313 static void inputStart (subparser *s)
314 {
315 	struct sAutomakeSubparser *automake = (struct sAutomakeSubparser*)s;
316 
317 	automake->directories = hashTableNew (11, hashCstrhash, hashCstreq, eFree, eFree);
318 	automake->index = CORK_NIL;
319 	automake->in_subdirs = false;
320 }
321 
inputEnd(subparser * s)322 static void inputEnd (subparser *s)
323 {
324 	struct sAutomakeSubparser *automake = (struct sAutomakeSubparser*)s;
325 
326 	hashTableDelete (automake->directories);
327 	automake->directories = NULL;
328 }
329 
findAutomakeTags(void)330 static void findAutomakeTags (void)
331 {
332 	scheduleRunningBaseparser (0);
333 }
334 
AutomakeParser(void)335 extern parserDefinition* AutomakeParser (void)
336 {
337 	static const char *const extensions [] = { "am", NULL };
338 	static const char *const patterns [] = { "Makefile.am", "GNUmakefile.am", NULL };
339 	static struct sAutomakeSubparser automakeSubparser = {
340 		.make = {
341 			.subparser = {
342 				.direction = SUBPARSER_SUB_RUNS_BASE,
343 				.inputStart = inputStart,
344 				.inputEnd = inputEnd,
345 			},
346 			.valueNotify = valueCallback,
347 			.directiveNotify = directiveCallback,
348 			.newMacroNotify = newMacroCallback,
349 		},
350 	};
351 	static parserDependency dependencies [] = {
352 		[0] = { DEPTYPE_SUBPARSER, "Make", &automakeSubparser },
353 	};
354 
355 	parserDefinition* const def = parserNew ("Automake");
356 
357 
358 	def->dependencies = dependencies;
359 	def->dependencyCount = ARRAY_SIZE(dependencies);
360 	def->kindTable      = AutomakeKinds;
361 	def->kindCount  = ARRAY_SIZE (AutomakeKinds);
362 	def->extensions = extensions;
363 	def->patterns   = patterns;
364 	def->parser     = findAutomakeTags;
365 	def->useCork    = CORK_QUEUE;
366 	return def;
367 }
368