cosmetix
[k8jam.git] / builtins.c
blob697836fab2501ab321602680e6179fd3e4ca6805
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_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
37 #include <unistd.h>
38 #include <limits.h>
40 #include "jam.h"
42 #include "lists.h"
43 #include "parse.h"
44 #include "builtins.h"
45 #include "rules.h"
46 #include "filesys.h"
47 #include "newstr.h"
48 #include "regexp.h"
49 #include "pathsys.h"
50 #include "hdrmacro.h"
52 #include "kstrings.h"
56 * compile_builtin() - define builtin rules
59 #define P0 (PARSE *)0
60 #define C0 (char *)0
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);
157 LIST *l;
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. */
164 if (parse->num) {
165 if (!t->includes) t->includes = copytarget(t);
166 t = t->includes;
168 t->depends = targetlist(t->depends, sources);
170 return L0;
175 * builtin_echo() - ECHO rule
177 * The ECHO builtin rule echoes the targets to the user. No other
178 * actions are taken.
180 LIST *builtin_echo (PARSE *parse, LOL *args, int *jmp) {
181 list_print(lol_get(args, 0));
182 printf("\n");
183 return L0;
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));
195 return L0;
200 * builtin_oflush() - O-FLUSH rule
202 * The O-FLUSH builtin rule flushes current output stream.
203 * Used with ECHO-N.
205 LIST *builtin_oflush (PARSE *parse, LOL *args, int *jmp) {
206 fflush(stdout);
207 return L0;
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));
219 printf("\n");
220 exit(EXITBAD); /* yeech */
221 return L0;
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;
234 return L0;
239 * builtin_globbing() - GLOB rule
241 struct globbing {
242 LIST *patterns;
243 LIST *results;
247 static void builtin_glob_back (void *closure, const char *file, int status, time_t time) {
248 struct globbing *globbing = (struct globbing *)closure;
249 LIST *l;
250 PATHNAME f;
251 char buf[MAXJPATH];
253 /* Null out directory for matching. */
254 /* We wish we had file_dirscan() pass up a PATHNAME. */
255 path_parse(file, &f);
256 f.f_dir.len = 0;
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);
271 break;
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) {
295 LIST *l, *r;
296 LIST *result = 0;
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)) {
304 int i, top;
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++) {
310 char buf[MAXSYM];
311 int l = re->endp[i]-re->startp[i];
312 memcpy(buf, re->startp[i], l);
313 buf[l] = 0;
314 result = list_new(result, buf, 0);
318 free((char *)re);
321 return result;
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);
332 macro_headers(t);
335 return L0;
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");
350 return L0;
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");*/
363 return arg;
367 /* backported from boost-jam */
368 /* Command shcmd [[ : options ]] */
369 LIST *builtin_command (PARSE *parse, LOL *args, int *jmp) {
370 LIST *result = 0;
371 LIST *l;
372 int ret;
373 char buffer[1024], buf1[32], *spos, *epos;
374 FILE *p = NULL;
375 int exitStatus = -1;
376 int optExitStatus = 0;
377 int optNoOutput = 0;
378 int optTrimLeft = 1;
379 int optTrimRight = 1;
380 int optStatus1st = 0;
381 int optParseOut = 0;
382 int optSpaceBreak = 1;
383 int optTabBreak = 1;
384 int optCRBreak = 1;
385 int optLFBreak = 1;
386 tKString cmd, str;
389 /* build shell command */
390 kStringNew(&cmd);
391 /* for each arg */
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 */
418 fflush(NULL);
419 p = popen(cmd.value, "r");
420 kStringFree(&cmd);
421 if (!p) return L0;
423 kStringNew(&str);
424 while ((ret = fread(buffer, sizeof(char), sizeof(buffer)-1, p)) > 0) {
425 if (!optNoOutput) {
426 buffer[ret] = 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 */
438 if (!optNoOutput) {
439 if (!optParseOut) {
440 /* don't parse */
441 if (optTrimRight) {
442 spos = str.value+str.size-1;
443 while (spos >= str.value && *((unsigned char *)spos) <= ' ') spos--;
444 *(++spos) = '\0';
445 str.size = spos-str.value;
447 spos = str.value;
448 if (optTrimLeft) while (*spos && *((unsigned char *)spos) <= ' ') spos++;
449 result = list_new(result, spos, 0);
450 } else {
451 /* parse output */
452 ret = 0; /* was anything added? list must has at least one element */
453 spos = str.value;
454 while (*spos) {
455 /* skip delimiters */
456 while (*spos) {
457 unsigned char ch = (unsigned char)(*spos);
458 if (ch == ' ') {
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;
467 spos++;
469 if (!*spos) break;
470 epos = spos+1;
471 while (*epos) {
472 int ch = *epos;
473 if (ch == ' ') {
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;
482 epos++;
484 kStringNew(&cmd);
485 kStringAppendRange(&cmd, spos, epos);
486 result = list_new(result, cmd.value, 0);
487 kStringFree(&cmd);
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);
493 kStringFree(&str);
495 /* the command exit result next */
496 if (optExitStatus && !optStatus1st) {
497 sprintf(buf1, "%d", exitStatus);
498 result = list_new(result, buf1, 0);
501 return result;