option.c: fixed warnings
[k8jam.git] / src / headers.c
blob4e37679ea62a52b15ca4b8302c69d12c37293e7a
1 /*
2 * Copyright 1993, 2000 Christopher Seiwald.
3 * This file is part of Jam - see jam.c for Copyright information.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 * headers.c - handle #includes in source files
20 * Using regular expressions provided as the variable $(HDRSCAN),
21 * headers() searches a file for #include files and phonies up a
22 * rule invocation:
24 * $(HDRRULE) <target> : <include files> ;
26 * External routines:
27 * headers() - scan a target for include files and call HDRRULE
29 * Internal routines:
30 * headers1() - using regexp, scan a file and build include LIST
32 #include "jam.h"
33 #include "lists.h"
34 #include "parse.h"
35 #include "compile.h"
36 #include "rules.h"
37 #include "variable.h"
38 #include "re9.h"
39 #include "headers.h"
40 #include "newstr.h"
41 #include "hdrmacro.h"
42 #include "hcache.h"
43 #include "dstrings.h"
44 #include "gdcdeps.h"
48 #ifndef OPT_HEADER_CACHE_EXT
49 static LIST *headers1 (const char *file, LIST *hdrscan);
50 #endif
55 * headers() - scan a target for include files and call HDRRULE
57 #define MAXINC (32)
59 void headers (TARGET *t) {
60 LIST *hdrscan;
61 LIST *hdrrule;
62 LOL lol;
63 if (!(hdrscan = var_get("HDRSCAN")) || !(hdrrule = var_get("HDRRULE"))) return;
64 /* doctor up call to HDRRULE rule */
65 /* call headers1() to get LIST of included files */
66 if (DEBUG_HEADER) printf("header scan %s\n", t->name);
67 lol_init(&lol);
68 lol_add(&lol, list_new(L0, t->name, 1));
70 #ifdef OPT_HEADER_CACHE_EXT
71 lol_add(&lol, hcache(t, hdrscan));
72 #else
73 lol_add(&lol, headers1(t->boundname, hdrscan));
74 #endif
76 lol_add(&lol, hcache(t, hdrscan));
77 if (lol_get(&lol, 1)) list_free(evaluate_rule(hdrrule->string, &lol, L0));
78 /* clean up */
79 lol_free(&lol);
83 struct lcicacheentry_s {
84 const char *str;
85 const char *val;
86 struct lcicacheentry_s *children;
87 struct lcicacheentry_s *next;
89 typedef struct lcicacheentry_s lcicacheentry_t;
91 static lcicacheentry_t *lcicache = NULL;
94 const char *lci_cache_find (const char *basefile, int bflen, dstring_t *fname) {
95 if (basefile == NULL || basefile[0] == 0 || bflen < 1) return NULL;
96 if (fname == NULL || dstr_cstr(fname)[0] == 0) return NULL;
97 for (lcicacheentry_t *e0 = lcicache; e0 != NULL; e0 = e0->next) {
98 int slen = strlen(e0->str);
99 if (slen == bflen && memcmp(e0->str, basefile, slen) == 0) {
100 // found bucket
101 const char *fnc = dstr_cstr(fname);
102 for (lcicacheentry_t *e1 = e0->children; e1 != NULL; e1 = e1->next) {
103 if (strcmp(e1->str, fnc) == 0) return e1->val;
107 return NULL;
111 void lci_cache_put (const char *basefile, int bflen, dstring_t *fname, dstring_t *xname) {
112 if (basefile == NULL || basefile[0] == 0 || bflen < 1) return;
113 if (fname == NULL || dstr_cstr(fname)[0] == 0) return;
114 if (xname == NULL) return;
115 // create final entry
116 lcicacheentry_t *ne = calloc(1, sizeof(lcicacheentry_t));
117 ne->str = strdup(dstr_cstr(fname));
118 ne->val = strdup(dstr_cstr(xname));
119 // find bucket
120 for (lcicacheentry_t *e0 = lcicache; e0 != NULL; e0 = e0->next) {
121 int slen = strlen(e0->str);
122 if (slen == bflen && memcmp(e0->str, basefile, slen) == 0) {
123 // found bucket
124 ne->next = e0->children;
125 e0->children = ne;
126 return;
129 // create new bucket
130 lcicacheentry_t *bk = calloc(1, sizeof(lcicacheentry_t));
131 bk->str = strdup(basefile);
132 bk->children = ne;
133 bk->next = lcicache;
134 lcicache = bk;
138 static void resolve_local_include (dstring_t *dest, const char *basefile, dstring_t *fname) {
139 dstr_init(dest);
140 if (dstr_cstr(fname)[0] == '/') { dstr_set_cstr(dest, dstr_cstr(fname)); return; }
141 int bflen = strlen(basefile);
142 while (bflen > 0 && basefile[bflen-1] != '/') --bflen;
143 if (bflen == 0) { dstr_set_cstr(dest, dstr_cstr(fname)); return; }
144 // check cache
145 const char *riname = lci_cache_find(basefile, bflen, fname);
146 if (riname != NULL) {
147 // i found her!
148 dstr_set_cstr(dest, riname);
149 return;
151 // build name
153 if (basefile[0] != '/') {
154 static char buf[8192];
155 char *dir = getcwd(buf, sizeof(buf));
156 if (dir && dir[0]) {
157 dstr_push_cstr(dest, dir);
158 if (dir[strlen(dir)-1] != '/') dstr_push_char(dest, '/');
162 dstr_push_buf(dest, basefile, bflen);
163 dstr_push_cstr(dest, dstr_cstr(fname));
164 if (access(dstr_cstr(dest), R_OK) != 0) dstr_set_cstr(dest, dstr_cstr(fname));
165 // put it in cache
166 lci_cache_put(basefile, bflen, fname, dest);
171 * headers1() - using regexp, scan a file and build include LIST
174 #ifndef OPT_HEADER_CACHE_EXT
175 static
176 #endif
178 LIST *headers1 (const char *file, LIST *hdrscan) {
179 FILE *f;
180 int i;
181 int rec = 0;
182 LIST *result = NULL;
183 regexp_t *re[MAXINC];
184 regexp_t *re_macros, *re_dlinc = NULL;
185 re9_sub_t mt[4];
186 int line_size = 16384;
187 char *buf; /* line_size size */
188 dstring_t buf2, buf3;
189 int dlang = 0;
190 for (const LIST *t = var_get("HDRPATH_IGNORE"); t != NULL; t = t->next) {
191 if (t->string && t->string[0]) {
192 if (strncmp(file, t->string, strlen(t->string)) == 0) {
193 if (DEBUG_HEADER) printf("DBG: ignoring header [%s]\n", file);
194 return result;
199 char *t = strrchr(file, '.');
200 dlang = (t != NULL && (strcmp(t, ".d") == 0 || strcmp(t, ".di") == 0));
202 if (DEBUG_HEADER) printf("DBG: trying header [%s] (D=%d)\n", file, dlang);
203 //fprintf(stderr, "DBG: trying header [%s] (D=%d)\n", file, dlang);
204 if (dlang) {
205 // try GDC
208 LIST *gdc = var_get("GDC");
209 fprintf(stderr, "GDC: <"); list_print_ex(stderr, gdc, LPFLAG_NO_TRSPACE); fprintf(stderr, ">\n");
210 LIST *gdcfa = var_get("GDCFLAGS.all");
211 fprintf(stderr, "GDCFLAGS.all: <"); list_print_ex(stderr, gdcfa, LPFLAG_NO_TRSPACE); fprintf(stderr, ">\n");
212 LIST *gdcf = var_get("GDCFLAGS");
213 fprintf(stderr, "GDCFLAGS: <"); list_print_ex(stderr, gdcf, LPFLAG_NO_TRSPACE); fprintf(stderr, ">\n");
214 LIST *t;
215 t = var_get("TOP");
216 fprintf(stderr, "TOP: <"); list_print_ex(stderr, t, LPFLAG_NO_TRSPACE); fprintf(stderr, ">\n");
217 t = var_get("SUBDIR_TOKENS");
218 fprintf(stderr, "SUBDIR_TOKENS: <"); list_print_ex(stderr, t, LPFLAG_NO_TRSPACE); fprintf(stderr, ">\n");
219 t = var_get("HDRS");
220 fprintf(stderr, "HDRS: <"); list_print_ex(stderr, t, LPFLAG_NO_TRSPACE); fprintf(stderr, ">\n");
222 fprintf(stderr, "DBG: trying header [%s] (D=%d)\n", file, dlang);
224 if (DEBUG_HEADER) printf("trying GDC deps for [%s]...\n", file);
225 result = gdcDeps(file);
226 if (result != NULL) {
227 if (DEBUG_HEADER) {
228 printf("GDC deps for [%s]\n", file);
229 for (const LIST *l = result; l != NULL; l = l->next) printf(" <%s>\n", l->string);
231 return result;
234 if (!(f = fopen(file, "r"))) return result;
235 //fprintf(stderr, "DBG: processing header [%s] (D=%d)\n", file, dlang);
238 LIST *t = var_get("DLANG");
239 dlang = (t != NULL && t->string[0]);
240 //fprintf(stderr, "DLANG: %d\n", dlang);
243 if (dlang) {
244 re[rec++] = regexp_compile("\\bimport\\s+([^;]+)\\s*;", 0); // don't use ".+n?;", it's buggy
245 re[rec++] = regexp_compile("\\bimport\\s*\\(([^)]+)\\)", 0);
246 re_dlinc = regexp_compile("\\s*([^,;]+)\\s*", 0);
248 if (!dlang) {
249 while (rec < MAXINC && hdrscan) {
250 //printf("header re %d: [%s]\n", rec, hdrscan->string);
251 re[rec] = regexp_compile(hdrscan->string, 0);
252 ++rec;
253 hdrscan = list_next(hdrscan);
256 /* the following regexp is used to detect cases where a */
257 /* file is included through a line line "#include MACRO" */
258 re_macros = regexp_compile("^\\s*#\\s*include\\s+([A-Za-z_][A-Za-z0-9_]*).*$", 0);
259 if (DEBUG_HEADER) printf("header processing: [%s] (%d)\n", file, rec);
260 if ((buf = malloc(line_size)) == NULL) { fprintf(stderr, "FATAL: out of memory!\n"); abort(); }
261 dstr_init(&buf2);
262 dstr_init(&buf3);
263 while (fgets(buf, line_size, f)) {
264 for (i = 0; i < rec; ++i) {
265 mt[0].sp = mt[0].ep = NULL;
266 if (regexp_execute(re[i], buf, mt, 2) > 0 && mt[1].sp != NULL) {
267 /* copy and terminate extracted string */
268 int l = mt[1].ep-mt[1].sp;
269 dstr_set_buf(&buf2, mt[1].sp, l);
270 if (dlang && i <= 1) {
271 //fprintf(stderr, "dlang: [%s]\n", dstr_cstr(&buf2));
272 if (i == 0) {
273 /* this is DLang 'import' directive */
274 const char *s = dstr_cstr(&buf2);
275 //fprintf(stderr, "=== %d; [%s]\n", l, dstr_cstr(&buf2));
276 while (*s) {
277 char *p, *t;
278 int againD = 0;
279 if (!regexp_execute(re_dlinc, s, mt, 2) || mt[1].sp == NULL) break;
280 dstr_set_buf(&buf3, mt[1].sp, mt[1].ep-mt[1].sp);
281 dstr_push_cstr(&buf3, ".d");
282 p = dstr_cstr(&buf3);
283 //fprintf(stderr, "*** [%s]\n", dstr_cstr(&buf3));
284 //fprintf(stderr, "+++000 [%s]\n", dstr_cstr(&buf3));
285 if ((t = strchr(p, ':')) != NULL) { againD = 1; *t = 0; }
286 if ((t = strchr(p, '=')) != NULL) p = t+1;
287 //fprintf(stderr, "+++001 [%s]\n", dstr_cstr(&buf3));
288 while (*p && isspace(*p)) ++p;
289 if (*p) {
290 t = p+strlen(p);
291 while (t > p && isspace(t[-1])) --t;
292 *t = 0;
294 // add ".d" again if it was cut
295 // note that we have always place for it
296 if (*p && againD) strcat(p, ".d");
297 //fprintf(stderr, "+++002 [%s]\n", dstr_cstr(&buf3));
298 if (*p) {
299 for (t = p; *t; ++t) if (*t == '.') *t = '/';
300 //fprintf(stderr, "+++ [%s]\n", dstr_cstr(&buf3));
301 *(strrchr(p, '/')) = '.'; // restore '.d'
302 //fprintf(stderr, "dlang include: [%s]\n", p);
303 result = list_new(result, p, 0);
304 // add name/package.d too
305 char *pd = malloc(strlen(p)+32);
306 strcpy(pd, p);
307 strcpy(strrchr(pd, '.'), "/package.d");
308 //fprintf(stderr, "dlang include pkg: [%s]\n", pd);
309 result = list_new(result, pd, 0);
310 free(pd);
312 s = mt[1].ep;
314 } else {
315 /* this is DLang import() operator */
316 /* TODO: process string escapes */
317 //fprintf(stderr, "II: [%s]\n", dstr_cstr(&buf2));
318 if (dstr_cstr(&buf2)[0] == '"' || dstr_cstr(&buf2)[0] == '`') {
319 char *fn = strdup(dstr_cstr(&buf2)+1);
320 if (fn[0] && fn[1] && fn[strlen(fn)-1] == dstr_cstr(&buf2)[dstr_len(&buf2)-1]) {
321 fn[strlen(fn)-1] = 0;
322 //fprintf(stderr, "import directive: [%s]\n", fn);
323 result = list_new(result, fn, 0);
325 free(fn);
328 } else {
329 // check if this is local include
330 dstring_t xname;
331 resolve_local_include(&xname, file, &buf2);
332 //fprintf(stderr, "FILE: <%s>; include: <%s>\n", file, dstr_cstr(&xname));
333 result = list_new(result, dstr_cstr(&xname), 0);
334 dstr_done(&xname);
336 if (DEBUG_HEADER) printf("header found: %s\n", dstr_cstr(&buf2));
339 /* special treatment for #include MACRO */
340 mt[0].sp = mt[0].ep = NULL;
341 if (regexp_execute(re_macros, buf, mt, 2) > 0 && mt[1].sp != NULL) {
342 const char *header_filename;
343 int l = mt[1].ep-mt[1].sp;
344 dstr_set_buf(&buf2, mt[1].sp, l);
345 if (DEBUG_HEADER) printf("macro header found: %s", dstr_cstr(&buf2));
346 header_filename = macro_header_get(dstr_cstr(&buf2));
347 if (header_filename) {
348 if (DEBUG_HEADER) printf(" resolved to '%s'\n", header_filename);
349 result = list_new(result, header_filename, 0);
350 } else {
351 if (DEBUG_HEADER) printf(" ignored !!\n");
355 dstr_done(&buf3);
356 dstr_done(&buf2);
357 free(buf);
358 fclose(f);
359 if (re_dlinc != NULL) regexp_free(re_dlinc);
360 regexp_free(re_macros);
361 while (--rec >= 0) regexp_free(re[rec]);
362 return result;