Command built-in can parse output into list (no need to Match it now)
[k8jam.git] / builtins.c
blob5f7d623f467bfebe2248c51dd67f294667a60fbe
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_exit() - EXIT rule
19 * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule
20 * builtin_glob() - GLOB rule
21 * builtin_match() - MATCH rule
22 * builtin_hdrmacro() - HDRMACRO rule
24 * 01/10/01 (seiwald) - split from compile.c
25 * 01/08/01 (seiwald) - new 'Glob' (file expansion) builtin
26 * 03/02/02 (seiwald) - new 'Match' (regexp match) builtin
27 * 04/03/02 (seiwald) - Glob matches only filename, not directory
28 * 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
29 * 10/22/02 (seiwald) - working return/break/continue statements
30 * 11/04/02 (seiwald) - const-ing for string literals
31 * 12/03/02 (seiwald) - fix odd includes support by grafting them onto depends
32 * 01/14/03 (seiwald) - fix includes fix with new internal includes TARGET
35 #include <unistd.h>
36 #include <limits.h>
38 #include "jam.h"
40 #include "lists.h"
41 #include "parse.h"
42 #include "builtins.h"
43 #include "rules.h"
44 #include "filesys.h"
45 #include "newstr.h"
46 #include "regexp.h"
47 #include "pathsys.h"
48 #include "hdrmacro.h"
49 #include "strings.h"
53 * compile_builtin() - define builtin rules
56 #define P0 (PARSE *)0
57 #define C0 (char *)0
60 int glob (const char *s, const char *c);
63 void load_builtins (void) {
64 bindrule("Always")->procedure =
65 bindrule("ALWAYS")->procedure =
66 parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TOUCHED);
68 bindrule("Depends")->procedure =
69 bindrule("DEPENDS")->procedure =
70 parse_make(builtin_depends, P0, P0, P0, C0, C0, 0);
72 bindrule("echo")->procedure =
73 bindrule("Echo")->procedure =
74 bindrule("ECHO")->procedure =
75 parse_make(builtin_echo, P0, P0, P0, C0, C0, 0);
77 bindrule("exit")->procedure =
78 bindrule("Exit")->procedure =
79 bindrule("EXIT")->procedure =
80 parse_make(builtin_exit, P0, P0, P0, C0, C0, 0);
82 bindrule("Glob")->procedure =
83 bindrule("GLOB")->procedure =
84 parse_make(builtin_glob, P0, P0, P0, C0, C0, 0);
86 bindrule("Includes")->procedure =
87 bindrule("INCLUDES")->procedure =
88 parse_make(builtin_depends, P0, P0, P0, C0, C0, 1);
90 bindrule("Leaves")->procedure =
91 bindrule("LEAVES")->procedure =
92 parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_LEAVES);
94 bindrule("Match")->procedure =
95 bindrule("MATCH")->procedure =
96 parse_make(builtin_match, P0, P0, P0, C0, C0, 0);
98 bindrule("NoCare")->procedure =
99 bindrule("NOCARE")->procedure =
100 parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOCARE);
102 bindrule("NOTIME")->procedure =
103 bindrule("NotFile")->procedure =
104 bindrule("NOTFILE")->procedure =
105 parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOTFILE);
107 bindrule("NoUpdate")->procedure =
108 bindrule("NOUPDATE")->procedure =
109 parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOUPDATE);
111 bindrule("Temporary")->procedure =
112 bindrule("TEMPORARY")->procedure =
113 parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TEMP);
115 bindrule("HdrMacro")->procedure =
116 bindrule("HDRMACRO")->procedure =
117 parse_make(builtin_hdrmacro, P0, P0, P0, C0, C0, 0);
119 bindrule("PWD")->procedure =
120 bindrule("Pwd")->procedure =
121 parse_make(builtin_pwd, P0, P0, P0, C0, C0, 0);
123 bindrule("SORT")->procedure =
124 bindrule("Sort")->procedure =
125 parse_make(builtin_sort, P0, P0, P0, C0, C0, 0);
127 bindrule("COMMAND")->procedure =
128 bindrule("Command")->procedure =
129 parse_make(builtin_command, P0, P0, P0, C0, C0, 0);
134 * builtin_depends() - DEPENDS/INCLUDES rule
136 * The DEPENDS builtin rule appends each of the listed sources on the
137 * dependency list of each of the listed targets. It binds both the
138 * targets and sources as TARGETs.
140 LIST *builtin_depends (PARSE *parse, LOL *args, int *jmp) {
141 LIST *targets = lol_get(args, 0);
142 LIST *sources = lol_get(args, 1);
143 /*k8: int which = parse->num;*/
144 LIST *l;
146 for (l = targets; l; l = list_next(l)) {
147 TARGET *t = bindtarget(l->string);
148 /* If doing INCLUDES, switch to the TARGET's include */
149 /* TARGET, creating it if needed. The internal include */
150 /* TARGET shares the name of its parent. */
151 if (parse->num) {
152 if (!t->includes) t->includes = copytarget(t);
153 t = t->includes;
155 t->depends = targetlist(t->depends, sources);
157 return L0;
162 * builtin_echo() - ECHO rule
164 * The ECHO builtin rule echoes the targets to the user. No other
165 * actions are taken.
167 LIST * builtin_echo (PARSE *parse, LOL *args, int *jmp) {
168 list_print(lol_get(args, 0));
169 printf("\n");
170 return L0;
175 * builtin_exit() - EXIT rule
177 * The EXIT builtin rule echoes the targets to the user and exits
178 * the program with a failure status.
180 LIST *builtin_exit (PARSE *parse, LOL *args, int *jmp) {
181 list_print(lol_get(args, 0));
182 printf("\n");
183 exit(EXITBAD); /* yeech */
184 return L0;
189 * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule
191 * Builtin_flags() marks the target with the appropriate flag, for use
192 * by make0(). It binds each target as a TARGET.
194 LIST *builtin_flags (PARSE *parse, LOL *args, int *jmp) {
195 LIST *l = lol_get(args, 0);
197 for (; l; l = list_next(l)) bindtarget(l->string)->flags |= parse->num;
198 return L0;
203 * builtin_globbing() - GLOB rule
205 struct globbing {
206 LIST *patterns;
207 LIST *results;
211 static void builtin_glob_back (void *closure, const char *file, int status, time_t time) {
212 struct globbing *globbing = (struct globbing *)closure;
213 LIST *l;
214 PATHNAME f;
215 char buf[MAXJPATH];
217 /* Null out directory for matching. */
218 /* We wish we had file_dirscan() pass up a PATHNAME. */
219 path_parse(file, &f);
220 f.f_dir.len = 0;
222 /* For globbing, we unconditionally ignore current and parent
223 directory items. Since those items always exist, there's no
224 reason why caller of GLOB would want to see them.
225 We could also change file_dirscan, but then paths with embedded
226 "." and ".." won't work anywhere.
228 /* k8: will this break anything? it shouldn't... */
229 if (!strcmp(f.f_base.ptr, ".") || !strcmp(f.f_base.ptr, "..")) return;
231 path_build(&f, buf, 0);
233 for (l = globbing->patterns; l; l = l->next) {
234 if (!glob(l->string, buf)) {
235 globbing->results = list_new(globbing->results, file, 0);
236 break;
242 LIST *builtin_glob (PARSE *parse, LOL *args, int *jmp) {
243 struct globbing globbing;
244 LIST *l = lol_get(args, 0);
245 LIST *r = lol_get(args, 1);
247 globbing.results = L0;
248 globbing.patterns = r;
250 for (; l; l = list_next(l)) file_dirscan(l->string, builtin_glob_back, &globbing);
252 return globbing.results;
257 * builtin_match() - MATCH rule, regexp matching
259 LIST *builtin_match (PARSE *parse, LOL *args, int *jmp) {
260 LIST *l, *r;
261 LIST *result = 0;
263 /* For each pattern */
264 for (l = lol_get(args, 0); l; l = l->next) {
265 regexp *re = regcomp(l->string);
266 /* For each string to match against */
267 for (r = lol_get(args, 1); r; r = r->next) {
268 if (regexec(re, r->string)) {
269 int i, top;
270 /* Find highest parameter */
271 for (top = NSUBEXP; top-- > 1;) if (re->startp[top]) break;
272 /* And add all parameters up to highest onto list. */
273 /* Must have parameters to have results! */
274 for (i = 1; i <= top; i++) {
275 char buf[MAXSYM];
276 int l = re->endp[i]-re->startp[i];
277 memcpy(buf, re->startp[i], l);
278 buf[l] = 0;
279 result = list_new(result, buf, 0);
283 free((char *)re);
286 return result;
290 LIST *builtin_hdrmacro (PARSE *parse, LOL *args, int *jmp) {
291 LIST *l = lol_get(args, 0);
293 for (; l; l = list_next(l)) {
294 TARGET *t = bindtarget(l->string);
295 /* scan file for header filename macro definitions */
296 if (DEBUG_HEADER) printf("scanning '%s' for header file macro definitions\n", l->string);
297 macro_headers(t);
300 return L0;
304 /* backported from boost-jam */
306 * Return the current working directory.
308 * Usage: pwd = [ PWD ] ;
310 LIST *builtin_pwd (PARSE *parse, LOL *args, int *jmp) {
311 char pwd_buffer[PATH_MAX];
313 if (!getcwd(pwd_buffer, sizeof(pwd_buffer))) {
314 perror("can not get current directory");
315 return L0;
317 return list_new(L0, pwd_buffer, 0);
322 /* backported from boost-jam */
323 LIST *builtin_sort (PARSE *parse, LOL *args, int *jmp) {
324 LIST *arg = lol_get(args, 0);
325 /*printf("unsorted: "); list_print(arg); printf("\n");*/
326 arg = list_sort(arg);
327 /*printf("sorted: "); list_print(arg); printf("\n");*/
328 return arg;
332 /* backported from boost-jam */
333 /* Command shcmd [[ : options ]] */
334 LIST *builtin_command (PARSE *parse, LOL *args, int *jmp) {
335 LIST *result = 0;
336 LIST *l;
337 int ret;
338 char buffer[1024], buf1[32], *spos, *epos;
339 FILE *p = NULL;
340 int exitStatus = -1;
341 int optExitStatus = 0;
342 int optNoOutput = 0;
343 int optTrimLeft = 1;
344 int optTrimRight = 1;
345 int optStatus1st = 0;
346 int optParseOut = 0;
347 int optSpaceBreak = 1;
348 int optTabBreak = 1;
349 int optCRBreak = 1;
350 int optLFBreak = 1;
351 TKString cmd, str;
354 /* build shell command */
355 KStringNew(&cmd);
356 /* for each arg */
357 for (l = lol_get(args, 0); l; l = l->next) {
358 if (str.size) KStringPushBack(&cmd, ' ');
359 KStringAppend(&cmd, l->string);
361 /* no shell command? */
362 if (!cmd.size) { KStringFree(&cmd); return L0; }
364 /* for each string in 2nd list: check for arg */
365 for (l = lol_get(args, 1); l; l = l->next) {
366 if (!strcmp("exit-status", l->string)) optExitStatus = 1;
367 else if (!strcmp("exit-code", l->string)) optExitStatus = 1;
368 else if (!strcmp("status-first", l->string)) optStatus1st = 1;
369 else if (!strcmp("code-first", l->string)) optStatus1st = 1;
370 else if (!strcmp("no-output", l->string)) optNoOutput = 1;
371 else if (!strcmp("no-trim", l->string)) optTrimLeft = optTrimRight = 0;
372 else if (!strcmp("no-trim-left", l->string)) optTrimLeft = 0;
373 else if (!strcmp("no-trim-right", l->string)) optTrimRight = 0;
374 else if (!strcmp("parse-output", l->string)) optParseOut = 1;
375 else if (!strcmp("no-space-break", l->string)) optSpaceBreak = 0;
376 else if (!strcmp("no-tab-break", l->string)) optTabBreak = 0;
377 else if (!strcmp("no-nl-break", l->string)) optCRBreak = 0;
378 else if (!strcmp("no-cr-break", l->string)) optCRBreak = 0;
379 else if (!strcmp("no-lf-break", l->string)) optLFBreak = 0;
380 /* FIXME: don't ignore invalid options */
383 fflush(NULL);
384 p = popen(cmd.value, "r");
385 KStringFree(&cmd);
386 if (!p) return L0;
388 KStringNew(&str);
389 while ((ret = fread(buffer, sizeof(char), sizeof(buffer)-1, p)) > 0) {
390 if (!optNoOutput) {
391 buffer[ret] = 0;
392 KStringAppend(&str, buffer);
395 exitStatus = pclose(p);
397 if (optExitStatus && optStatus1st) {
398 sprintf(buf1, "%d", exitStatus);
399 result = list_new(result, buf1, 0);
402 /* trim output if necessary */
403 if (!optNoOutput) {
404 if (!optParseOut) {
405 /* don't parse */
406 if (optTrimRight) {
407 spos = str.value+str.size-1;
408 while (spos >= str.value && *((unsigned char *)spos) <= ' ') spos--;
409 *(++spos) = '\0';
410 str.size = spos-str.value;
412 spos = str.value;
413 if (optTrimLeft) {
414 while (*spos && *((unsigned char *)spos) <= ' ') spos++;
416 result = list_new(result, spos, 0);
417 } else {
418 /* parse output */
419 ret = 0; /* was anything added? list must hasat least one element */
420 spos = str.value;
421 while (*spos) {
422 /* skip delimiters */
423 while (*spos) {
424 int ch = *spos;
425 if (ch == ' ') {
426 if (!optSpaceBreak) break;
427 } else if (ch == '\t') {
428 if (!optTabBreak) break;
429 } else if (ch == '\n') {
430 if (!optCRBreak) break;
431 } else if (ch == '\r') {
432 if (!optLFBreak) break;
433 } else if ((unsigned char)ch > ' ') break;
434 spos++;
436 if (!*spos) break;
437 epos = spos+1;
438 while (*epos) {
439 int ch = *epos;
440 if (ch == ' ') {
441 if (optSpaceBreak) break;
442 } else if (ch == '\t') {
443 if (optTabBreak) break;
444 } else if (ch == '\n') {
445 if (optCRBreak) break;
446 } else if (ch == '\r') {
447 if (optLFBreak) break;
448 } else if ((unsigned char)ch <= ' ') break;
449 epos++;
451 KStringNew(&cmd);
452 KStringAppendRange(&cmd, spos, epos);
453 result = list_new(result, cmd.value, 0);
454 KStringFree(&cmd);
455 ret = 1; spos = epos;
457 if (!ret) { buf1[0] = '\0'; result = list_new(result, buf1, 0); }
459 } else result = list_new(result, str.value, 0);
460 KStringFree(&str);
462 /* the command exit result next */
463 if (optExitStatus && !optStatus1st) {
464 sprintf(buf1, "%d", exitStatus);
465 result = list_new(result, buf1, 0);
468 return result;