bugs in Jambase fixed; new vars added (for building Main in separate dirs); C++ vars...
[k8jam.git] / builtins.c
blob254be31fadcf31e283b70de2380daddba8351818
1 /*
2 * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
4 * This file is part of Jam - see jam.c for Copyright information.
5 */
7 /*
8 * builtins.c - builtin jam rules
10 * External routines:
12 * load_builtin() - define builtin rules
14 * Internal routines:
16 * builtin_depends() - DEPENDS/INCLUDES rule
17 * builtin_echo() - ECHO rule
18 * builtin_exit() - EXIT rule
19 * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule
20 * builtin_glob() - GLOB rule
21 * builtin_match() - MATCH rule
22 * builtin_hdrmacro() - HDRMACRO rule
24 * 01/10/01 (seiwald) - split from compile.c
25 * 01/08/01 (seiwald) - new 'Glob' (file expansion) builtin
26 * 03/02/02 (seiwald) - new 'Match' (regexp match) builtin
27 * 04/03/02 (seiwald) - Glob matches only filename, not directory
28 * 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
29 * 10/22/02 (seiwald) - working return/break/continue statements
30 * 11/04/02 (seiwald) - const-ing for string literals
31 * 12/03/02 (seiwald) - fix odd includes support by grafting them onto depends
32 * 01/14/03 (seiwald) - fix includes fix with new internal includes TARGET
35 #include <unistd.h>
36 #include <limits.h>
38 #include "jam.h"
40 #include "lists.h"
41 #include "parse.h"
42 #include "builtins.h"
43 #include "rules.h"
44 #include "filesys.h"
45 #include "newstr.h"
46 #include "regexp.h"
47 #include "pathsys.h"
48 #include "hdrmacro.h"
50 #include "kstrings.h"
54 * compile_builtin() - define builtin rules
57 #define P0 (PARSE *)0
58 #define C0 (char *)0
61 int glob (const char *s, const char *c);
64 void load_builtins (void) {
65 bindrule("Always")->procedure =
66 bindrule("ALWAYS")->procedure =
67 parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TOUCHED);
69 bindrule("Depends")->procedure =
70 bindrule("DEPENDS")->procedure =
71 parse_make(builtin_depends, P0, P0, P0, C0, C0, 0);
73 bindrule("echo")->procedure =
74 bindrule("Echo")->procedure =
75 bindrule("ECHO")->procedure =
76 parse_make(builtin_echo, P0, P0, P0, C0, C0, 0);
78 bindrule("exit")->procedure =
79 bindrule("Exit")->procedure =
80 bindrule("EXIT")->procedure =
81 parse_make(builtin_exit, P0, P0, P0, C0, C0, 0);
83 bindrule("Glob")->procedure =
84 bindrule("GLOB")->procedure =
85 parse_make(builtin_glob, P0, P0, P0, C0, C0, 0);
87 bindrule("Includes")->procedure =
88 bindrule("INCLUDES")->procedure =
89 parse_make(builtin_depends, P0, P0, P0, C0, C0, 1);
91 bindrule("Leaves")->procedure =
92 bindrule("LEAVES")->procedure =
93 parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_LEAVES);
95 bindrule("Match")->procedure =
96 bindrule("MATCH")->procedure =
97 parse_make(builtin_match, P0, P0, P0, C0, C0, 0);
99 bindrule("NoCare")->procedure =
100 bindrule("NOCARE")->procedure =
101 parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOCARE);
103 bindrule("NOTIME")->procedure =
104 bindrule("NotFile")->procedure =
105 bindrule("NOTFILE")->procedure =
106 parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOTFILE);
108 bindrule("NoUpdate")->procedure =
109 bindrule("NOUPDATE")->procedure =
110 parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOUPDATE);
112 bindrule("Temporary")->procedure =
113 bindrule("TEMPORARY")->procedure =
114 parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TEMP);
116 bindrule("HdrMacro")->procedure =
117 bindrule("HDRMACRO")->procedure =
118 parse_make(builtin_hdrmacro, P0, P0, P0, C0, C0, 0);
120 bindrule("PWD")->procedure =
121 bindrule("Pwd")->procedure =
122 parse_make(builtin_pwd, P0, P0, P0, C0, C0, 0);
124 bindrule("SORT")->procedure =
125 bindrule("Sort")->procedure =
126 parse_make(builtin_sort, P0, P0, P0, C0, C0, 0);
128 bindrule("COMMAND")->procedure =
129 bindrule("Command")->procedure =
130 parse_make(builtin_command, P0, P0, P0, C0, C0, 0);
135 * builtin_depends() - DEPENDS/INCLUDES rule
137 * The DEPENDS builtin rule appends each of the listed sources on the
138 * dependency list of each of the listed targets. It binds both the
139 * targets and sources as TARGETs.
141 LIST *builtin_depends (PARSE *parse, LOL *args, int *jmp) {
142 LIST *targets = lol_get(args, 0);
143 LIST *sources = lol_get(args, 1);
144 LIST *l;
146 for (l = targets; l; l = list_next(l)) {
147 TARGET *t = bindtarget(l->string);
148 /* If doing INCLUDES, switch to the TARGET's include */
149 /* TARGET, creating it if needed. The internal include */
150 /* TARGET shares the name of its parent. */
151 if (parse->num) {
152 if (!t->includes) t->includes = copytarget(t);
153 t = t->includes;
155 t->depends = targetlist(t->depends, sources);
157 return L0;
162 * builtin_echo() - ECHO rule
164 * The ECHO builtin rule echoes the targets to the user. No other
165 * actions are taken.
167 LIST *builtin_echo (PARSE *parse, LOL *args, int *jmp) {
168 list_print(lol_get(args, 0));
169 printf("\n");
170 return L0;
175 * builtin_exit() - EXIT rule
177 * The EXIT builtin rule echoes the targets to the user and exits
178 * the program with a failure status.
180 LIST *builtin_exit (PARSE *parse, LOL *args, int *jmp) {
181 list_print(lol_get(args, 0));
182 printf("\n");
183 exit(EXITBAD); /* yeech */
184 return L0;
189 * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule
191 * Builtin_flags() marks the target with the appropriate flag, for use
192 * by make0(). It binds each target as a TARGET.
194 LIST *builtin_flags (PARSE *parse, LOL *args, int *jmp) {
195 LIST *l = lol_get(args, 0);
196 for (; l; l = list_next(l)) bindtarget(l->string)->flags |= parse->num;
197 return L0;
202 * builtin_globbing() - GLOB rule
204 struct globbing {
205 LIST *patterns;
206 LIST *results;
210 static void builtin_glob_back (void *closure, const char *file, int status, time_t time) {
211 struct globbing *globbing = (struct globbing *)closure;
212 LIST *l;
213 PATHNAME f;
214 char buf[MAXJPATH];
216 /* Null out directory for matching. */
217 /* We wish we had file_dirscan() pass up a PATHNAME. */
218 path_parse(file, &f);
219 f.f_dir.len = 0;
221 /* For globbing, we unconditionally ignore current and parent
222 directory items. Since those items always exist, there's no
223 reason why caller of GLOB would want to see them.
224 We could also change file_dirscan, but then paths with embedded
225 "." and ".." won't work anywhere.
227 /* k8: will this break anything? it shouldn't... */
228 if (!strcmp(f.f_base.ptr, ".") || !strcmp(f.f_base.ptr, "..")) return;
230 path_build(&f, buf, 0);
231 for (l = globbing->patterns; l; l = l->next) {
232 if (!glob(l->string, buf)) {
233 globbing->results = list_new(globbing->results, file, 0);
234 break;
240 LIST *builtin_glob (PARSE *parse, LOL *args, int *jmp) {
241 struct globbing globbing;
242 LIST *l = lol_get(args, 0);
243 LIST *r = lol_get(args, 1);
245 globbing.results = L0;
246 globbing.patterns = r;
248 for (; l; l = list_next(l)) file_dirscan(l->string, builtin_glob_back, &globbing);
250 return globbing.results;
255 * builtin_match() - MATCH rule, regexp matching
257 LIST *builtin_match (PARSE *parse, LOL *args, int *jmp) {
258 LIST *l, *r;
259 LIST *result = 0;
261 /* For each pattern */
262 for (l = lol_get(args, 0); l; l = l->next) {
263 regexp *re = regcomp(l->string);
264 /* For each string to match against */
265 for (r = lol_get(args, 1); r; r = r->next) {
266 if (regexec(re, r->string)) {
267 int i, top;
268 /* Find highest parameter */
269 for (top = NSUBEXP; top-- > 1;) if (re->startp[top]) break;
270 /* And add all parameters up to highest onto list. */
271 /* Must have parameters to have results! */
272 for (i = 1; i <= top; i++) {
273 char buf[MAXSYM];
274 int l = re->endp[i]-re->startp[i];
275 memcpy(buf, re->startp[i], l);
276 buf[l] = 0;
277 result = list_new(result, buf, 0);
281 free((char *)re);
284 return result;
288 LIST *builtin_hdrmacro (PARSE *parse, LOL *args, int *jmp) {
289 LIST *l = lol_get(args, 0);
291 for (; l; l = list_next(l)) {
292 TARGET *t = bindtarget(l->string);
293 /* scan file for header filename macro definitions */
294 if (DEBUG_HEADER) printf("scanning '%s' for header file macro definitions\n", l->string);
295 macro_headers(t);
298 return L0;
302 /* backported from boost-jam */
304 * Return the current working directory.
306 * Usage: pwd = [ PWD ] ;
308 LIST *builtin_pwd (PARSE *parse, LOL *args, int *jmp) {
309 char pwd_buffer[PATH_MAX];
311 if (!getcwd(pwd_buffer, sizeof(pwd_buffer))) {
312 perror("can not get current directory");
313 return L0;
315 return list_new(L0, pwd_buffer, 0);
320 /* backported from boost-jam */
321 LIST *builtin_sort (PARSE *parse, LOL *args, int *jmp) {
322 LIST *arg = lol_get(args, 0);
323 /*printf("unsorted: "); list_print(arg); printf("\n");*/
324 arg = list_sort(arg);
325 /*printf("sorted: "); list_print(arg); printf("\n");*/
326 return arg;
330 /* backported from boost-jam */
331 /* Command shcmd [[ : options ]] */
332 LIST *builtin_command (PARSE *parse, LOL *args, int *jmp) {
333 LIST *result = 0;
334 LIST *l;
335 int ret;
336 char buffer[1024], buf1[32], *spos, *epos;
337 FILE *p = NULL;
338 int exitStatus = -1;
339 int optExitStatus = 0;
340 int optNoOutput = 0;
341 int optTrimLeft = 1;
342 int optTrimRight = 1;
343 int optStatus1st = 0;
344 int optParseOut = 0;
345 int optSpaceBreak = 1;
346 int optTabBreak = 1;
347 int optCRBreak = 1;
348 int optLFBreak = 1;
349 tKString cmd, str;
352 /* build shell command */
353 kStringNew(&cmd);
354 /* for each arg */
355 for (l = lol_get(args, 0); l; l = l->next) {
356 if (str.size) kStringPushBack(&cmd, ' ');
357 kStringAppend(&cmd, l->string);
359 /* no shell command? */
360 if (!cmd.size) { kStringFree(&cmd); return L0; }
362 /* for each string in 2nd list: check for arg */
363 for (l = lol_get(args, 1); l; l = l->next) {
364 if (!strcmp("exit-status", l->string)) optExitStatus = 1;
365 else if (!strcmp("exit-code", l->string)) optExitStatus = 1;
366 else if (!strcmp("status-first", l->string)) optStatus1st = 1;
367 else if (!strcmp("code-first", l->string)) optStatus1st = 1;
368 else if (!strcmp("no-output", l->string)) optNoOutput = 1;
369 else if (!strcmp("no-trim", l->string)) optTrimLeft = optTrimRight = 0;
370 else if (!strcmp("no-trim-left", l->string)) optTrimLeft = 0;
371 else if (!strcmp("no-trim-right", l->string)) optTrimRight = 0;
372 else if (!strcmp("parse-output", l->string)) optParseOut = 1;
373 else if (!strcmp("no-space-break", l->string)) optSpaceBreak = 0;
374 else if (!strcmp("no-tab-break", l->string)) optTabBreak = 0;
375 else if (!strcmp("no-nl-break", l->string)) optLFBreak = 0;
376 else if (!strcmp("no-lf-break", l->string)) optLFBreak = 0;
377 else if (!strcmp("no-cr-break", l->string)) optCRBreak = 0;
378 /* FIXME: don't ignore invalid options */
381 fflush(NULL);
382 p = popen(cmd.value, "r");
383 kStringFree(&cmd);
384 if (!p) return L0;
386 kStringNew(&str);
387 while ((ret = fread(buffer, sizeof(char), sizeof(buffer)-1, p)) > 0) {
388 if (!optNoOutput) {
389 buffer[ret] = 0;
390 kStringAppend(&str, buffer);
393 exitStatus = pclose(p);
395 if (optExitStatus && optStatus1st) {
396 sprintf(buf1, "%d", exitStatus);
397 result = list_new(result, buf1, 0);
400 /* trim output if necessary */
401 if (!optNoOutput) {
402 if (!optParseOut) {
403 /* don't parse */
404 if (optTrimRight) {
405 spos = str.value+str.size-1;
406 while (spos >= str.value && *((unsigned char *)spos) <= ' ') spos--;
407 *(++spos) = '\0';
408 str.size = spos-str.value;
410 spos = str.value;
411 if (optTrimLeft) while (*spos && *((unsigned char *)spos) <= ' ') spos++;
412 result = list_new(result, spos, 0);
413 } else {
414 /* parse output */
415 ret = 0; /* was anything added? list must has at least one element */
416 spos = str.value;
417 while (*spos) {
418 /* skip delimiters */
419 while (*spos) {
420 unsigned char ch = (unsigned char)(*spos);
421 if (ch == ' ') {
422 if (!optSpaceBreak) break;
423 } else if (ch == '\t') {
424 if (!optTabBreak) break;
425 } else if (ch == '\r') {
426 if (!optCRBreak) break;
427 } else if (ch == '\n') {
428 if (!optLFBreak) break;
429 } else if (ch > ' ') break;
430 spos++;
432 if (!*spos) break;
433 epos = spos+1;
434 while (*epos) {
435 int ch = *epos;
436 if (ch == ' ') {
437 if (optSpaceBreak) break;
438 } else if (ch == '\t') {
439 if (optTabBreak) break;
440 } else if (ch == '\r') {
441 if (optCRBreak) break;
442 } else if (ch == '\n') {
443 if (optLFBreak) break;
444 } else if ((unsigned char)ch <= ' ') break;
445 epos++;
447 kStringNew(&cmd);
448 kStringAppendRange(&cmd, spos, epos);
449 result = list_new(result, cmd.value, 0);
450 kStringFree(&cmd);
451 ret = 1; spos = epos;
453 if (!ret) { buf1[0] = '\0'; result = list_new(result, buf1, 0); }
455 } else result = list_new(result, str.value, 0);
456 kStringFree(&str);
458 /* the command exit result next */
459 if (optExitStatus && !optStatus1st) {
460 sprintf(buf1, "%d", exitStatus);
461 result = list_new(result, buf1, 0);
464 return result;