added iterator funcions for hash and rulelist
[k8jam.git] / src / builtins.c
blobf34b10d1d5ef1e6edd43822d05c0894d91ee24be
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
36 #include <stdarg.h>
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"
56 int compat_old_builtin_names = 0;
60 * builtin_depends() - DEPENDS/INCLUDES rule
62 * The DEPENDS builtin rule appends each of the listed sources on the
63 * dependency list of each of the listed targets.
64 * It binds both the targets and sources as TARGETs.
66 static LIST *builtin_depends (PARSE *parse, LOL *args, int *jmp) {
67 LIST *targets = lol_get(args, 0);
68 LIST *sources = lol_get(args, 1);
69 LIST *l;
71 for (l = targets; l; l = list_next(l)) {
72 TARGET *t = bindtarget(l->string);
73 /* If doing INCLUDES, switch to the TARGET's include */
74 /* TARGET, creating it if needed. The internal include */
75 /* TARGET shares the name of its parent. */
76 if (parse->num) {
77 if (!t->includes) t->includes = copytarget(t);
78 t = t->includes;
80 t->depends = targetlist(t->depends, sources);
82 return L0;
87 * builtin_echo() - ECHO rule
89 * The ECHO builtin rule echoes the targets to the user.
90 * No other actions are taken.
92 static LIST *builtin_echo (PARSE *parse, LOL *args, int *jmp) {
93 list_print(lol_get(args, 0));
94 printf("\n");
95 return L0;
100 * builtin_echon() - ECHO-N rule
102 * The ECHO-N builtin rule echoes the targets to the user.
103 * No other actions are taken, no newline is written.
105 static LIST *builtin_echon (PARSE *parse, LOL *args, int *jmp) {
106 list_print(lol_get(args, 0));
107 return L0;
112 * builtin_oflush() - O-FLUSH rule
114 * The O-FLUSH builtin rule flushes current output stream.
115 * Used with ECHO-N.
117 static LIST *builtin_oflush (PARSE *parse, LOL *args, int *jmp) {
118 fflush(stdout);
119 return L0;
124 * builtin_exit() - EXIT rule
126 * The EXIT builtin rule echoes the targets to the user and exits
127 * the program with a failure status.
129 static LIST *builtin_exit (PARSE *parse, LOL *args, int *jmp) {
130 list_print(lol_get(args, 0));
131 printf("\n");
132 exit(EXITBAD); /* yeech */
133 #if __GNUC__ <= 2
134 return L0;
135 #endif
140 * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule
142 * Builtin_flags() marks the target with the appropriate flag, for use by make0().
143 * It binds each target as a TARGET.
145 static LIST *builtin_flags (PARSE *parse, LOL *args, int *jmp) {
146 LIST *l = lol_get(args, 0);
147 for (; l; l = list_next(l)) bindtarget(l->string)->flags |= parse->num;
148 return L0;
153 * builtin_flags_forcecare() - ForceCare rule
155 static LIST *builtin_flags_forcecare (PARSE *parse, LOL *args, int *jmp) {
156 LIST *l = lol_get(args, 0);
157 for( ; l; l = list_next(l)) {
158 TARGET *t = bindtarget(l->string);
159 t->flags |= T_FLAG_FORCECARE;
160 t->flags &= ~T_FLAG_NOCARE;
162 return L0;
167 * builtin_globbing() - GLOB rule
169 struct globbing {
170 LIST *patterns;
171 LIST *results;
175 static void builtin_glob_back (void *closure, const char *file, int status, time_t time) {
176 struct globbing *globbing = (struct globbing *)closure;
177 LIST *l;
178 PATHNAME f;
179 char buf[MAXJPATH];
180 /* null out directory for matching */
181 /* we wish we had file_dirscan() pass up a PATHNAME */
182 path_parse(file, &f);
183 f.f_dir.len = 0;
184 /* For globbing, we unconditionally ignore current and parent
185 * directory items. Since those items always exist, there's no
186 * reason why caller of GLOB would want to see them.
187 * We could also change file_dirscan, but then paths with embedded
188 * "." and ".." won't work anywhere. */
189 /* k8: will this break anything? it shouldn't... */
190 if (!strcmp(f.f_base.ptr, ".") || !strcmp(f.f_base.ptr, "..")) return;
191 path_build(&f, buf, 0);
192 for (l = globbing->patterns; l; l = l->next) {
193 if (!matchglob(l->string, buf)) {
194 globbing->results = list_new(globbing->results, file, 0);
195 break;
201 static LIST *builtin_glob (PARSE *parse, LOL *args, int *jmp) {
202 struct globbing globbing;
203 LIST *l = lol_get(args, 0);
204 LIST *r = lol_get(args, 1);
205 globbing.results = L0;
206 globbing.patterns = r;
207 for (; l; l = list_next(l)) file_dirscan(l->string, builtin_glob_back, &globbing);
208 return globbing.results;
213 * builtin_match() - MATCH rule, regexp matching
215 static LIST *builtin_match (PARSE *parse, LOL *args, int *jmp) {
216 LIST *l, *r;
217 LIST *res = 0;
218 /* for each pattern */
219 for (l = lol_get(args, 0); l; l = l->next) {
220 HSRegExp re;
221 HSRxMatch *mt;
222 int err;
224 if ((err = hsrxCompile(&re, l->string, HSRX_EXTENDED)) != 0) {
225 static char errbuf[512];
227 hsrxError(err, &re, errbuf, sizeof(errbuf));
228 hsrxFree(&re);
229 printf("FATAL: %s\n", errbuf);
230 exit(42);
232 mt = malloc(sizeof(HSRxMatch)*(re.re_nsub+1));
233 if (mt == NULL) { printf("FATAL: out of memory!\n"); exit(42); }
234 /* for each string to match against */
235 for (r = lol_get(args, 1); r; r = r->next) {
236 if (hsrxExec(&re, r->string, re.re_nsub+1, mt, 0) == HSRX_NOERROR) {
237 int i;
238 /* add all parameters up to highest onto list */
239 /* must have parameters to have results! */
240 for (i = 1; i <= re.re_nsub; ++i) {
241 char buf[MAXSYM];
242 //FIXME
243 int l = mt[i].rm_eo-mt[i].rm_so;
244 if (l > 0) memcpy(buf, r->string+mt[i].rm_so, l);
245 buf[l] = 0;
246 res = list_new(res, buf, 0);
250 free(mt);
251 hsrxFree(&re);
253 return res;
257 static LIST *builtin_hdrmacro (PARSE *parse, LOL *args, int *jmp) {
258 LIST *l = lol_get(args, 0);
259 for (; l; l = list_next(l)) {
260 TARGET *t = bindtarget(l->string);
261 /* scan file for header filename macro definitions */
262 if (DEBUG_HEADER) printf("scanning '%s' for header file macro definitions\n", l->string);
263 macro_headers(t);
265 return L0;
269 /* backported from boost-jam */
271 * Return the current working directory.
273 * Usage: pwd = [ PWD ] ;
275 static LIST *builtin_pwd (PARSE *parse, LOL *args, int *jmp) {
276 char pwd_buffer[PATH_MAX];
277 if (!getcwd(pwd_buffer, sizeof(pwd_buffer))) {
278 perror("can not get current directory");
279 return L0;
281 return list_new(L0, pwd_buffer, 0);
285 /* backported from boost-jam */
286 static LIST *builtin_sort (PARSE *parse, LOL *args, int *jmp) {
287 LIST *arg = lol_get(args, 0);
288 arg = list_sort(arg);
289 return arg;
293 /* backported from boost-jam; greatly improved */
294 /* Command shcmd [[ : options ]] */
295 static LIST *builtin_command (PARSE *parse, LOL *args, int *jmp) {
296 LIST *res = NULL;
297 LIST *l;
298 int ret;
299 char buffer[1024], buf1[32], *spos, *epos;
300 FILE *p = NULL;
301 int exitStatus = -1;
302 int optExitStatus = 0;
303 int optNoOutput = 0;
304 int optTrimLeft = 1;
305 int optTrimRight = 1;
306 int optStatus1st = 0;
307 int optParseOut = 0;
308 int optSpaceBreak = 1;
309 int optTabBreak = 1;
310 int optCRBreak = 1;
311 int optLFBreak = 1;
312 tKString str;
314 /* for each string in 2nd list: check for arg */
315 for (l = lol_get(args, 1); l != NULL; l = l->next) {
316 if (!strcmp("exit-status", l->string)) optExitStatus = 1;
317 else if (!strcmp("exit-code", l->string)) optExitStatus = 1;
318 else if (!strcmp("status-first", l->string)) optStatus1st = 1;
319 else if (!strcmp("code-first", l->string)) optStatus1st = 1;
320 else if (!strcmp("no-output", l->string)) optNoOutput = 1;
321 else if (!strcmp("no-trim", l->string)) optTrimLeft = optTrimRight = 0;
322 else if (!strcmp("no-trim-left", l->string)) optTrimLeft = 0;
323 else if (!strcmp("no-trim-right", l->string)) optTrimRight = 0;
324 else if (!strcmp("parse-output", l->string)) optParseOut = 1;
325 else if (!strcmp("no-space-break", l->string)) optSpaceBreak = 0;
326 else if (!strcmp("no-tab-break", l->string)) optTabBreak = 0;
327 else if (!strcmp("no-nl-break", l->string)) optLFBreak = 0;
328 else if (!strcmp("no-lf-break", l->string)) optLFBreak = 0;
329 else if (!strcmp("no-cr-break", l->string)) optCRBreak = 0;
330 else {
331 printf("jam: invalid option for COMMAND built-in: '%s'\n", l->string);
332 exit(EXITBAD); /* yeech */
335 /* build shell command */
336 kStringNew(&str);
337 /* for each arg */
338 for (l = lol_get(args, 0); l; l = l->next) {
339 if (kStringLen(&str)) kStringPushBack(&str, ' ');
340 kStringAppendCStr(&str, l->string);
342 /* no shell command? */
343 if (kStringLen(&str) < 1) { kStringFree(&str); return L0; }
345 fflush(NULL);
346 p = popen(kStringCStr(&str), "r");
347 if (!p) { kStringFree(&str); return L0; }
349 kStringClear(&str);
350 while ((ret = fread(buffer, sizeof(char), sizeof(buffer)-1, p)) > 0) {
351 if (!optNoOutput) {
352 buffer[ret] = 0;
353 kStringAppendCStr(&str, buffer);
356 exitStatus = pclose(p);
357 if (optExitStatus && optStatus1st) {
358 sprintf(buf1, "%d", exitStatus);
359 res = list_new(res, buf1, 0);
361 /* trim output if necessary */
362 if (!optNoOutput) {
363 if (!optParseOut) {
364 /* don't parse */
365 if (optTrimRight) {
366 // trim trailing blanks
367 int sl = kStringLen(&str);
368 spos = kStringCStr(&str);
369 while (sl > 0 && (unsigned char)spos[sl-1] <= ' ') --sl;
370 kStringTruncate(&str, sl);
372 spos = kStringCStr(&str);
373 if (optTrimLeft) {
374 // trim leading blanks
375 while (*spos && *((unsigned char *)spos) <= ' ') ++spos;
377 res = list_new(res, spos, 0);
378 } else {
379 tKString tmp;
380 /* parse output */
381 ret = 0; /* was anything added? list must have at least one element */
382 spos = kStringCStr(&str);
383 kStringNew(&tmp);
384 while (*spos) {
385 /* skip delimiters */
386 while (*spos) {
387 unsigned char ch = (unsigned char)(*spos);
388 if (ch == ' ') { if (!optSpaceBreak) break; }
389 else if (ch == '\t') { if (!optTabBreak) break; }
390 else if (ch == '\r') { if (!optCRBreak) break; }
391 else if (ch == '\n') { if (!optLFBreak) break; }
392 else if (ch > ' ') break;
393 ++spos;
395 if (!*spos) break;
396 epos = spos+1;
397 while (*epos) {
398 int ch = *epos;
399 if (ch == ' ') { if (optSpaceBreak) break; }
400 else if (ch == '\t') { if (optTabBreak) break; }
401 else if (ch == '\r') { if (optCRBreak) break; }
402 else if (ch == '\n') { if (optLFBreak) break; }
403 else if ((unsigned char)ch <= ' ') break;
404 ++epos;
406 kStringClear(&tmp);
407 kStringAppendRange(&tmp, spos, epos);
408 res = list_new(res, kStringCStr(&tmp), 0);
409 ret = 1;
410 spos = epos;
412 kStringFree(&tmp);
413 if (!ret) { buf1[0] = '\0'; res = list_new(res, buf1, 0); }
415 } else {
416 res = list_new(res, kStringCStr(&str), 0);
418 kStringFree(&str);
419 /* command exit result next */
420 if (optExitStatus && !optStatus1st) {
421 sprintf(buf1, "%d", exitStatus);
422 res = list_new(res, buf1, 0);
424 return res;
428 static LIST *builtin_expri1 (PARSE *parse, LOL *args, int *jmp) {
429 char buffer[100];
430 int op0, op1, res, comp = 0;
431 LIST *el = lol_get(args, 0);
433 if (!el || !el->next || !el->next->next) return L0;
434 op0 = atoi(el->string);
435 op1 = atoi(el->next->next->string);
436 res = 0;
437 switch (el->next->string[0]) {
438 case '+': res = op0+op1; break;
439 case '-': res = op0-op1; break;
440 case '*': res = op0*op1; break;
441 case '/': res = op0/op1; break;
442 case '%': res = op0%op1; break;
443 case '<':
444 comp = 1;
445 if (el->next->string[1] == '=') res = op0<=op1; else res = op0<op1;
446 break;
447 case '=': comp = 1; res = op0==op1; break;
448 case '!': comp = 1; res = op0!=op1; break;
449 case '>':
450 comp = 1;
451 if (el->next->string[1] == '=') res = op0>=op1; else res = op0>op1;
452 break;
453 default:
454 printf("jam: rule ExprI1: unknown operator: '%s'\n", el->next->string);
455 exit(EXITBAD);
457 if (comp) return res?list_new(L0, "tan", 0):L0;
458 sprintf(buffer, "%d", res);
459 return list_new(L0, buffer, 0);
463 /* Based on code from ftjam by David Turner */
464 static LIST *builtin_split (PARSE *parse, LOL *args, int *jmp) {
465 LIST *input = lol_get(args, 0);
466 LIST *tokens = lol_get(args, 1);
467 LIST *res = L0;
468 char token[256];
469 tKString str;
470 int explode = 0;
472 kStringNew(&str);
473 /* build token array */
474 if (tokens == NULL) {
475 memset(token, 1, sizeof(token));
476 explode = 1;
477 } else {
478 memset(token, 0, sizeof(token));
479 for (; tokens; tokens = tokens->next) {
480 const char *s = tokens->string;
481 for (; *s; ++s) token[(unsigned char)*s] = 1;
483 if (memchr(token, 1, sizeof(token)) == NULL) {
484 memset(token, 1, sizeof(token));
485 explode = 1;
488 token[0] = 0;
489 /* now parse the input and split it */
490 for (; input; input = input->next) {
491 const char *ptr = input->string;
492 const char *lastPtr = input->string;
493 while (*ptr) {
494 if (token[(unsigned char)*ptr]) {
495 size_t count = ptr-lastPtr+explode;
496 if (count > 0) {
497 kStringClear(&str);
498 kStringAppendRange(&str, lastPtr, ptr+explode);
499 res = list_new(res, kStringCStr(&str), 0);
501 lastPtr = ptr+1;
503 ++ptr;
505 if (ptr > lastPtr) res = list_new(res, lastPtr, 0);
507 kStringFree(&str);
508 return res;
513 * builtin_dependslist()
515 * The DependsList builtin rule returns list of dependencies for
516 * a given target.
518 static LIST *builtin_dependslist (PARSE *parse, LOL *args, int *jmp) {
519 LIST *res = L0;
520 LIST *parents;
522 for (parents = lol_get(args, 0); parents; parents = parents->next) {
523 TARGET *t = bindtarget(parents->string);
524 TARGETS *child;
526 for (child = t->depends; child; child = child->next) res = list_new(res, child->target->name, 1);
528 return res;
532 static LIST *builtin_normpath (PARSE *parse, LOL *args, int *jmp) {
533 LIST *el = lol_get(args, 0);
534 char *buf;
535 int bsz;
537 if (!el || !el->string) return L0;
538 bsz = strlen(el->string)*2+1024;
539 buf = malloc(bsz);
540 if (buf == NULL) return L0;
541 if (!normalize_path(el->string, buf, bsz)) { free(buf); return L0; }
542 el = list_new(NULL, buf, 0);
543 free(buf);
544 return el;
548 static LIST *builtin_listlength (PARSE *parse, LOL *args, int *jmp) {
549 char buffer[100];
550 LIST *el = lol_get(args, 0);
552 if (!el) return L0;
553 sprintf(buffer, "%d", list_length(el));
554 return list_new(L0, buffer, 0);
558 static LIST *builtin_haverule (PARSE *parse, LOL *args, int *jmp) {
559 LIST *el = lol_get(args, 0);
561 if (!el) return L0;
562 for (; el; el = el->next) {
563 //fprintf(stderr, "CHECK: [%s]: %d\n", el->string, haverule(el->string));
564 if (!haverule(el->string)) return L0;
566 return list_new(L0, "1", 0);
571 * compile_builtin() - define builtin rules
574 #define P0 ((PARSE *)0)
575 #define C0 ((char *)0)
578 static JAMFA_SENTINEL void bind_builtin (PARSE *pp, ...) {
579 va_list ap;
580 const char *name, *lastname = "BAD";
582 va_start(ap, pp);
583 while ((name = va_arg(ap, const char *))) {
584 int updown = name[0]==':'?1:(name[0]=='.'?-1:0);
586 if (updown) {
587 if (updown == 1 && !compat_old_builtin_names) continue;
588 if (!(*(++name))) {
589 char *s = alloca(strlen(lastname)+1), *p;
591 strcpy(s, lastname);
592 for (p = s; *p; ++p) {
593 if (updown > 0) {
594 if (*p >= 'a' && *p <= 'z') (*p) -= 32; // upcase
595 } else {
596 if (*p >= 'A' && *p <= 'Z') (*p) += 32; // locase
599 name = s;
601 //fprintf(stderr, "%2d: [%s]\n", updown, name);
602 } else {
603 lastname = name;
605 bindrule(name)->procedure = pp;
607 va_end(ap);
611 void load_builtins (void) {
612 bind_builtin(parse_make(builtin_depends, P0, P0, P0, C0, C0, 0),
613 "Depends",
614 ":", NULL);
615 bind_builtin(parse_make(builtin_depends, P0, P0, P0, C0, C0, 1),
616 "Includes",
617 ":", NULL);
618 bind_builtin(parse_make(builtin_dependslist, P0, P0, P0, C0, C0, 0),
619 "DependsList",
620 ":", NULL);
623 bind_builtin(parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TOUCHED),
624 "Always",
625 ":", NULL);
626 bind_builtin(parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_LEAVES),
627 "Leaves",
628 ":", NULL);
629 bind_builtin(parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOCARE),
630 "NoCare",
631 ":", NULL);
632 bind_builtin(parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOTFILE),
633 "NotFile", ":",
634 "NoTime",
635 ":", NULL);
636 bind_builtin(parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOUPDATE),
637 "NoUpdate",
638 ":", NULL);
639 bind_builtin(parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TEMP),
640 "Temporary",
641 ":", NULL);
642 bind_builtin(parse_make(builtin_flags_forcecare, P0, P0, P0, C0, C0, T_FLAG_FORCECARE),
643 "ForceCare",
644 ":", NULL);
646 bind_builtin(parse_make(builtin_echo, P0, P0, P0, C0, C0, 0),
647 "Echo", ".",
648 ":", NULL);
649 bind_builtin(parse_make(builtin_echon, P0, P0, P0, C0, C0, 0),
650 "Echo-n", ".",
651 ":", NULL);
652 bind_builtin(parse_make(builtin_oflush, P0, P0, P0, C0, C0, 0),
653 "O-Flush", "O-flush", ".",
654 ":", NULL);
656 bind_builtin(parse_make(builtin_exit, P0, P0, P0, C0, C0, 0),
657 "Exit", ".",
658 ":", NULL);
660 bind_builtin(parse_make(builtin_glob, P0, P0, P0, C0, C0, 0),
661 "Glob",
662 ":", NULL);
663 bind_builtin(parse_make(builtin_match, P0, P0, P0, C0, C0, 0),
664 "Match",
665 ":", NULL);
667 bind_builtin(parse_make(builtin_hdrmacro, P0, P0, P0, C0, C0, 0),
668 "HdrMacro",
669 ":", NULL);
671 bind_builtin(parse_make(builtin_pwd, P0, P0, P0, C0, C0, 0),
672 "Pwd",
673 ":", NULL);
675 bind_builtin(parse_make(builtin_sort, P0, P0, P0, C0, C0, 0),
676 "Sort",
677 ":", NULL);
679 bind_builtin(parse_make(builtin_command, P0, P0, P0, C0, C0, 0),
680 "Command",
681 ":", NULL);
683 bind_builtin(parse_make(builtin_expri1, P0, P0, P0, C0, C0, 0),
684 "ExprI1",
685 ":", NULL);
687 bind_builtin(parse_make(builtin_split, P0, P0, P0, C0, C0, 0),
688 "Split",
689 ":", NULL);
691 bind_builtin(parse_make(builtin_normpath, P0, P0, P0, C0, C0, 0),
692 "NormPath",
693 ":", NULL);
695 bind_builtin(parse_make(builtin_listlength, P0, P0, P0, C0, C0, 0),
696 "ListLength",
697 ":", NULL);
699 bind_builtin(parse_make(builtin_haverule, P0, P0, P0, C0, C0, 0),
700 "HaveRule",
701 ":", NULL);