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
51 #include "matchglob.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);
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. */
77 if (!t
->includes
) t
->includes
= copytarget(t
);
80 t
->depends
= targetlist(t
->depends
, sources
);
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));
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));
113 * builtin_oflush() - O-FLUSH rule
115 * The O-FLUSH builtin rule flushes current output stream.
118 static LIST
*builtin_oflush (PARSE
*parse
, LOL
*args
, int *jmp
) {
125 * builtin_exit() - EXIT rule
127 * The EXIT builtin rule echoes the targets to the user and exits
128 * the program with a failure status.
130 static LIST
*builtin_exit (PARSE
*parse
, LOL
*args
, int *jmp
) {
131 list_print(lol_get(args
, 0));
133 exit(EXITBAD
); /* yeech */
141 * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule
143 * Builtin_flags() marks the target with the appropriate flag, for use by make0().
144 * It binds each target as a TARGET.
146 static LIST
*builtin_flags (PARSE
*parse
, LOL
*args
, int *jmp
) {
147 LIST
*l
= lol_get(args
, 0);
148 int flag
= parse
->num
, andflag
= ~0;
151 case T_FLAG_NOCARE
: andflag
= ~T_FLAG_FORCECARE
; break;
152 case T_FLAG_FORCECARE
: andflag
= ~T_FLAG_NOCARE
; break;
153 case 666: flag
= 0; andflag
= ~T_FLAG_NOTFILE
; break;
156 for (; l
; l
= list_next(l
)) {
157 TARGET
*t
= bindtarget(l
->string
);
174 * builtin_globbing() - GLOB rule
180 int cmptype
; // <0:glob; 0: plain; >0:# of regexps
181 int mode
; // GLOB_xxx
187 static void builtin_glob_back (void *closure
, const char *file
, int status
, time_t time
) {
188 struct globbing
*globbing
= (struct globbing
*)closure
;
192 /* null out directory for matching */
193 /* we wish we had file_dirscan() pass up a PATHNAME */
194 path_parse(file
, &f
);
196 /* For globbing, we unconditionally ignore current and parent
197 * directory items. Since those items always exist, there's no
198 * reason why caller of GLOB would want to see them.
199 * We could also change file_dirscan, but then paths with embedded
200 * "." and ".." won't work anywhere. */
201 /* k8: will this break anything? it shouldn't... */
202 if (!strcmp(f
.f_base
.ptr
, ".") || !strcmp(f
.f_base
.ptr
, "..")) return;
203 path_build(&f
, buf
, 0);
206 fprintf(stderr, "buf: [%s]\n", buf);
208 for (c = 0; c < 6; ++c) fprintf(stderr, " %d: [%s]\n", c, f.part[c].ptr);
210 if (globbing
->mode
!= GLOB_ANY
) {
211 int ftype
= getFileType(file
);
213 switch (globbing
->mode
) {
214 case GLOB_DIRS
: if (ftype
!= 1) return; break;
215 case GLOB_FILES
: if (ftype
!= 0) return; break;
220 if (globbing
->cmptype
< 0) {
221 for (l
= globbing
->patterns
; l
; l
= l
->next
) {
222 if (matchglobex(l
->string
, buf
, globbing
->casesens
) == 0) {
223 globbing
->results
= list_new(globbing
->results
, globbing
->namesonly
?buf
:file
, 0);
227 } else if (globbing
->cmptype
> 0) {
230 for (f
= 0; f
< globbing
->cmptype
; ++f
) {
231 if (hsrxExec(&globbing
->re
[f
], buf
, 0, NULL
, 0) == HSRX_NOERROR
) {
232 globbing
->results
= list_new(globbing
->results
, globbing
->namesonly
?buf
:file
, 0);
237 for (l
= globbing
->patterns
; l
; l
= l
->next
) {
238 if ((globbing
->casesens
?strcmp
:strcasecmp
)(l
->string
, buf
) == 0) {
239 globbing
->results
= list_new(globbing
->results
, globbing
->namesonly
?buf
:file
, 0);
247 static LIST
*builtin_glob (PARSE
*parse
, LOL
*args
, int *jmp
) {
248 LIST
*l
= lol_get(args
, 0);
249 LIST
*r
= lol_get(args
, 1);
251 struct globbing globbing
;
254 globbing
.results
= L0
;
255 globbing
.patterns
= r
;
256 globbing
.casesens
= 1;
257 globbing
.cmptype
= -1;
258 globbing
.mode
= GLOB_ANY
;
259 globbing
.namesonly
= 0;
261 for (lo
= lol_get(args
, 2); lo
!= NULL
; lo
= lo
->next
) {
262 if (!strcmp("case-sensitive", lo
->string
)) globbing
.casesens
= 1;
263 else if (!strcmp("case-insensitive", lo
->string
)) globbing
.casesens
= 0;
264 else if (!strcmp("ignore-case", lo
->string
)) globbing
.casesens
= 0;
265 else if (!strcmp("glob", lo
->string
)) globbing
.cmptype
= -1;
266 else if (!strcmp("regexp", lo
->string
)) globbing
.cmptype
= 1;
267 else if (!strcmp("plain", lo
->string
)) globbing
.cmptype
= 0;
268 else if (!strcmp("dirs-only", lo
->string
)) globbing
.mode
= GLOB_DIRS
;
269 else if (!strcmp("files-only", lo
->string
)) globbing
.mode
= GLOB_FILES
;
270 else if (!strcmp("any", lo
->string
)) globbing
.mode
= GLOB_ANY
;
271 else if (!strcmp("names-only", lo
->string
)) globbing
.namesonly
= 1;
272 else if (!strcmp("full-path", lo
->string
)) globbing
.namesonly
= 0;
274 printf("jam: invalid option for Glob built-in: '%s'\n", lo
->string
);
275 exit(EXITBAD
); /* yeech */
279 if (globbing
.cmptype
> 0) {
280 /* compile regexps */
283 globbing
.cmptype
= list_length(r
);
284 globbing
.re
= malloc(sizeof(HSRegExp
)*globbing
.cmptype
);
285 if (globbing
.re
== NULL
) {
286 printf("FATAL: out of memory in Glob\n");
289 for (f
= 0; r
; r
= r
->next
, ++f
) {
292 if ((err
= hsrxCompile(&globbing
.re
[f
], r
->string
, HSRX_EXTENDED
| (globbing
.casesens
? 0 : HSRX_ICASE
))) != 0) {
293 static char errbuf
[512];
295 hsrxError(err
, &globbing
.re
[f
], errbuf
, sizeof(errbuf
));
296 hsrxFree(&globbing
.re
[f
]);
297 printf("FATAL: invalid regexp in Glob: %s\n", errbuf
);
305 for (; l
; l
= list_next(l
)) file_dirscan(l
->string
, builtin_glob_back
, &globbing
);
310 for (f
= 0; f
< globbing
.cmptype
; ++f
) hsrxFree(&globbing
.re
[f
]);
314 return globbing
.results
;
319 * builtin_match() - MATCH rule, regexp matching
321 static LIST
*builtin_match (PARSE
*parse
, LOL
*args
, int *jmp
) {
327 for (lo
= lol_get(args
, 2); lo
!= NULL
; lo
= lo
->next
) {
328 if (!strcmp("case-sensitive", lo
->string
)) casesens
= 1;
329 else if (!strcmp("case-insensitive", lo
->string
)) casesens
= 0;
330 else if (!strcmp("ignore-case", lo
->string
)) casesens
= 0;
331 else if (!strcmp("glob", lo
->string
)) cmptype
= -1;
332 else if (!strcmp("regexp", lo
->string
)) cmptype
= 1;
333 else if (!strcmp("plain", lo
->string
)) cmptype
= 0;
335 printf("jam: invalid option for Match built-in: '%s'\n", lo
->string
);
336 exit(EXITBAD
); /* yeech */
339 /* for each pattern */
340 for (l
= lol_get(args
, 0); l
; l
= l
->next
) {
348 if ((err
= hsrxCompile(&re
, l
->string
, HSRX_EXTENDED
)) != 0) {
349 static char errbuf
[512];
351 hsrxError(err
, &re
, errbuf
, sizeof(errbuf
));
353 printf("FATAL: %s\n", errbuf
);
356 mt
= malloc(sizeof(HSRxMatch
)*(re
.re_nsub
+1));
357 if (mt
== NULL
) { printf("FATAL: out of memory!\n"); exit(42); }
358 /* for each string to match against */
359 for (r
= lol_get(args
, 1); r
; r
= r
->next
) {
360 if (hsrxExec(&re
, r
->string
, re
.re_nsub
+1, mt
, 0) == HSRX_NOERROR
) {
362 /* add all parameters up to highest onto list */
363 /* must have parameters to have results! */
364 for (i
= 1; i
<= re
.re_nsub
; ++i
) {
367 int l
= mt
[i
].rm_eo
-mt
[i
].rm_so
;
368 if (l
> 0) memcpy(buf
, r
->string
+mt
[i
].rm_so
, l
);
370 res
= list_new(res
, buf
, 0);
376 } else if (cmptype
< 0) {
377 for (r
= lol_get(args
, 1); r
; r
= r
->next
) {
378 if (matchglobex(l
->string
, r
->string
, casesens
) == 0) {
379 res
= list_new(res
, r
->string
, 0);
383 for (r
= lol_get(args
, 1); r
; r
= r
->next
) {
384 if ((casesens
?strcmp
:strcasecmp
)(l
->string
, r
->string
) == 0) {
385 res
= list_new(res
, r
->string
, 0);
394 static LIST
*builtin_hdrmacro (PARSE
*parse
, LOL
*args
, int *jmp
) {
395 LIST
*l
= lol_get(args
, 0);
396 for (; l
; l
= list_next(l
)) {
397 TARGET
*t
= bindtarget(l
->string
);
398 /* scan file for header filename macro definitions */
399 if (DEBUG_HEADER
) printf("scanning '%s' for header file macro definitions\n", l
->string
);
406 /* backported from boost-jam */
408 * Return the current working directory.
410 * Usage: pwd = [ PWD ] ;
412 static LIST
*builtin_pwd (PARSE
*parse
, LOL
*args
, int *jmp
) {
413 char pwd_buffer
[PATH_MAX
];
414 if (!getcwd(pwd_buffer
, sizeof(pwd_buffer
))) {
415 perror("can not get current directory");
418 return list_new(L0
, pwd_buffer
, 0);
422 /* backported from boost-jam */
423 static LIST
*builtin_sort (PARSE
*parse
, LOL
*args
, int *jmp
) {
424 LIST
*arg
= lol_get(args
, 0);
425 arg
= list_sort(arg
);
430 /* backported from boost-jam; greatly improved */
431 /* Command shcmd [[ : options ]] */
432 static LIST
*builtin_command (PARSE
*parse
, LOL
*args
, int *jmp
) {
436 char buffer
[1024], buf1
[32], *spos
, *epos
;
439 int optExitStatus
= 0;
442 int optTrimRight
= 1;
443 int optStatus1st
= 0;
445 int optSpaceBreak
= 1;
451 /* for each string in 2nd list: check for arg */
452 for (l
= lol_get(args
, 1); l
!= NULL
; l
= l
->next
) {
453 if (!strcmp("exit-status", l
->string
)) optExitStatus
= 1;
454 else if (!strcmp("exit-code", l
->string
)) optExitStatus
= 1;
455 else if (!strcmp("status-first", l
->string
)) optStatus1st
= 1;
456 else if (!strcmp("code-first", l
->string
)) optStatus1st
= 1;
457 else if (!strcmp("no-output", l
->string
)) optNoOutput
= 1;
458 else if (!strcmp("no-trim", l
->string
)) optTrimLeft
= optTrimRight
= 0;
459 else if (!strcmp("no-trim-left", l
->string
)) optTrimLeft
= 0;
460 else if (!strcmp("no-trim-right", l
->string
)) optTrimRight
= 0;
461 else if (!strcmp("parse-output", l
->string
)) optParseOut
= 1;
462 else if (!strcmp("no-space-break", l
->string
)) optSpaceBreak
= 0;
463 else if (!strcmp("no-tab-break", l
->string
)) optTabBreak
= 0;
464 else if (!strcmp("no-nl-break", l
->string
)) optLFBreak
= 0;
465 else if (!strcmp("no-lf-break", l
->string
)) optLFBreak
= 0;
466 else if (!strcmp("no-cr-break", l
->string
)) optCRBreak
= 0;
468 printf("jam: invalid option for Command built-in: '%s'\n", l
->string
);
469 exit(EXITBAD
); /* yeech */
472 /* build shell command */
475 for (l
= lol_get(args
, 0); l
; l
= l
->next
) {
476 if (kStringLen(&str
)) kStringPushBack(&str
, ' ');
477 kStringAppendCStr(&str
, l
->string
);
479 /* no shell command? */
480 if (kStringLen(&str
) < 1) { kStringFree(&str
); return L0
; }
483 p
= popen(kStringCStr(&str
), "r");
484 if (!p
) { kStringFree(&str
); return L0
; }
487 while ((ret
= fread(buffer
, sizeof(char), sizeof(buffer
)-1, p
)) > 0) {
490 kStringAppendCStr(&str
, buffer
);
493 exitStatus
= pclose(p
);
494 if (optExitStatus
&& optStatus1st
) {
495 sprintf(buf1
, "%d", exitStatus
);
496 res
= list_new(res
, buf1
, 0);
498 /* trim output if necessary */
503 // trim trailing blanks
504 int sl
= kStringLen(&str
);
505 spos
= kStringCStr(&str
);
506 while (sl
> 0 && (unsigned char)spos
[sl
-1] <= ' ') --sl
;
507 kStringTruncate(&str
, sl
);
509 spos
= kStringCStr(&str
);
511 // trim leading blanks
512 while (*spos
&& *((unsigned char *)spos
) <= ' ') ++spos
;
514 res
= list_new(res
, spos
, 0);
518 ret
= 0; /* was anything added? list must have at least one element */
519 spos
= kStringCStr(&str
);
522 /* skip delimiters */
524 unsigned char ch
= (unsigned char)(*spos
);
525 if (ch
== ' ') { if (!optSpaceBreak
) break; }
526 else if (ch
== '\t') { if (!optTabBreak
) break; }
527 else if (ch
== '\r') { if (!optCRBreak
) break; }
528 else if (ch
== '\n') { if (!optLFBreak
) break; }
529 else if (ch
> ' ') break;
536 if (ch
== ' ') { if (optSpaceBreak
) break; }
537 else if (ch
== '\t') { if (optTabBreak
) break; }
538 else if (ch
== '\r') { if (optCRBreak
) break; }
539 else if (ch
== '\n') { if (optLFBreak
) break; }
540 else if ((unsigned char)ch
<= ' ') break;
544 kStringAppendRange(&tmp
, spos
, epos
);
545 res
= list_new(res
, kStringCStr(&tmp
), 0);
550 if (!ret
) { buf1
[0] = '\0'; res
= list_new(res
, buf1
, 0); }
554 /* command exit result next */
555 if (optExitStatus
&& !optStatus1st
) {
556 sprintf(buf1
, "%d", exitStatus
);
557 res
= list_new(res
, buf1
, 0);
563 static LIST
*builtin_expri1 (PARSE
*parse
, LOL
*args
, int *jmp
) {
565 int op0
, op1
, res
, comp
= 0;
566 LIST
*el
= lol_get(args
, 0);
568 if (!el
|| !el
->next
|| !el
->next
->next
) return L0
;
569 op0
= atoi(el
->string
);
570 op1
= atoi(el
->next
->next
->string
);
572 switch (el
->next
->string
[0]) {
573 case '+': res
= op0
+op1
; break;
574 case '-': res
= op0
-op1
; break;
575 case '*': res
= op0
*op1
; break;
576 case '/': res
= op0
/op1
; break;
577 case '%': res
= op0
%op1
; break;
580 if (el
->next
->string
[1] == '=') res
= op0
<=op1
; else res
= op0
<op1
;
582 case '=': comp
= 1; res
= op0
==op1
; break;
583 case '!': comp
= 1; res
= op0
!=op1
; break;
586 if (el
->next
->string
[1] == '=') res
= op0
>=op1
; else res
= op0
>op1
;
589 printf("jam: rule ExprI1: unknown operator: '%s'\n", el
->next
->string
);
592 if (comp
) return res
?list_new(L0
, "tan", 0):L0
;
593 sprintf(buffer
, "%d", res
);
594 return list_new(L0
, buffer
, 0);
598 /* Based on code from ftjam by David Turner */
599 static LIST
*builtin_split (PARSE
*parse
, LOL
*args
, int *jmp
) {
600 LIST
*input
= lol_get(args
, 0);
601 LIST
*tokens
= lol_get(args
, 1);
608 /* build token array */
609 if (tokens
== NULL
) {
610 memset(token
, 1, sizeof(token
));
613 memset(token
, 0, sizeof(token
));
614 for (; tokens
; tokens
= tokens
->next
) {
615 const char *s
= tokens
->string
;
616 for (; *s
; ++s
) token
[(unsigned char)*s
] = 1;
618 if (memchr(token
, 1, sizeof(token
)) == NULL
) {
619 memset(token
, 1, sizeof(token
));
624 /* now parse the input and split it */
625 for (; input
; input
= input
->next
) {
626 const char *ptr
= input
->string
;
627 const char *lastPtr
= input
->string
;
629 if (token
[(unsigned char)*ptr
]) {
630 size_t count
= ptr
-lastPtr
+explode
;
633 kStringAppendRange(&str
, lastPtr
, ptr
+explode
);
634 res
= list_new(res
, kStringCStr(&str
), 0);
640 if (ptr
> lastPtr
) res
= list_new(res
, lastPtr
, 0);
648 * builtin_dependslist()
650 * The DependsList builtin rule returns list of dependencies for
653 static LIST
*builtin_dependslist (PARSE
*parse
, LOL
*args
, int *jmp
) {
657 for (parents
= lol_get(args
, 0); parents
; parents
= parents
->next
) {
658 TARGET
*t
= bindtarget(parents
->string
);
661 for (child
= t
->depends
; child
; child
= child
->next
) res
= list_new(res
, child
->target
->name
, 1);
667 static LIST
*builtin_normpath (PARSE
*parse
, LOL
*args
, int *jmp
) {
668 LIST
*el
= lol_get(args
, 0);
672 if (!el
|| !el
->string
) return L0
;
673 bsz
= strlen(el
->string
)*2+1024;
675 if (buf
== NULL
) return L0
;
676 if (!normalize_path(el
->string
, buf
, bsz
)) { free(buf
); return L0
; }
677 el
= list_new(NULL
, buf
, 0);
683 static LIST
*builtin_listlength (PARSE
*parse
, LOL
*args
, int *jmp
) {
685 LIST
*el
= lol_get(args
, 0);
688 sprintf(buffer
, "%d", list_length(el
));
689 return list_new(L0
, buffer
, 0);
706 static int hr_normal (const void *hdata
, void *udata
) {
707 const RULE
*r
= (const RULE
*)hdata
;
708 const HRNormalData
*d
= (const HRNormalData
*)udata
;
710 if (strcasecmp(r
->name
, d
->str
) == 0) {
711 if (d
->wantAction
&& r
->actions
) return 1; // got it
712 if (!d
->wantAction
&& r
->procedure
) return 1; // got it
717 static int hr_glob (const void *hdata
, void *udata
) {
718 const RULE
*r
= (const RULE
*)hdata
;
719 const HRNormalData
*d
= (const HRNormalData
*)udata
;
721 if (matchglobex(d
->str
, r
->name
, d
->casesens
) == 0) {
722 //fprintf(stderr, ":[%s]\n", r->name);
723 if (d
->wantAction
&& r
->actions
) return 1; // got it
724 if (!d
->wantAction
&& r
->procedure
) return 1; // got it
735 static int hr_regexp (const void *hdata
, void *udata
) {
736 const RULE
*r
= (const RULE
*)hdata
;
737 HRREData
*d
= (HRREData
*)udata
;
739 if (hsrxExec(&d
->re
, r
->name
, 0, NULL
, 0) == HSRX_NOERROR
) {
740 //fprintf(stderr, ":[%s]\n", r->name);
741 if (d
->wantAction
&& r
->actions
) return 1; // got it
742 if (!d
->wantAction
&& r
->procedure
) return 1; // got it
748 static LIST
*builtin_haveruleactions (PARSE
*parse
, LOL
*args
, int *jmp
) {
749 LIST
*el
= lol_get(args
, 0), *l
;
750 int wantAction
= parse
->num
;
752 int cmptype
= 0; // <0:glob; >0:regexp
756 for (l
= lol_get(args
, 1); l
!= NULL
; l
= l
->next
) {
757 if (!strcmp("case-sensitive", l
->string
)) casesens
= 1;
758 else if (!strcmp("case-insensitive", l
->string
)) casesens
= 0;
759 else if (!strcmp("ignore-case", l
->string
)) casesens
= 0;
760 else if (!strcmp("glob", l
->string
)) cmptype
= -1;
761 else if (!strcmp("regexp", l
->string
)) cmptype
= 1;
762 else if (!strcmp("plain", l
->string
)) cmptype
= 0;
764 printf("jam: invalid option for Have%s built-in: '%s'\n", wantAction
?"Actions":"Rule", l
->string
);
765 exit(EXITBAD
); /* yeech */
769 if (casesens
== 1 && cmptype
== 0) {
771 for (; el
; el
= el
->next
) {
772 RULE
*r
= findrule(el
->string
);
775 if (wantAction
&& !r
->actions
) return L0
;
776 if (!wantAction
&& !r
->procedure
) return L0
;
778 } else if (cmptype
< 0) {
782 nfo
.wantAction
= wantAction
;
783 nfo
.casesens
= casesens
;
785 for (; el
; el
= el
->next
) {
787 nfo
.str
= el
->string
;
788 if (!iteraterules(hr_glob
, &nfo
)) return L0
;
790 } else if (cmptype
> 0) {
794 nfo
.wantAction
= wantAction
;
796 for (; el
; el
= el
->next
) {
799 if ((err
= hsrxCompile(&nfo
.re
, el
->string
, HSRX_EXTENDED
| (casesens
? 0 : HSRX_ICASE
))) != 0) {
800 static char errbuf
[512];
802 hsrxError(err
, &nfo
.re
, errbuf
, sizeof(errbuf
));
804 printf("FATAL: invalid regexp in Have%s: %s\n", wantAction
?"Actions":"Rule", errbuf
);
807 err
= iteraterules(hr_regexp
, &nfo
);
812 // normal, case-insensitive
815 nfo
.wantAction
= wantAction
;
817 for (; el
; el
= el
->next
) {
818 nfo
.str
= el
->string
;
819 if (!iteraterules(hr_normal
, &nfo
)) return L0
;
822 return list_new(L0
, "1", 0);
827 * compile_builtin() - define builtin rules
830 #define P0 ((PARSE *)0)
831 #define C0 ((char *)0)
834 /* ":" -- previous name in upper case; "." -- previous name in lower case */
835 static JAMFA_SENTINEL
void bind_builtin (PARSE
*pp
, ...) {
837 const char *name
, *lastname
= "BAD";
840 while ((name
= va_arg(ap
, const char *))) {
841 int updown
= name
[0]==':'?1:(name
[0]=='.'?-1:0);
844 if (updown
== 1 && !compat_old_builtin_names
) continue;
846 char *s
= alloca(strlen(lastname
)+1), *p
;
849 for (p
= s
; *p
; ++p
) {
851 if (*p
>= 'a' && *p
<= 'z') (*p
) -= 32; // upcase
853 if (*p
>= 'A' && *p
<= 'Z') (*p
) += 32; // locase
858 //fprintf(stderr, "%2d: [%s]\n", updown, name);
862 bindrule(name
)->procedure
= pp
;
868 void load_builtins (void) {
869 bind_builtin(parse_make(builtin_depends
, P0
, P0
, P0
, C0
, C0
, 0),
872 bind_builtin(parse_make(builtin_depends
, P0
, P0
, P0
, C0
, C0
, 1),
875 bind_builtin(parse_make(builtin_dependslist
, P0
, P0
, P0
, C0
, C0
, 0),
879 bind_builtin(parse_make(builtin_flags
, P0
, P0
, P0
, C0
, C0
, T_FLAG_TOUCHED
),
882 bind_builtin(parse_make(builtin_flags
, P0
, P0
, P0
, C0
, C0
, T_FLAG_LEAVES
),
885 bind_builtin(parse_make(builtin_flags
, P0
, P0
, P0
, C0
, C0
, T_FLAG_NOCARE
),
888 bind_builtin(parse_make(builtin_flags
, P0
, P0
, P0
, C0
, C0
, T_FLAG_NOTFILE
),
892 bind_builtin(parse_make(builtin_flags
, P0
, P0
, P0
, C0
, C0
, T_FLAG_NOUPDATE
),
895 bind_builtin(parse_make(builtin_flags
, P0
, P0
, P0
, C0
, C0
, T_FLAG_TEMP
),
898 bind_builtin(parse_make(builtin_flags
, P0
, P0
, P0
, C0
, C0
, T_FLAG_FORCECARE
),
901 bind_builtin(parse_make(builtin_flags
, P0
, P0
, P0
, C0
, C0
, 666),
905 bind_builtin(parse_make(builtin_echo
, P0
, P0
, P0
, C0
, C0
, 0),
908 bind_builtin(parse_make(builtin_echon
, P0
, P0
, P0
, C0
, C0
, 0),
911 bind_builtin(parse_make(builtin_oflush
, P0
, P0
, P0
, C0
, C0
, 0),
912 "O-Flush", "O-flush", ".",
915 bind_builtin(parse_make(builtin_exit
, P0
, P0
, P0
, C0
, C0
, 0),
919 bind_builtin(parse_make(builtin_glob
, P0
, P0
, P0
, C0
, C0
, 0),
922 bind_builtin(parse_make(builtin_match
, P0
, P0
, P0
, C0
, C0
, 0),
926 bind_builtin(parse_make(builtin_hdrmacro
, P0
, P0
, P0
, C0
, C0
, 0),
930 bind_builtin(parse_make(builtin_pwd
, P0
, P0
, P0
, C0
, C0
, 0),
934 bind_builtin(parse_make(builtin_sort
, P0
, P0
, P0
, C0
, C0
, 0),
938 bind_builtin(parse_make(builtin_command
, P0
, P0
, P0
, C0
, C0
, 0),
942 bind_builtin(parse_make(builtin_expri1
, P0
, P0
, P0
, C0
, C0
, 0),
946 bind_builtin(parse_make(builtin_split
, P0
, P0
, P0
, C0
, C0
, 0),
950 bind_builtin(parse_make(builtin_normpath
, P0
, P0
, P0
, C0
, C0
, 0),
954 bind_builtin(parse_make(builtin_listlength
, P0
, P0
, P0
, C0
, C0
, 0),
958 bind_builtin(parse_make(builtin_haveruleactions
, P0
, P0
, P0
, C0
, C0
, 0),
961 bind_builtin(parse_make(builtin_haveruleactions
, P0
, P0
, P0
, C0
, C0
, 1),