1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 
5 #define LUA_CORE /* make sure that we don't try to import these functions */
6 #include <lua.h>
7 #include <lauxlib.h>
8 
9 #include "path.h"
10 #include "node.h"
11 #include "cache.h"
12 #include "context.h"
13 #include "mem.h"
14 #include "support.h"
15 #include "session.h"
16 #include "luafuncs.h"
17 
processline(char * line,char ** start,char ** end,int * systemheader)18 static int processline(char *line, char **start, char **end, int *systemheader)
19 {
20 	const char *include_text = "include";
21 	char *current = line;
22 	*start = 0;
23 	*end = 0;
24 	*systemheader = 0;
25 
26 	/* search for # */
27 	while(*current != '#')
28 	{
29 		if(*current == ' ' || *current == '\t')
30 			current++; /* next char */
31 		else
32 			return 0; /* this catches \0 aswell */
33 	}
34 
35 	current++; /* skip # */
36 
37 	/* search for first character */
38 	while(1)
39 	{
40 		if(*current == ' ' || *current == '\t')
41 			current++;
42 		else if(*current == 0)
43 			return 0;
44 		else
45 			break;
46 	}
47 
48 	/* match "include" */
49 	while(*include_text)
50 	{
51 		if(*current == *include_text)
52 		{
53 			current++;
54 			include_text++;
55 		}
56 		else
57 			return 0;
58 	}
59 
60 	/* search for first character */
61 	while(1)
62 	{
63 		if(*current == ' ' || *current == '\t')
64 			current++;
65 		else if(*current == 0)
66 			return 0;
67 		else
68 			break;
69 	}
70 
71 	/* match starting < or " */
72 	*start = current+1;
73 	if(*current == '<')
74 		*systemheader = 1;
75 	else if(*current == '"')
76 		*systemheader = 0;
77 	else
78 		return 0;
79 
80 	/* skip < or " */
81 	current++;
82 
83 	/* search for < or " to end it */
84 	while(1)
85 	{
86 		if(*current == '>' || *current == '"')
87 			break;
88 		else if(*current == 0)
89 			return 0;
90 		else
91 			current++;
92 	}
93 
94 	*end = current;
95 	return 1;
96 }
97 
98 struct CACHERUNINFO
99 {
100 	struct CONTEXT *context;
101 	int (*callback)(struct NODE *, void *, const char *, int);
102 	void *userdata;
103 };
104 
105 static int dependency_cpp_run(struct CONTEXT *context, struct NODE *node,
106 		int (*callback)(struct NODE *, void *, const char *, int), void *userdata);
107 
cachehit_callback(struct NODE * node,struct CACHENODE * cachenode,void * user)108 static void cachehit_callback(struct NODE *node, struct CACHENODE *cachenode, void *user)
109 {
110 	struct CACHERUNINFO *info = (struct CACHERUNINFO *)user;
111 
112 	/* check if the file has been removed */
113 	struct NODE *existing_node = node_find_byhash(node->graph, cachenode->hashid);
114 	if(existing_node)
115 	{
116 		struct NODE *newnode = node_add_dependency_withnode(node, existing_node);
117 		dependency_cpp_run(info->context, newnode, info->callback, info->userdata);
118 	}
119 	else
120 	{
121 		if(file_timestamp(cachenode->filename) == 0)
122 			node->dirty = NODEDIRTY_MISSING;
123 		else
124 		{
125 			struct NODE *newnode = node_add_dependency(node, cachenode->filename);
126 			dependency_cpp_run(info->context, newnode, info->callback, info->userdata);
127 		}
128 	}
129 }
130 
131 /* dependency calculator for c/c++ preprocessor */
dependency_cpp_run(struct CONTEXT * context,struct NODE * node,int (* callback)(struct NODE *,void *,const char *,int),void * userdata)132 static int dependency_cpp_run(struct CONTEXT *context, struct NODE *node,
133 		int (*callback)(struct NODE *, void *, const char *, int), void *userdata)
134 {
135 	char *linestart;
136 	char *includestart;
137 	char *includeend;
138 	int systemheader;
139 	int errorcode = 0;
140 	int linecount = 0;
141 
142 	/* open file */
143 	long filesize;
144 	long readitems;
145 	char *filebuf;
146 	char *filebufcur;
147 	char *filebufend;
148 	FILE *file;
149 	struct CACHERUNINFO cacheinfo;
150 
151 	/* don't run depcheck twice */
152 	if(node->depchecked)
153 		return 0;
154 
155 	/* mark the node for caching */
156 	node_cached(node);
157 
158 	/* check if we have the dependencies in the cache frist */
159 	cacheinfo.context = context;
160 	cacheinfo.callback = callback;
161 	cacheinfo.userdata = userdata;
162 	if(cache_do_dependency(context, node, cachehit_callback, &cacheinfo))
163 		return 0;
164 
165 	/* mark the node as checked */
166 	node->depchecked = 1;
167 
168 	file = fopen(node->filename, "rb");
169 	if(!file)
170 		return 0;
171 
172 	/* read the whole file */
173 	fseek(file, 0, SEEK_END);
174 	filesize = ftell(file);
175 	fseek(file, 0, SEEK_SET);
176 
177 	filebuf = malloc(filesize+1); /* +1 for null termination */
178 
179 	if(!filebuf)
180 	{
181 		printf("cpp-dep: error allocating %ld bytes\n", filesize);
182 		fclose(file);
183 		return 1;
184 	}
185 
186 	/* read the file and close it */
187 	readitems = fread(filebuf, 1, filesize, file);
188 	fclose(file);
189 
190 	if(readitems != filesize)
191 	{
192 		printf("cpp-dep: error reading the complete file. %ld of %ld bytes read\n", readitems, filesize);
193 		free(filebuf);
194 		return 1;
195 	}
196 
197 	filebufcur = filebuf;
198 	filebufend = filebuf+filesize;
199 
200 	while(filebufcur < filebufend)
201 	{
202 		/* search for next line */
203 		linestart = filebufcur;
204 		while(filebufcur != filebufend && *filebufcur != '\n' && *filebufcur != '\r')
205 			filebufcur++;
206 		*filebufcur = 0;
207 		filebufcur++;
208 		linecount++;
209 
210 		if(processline(linestart, &includestart, &includeend, &systemheader))
211 		{
212 			*includeend = 0;
213 			/* run callback */
214 			errorcode = callback(node, userdata, includestart, systemheader);
215 			if(errorcode)
216 				break;
217 		}
218 	}
219 
220 	/* clean up and return*/
221 	free(filebuf);
222 	return errorcode;
223 }
224 
225 struct CPPDEPINFO
226 {
227 	struct CONTEXT *context;
228 	struct STRINGLIST *paths;
229 };
230 
231 /* */
dependency_cpp_callback(struct NODE * node,void * user,const char * filename,int sys)232 static int dependency_cpp_callback(struct NODE *node, void *user, const char *filename, int sys)
233 {
234 	struct CPPDEPINFO *depinfo = (struct CPPDEPINFO *)user;
235 	struct NODE *depnode;
236 	struct CPPDEPINFO recurseinfo;
237 	char buf[MAX_PATH_LENGTH];
238 	int check_system = sys;
239 	int found = 0;
240 
241 	if(!sys)
242 	{
243 		/* "normal.header" */
244 		int flen = strlen(node->filename)-1;
245 		while(flen)
246 		{
247 			if(node->filename[flen] == '/')
248 				break;
249 			flen--;
250 		}
251 		path_join(node->filename, flen, filename, -1, buf, sizeof(buf));
252 
253 		if(file_exist(buf) || node_find(node->graph, buf))
254 			found = 1;
255 		else
256 		{
257 			/* file does not exist */
258 			check_system = 1;
259 		}
260 	}
261 
262 	if(check_system)
263 	{
264 		/* <system.header> */
265 		if(path_isabs(filename))
266 		{
267 			if(file_exist(filename) || node_find(node->graph, filename))
268 			{
269 				strcpy(buf, filename);
270 				found = 1;
271 			}
272 		}
273 		else
274 		{
275 			struct STRINGLIST *cur;
276 			int flen = strlen(filename);
277 
278 			for(cur = depinfo->paths; cur; cur = cur->next)
279 			{
280 				path_join(cur->str, cur->len, filename, flen, buf, sizeof(buf));
281 				if(file_exist(buf) || node_find(node->graph, buf))
282 				{
283 					found = 1;
284 					break;
285 				}
286 			}
287 		}
288 	}
289 
290 	/* */
291 	if(found)
292 	{
293 		path_normalize(buf);
294 		depnode = node_add_dependency(node, buf);
295 		if(!depnode)
296 			return 2;
297 
298 		/* do the dependency walk */
299 		if(!depnode->depchecked)
300 		{
301 			recurseinfo.paths = depinfo->paths;
302 			recurseinfo.context = depinfo->context;
303 			if(dependency_cpp_run(depinfo->context, depnode, dependency_cpp_callback, &recurseinfo) != 0)
304 				return 3;
305 		}
306 	}
307 
308 	return 0;
309 }
310 
311 static struct STRINGLIST *current_includepaths = NULL;
312 
dependency_cpp_do_run(struct CONTEXT * context,struct DEFERRED * info)313 static int dependency_cpp_do_run(struct CONTEXT *context, struct DEFERRED *info)
314 {
315 	struct CPPDEPINFO depinfo;
316 	depinfo.context = context;
317 	depinfo.paths = (struct STRINGLIST *)info->user;
318 	if(dependency_cpp_run(context, info->node, dependency_cpp_callback, &depinfo) != 0)
319 		return -1;
320 	return 0;
321 }
322 
323 /* */
lf_add_dependency_cpp_set_paths(lua_State * L)324 int lf_add_dependency_cpp_set_paths(lua_State *L)
325 {
326 	struct CONTEXT *context;
327 	int n = lua_gettop(L);
328 
329 	if(n != 1)
330 		luaL_error(L, "add_dependency_cpp_set_paths: incorrect number of arguments");
331 	luaL_checktype(L, 1, LUA_TTABLE);
332 
333 	context = context_get_pointer(L);
334 	current_includepaths = NULL;
335 	build_stringlist(L, context->deferredheap, &current_includepaths, 1);
336 	return 0;
337 }
338 
339 /* */
lf_add_dependency_cpp(lua_State * L)340 int lf_add_dependency_cpp(lua_State *L)
341 {
342 	struct CONTEXT *context;
343 	struct DEFERRED *deferred;
344 	int n = lua_gettop(L);
345 
346 	if(n != 1)
347 		luaL_error(L, "add_dependency_cpp_set: incorrect number of arguments");
348 	luaL_checkstring(L,1);
349 
350 	context = context_get_pointer(L);
351 
352 	deferred = (struct DEFERRED *)mem_allocate(context->deferredheap, sizeof(struct DEFERRED));
353 	deferred->node = node_find(context->graph, lua_tostring(L,1));
354 	deferred->user = current_includepaths;
355 	deferred->run = dependency_cpp_do_run;
356 	deferred->next = context->firstdeferred;
357 	context->firstdeferred = deferred;
358 	return 0;
359 }
360