fixed 'install' target
[k8jam.git] / src / builtins.c
blob007ab62fd9f26f074abad756fcfe335627f6767b
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 fflush(stdout);
108 return L0;
113 * builtin_oflush() - O-FLUSH rule
115 * The O-FLUSH builtin rule flushes current output stream.
116 * Used with ECHO-N.
118 static LIST *builtin_oflush (PARSE *parse, LOL *args, int *jmp) {
119 fflush(stdout);
120 return L0;
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));
132 printf("\n");
133 exit(EXITBAD); /* yeech */
134 #if __GNUC__ <= 2
135 return L0;
136 #endif
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;
150 switch (flag) {
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);
159 t->flags |= flag;
160 t->flags &= andflag;
162 return L0;
166 enum {
167 GLOB_ANY,
168 GLOB_DIRS,
169 GLOB_FILES
174 * builtin_globbing() - GLOB rule
176 struct globbing {
177 LIST *patterns;
178 LIST *results;
179 int casesens;
180 int cmptype; // <0:glob; 0: plain; >0:# of regexps
181 int mode; // GLOB_xxx
182 int namesonly;
183 HSRegExp *re;
187 static void builtin_glob_back (void *closure, const char *file, int status, time_t time) {
188 struct globbing *globbing = (struct globbing *)closure;
189 LIST *l;
190 PATHNAME f;
191 char buf[MAXJPATH];
192 /* null out directory for matching */
193 /* we wish we had file_dirscan() pass up a PATHNAME */
194 path_parse(file, &f);
195 f.f_dir.len = 0;
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);
207 int c;
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;
216 default: ;
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);
224 break;
227 } else if (globbing->cmptype > 0) {
228 int f;
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);
233 break;
236 } else {
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);
240 break;
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);
250 LIST *lo;
251 struct globbing globbing;
253 if (!r) return L0;
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;
273 else {
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 */
281 int f;
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");
287 exit(42);
289 for (f = 0; r; r = r->next, ++f) {
290 int err;
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);
298 exit(42);
301 } else {
302 globbing.re = NULL;
305 for (; l; l = list_next(l)) file_dirscan(l->string, builtin_glob_back, &globbing);
307 if (globbing.re) {
308 int f;
310 for (f = 0; f < globbing.cmptype; ++f) hsrxFree(&globbing.re[f]);
311 free(globbing.re);
314 return globbing.results;
319 * builtin_match() - MATCH rule, regexp matching
321 static LIST *builtin_match (PARSE *parse, LOL *args, int *jmp) {
322 LIST *l, *lo;
323 LIST *res = 0;
324 int casesens = 1;
325 int cmptype = 1;
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;
334 else {
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) {
341 LIST *r;
343 if (cmptype > 0) {
344 HSRegExp re;
345 HSRxMatch *mt;
346 int err;
348 if ((err = hsrxCompile(&re, l->string, HSRX_EXTENDED)) != 0) {
349 static char errbuf[512];
351 hsrxError(err, &re, errbuf, sizeof(errbuf));
352 hsrxFree(&re);
353 printf("FATAL: %s\n", errbuf);
354 exit(42);
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) {
361 int i;
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) {
365 char buf[MAXSYM];
366 //FIXME
367 int l = mt[i].rm_eo-mt[i].rm_so;
368 if (l > 0) memcpy(buf, r->string+mt[i].rm_so, l);
369 buf[l] = 0;
370 res = list_new(res, buf, 0);
374 free(mt);
375 hsrxFree(&re);
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);
382 } else {
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);
390 return res;
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);
400 macro_headers(t);
402 return L0;
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");
416 return L0;
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);
426 return arg;
430 /* backported from boost-jam; greatly improved */
431 /* Command shcmd [[ : options ]] */
432 static LIST *builtin_command (PARSE *parse, LOL *args, int *jmp) {
433 LIST *res = NULL;
434 LIST *l;
435 int ret;
436 char buffer[1024], buf1[32], *spos, *epos;
437 FILE *p = NULL;
438 int exitStatus = -1;
439 int optExitStatus = 0;
440 int optNoOutput = 0;
441 int optTrimLeft = 1;
442 int optTrimRight = 1;
443 int optStatus1st = 0;
444 int optParseOut = 0;
445 int optSpaceBreak = 1;
446 int optTabBreak = 1;
447 int optCRBreak = 1;
448 int optLFBreak = 1;
449 tKString str;
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;
467 else {
468 printf("jam: invalid option for Command built-in: '%s'\n", l->string);
469 exit(EXITBAD); /* yeech */
472 /* build shell command */
473 kStringNew(&str);
474 /* for each arg */
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; }
482 fflush(NULL);
483 p = popen(kStringCStr(&str), "r");
484 if (!p) { kStringFree(&str); return L0; }
486 kStringClear(&str);
487 while ((ret = fread(buffer, sizeof(char), sizeof(buffer)-1, p)) > 0) {
488 if (!optNoOutput) {
489 buffer[ret] = 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 */
499 if (!optNoOutput) {
500 if (!optParseOut) {
501 /* don't parse */
502 if (optTrimRight) {
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);
510 if (optTrimLeft) {
511 // trim leading blanks
512 while (*spos && *((unsigned char *)spos) <= ' ') ++spos;
514 res = list_new(res, spos, 0);
515 } else {
516 tKString tmp;
517 /* parse output */
518 ret = 0; /* was anything added? list must have at least one element */
519 spos = kStringCStr(&str);
520 kStringNew(&tmp);
521 while (*spos) {
522 /* skip delimiters */
523 while (*spos) {
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;
530 ++spos;
532 if (!*spos) break;
533 epos = spos+1;
534 while (*epos) {
535 int ch = *epos;
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;
541 ++epos;
543 kStringClear(&tmp);
544 kStringAppendRange(&tmp, spos, epos);
545 res = list_new(res, kStringCStr(&tmp), 0);
546 ret = 1;
547 spos = epos;
549 kStringFree(&tmp);
550 if (!ret) { buf1[0] = '\0'; res = list_new(res, buf1, 0); }
553 kStringFree(&str);
554 /* command exit result next */
555 if (optExitStatus && !optStatus1st) {
556 sprintf(buf1, "%d", exitStatus);
557 res = list_new(res, buf1, 0);
559 return res;
563 static LIST *builtin_expri1 (PARSE *parse, LOL *args, int *jmp) {
564 char buffer[100];
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);
571 res = 0;
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;
578 case '<':
579 comp = 1;
580 if (el->next->string[1] == '=') res = op0<=op1; else res = op0<op1;
581 break;
582 case '=': comp = 1; res = op0==op1; break;
583 case '!': comp = 1; res = op0!=op1; break;
584 case '>':
585 comp = 1;
586 if (el->next->string[1] == '=') res = op0>=op1; else res = op0>op1;
587 break;
588 default:
589 printf("jam: rule ExprI1: unknown operator: '%s'\n", el->next->string);
590 exit(EXITBAD);
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);
602 LIST *res = L0;
603 char token[256];
604 tKString str;
605 int explode = 0;
607 kStringNew(&str);
608 /* build token array */
609 if (tokens == NULL) {
610 memset(token, 1, sizeof(token));
611 explode = 1;
612 } else {
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));
620 explode = 1;
623 token[0] = 0;
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;
628 while (*ptr) {
629 if (token[(unsigned char)*ptr]) {
630 size_t count = ptr-lastPtr+explode;
631 if (count > 0) {
632 kStringClear(&str);
633 kStringAppendRange(&str, lastPtr, ptr+explode);
634 res = list_new(res, kStringCStr(&str), 0);
636 lastPtr = ptr+1;
638 ++ptr;
640 if (ptr > lastPtr) res = list_new(res, lastPtr, 0);
642 kStringFree(&str);
643 return res;
648 * builtin_dependslist()
650 * The DependsList builtin rule returns list of dependencies for
651 * a given target.
653 static LIST *builtin_dependslist (PARSE *parse, LOL *args, int *jmp) {
654 LIST *res = L0;
655 LIST *parents;
657 for (parents = lol_get(args, 0); parents; parents = parents->next) {
658 TARGET *t = bindtarget(parents->string);
659 TARGETS *child;
661 for (child = t->depends; child; child = child->next) res = list_new(res, child->target->name, 1);
663 return res;
667 static LIST *builtin_normpath (PARSE *parse, LOL *args, int *jmp) {
668 LIST *el = lol_get(args, 0);
669 char *buf;
670 int bsz;
672 if (!el || !el->string) return L0;
673 bsz = strlen(el->string)*2+1024;
674 buf = malloc(bsz);
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);
678 free(buf);
679 return el;
683 static LIST *builtin_listlength (PARSE *parse, LOL *args, int *jmp) {
684 char buffer[100];
685 LIST *el = lol_get(args, 0);
687 if (!el) return L0;
688 sprintf(buffer, "%d", list_length(el));
689 return list_new(L0, buffer, 0);
693 typedef struct {
694 LIST *el;
695 int wantAction;
696 int casesens;
697 } HRNGCI;
700 typedef struct {
701 const char *str;
702 int wantAction;
703 int casesens;
704 } HRNormalData;
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
714 return 0;
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
726 return 0;
730 typedef struct {
731 HSRegExp re;
732 int wantAction;
733 } HRREData;
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
744 return 0;
748 static LIST *builtin_haveruleactions (PARSE *parse, LOL *args, int *jmp) {
749 LIST *el = lol_get(args, 0), *l;
750 int wantAction = parse->num;
751 int casesens = 1;
752 int cmptype = 0; // <0:glob; >0:regexp
754 if (!el) return L0;
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;
763 else {
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) {
770 // standard mode
771 for (; el; el = el->next) {
772 RULE *r = findrule(el->string);
774 if (!r) return L0;
775 if (wantAction && !r->actions) return L0;
776 if (!wantAction && !r->procedure) return L0;
778 } else if (cmptype < 0) {
779 // glob
780 HRNormalData nfo;
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) {
791 // regexp
792 HRREData nfo;
794 nfo.wantAction = wantAction;
796 for (; el; el = el->next) {
797 int err;
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));
803 hsrxFree(&nfo.re);
804 printf("FATAL: invalid regexp in Have%s: %s\n", wantAction?"Actions":"Rule", errbuf);
805 exit(42);
807 err = iteraterules(hr_regexp, &nfo);
808 hsrxFree(&nfo.re);
809 if (!err) return L0;
811 } else {
812 // normal, case-insensitive
813 HRNormalData nfo;
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, ...) {
836 va_list ap;
837 const char *name, *lastname = "BAD";
839 va_start(ap, pp);
840 while ((name = va_arg(ap, const char *))) {
841 int updown = name[0]==':'?1:(name[0]=='.'?-1:0);
843 if (updown) {
844 if (updown == 1 && !compat_old_builtin_names) continue;
845 if (!(*(++name))) {
846 char *s = alloca(strlen(lastname)+1), *p;
848 strcpy(s, lastname);
849 for (p = s; *p; ++p) {
850 if (updown > 0) {
851 if (*p >= 'a' && *p <= 'z') (*p) -= 32; // upcase
852 } else {
853 if (*p >= 'A' && *p <= 'Z') (*p) += 32; // locase
856 name = s;
858 //fprintf(stderr, "%2d: [%s]\n", updown, name);
859 } else {
860 lastname = name;
862 bindrule(name)->procedure = pp;
864 va_end(ap);
868 void load_builtins (void) {
869 bind_builtin(parse_make(builtin_depends, P0, P0, P0, C0, C0, 0),
870 "Depends",
871 ":", NULL);
872 bind_builtin(parse_make(builtin_depends, P0, P0, P0, C0, C0, 1),
873 "Includes",
874 ":", NULL);
875 bind_builtin(parse_make(builtin_dependslist, P0, P0, P0, C0, C0, 0),
876 "DependsList",
877 ":", NULL);
879 bind_builtin(parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TOUCHED),
880 "Always",
881 ":", NULL);
882 bind_builtin(parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_LEAVES),
883 "Leaves",
884 ":", NULL);
885 bind_builtin(parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOCARE),
886 "NoCare",
887 ":", NULL);
888 bind_builtin(parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOTFILE),
889 "NotFile", ":",
890 "NoTime",
891 ":", NULL);
892 bind_builtin(parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOUPDATE),
893 "NoUpdate",
894 ":", NULL);
895 bind_builtin(parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TEMP),
896 "Temporary",
897 ":", NULL);
898 bind_builtin(parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_FORCECARE),
899 "ForceCare",
900 ":", NULL);
901 bind_builtin(parse_make(builtin_flags, P0, P0, P0, C0, C0, 666),
902 "ForceFile", ":",
903 ":", NULL);
905 bind_builtin(parse_make(builtin_echo, P0, P0, P0, C0, C0, 0),
906 "Echo", ".",
907 ":", NULL);
908 bind_builtin(parse_make(builtin_echon, P0, P0, P0, C0, C0, 0),
909 "Echo-n", ".",
910 ":", NULL);
911 bind_builtin(parse_make(builtin_oflush, P0, P0, P0, C0, C0, 0),
912 "O-Flush", "O-flush", ".",
913 ":", NULL);
915 bind_builtin(parse_make(builtin_exit, P0, P0, P0, C0, C0, 0),
916 "Exit", ".",
917 ":", NULL);
919 bind_builtin(parse_make(builtin_glob, P0, P0, P0, C0, C0, 0),
920 "Glob",
921 ":", NULL);
922 bind_builtin(parse_make(builtin_match, P0, P0, P0, C0, C0, 0),
923 "Match",
924 ":", NULL);
926 bind_builtin(parse_make(builtin_hdrmacro, P0, P0, P0, C0, C0, 0),
927 "HdrMacro",
928 ":", NULL);
930 bind_builtin(parse_make(builtin_pwd, P0, P0, P0, C0, C0, 0),
931 "Pwd",
932 ":", NULL);
934 bind_builtin(parse_make(builtin_sort, P0, P0, P0, C0, C0, 0),
935 "Sort",
936 ":", NULL);
938 bind_builtin(parse_make(builtin_command, P0, P0, P0, C0, C0, 0),
939 "Command",
940 ":", NULL);
942 bind_builtin(parse_make(builtin_expri1, P0, P0, P0, C0, C0, 0),
943 "ExprI1",
944 ":", NULL);
946 bind_builtin(parse_make(builtin_split, P0, P0, P0, C0, C0, 0),
947 "Split",
948 ":", NULL);
950 bind_builtin(parse_make(builtin_normpath, P0, P0, P0, C0, C0, 0),
951 "NormPath",
952 ":", NULL);
954 bind_builtin(parse_make(builtin_listlength, P0, P0, P0, C0, C0, 0),
955 "ListLength",
956 ":", NULL);
958 bind_builtin(parse_make(builtin_haveruleactions, P0, P0, P0, C0, C0, 0),
959 "HaveRule",
960 ":", NULL);
961 bind_builtin(parse_make(builtin_haveruleactions, P0, P0, P0, C0, C0, 1),
962 "HaveActions",
963 ":", NULL);