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 <stdio.h>
16 #include <string.h>
17 #include <ctype.h>
18
19 #include "options.h"
20 #include "parse.h"
21 #include "read.h"
22 #include "routines.h"
23 #include "vstring.h"
24 #include "xtag.h"
25
26 /*
27 * DATA DEFINITIONS
28 */
29 typedef enum {
30 K_MACRO, K_TARGET
31 } shKind;
32
33 static kindDefinition MakeKinds [] = {
34 { true, 'm', "macro", "macros"},
35 { true, 't', "target", "targets"}
36 };
37
38 /*
39 * FUNCTION DEFINITIONS
40 */
41
nextChar(void)42 static int nextChar (void)
43 {
44 int c = getcFromInputFile ();
45 if (c == '\\')
46 {
47 c = getcFromInputFile ();
48 if (c == '\n')
49 c = nextChar ();
50 }
51 return c;
52 }
53
skipLine(void)54 static void skipLine (void)
55 {
56 int c;
57 do
58 c = nextChar ();
59 while (c != EOF && c != '\n');
60 if (c == '\n')
61 ungetcToInputFile (c);
62 }
63
skipToNonWhite(int c)64 static int skipToNonWhite (int c)
65 {
66 while (c != '\n' && isspace (c))
67 c = nextChar ();
68 return c;
69 }
70
isIdentifier(int c)71 static bool isIdentifier (int c)
72 {
73 return (bool)(c != '\0' && (isalnum (c) || strchr (".-_/$(){}%", c) != NULL));
74 }
75
isSpecialTarget(vString * const name)76 static bool isSpecialTarget (vString *const name)
77 {
78 size_t i = 0;
79 /* All special targets begin with '.'. */
80 if (vStringLength (name) < 1 || vStringChar (name, i++) != '.') {
81 return false;
82 }
83 while (i < vStringLength (name)) {
84 char ch = vStringChar (name, i++);
85 if (ch != '_' && !isupper (ch))
86 {
87 return false;
88 }
89 }
90 return true;
91 }
92
newTarget(vString * const name)93 static void newTarget (vString *const name)
94 {
95 /* Ignore GNU Make's "special targets". */
96 if (isSpecialTarget (name))
97 {
98 return;
99 }
100 makeSimpleTag (name, K_TARGET);
101 }
102
newMacro(vString * const name)103 static void newMacro (vString *const name)
104 {
105 makeSimpleTag (name, K_MACRO);
106 }
107
readIdentifier(const int first,vString * const id)108 static void readIdentifier (const int first, vString *const id)
109 {
110 int depth = 0;
111 int c = first;
112 vStringClear (id);
113 while (isIdentifier (c) || (depth > 0 && c != EOF && c != '\n'))
114 {
115 if (c == '(' || c == '{')
116 depth++;
117 else if (depth > 0 && (c == ')' || c == '}'))
118 depth--;
119 vStringPut (id, c);
120 c = nextChar ();
121 }
122 ungetcToInputFile (c);
123 }
124
findMakeTags(void)125 static void findMakeTags (void)
126 {
127 stringList *identifiers = stringListNew ();
128 bool newline = true;
129 bool in_define = false;
130 bool in_rule = false;
131 bool variable_possible = true;
132 int c;
133
134 while ((c = nextChar ()) != EOF)
135 {
136 if (newline)
137 {
138 if (in_rule)
139 {
140 if (c == '\t' || (c = skipToNonWhite (c)) == '#')
141 {
142 skipLine (); /* skip rule or comment */
143 c = nextChar ();
144 }
145 else if (c != '\n')
146 in_rule = false;
147 }
148 stringListClear (identifiers);
149 variable_possible = (bool)(!in_rule);
150 newline = false;
151 }
152 if (c == '\n')
153 newline = true;
154 else if (isspace (c))
155 continue;
156 else if (c == '#')
157 skipLine ();
158 else if (variable_possible && c == '?')
159 {
160 c = nextChar ();
161 ungetcToInputFile (c);
162 variable_possible = (c == '=');
163 }
164 else if (variable_possible && c == ':' &&
165 stringListCount (identifiers) > 0)
166 {
167 c = nextChar ();
168 ungetcToInputFile (c);
169 if (c != '=')
170 {
171 unsigned int i;
172 for (i = 0; i < stringListCount (identifiers); i++)
173 newTarget (stringListItem (identifiers, i));
174 stringListClear (identifiers);
175 in_rule = true;
176 }
177 }
178 else if (variable_possible && c == '=' &&
179 stringListCount (identifiers) == 1)
180 {
181 newMacro (stringListItem (identifiers, 0));
182 skipLine ();
183 in_rule = false;
184 }
185 else if (variable_possible && isIdentifier (c))
186 {
187 vString *name = vStringNew ();
188 readIdentifier (c, name);
189 stringListAdd (identifiers, name);
190
191 if (stringListCount (identifiers) == 1)
192 {
193 if (in_define && ! strcmp (vStringValue (name), "endef"))
194 in_define = false;
195 else if (in_define)
196 skipLine ();
197 else if (! strcmp (vStringValue (name), "define"))
198 {
199 in_define = true;
200 c = skipToNonWhite (nextChar ());
201 vStringClear (name);
202 /* all remaining characters on the line are the name -- even spaces */
203 while (c != EOF && c != '\n')
204 {
205 vStringPut (name, c);
206 c = nextChar ();
207 }
208 if (c == '\n')
209 ungetcToInputFile (c);
210 vStringStripTrailing (name);
211 newMacro (name);
212 }
213 else if (! strcmp (vStringValue (name), "export"))
214 stringListClear (identifiers);
215 }
216 }
217 else
218 variable_possible = false;
219 }
220 stringListDelete (identifiers);
221 }
222
MakefileParser(void)223 extern parserDefinition* MakefileParser (void)
224 {
225 static const char *const patterns [] = { "[Mm]akefile", "GNUmakefile", NULL };
226 static const char *const extensions [] = { "mak", "mk", NULL };
227 parserDefinition* const def = parserNew ("Make");
228 def->kindTable = MakeKinds;
229 def->kindCount = ARRAY_SIZE (MakeKinds);
230 def->patterns = patterns;
231 def->extensions = extensions;
232 def->parser = findMakeTags;
233 return def;
234 }
235