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