2 * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
4 * This file is part of Jam - see jam.c for Copyright information.
8 * builtins.c - builtin jam rules
12 * load_builtin() - define builtin rules
16 * builtin_depends() - DEPENDS/INCLUDES rule
17 * builtin_echo() - ECHO rule
18 * builtin_echon() - ECHO-N rule
19 * builtin_oflush() - O-FLUSH rule
20 * builtin_exit() - EXIT rule
21 * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule
22 * builtin_glob() - GLOB rule
23 * builtin_match() - MATCH rule
24 * builtin_hdrmacro() - HDRMACRO rule
26 * 01/10/01 (seiwald) - split from compile.c
27 * 01/08/01 (seiwald) - new 'Glob' (file expansion) builtin
28 * 03/02/02 (seiwald) - new 'Match' (regexp match) builtin
29 * 04/03/02 (seiwald) - Glob matches only filename, not directory
30 * 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
31 * 10/22/02 (seiwald) - working return/break/continue statements
32 * 11/04/02 (seiwald) - const-ing for string literals
33 * 12/03/02 (seiwald) - fix odd includes support by grafting them onto depends
34 * 01/14/03 (seiwald) - fix includes fix with new internal includes TARGET
56 * compile_builtin() - define builtin rules
63 int glob (const char *s
, const char *c
);
66 void load_builtins (void) {
67 bindrule("Always")->procedure
=
68 bindrule("ALWAYS")->procedure
=
69 parse_make(builtin_flags
, P0
, P0
, P0
, C0
, C0
, T_FLAG_TOUCHED
);
71 bindrule("Depends")->procedure
=
72 bindrule("DEPENDS")->procedure
=
73 parse_make(builtin_depends
, P0
, P0
, P0
, C0
, C0
, 0);
75 bindrule("echo")->procedure
=
76 bindrule("Echo")->procedure
=
77 bindrule("ECHO")->procedure
=
78 parse_make(builtin_echo
, P0
, P0
, P0
, C0
, C0
, 0);
80 bindrule("echo-n")->procedure
=
81 bindrule("Echo-n")->procedure
=
82 bindrule("ECHO-N")->procedure
=
83 parse_make(builtin_echon
, P0
, P0
, P0
, C0
, C0
, 0);
85 bindrule("o-flush")->procedure
=
86 bindrule("O-flush")->procedure
=
87 bindrule("O-Flush")->procedure
=
88 bindrule("O-FLUSH")->procedure
=
89 parse_make(builtin_oflush
, P0
, P0
, P0
, C0
, C0
, 0);
91 bindrule("exit")->procedure
=
92 bindrule("Exit")->procedure
=
93 bindrule("EXIT")->procedure
=
94 parse_make(builtin_exit
, P0
, P0
, P0
, C0
, C0
, 0);
96 bindrule("Glob")->procedure
=
97 bindrule("GLOB")->procedure
=
98 parse_make(builtin_glob
, P0
, P0
, P0
, C0
, C0
, 0);
100 bindrule("Includes")->procedure
=
101 bindrule("INCLUDES")->procedure
=
102 parse_make(builtin_depends
, P0
, P0
, P0
, C0
, C0
, 1);
104 bindrule("Leaves")->procedure
=
105 bindrule("LEAVES")->procedure
=
106 parse_make(builtin_flags
, P0
, P0
, P0
, C0
, C0
, T_FLAG_LEAVES
);
108 bindrule("Match")->procedure
=
109 bindrule("MATCH")->procedure
=
110 parse_make(builtin_match
, P0
, P0
, P0
, C0
, C0
, 0);
112 bindrule("NoCare")->procedure
=
113 bindrule("NOCARE")->procedure
=
114 parse_make(builtin_flags
, P0
, P0
, P0
, C0
, C0
, T_FLAG_NOCARE
);
116 bindrule("NOTIME")->procedure
=
117 bindrule("NotFile")->procedure
=
118 bindrule("NOTFILE")->procedure
=
119 parse_make(builtin_flags
, P0
, P0
, P0
, C0
, C0
, T_FLAG_NOTFILE
);
121 bindrule("NoUpdate")->procedure
=
122 bindrule("NOUPDATE")->procedure
=
123 parse_make(builtin_flags
, P0
, P0
, P0
, C0
, C0
, T_FLAG_NOUPDATE
);
125 bindrule("Temporary")->procedure
=
126 bindrule("TEMPORARY")->procedure
=
127 parse_make(builtin_flags
, P0
, P0
, P0
, C0
, C0
, T_FLAG_TEMP
);
129 bindrule("HdrMacro")->procedure
=
130 bindrule("HDRMACRO")->procedure
=
131 parse_make(builtin_hdrmacro
, P0
, P0
, P0
, C0
, C0
, 0);
133 bindrule("PWD")->procedure
=
134 bindrule("Pwd")->procedure
=
135 parse_make(builtin_pwd
, P0
, P0
, P0
, C0
, C0
, 0);
137 bindrule("SORT")->procedure
=
138 bindrule("Sort")->procedure
=
139 parse_make(builtin_sort
, P0
, P0
, P0
, C0
, C0
, 0);
141 bindrule("COMMAND")->procedure
=
142 bindrule("Command")->procedure
=
143 parse_make(builtin_command
, P0
, P0
, P0
, C0
, C0
, 0);
148 * builtin_depends() - DEPENDS/INCLUDES rule
150 * The DEPENDS builtin rule appends each of the listed sources on the
151 * dependency list of each of the listed targets. It binds both the
152 * targets and sources as TARGETs.
154 LIST
*builtin_depends (PARSE
*parse
, LOL
*args
, int *jmp
) {
155 LIST
*targets
= lol_get(args
, 0);
156 LIST
*sources
= lol_get(args
, 1);
159 for (l
= targets
; l
; l
= list_next(l
)) {
160 TARGET
*t
= bindtarget(l
->string
);
161 /* If doing INCLUDES, switch to the TARGET's include */
162 /* TARGET, creating it if needed. The internal include */
163 /* TARGET shares the name of its parent. */
165 if (!t
->includes
) t
->includes
= copytarget(t
);
168 t
->depends
= targetlist(t
->depends
, sources
);
175 * builtin_echo() - ECHO rule
177 * The ECHO builtin rule echoes the targets to the user. No other
180 LIST
*builtin_echo (PARSE
*parse
, LOL
*args
, int *jmp
) {
181 list_print(lol_get(args
, 0));
188 * builtin_echon() - ECHO-N rule
190 * The ECHO-N builtin rule echoes the targets to the user. No other
191 * actions are taken, no newline is written.
193 LIST
*builtin_echon (PARSE
*parse
, LOL
*args
, int *jmp
) {
194 list_print(lol_get(args
, 0));
200 * builtin_oflush() - O-FLUSH rule
202 * The O-FLUSH builtin rule flushes current output stream.
205 LIST
*builtin_oflush (PARSE
*parse
, LOL
*args
, int *jmp
) {
212 * builtin_exit() - EXIT rule
214 * The EXIT builtin rule echoes the targets to the user and exits
215 * the program with a failure status.
217 LIST
*builtin_exit (PARSE
*parse
, LOL
*args
, int *jmp
) {
218 list_print(lol_get(args
, 0));
220 exit(EXITBAD
); /* yeech */
226 * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule
228 * Builtin_flags() marks the target with the appropriate flag, for use
229 * by make0(). It binds each target as a TARGET.
231 LIST
*builtin_flags (PARSE
*parse
, LOL
*args
, int *jmp
) {
232 LIST
*l
= lol_get(args
, 0);
233 for (; l
; l
= list_next(l
)) bindtarget(l
->string
)->flags
|= parse
->num
;
239 * builtin_globbing() - GLOB rule
247 static void builtin_glob_back (void *closure
, const char *file
, int status
, time_t time
) {
248 struct globbing
*globbing
= (struct globbing
*)closure
;
253 /* Null out directory for matching. */
254 /* We wish we had file_dirscan() pass up a PATHNAME. */
255 path_parse(file
, &f
);
258 /* For globbing, we unconditionally ignore current and parent
259 directory items. Since those items always exist, there's no
260 reason why caller of GLOB would want to see them.
261 We could also change file_dirscan, but then paths with embedded
262 "." and ".." won't work anywhere.
264 /* k8: will this break anything? it shouldn't... */
265 if (!strcmp(f
.f_base
.ptr
, ".") || !strcmp(f
.f_base
.ptr
, "..")) return;
267 path_build(&f
, buf
, 0);
268 for (l
= globbing
->patterns
; l
; l
= l
->next
) {
269 if (!glob(l
->string
, buf
)) {
270 globbing
->results
= list_new(globbing
->results
, file
, 0);
277 LIST
*builtin_glob (PARSE
*parse
, LOL
*args
, int *jmp
) {
278 struct globbing globbing
;
279 LIST
*l
= lol_get(args
, 0);
280 LIST
*r
= lol_get(args
, 1);
282 globbing
.results
= L0
;
283 globbing
.patterns
= r
;
285 for (; l
; l
= list_next(l
)) file_dirscan(l
->string
, builtin_glob_back
, &globbing
);
287 return globbing
.results
;
292 * builtin_match() - MATCH rule, regexp matching
294 LIST
*builtin_match (PARSE
*parse
, LOL
*args
, int *jmp
) {
298 /* For each pattern */
299 for (l
= lol_get(args
, 0); l
; l
= l
->next
) {
300 regexp
*re
= regcomp(l
->string
);
301 /* For each string to match against */
302 for (r
= lol_get(args
, 1); r
; r
= r
->next
) {
303 if (regexec(re
, r
->string
)) {
305 /* Find highest parameter */
306 for (top
= NSUBEXP
; top
-- > 1;) if (re
->startp
[top
]) break;
307 /* And add all parameters up to highest onto list. */
308 /* Must have parameters to have results! */
309 for (i
= 1; i
<= top
; i
++) {
311 int l
= re
->endp
[i
]-re
->startp
[i
];
312 memcpy(buf
, re
->startp
[i
], l
);
314 result
= list_new(result
, buf
, 0);
325 LIST
*builtin_hdrmacro (PARSE
*parse
, LOL
*args
, int *jmp
) {
326 LIST
*l
= lol_get(args
, 0);
328 for (; l
; l
= list_next(l
)) {
329 TARGET
*t
= bindtarget(l
->string
);
330 /* scan file for header filename macro definitions */
331 if (DEBUG_HEADER
) printf("scanning '%s' for header file macro definitions\n", l
->string
);
339 /* backported from boost-jam */
341 * Return the current working directory.
343 * Usage: pwd = [ PWD ] ;
345 LIST
*builtin_pwd (PARSE
*parse
, LOL
*args
, int *jmp
) {
346 char pwd_buffer
[PATH_MAX
];
348 if (!getcwd(pwd_buffer
, sizeof(pwd_buffer
))) {
349 perror("can not get current directory");
352 return list_new(L0
, pwd_buffer
, 0);
357 /* backported from boost-jam */
358 LIST
*builtin_sort (PARSE
*parse
, LOL
*args
, int *jmp
) {
359 LIST
*arg
= lol_get(args
, 0);
360 /*printf("unsorted: "); list_print(arg); printf("\n");*/
361 arg
= list_sort(arg
);
362 /*printf("sorted: "); list_print(arg); printf("\n");*/
367 /* backported from boost-jam */
368 /* Command shcmd [[ : options ]] */
369 LIST
*builtin_command (PARSE
*parse
, LOL
*args
, int *jmp
) {
373 char buffer
[1024], buf1
[32], *spos
, *epos
;
376 int optExitStatus
= 0;
379 int optTrimRight
= 1;
380 int optStatus1st
= 0;
382 int optSpaceBreak
= 1;
389 /* build shell command */
392 for (l
= lol_get(args
, 0); l
; l
= l
->next
) {
393 if (str
.size
) kStringPushBack(&cmd
, ' ');
394 kStringAppend(&cmd
, l
->string
);
396 /* no shell command? */
397 if (!cmd
.size
) { kStringFree(&cmd
); return L0
; }
399 /* for each string in 2nd list: check for arg */
400 for (l
= lol_get(args
, 1); l
; l
= l
->next
) {
401 if (!strcmp("exit-status", l
->string
)) optExitStatus
= 1;
402 else if (!strcmp("exit-code", l
->string
)) optExitStatus
= 1;
403 else if (!strcmp("status-first", l
->string
)) optStatus1st
= 1;
404 else if (!strcmp("code-first", l
->string
)) optStatus1st
= 1;
405 else if (!strcmp("no-output", l
->string
)) optNoOutput
= 1;
406 else if (!strcmp("no-trim", l
->string
)) optTrimLeft
= optTrimRight
= 0;
407 else if (!strcmp("no-trim-left", l
->string
)) optTrimLeft
= 0;
408 else if (!strcmp("no-trim-right", l
->string
)) optTrimRight
= 0;
409 else if (!strcmp("parse-output", l
->string
)) optParseOut
= 1;
410 else if (!strcmp("no-space-break", l
->string
)) optSpaceBreak
= 0;
411 else if (!strcmp("no-tab-break", l
->string
)) optTabBreak
= 0;
412 else if (!strcmp("no-nl-break", l
->string
)) optLFBreak
= 0;
413 else if (!strcmp("no-lf-break", l
->string
)) optLFBreak
= 0;
414 else if (!strcmp("no-cr-break", l
->string
)) optCRBreak
= 0;
415 /* FIXME: don't ignore invalid options */
419 p
= popen(cmd
.value
, "r");
424 while ((ret
= fread(buffer
, sizeof(char), sizeof(buffer
)-1, p
)) > 0) {
427 kStringAppend(&str
, buffer
);
430 exitStatus
= pclose(p
);
432 if (optExitStatus
&& optStatus1st
) {
433 sprintf(buf1
, "%d", exitStatus
);
434 result
= list_new(result
, buf1
, 0);
437 /* trim output if necessary */
442 spos
= str
.value
+str
.size
-1;
443 while (spos
>= str
.value
&& *((unsigned char *)spos
) <= ' ') spos
--;
445 str
.size
= spos
-str
.value
;
448 if (optTrimLeft
) while (*spos
&& *((unsigned char *)spos
) <= ' ') spos
++;
449 result
= list_new(result
, spos
, 0);
452 ret
= 0; /* was anything added? list must has at least one element */
455 /* skip delimiters */
457 unsigned char ch
= (unsigned char)(*spos
);
459 if (!optSpaceBreak
) break;
460 } else if (ch
== '\t') {
461 if (!optTabBreak
) break;
462 } else if (ch
== '\r') {
463 if (!optCRBreak
) break;
464 } else if (ch
== '\n') {
465 if (!optLFBreak
) break;
466 } else if (ch
> ' ') break;
474 if (optSpaceBreak
) break;
475 } else if (ch
== '\t') {
476 if (optTabBreak
) break;
477 } else if (ch
== '\r') {
478 if (optCRBreak
) break;
479 } else if (ch
== '\n') {
480 if (optLFBreak
) break;
481 } else if ((unsigned char)ch
<= ' ') break;
485 kStringAppendRange(&cmd
, spos
, epos
);
486 result
= list_new(result
, cmd
.value
, 0);
488 ret
= 1; spos
= epos
;
490 if (!ret
) { buf1
[0] = '\0'; result
= list_new(result
, buf1
, 0); }
492 } else result
= list_new(result
, str
.value
, 0);
495 /* the command exit result next */
496 if (optExitStatus
&& !optStatus1st
) {
497 sprintf(buf1
, "%d", exitStatus
);
498 result
= list_new(result
, buf1
, 0);