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, ¤t_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