cosmetic renamings (glob() --> matchglob())
[k8jam.git] / src / builtins.c
blob1f7610436276a0ec65ee75b0292ecaaacd29feb2
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 "hsregexp.h"
49 #include "pathsys.h"
50 #include "hdrmacro.h"
51 #include "matchglob.h"
53 #include "kstrings.h"
57 * compile_builtin() - define builtin rules
60 #define P0 ((PARSE *)0)
61 #define C0 ((char *)0)
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("echo-n")->procedure =
79 bindrule("Echo-n")->procedure =
80 bindrule("ECHO-N")->procedure =
81 parse_make(builtin_echon, P0, P0, P0, C0, C0, 0);
83 bindrule("o-flush")->procedure =
84 bindrule("O-flush")->procedure =
85 bindrule("O-Flush")->procedure =
86 bindrule("O-FLUSH")->procedure =
87 parse_make(builtin_oflush, P0, P0, P0, C0, C0, 0);
89 bindrule("exit")->procedure =
90 bindrule("Exit")->procedure =
91 bindrule("EXIT")->procedure =
92 parse_make(builtin_exit, P0, P0, P0, C0, C0, 0);
94 bindrule("Glob")->procedure =
95 bindrule("GLOB")->procedure =
96 parse_make(builtin_glob, P0, P0, P0, C0, C0, 0);
98 bindrule("Includes")->procedure =
99 bindrule("INCLUDES")->procedure =
100 parse_make(builtin_depends, P0, P0, P0, C0, C0, 1);
102 bindrule("Leaves")->procedure =
103 bindrule("LEAVES")->procedure =
104 parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_LEAVES);
106 bindrule("Match")->procedure =
107 bindrule("MATCH")->procedure =
108 parse_make(builtin_match, P0, P0, P0, C0, C0, 0);
110 bindrule("NoCare")->procedure =
111 bindrule("NOCARE")->procedure =
112 parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOCARE);
114 bindrule("NOTIME")->procedure =
115 bindrule("NotFile")->procedure =
116 bindrule("NOTFILE")->procedure =
117 parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOTFILE);
119 bindrule("NoUpdate")->procedure =
120 bindrule("NOUPDATE")->procedure =
121 parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOUPDATE);
123 bindrule("Temporary")->procedure =
124 bindrule("TEMPORARY")->procedure =
125 parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TEMP);
127 bindrule("HdrMacro")->procedure =
128 bindrule("HDRMACRO")->procedure =
129 parse_make(builtin_hdrmacro, P0, P0, P0, C0, C0, 0);
131 bindrule("PWD")->procedure =
132 bindrule("Pwd")->procedure =
133 parse_make(builtin_pwd, P0, P0, P0, C0, C0, 0);
135 bindrule("SORT")->procedure =
136 bindrule("Sort")->procedure =
137 parse_make(builtin_sort, P0, P0, P0, C0, C0, 0);
139 bindrule("COMMAND")->procedure =
140 bindrule("Command")->procedure =
141 parse_make(builtin_command, P0, P0, P0, C0, C0, 0);
143 bindrule("ForceCare")->procedure =
144 bindrule("FORCECARE")->procedure =
145 parse_make(builtin_flags_forcecare, P0, P0, P0, C0, C0, T_FLAG_FORCECARE);
147 bindrule("ExprI1")->procedure =
148 bindrule("EXPRI1")->procedure =
149 parse_make(builtin_expri1, P0, P0, P0, C0, C0, 0);
151 bindrule("Split")->procedure =
152 bindrule("SPLIT")->procedure =
153 parse_make(builtin_split, P0, P0, P0, C0, C0, 0);
155 bindrule("DependsList")->procedure =
156 bindrule("DEPENDSLIST")->procedure =
157 parse_make(builtin_dependslist, P0, P0, P0, C0, C0, 0);
159 bindrule("NormPath")->procedure =
160 bindrule("NORMPATH")->procedure =
161 parse_make(builtin_normpath, P0, P0, P0, C0, C0, 0);
163 bindrule("ListLength")->procedure =
164 parse_make(builtin_listlength, P0, P0, P0, C0, C0, 0);
169 * builtin_depends() - DEPENDS/INCLUDES rule
171 * The DEPENDS builtin rule appends each of the listed sources on the
172 * dependency list of each of the listed targets.
173 * It binds both the targets and sources as TARGETs.
175 LIST *builtin_depends (PARSE *parse, LOL *args, int *jmp) {
176 LIST *targets = lol_get(args, 0);
177 LIST *sources = lol_get(args, 1);
178 LIST *l;
180 for (l = targets; l; l = list_next(l)) {
181 TARGET *t = bindtarget(l->string);
182 /* If doing INCLUDES, switch to the TARGET's include */
183 /* TARGET, creating it if needed. The internal include */
184 /* TARGET shares the name of its parent. */
185 if (parse->num) {
186 if (!t->includes) t->includes = copytarget(t);
187 t = t->includes;
189 t->depends = targetlist(t->depends, sources);
191 return L0;
196 * builtin_echo() - ECHO rule
198 * The ECHO builtin rule echoes the targets to the user.
199 * No other actions are taken.
201 LIST *builtin_echo (PARSE *parse, LOL *args, int *jmp) {
202 list_print(lol_get(args, 0));
203 printf("\n");
204 return L0;
209 * builtin_echon() - ECHO-N rule
211 * The ECHO-N builtin rule echoes the targets to the user.
212 * No other actions are taken, no newline is written.
214 LIST *builtin_echon (PARSE *parse, LOL *args, int *jmp) {
215 list_print(lol_get(args, 0));
216 return L0;
221 * builtin_oflush() - O-FLUSH rule
223 * The O-FLUSH builtin rule flushes current output stream.
224 * Used with ECHO-N.
226 LIST *builtin_oflush (PARSE *parse, LOL *args, int *jmp) {
227 fflush(stdout);
228 return L0;
233 * builtin_exit() - EXIT rule
235 * The EXIT builtin rule echoes the targets to the user and exits
236 * the program with a failure status.
238 LIST *builtin_exit (PARSE *parse, LOL *args, int *jmp) {
239 list_print(lol_get(args, 0));
240 printf("\n");
241 exit(EXITBAD); /* yeech */
242 #if __GNUC__ <= 2
243 return L0;
244 #endif
249 * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule
251 * Builtin_flags() marks the target with the appropriate flag, for use by make0().
252 * It binds each target as a TARGET.
254 LIST *builtin_flags (PARSE *parse, LOL *args, int *jmp) {
255 LIST *l = lol_get(args, 0);
256 for (; l; l = list_next(l)) bindtarget(l->string)->flags |= parse->num;
257 return L0;
262 * builtin_flags_forcecare() - ForceCare rule
264 LIST *builtin_flags_forcecare (PARSE *parse, LOL *args, int *jmp) {
265 LIST *l = lol_get(args, 0);
266 for( ; l; l = list_next(l)) {
267 TARGET *t = bindtarget(l->string);
268 t->flags |= T_FLAG_FORCECARE;
269 t->flags &= ~T_FLAG_NOCARE;
271 return L0;
276 * builtin_globbing() - GLOB rule
278 struct globbing {
279 LIST *patterns;
280 LIST *results;
284 static void builtin_glob_back (void *closure, const char *file, int status, time_t time) {
285 struct globbing *globbing = (struct globbing *)closure;
286 LIST *l;
287 PATHNAME f;
288 char buf[MAXJPATH];
289 /* null out directory for matching */
290 /* we wish we had file_dirscan() pass up a PATHNAME */
291 path_parse(file, &f);
292 f.f_dir.len = 0;
293 /* For globbing, we unconditionally ignore current and parent
294 * directory items. Since those items always exist, there's no
295 * reason why caller of GLOB would want to see them.
296 * We could also change file_dirscan, but then paths with embedded
297 * "." and ".." won't work anywhere. */
298 /* k8: will this break anything? it shouldn't... */
299 if (!strcmp(f.f_base.ptr, ".") || !strcmp(f.f_base.ptr, "..")) return;
300 path_build(&f, buf, 0);
301 for (l = globbing->patterns; l; l = l->next) {
302 if (!matchglob(l->string, buf)) {
303 globbing->results = list_new(globbing->results, file, 0);
304 break;
310 LIST *builtin_glob (PARSE *parse, LOL *args, int *jmp) {
311 struct globbing globbing;
312 LIST *l = lol_get(args, 0);
313 LIST *r = lol_get(args, 1);
314 globbing.results = L0;
315 globbing.patterns = r;
316 for (; l; l = list_next(l)) file_dirscan(l->string, builtin_glob_back, &globbing);
317 return globbing.results;
322 * builtin_match() - MATCH rule, regexp matching
324 LIST *builtin_match (PARSE *parse, LOL *args, int *jmp) {
325 LIST *l, *r;
326 LIST *res = 0;
327 /* for each pattern */
328 for (l = lol_get(args, 0); l; l = l->next) {
329 HSRegExp re;
330 HSRxMatch *mt;
331 int err;
333 if ((err = hsrxCompile(&re, l->string, HSRX_EXTENDED)) != 0) {
334 static char errbuf[512];
336 hsrxError(err, &re, errbuf, sizeof(errbuf));
337 hsrxFree(&re);
338 printf("FATAL: %s\n", errbuf);
339 exit(42);
341 mt = malloc(sizeof(HSRxMatch)*(re.re_nsub+1));
342 if (mt == NULL) { printf("FATAL: out of memory!\n"); exit(42); }
343 /* for each string to match against */
344 for (r = lol_get(args, 1); r; r = r->next) {
345 if (hsrxExec(&re, r->string, re.re_nsub+1, mt, 0) == HSRX_NOERROR) {
346 int i;
347 /* add all parameters up to highest onto list */
348 /* must have parameters to have results! */
349 for (i = 1; i <= re.re_nsub; ++i) {
350 char buf[MAXSYM];
351 //FIXME
352 int l = mt[i].rm_eo-mt[i].rm_so;
353 if (l > 0) memcpy(buf, r->string+mt[i].rm_so, l);
354 buf[l] = 0;
355 res = list_new(res, buf, 0);
359 free(mt);
360 hsrxFree(&re);
362 return res;
366 LIST *builtin_hdrmacro (PARSE *parse, LOL *args, int *jmp) {
367 LIST *l = lol_get(args, 0);
368 for (; l; l = list_next(l)) {
369 TARGET *t = bindtarget(l->string);
370 /* scan file for header filename macro definitions */
371 if (DEBUG_HEADER) printf("scanning '%s' for header file macro definitions\n", l->string);
372 macro_headers(t);
374 return L0;
378 /* backported from boost-jam */
380 * Return the current working directory.
382 * Usage: pwd = [ PWD ] ;
384 LIST *builtin_pwd (PARSE *parse, LOL *args, int *jmp) {
385 char pwd_buffer[PATH_MAX];
386 if (!getcwd(pwd_buffer, sizeof(pwd_buffer))) {
387 perror("can not get current directory");
388 return L0;
390 return list_new(L0, pwd_buffer, 0);
394 /* backported from boost-jam */
395 LIST *builtin_sort (PARSE *parse, LOL *args, int *jmp) {
396 LIST *arg = lol_get(args, 0);
397 arg = list_sort(arg);
398 return arg;
402 /* backported from boost-jam; greatly improved */
403 /* Command shcmd [[ : options ]] */
404 LIST *builtin_command (PARSE *parse, LOL *args, int *jmp) {
405 LIST *res = NULL;
406 LIST *l;
407 int ret;
408 char buffer[1024], buf1[32], *spos, *epos;
409 FILE *p = NULL;
410 int exitStatus = -1;
411 int optExitStatus = 0;
412 int optNoOutput = 0;
413 int optTrimLeft = 1;
414 int optTrimRight = 1;
415 int optStatus1st = 0;
416 int optParseOut = 0;
417 int optSpaceBreak = 1;
418 int optTabBreak = 1;
419 int optCRBreak = 1;
420 int optLFBreak = 1;
421 tKString str;
423 /* for each string in 2nd list: check for arg */
424 for (l = lol_get(args, 1); l != NULL; l = l->next) {
425 if (!strcmp("exit-status", l->string)) optExitStatus = 1;
426 else if (!strcmp("exit-code", l->string)) optExitStatus = 1;
427 else if (!strcmp("status-first", l->string)) optStatus1st = 1;
428 else if (!strcmp("code-first", l->string)) optStatus1st = 1;
429 else if (!strcmp("no-output", l->string)) optNoOutput = 1;
430 else if (!strcmp("no-trim", l->string)) optTrimLeft = optTrimRight = 0;
431 else if (!strcmp("no-trim-left", l->string)) optTrimLeft = 0;
432 else if (!strcmp("no-trim-right", l->string)) optTrimRight = 0;
433 else if (!strcmp("parse-output", l->string)) optParseOut = 1;
434 else if (!strcmp("no-space-break", l->string)) optSpaceBreak = 0;
435 else if (!strcmp("no-tab-break", l->string)) optTabBreak = 0;
436 else if (!strcmp("no-nl-break", l->string)) optLFBreak = 0;
437 else if (!strcmp("no-lf-break", l->string)) optLFBreak = 0;
438 else if (!strcmp("no-cr-break", l->string)) optCRBreak = 0;
439 else {
440 printf("jam: invalid option for COMMAND built-in: '%s'\n", l->string);
441 exit(EXITBAD); /* yeech */
444 /* build shell command */
445 kStringNew(&str);
446 /* for each arg */
447 for (l = lol_get(args, 0); l; l = l->next) {
448 if (kStringLen(&str)) kStringPushBack(&str, ' ');
449 kStringAppendCStr(&str, l->string);
451 /* no shell command? */
452 if (kStringLen(&str) < 1) { kStringFree(&str); return L0; }
454 fflush(NULL);
455 p = popen(kStringCStr(&str), "r");
456 if (!p) { kStringFree(&str); return L0; }
458 kStringClear(&str);
459 while ((ret = fread(buffer, sizeof(char), sizeof(buffer)-1, p)) > 0) {
460 if (!optNoOutput) {
461 buffer[ret] = 0;
462 kStringAppendCStr(&str, buffer);
465 exitStatus = pclose(p);
466 if (optExitStatus && optStatus1st) {
467 sprintf(buf1, "%d", exitStatus);
468 res = list_new(res, buf1, 0);
470 /* trim output if necessary */
471 if (!optNoOutput) {
472 if (!optParseOut) {
473 /* don't parse */
474 if (optTrimRight) {
475 // trim trailing blanks
476 int sl = kStringLen(&str);
477 spos = kStringCStr(&str);
478 while (sl > 0 && (unsigned char)spos[sl-1] <= ' ') --sl;
479 kStringTruncate(&str, sl);
481 spos = kStringCStr(&str);
482 if (optTrimLeft) {
483 // trim leading blanks
484 while (*spos && *((unsigned char *)spos) <= ' ') ++spos;
486 res = list_new(res, spos, 0);
487 } else {
488 tKString tmp;
489 /* parse output */
490 ret = 0; /* was anything added? list must have at least one element */
491 spos = kStringCStr(&str);
492 kStringNew(&tmp);
493 while (*spos) {
494 /* skip delimiters */
495 while (*spos) {
496 unsigned char ch = (unsigned char)(*spos);
497 if (ch == ' ') { if (!optSpaceBreak) break; }
498 else if (ch == '\t') { if (!optTabBreak) break; }
499 else if (ch == '\r') { if (!optCRBreak) break; }
500 else if (ch == '\n') { if (!optLFBreak) break; }
501 else if (ch > ' ') break;
502 ++spos;
504 if (!*spos) break;
505 epos = spos+1;
506 while (*epos) {
507 int ch = *epos;
508 if (ch == ' ') { if (optSpaceBreak) break; }
509 else if (ch == '\t') { if (optTabBreak) break; }
510 else if (ch == '\r') { if (optCRBreak) break; }
511 else if (ch == '\n') { if (optLFBreak) break; }
512 else if ((unsigned char)ch <= ' ') break;
513 ++epos;
515 kStringClear(&tmp);
516 kStringAppendRange(&tmp, spos, epos);
517 res = list_new(res, kStringCStr(&tmp), 0);
518 ret = 1;
519 spos = epos;
521 kStringFree(&tmp);
522 if (!ret) { buf1[0] = '\0'; res = list_new(res, buf1, 0); }
524 } else {
525 res = list_new(res, kStringCStr(&str), 0);
527 kStringFree(&str);
528 /* command exit result next */
529 if (optExitStatus && !optStatus1st) {
530 sprintf(buf1, "%d", exitStatus);
531 res = list_new(res, buf1, 0);
533 return res;
537 LIST *builtin_expri1 (PARSE *parse, LOL *args, int *jmp) {
538 char buffer[100];
539 int op0, op1, res, comp = 0;
540 LIST *el = lol_get(args, 0);
542 if (!el || !el->next || !el->next->next) return L0;
543 op0 = atoi(el->string);
544 op1 = atoi(el->next->next->string);
545 res = 0;
546 switch (el->next->string[0]) {
547 case '+': res = op0+op1; break;
548 case '-': res = op0-op1; break;
549 case '*': res = op0*op1; break;
550 case '/': res = op0/op1; break;
551 case '%': res = op0%op1; break;
552 case '<':
553 comp = 1;
554 if (el->next->string[1] == '=') res = op0<=op1; else res = op0<op1;
555 break;
556 case '=': comp = 1; res = op0==op1; break;
557 case '!': comp = 1; res = op0!=op1; break;
558 case '>':
559 comp = 1;
560 if (el->next->string[1] == '=') res = op0>=op1; else res = op0>op1;
561 break;
562 default:
563 printf("jam: rule ExprI1: unknown operator: '%s'\n", el->next->string);
564 exit(EXITBAD);
566 if (comp) return res?list_new(L0, "tan", 0):L0;
567 sprintf(buffer, "%d", res);
568 return list_new(L0, buffer, 0);
572 /* Based on code from ftjam by David Turner */
573 LIST *builtin_split (PARSE *parse, LOL *args, int *jmp) {
574 LIST *input = lol_get(args, 0);
575 LIST *tokens = lol_get(args, 1);
576 LIST *res = L0;
577 char token[256];
578 tKString str;
579 int explode = 0;
581 kStringNew(&str);
582 /* build token array */
583 if (tokens == NULL) {
584 memset(token, 1, sizeof(token));
585 explode = 1;
586 } else {
587 memset(token, 0, sizeof(token));
588 for (; tokens; tokens = tokens->next) {
589 const char *s = tokens->string;
590 for (; *s; ++s) token[(unsigned char)*s] = 1;
592 if (memchr(token, 1, sizeof(token)) == NULL) {
593 memset(token, 1, sizeof(token));
594 explode = 1;
597 token[0] = 0;
598 /* now parse the input and split it */
599 for (; input; input = input->next) {
600 const char *ptr = input->string;
601 const char *lastPtr = input->string;
602 while (*ptr) {
603 if (token[(unsigned char)*ptr]) {
604 size_t count = ptr-lastPtr+explode;
605 if (count > 0) {
606 kStringClear(&str);
607 kStringAppendRange(&str, lastPtr, ptr+explode);
608 res = list_new(res, kStringCStr(&str), 0);
610 lastPtr = ptr+1;
612 ++ptr;
614 if (ptr > lastPtr) res = list_new(res, lastPtr, 0);
616 kStringFree(&str);
617 return res;
621 LIST *builtin_dependslist (PARSE *parse, LOL *args, int *jmp) {
622 LIST *res = L0;
623 LIST *parents;
625 for (parents = lol_get(args, 0); parents; parents = parents->next) {
626 TARGET *t = bindtarget(parents->string);
627 TARGETS *child;
629 for (child = t->depends; child; child = child->next) res = list_new(res, child->target->name, 1);
631 return res;
635 LIST *builtin_normpath (PARSE *parse, LOL *args, int *jmp) {
636 LIST *el = lol_get(args, 0);
637 char *buf;
638 int bsz;
640 if (!el || !el->string) return L0;
641 bsz = strlen(el->string)*2+1024;
642 buf = malloc(bsz);
643 if (buf == NULL) return L0;
644 if (!normalize_path(el->string, buf, bsz)) { free(buf); return L0; }
645 el = list_new(NULL, buf, 0);
646 free(buf);
647 return el;
651 LIST *builtin_listlength (PARSE *parse, LOL *args, int *jmp) {
652 char buffer[100];
653 LIST *el = lol_get(args, 0);
655 if (!el) return L0;
656 sprintf(buffer, "%d", list_length(el));
657 return list_new(L0, buffer, 0);