1 /*
2 *   $Id: make.c 751 2010-02-27 17:41:57Z elliotth $
3 *
4 *   Copyright (c) 2000-2005, Darren Hiebert
5 *
6 *   This source code is released for free distribution under the terms of the
7 *   GNU General Public License.
8 *
9 *   This module contains functions for generating tags for makefiles.
10 */
11 
12 /*
13 *   INCLUDE FILES
14 */
15 #include "general.h"  /* must always come first */
16 
17 #include <string.h>
18 #include <ctype.h>
19 
20 #include "options.h"
21 #include "parse.h"
22 #include "read.h"
23 #include "vstring.h"
24 
25 /*
26 *   DATA DEFINITIONS
27 */
28 typedef enum {
29 	K_MACRO
30 } shKind;
31 
32 static kindOption MakeKinds [] = {
33 	{ TRUE, 'm', "macro", "macros"}
34 };
35 
36 /*
37 *   FUNCTION DEFINITIONS
38 */
39 
nextChar(void)40 static int nextChar (void)
41 {
42 	int c = fileGetc ();
43 	if (c == '\\')
44 	{
45 		c = fileGetc ();
46 		if (c == '\n')
47 			c = fileGetc ();
48 	}
49 	return c;
50 }
51 
skipLine(void)52 static void skipLine (void)
53 {
54 	int c;
55 	do
56 		c = nextChar ();
57 	while (c != EOF  &&  c != '\n');
58 	if (c == '\n')
59 		fileUngetc (c);
60 }
61 
skipToNonWhite(void)62 static int skipToNonWhite (void)
63 {
64 	int c;
65 	do
66 		c = nextChar ();
67 	while (c != '\n' && isspace (c));
68 	return c;
69 }
70 
isIdentifier(int c)71 static boolean isIdentifier (int c)
72 {
73 	return (boolean)(c != '\0' && (isalnum (c)  ||  strchr (".-_", c) != NULL));
74 }
75 
readIdentifier(const int first,vString * const id)76 static void readIdentifier (const int first, vString *const id)
77 {
78 	int c = first;
79 	vStringClear (id);
80 	while (isIdentifier (c))
81 	{
82 		vStringPut (id, c);
83 		c = nextChar ();
84 	}
85 	fileUngetc (c);
86 	vStringTerminate (id);
87 }
88 
skipToMatch(const char * const pair)89 static void skipToMatch (const char *const pair)
90 {
91 	const int begin = pair [0], end = pair [1];
92 	const unsigned long inputLineNumber = getInputLineNumber ();
93 	int matchLevel = 1;
94 	int c = '\0';
95 
96 	while (matchLevel > 0)
97 	{
98 		c = nextChar ();
99 		if (c == begin)
100 			++matchLevel;
101 		else if (c == end)
102 			--matchLevel;
103 		else if (c == '\n' || c == EOF)
104 			break;
105 	}
106 	if (c == EOF)
107 		verbose ("%s: failed to find match for '%c' at line %lu\n",
108 				getInputFileName (), begin, inputLineNumber);
109 }
110 
findMakeTags(void)111 static void findMakeTags (void)
112 {
113 	vString *name = vStringNew ();
114 	boolean newline = TRUE;
115 	boolean in_define = FALSE;
116 	boolean in_rule = FALSE;
117 	boolean variable_possible = TRUE;
118 	int c;
119 
120 	while ((c = nextChar ()) != EOF)
121 	{
122 		if (newline)
123 		{
124 			if (in_rule)
125 			{
126 				if (c == '\t')
127 				{
128 					skipLine ();  /* skip rule */
129 					continue;
130 				}
131 				else
132 					in_rule = FALSE;
133 			}
134 			variable_possible = (boolean)(!in_rule);
135 			newline = FALSE;
136 		}
137 		if (c == '\n')
138 			newline = TRUE;
139 		else if (isspace (c))
140 			continue;
141 		else if (c == '#')
142 			skipLine ();
143 		else if (c == '(')
144 			skipToMatch ("()");
145 		else if (c == '{')
146 			skipToMatch ("{}");
147 		else if (c == ':')
148 		{
149 			variable_possible = TRUE;
150 			in_rule = TRUE;
151 		}
152 		else if (variable_possible && isIdentifier (c))
153 		{
154 			readIdentifier (c, name);
155 			if (strcmp (vStringValue (name), "endef") == 0)
156 				in_define = FALSE;
157 			else if (in_define)
158 				skipLine ();
159 			else if (strcmp (vStringValue (name), "define") == 0  &&
160 				isIdentifier (c))
161 			{
162 				in_define = TRUE;
163 				c = skipToNonWhite ();
164 				readIdentifier (c, name);
165 				makeSimpleTag (name, MakeKinds, K_MACRO);
166 				skipLine ();
167 			}
168 			else {
169 				if (strcmp(vStringValue (name), "export") == 0 &&
170 					isIdentifier (c))
171 				{
172 					c = skipToNonWhite ();
173 					readIdentifier (c, name);
174 				}
175 				c = skipToNonWhite ();
176 				if (strchr (":?+", c) != NULL)
177 				{
178 					boolean append = (boolean)(c == '+');
179 					if (c == ':')
180 						in_rule = TRUE;
181 					c = nextChar ();
182 					if (c != '=')
183 						fileUngetc (c);
184 					else if (append)
185 					{
186 						skipLine ();
187 						continue;
188 					}
189 				}
190 				if (c == '=')
191 				{
192 					makeSimpleTag (name, MakeKinds, K_MACRO);
193 					in_rule = FALSE;
194 					skipLine ();
195 				}
196 			}
197 		}
198 		else
199 			variable_possible = FALSE;
200 	}
201 	vStringDelete (name);
202 }
203 
MakefileParser(void)204 extern parserDefinition* MakefileParser (void)
205 {
206 	static const char *const patterns [] = { "[Mm]akefile", "GNUmakefile", NULL };
207 	static const char *const extensions [] = { "mak", "mk", NULL };
208 	parserDefinition* const def = parserNew ("Make");
209 	def->kinds      = MakeKinds;
210 	def->kindCount  = KIND_COUNT (MakeKinds);
211 	def->patterns   = patterns;
212 	def->extensions = extensions;
213 	def->parser     = findMakeTags;
214 	return def;
215 }
216 
217 /* vi:set tabstop=4 shiftwidth=4: */
218