]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* |
2 | * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. | |
3 | * | |
4 | * This file is part of Jam - see jam.c for Copyright information. | |
5 | */ | |
6 | ||
7 | #include "jam.h" | |
8 | #include "builtins.h" | |
9 | ||
10 | #include "compile.h" | |
11 | #include "constants.h" | |
12 | #include "cwd.h" | |
b32b8144 | 13 | #include "debugger.h" |
7c673cae FG |
14 | #include "filesys.h" |
15 | #include "frames.h" | |
16 | #include "hash.h" | |
17 | #include "hdrmacro.h" | |
18 | #include "lists.h" | |
19 | #include "make.h" | |
20 | #include "md5.h" | |
21 | #include "native.h" | |
22 | #include "object.h" | |
23 | #include "parse.h" | |
24 | #include "pathsys.h" | |
25 | #include "rules.h" | |
26 | #include "strings.h" | |
27 | #include "subst.h" | |
28 | #include "timestamp.h" | |
29 | #include "variable.h" | |
30 | #include "output.h" | |
31 | ||
32 | #include <ctype.h> | |
33 | ||
34 | #ifdef OS_NT | |
35 | #include <windows.h> | |
36 | #ifndef FSCTL_GET_REPARSE_POINT | |
37 | /* MinGW's version of windows.h is missing this, so we need | |
38 | * to include winioctl.h directly | |
39 | */ | |
40 | #include <winioctl.h> | |
41 | #endif | |
42 | ||
43 | /* With VC8 (VS2005) these are not defined: | |
44 | * FSCTL_GET_REPARSE_POINT (expects WINVER >= 0x0500 _WIN32_WINNT >= 0x0500 ) | |
45 | * IO_REPARSE_TAG_SYMLINK (is part of a separate Driver SDK) | |
46 | * So define them explicitily to their expected values. | |
47 | */ | |
48 | #ifndef FSCTL_GET_REPARSE_POINT | |
49 | # define FSCTL_GET_REPARSE_POINT 0x000900a8 | |
50 | #endif | |
51 | #ifndef IO_REPARSE_TAG_SYMLINK | |
52 | # define IO_REPARSE_TAG_SYMLINK (0xA000000CL) | |
53 | #endif | |
54 | #endif /* OS_NT */ | |
55 | ||
56 | #if defined(USE_EXECUNIX) | |
57 | # include <sys/types.h> | |
58 | # include <sys/wait.h> | |
59 | #elif defined(OS_VMS) | |
60 | # include <wait.h> | |
61 | #else | |
62 | /* | |
63 | * NT does not have wait() and associated macros and uses the system() return | |
64 | * value instead. Status code group are documented at: | |
65 | * http://msdn.microsoft.com/en-gb/library/ff565436.aspx | |
66 | */ | |
67 | # define WIFEXITED(w) (((w) & 0XFFFFFF00) == 0) | |
68 | # define WEXITSTATUS(w)(w) | |
69 | #endif | |
70 | ||
71 | /* | |
72 | * builtins.c - builtin jam rules | |
73 | * | |
74 | * External routines: | |
75 | * load_builtins() - define builtin rules | |
76 | * unknown_rule() - reports an unknown rule occurrence to the | |
77 | * user and exits | |
78 | * | |
79 | * Internal routines: | |
80 | * append_if_exists() - if file exists, append it to the list | |
81 | * builtin_calc() - CALC rule | |
82 | * builtin_delete_module() - DELETE_MODULE ( MODULE ? ) | |
83 | * builtin_depends() - DEPENDS/INCLUDES rule | |
84 | * builtin_echo() - ECHO rule | |
85 | * builtin_exit() - EXIT rule | |
86 | * builtin_export() - EXPORT ( MODULE ? : RULES * ) | |
87 | * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule | |
88 | * builtin_glob() - GLOB rule | |
89 | * builtin_glob_recursive() - ??? | |
90 | * builtin_hdrmacro() - ??? | |
91 | * builtin_import() - IMPORT rule | |
92 | * builtin_match() - MATCH rule, regexp matching | |
93 | * builtin_rebuilds() - REBUILDS rule | |
94 | * builtin_rulenames() - RULENAMES ( MODULE ? ) | |
95 | * builtin_split_by_characters() - splits the given string into tokens | |
96 | * builtin_varnames() - VARNAMES ( MODULE ? ) | |
97 | * get_source_line() - get a frame's file and line number | |
98 | * information | |
99 | */ | |
100 | ||
101 | ||
102 | /* | |
103 | * compile_builtin() - define builtin rules | |
104 | */ | |
105 | ||
106 | #define P0 (PARSE *)0 | |
107 | #define C0 (OBJECT *)0 | |
108 | ||
109 | #if defined( OS_NT ) || defined( OS_CYGWIN ) | |
110 | LIST * builtin_system_registry ( FRAME *, int ); | |
111 | LIST * builtin_system_registry_names( FRAME *, int ); | |
112 | #endif | |
113 | ||
114 | int glob( char const * s, char const * c ); | |
115 | ||
116 | void backtrace ( FRAME * ); | |
117 | void backtrace_line ( FRAME * ); | |
118 | void print_source_line( FRAME * ); | |
119 | ||
120 | ||
121 | RULE * bind_builtin( char const * name_, LIST * (* f)( FRAME *, int flags ), | |
122 | int flags, char const * * args ) | |
123 | { | |
124 | FUNCTION * func; | |
125 | RULE * result; | |
126 | OBJECT * name = object_new( name_ ); | |
127 | ||
128 | func = function_builtin( f, flags, args ); | |
129 | ||
130 | result = new_rule_body( root_module(), name, func, 1 ); | |
131 | ||
132 | function_free( func ); | |
133 | ||
134 | object_free( name ); | |
135 | ||
136 | return result; | |
137 | } | |
138 | ||
139 | ||
140 | RULE * duplicate_rule( char const * name_, RULE * other ) | |
141 | { | |
142 | OBJECT * name = object_new( name_ ); | |
143 | RULE * result = import_rule( other, root_module(), name ); | |
144 | object_free( name ); | |
145 | return result; | |
146 | } | |
147 | ||
148 | ||
149 | /* | |
150 | * load_builtins() - define builtin rules | |
151 | */ | |
152 | ||
153 | void load_builtins() | |
154 | { | |
155 | duplicate_rule( "Always", | |
156 | bind_builtin( "ALWAYS", | |
157 | builtin_flags, T_FLAG_TOUCHED, 0 ) ); | |
158 | ||
159 | duplicate_rule( "Depends", | |
160 | bind_builtin( "DEPENDS", | |
161 | builtin_depends, 0, 0 ) ); | |
162 | ||
163 | duplicate_rule( "echo", | |
164 | duplicate_rule( "Echo", | |
165 | bind_builtin( "ECHO", | |
166 | builtin_echo, 0, 0 ) ) ); | |
167 | ||
168 | { | |
169 | char const * args[] = { "message", "*", ":", "result-value", "?", 0 }; | |
170 | duplicate_rule( "exit", | |
171 | duplicate_rule( "Exit", | |
172 | bind_builtin( "EXIT", | |
173 | builtin_exit, 0, args ) ) ); | |
174 | } | |
175 | ||
176 | { | |
177 | char const * args[] = { "directories", "*", ":", "patterns", "*", ":", | |
178 | "case-insensitive", "?", 0 }; | |
179 | duplicate_rule( "Glob", | |
180 | bind_builtin( "GLOB", builtin_glob, 0, args ) ); | |
181 | } | |
182 | ||
183 | { | |
184 | char const * args[] = { "patterns", "*", 0 }; | |
185 | bind_builtin( "GLOB-RECURSIVELY", | |
186 | builtin_glob_recursive, 0, args ); | |
187 | } | |
188 | ||
189 | duplicate_rule( "Includes", | |
190 | bind_builtin( "INCLUDES", | |
191 | builtin_depends, 1, 0 ) ); | |
192 | ||
193 | { | |
194 | char const * args[] = { "targets", "*", ":", "targets-to-rebuild", "*", | |
195 | 0 }; | |
196 | bind_builtin( "REBUILDS", | |
197 | builtin_rebuilds, 0, args ); | |
198 | } | |
199 | ||
200 | duplicate_rule( "Leaves", | |
201 | bind_builtin( "LEAVES", | |
202 | builtin_flags, T_FLAG_LEAVES, 0 ) ); | |
203 | ||
204 | duplicate_rule( "Match", | |
205 | bind_builtin( "MATCH", | |
206 | builtin_match, 0, 0 ) ); | |
207 | ||
208 | { | |
209 | char const * args[] = { "string", ":", "delimiters", 0 }; | |
210 | bind_builtin( "SPLIT_BY_CHARACTERS", | |
211 | builtin_split_by_characters, 0, args ); | |
212 | } | |
213 | ||
214 | duplicate_rule( "NoCare", | |
215 | bind_builtin( "NOCARE", | |
216 | builtin_flags, T_FLAG_NOCARE, 0 ) ); | |
217 | ||
218 | duplicate_rule( "NOTIME", | |
219 | duplicate_rule( "NotFile", | |
220 | bind_builtin( "NOTFILE", | |
221 | builtin_flags, T_FLAG_NOTFILE, 0 ) ) ); | |
222 | ||
223 | duplicate_rule( "NoUpdate", | |
224 | bind_builtin( "NOUPDATE", | |
225 | builtin_flags, T_FLAG_NOUPDATE, 0 ) ); | |
226 | ||
227 | duplicate_rule( "Temporary", | |
228 | bind_builtin( "TEMPORARY", | |
229 | builtin_flags, T_FLAG_TEMP, 0 ) ); | |
230 | ||
231 | bind_builtin( "ISFILE", | |
232 | builtin_flags, T_FLAG_ISFILE, 0 ); | |
233 | ||
234 | duplicate_rule( "HdrMacro", | |
235 | bind_builtin( "HDRMACRO", | |
236 | builtin_hdrmacro, 0, 0 ) ); | |
237 | ||
238 | /* FAIL_EXPECTED is used to indicate that the result of a target build | |
239 | * action should be inverted (ok <=> fail) this can be useful when | |
240 | * performing test runs from Jamfiles. | |
241 | */ | |
242 | bind_builtin( "FAIL_EXPECTED", | |
243 | builtin_flags, T_FLAG_FAIL_EXPECTED, 0 ); | |
244 | ||
245 | bind_builtin( "RMOLD", | |
246 | builtin_flags, T_FLAG_RMOLD, 0 ); | |
247 | ||
248 | { | |
249 | char const * args[] = { "targets", "*", 0 }; | |
250 | bind_builtin( "UPDATE", | |
251 | builtin_update, 0, args ); | |
252 | } | |
253 | ||
254 | { | |
255 | char const * args[] = { "targets", "*", | |
256 | ":", "log", "?", | |
257 | ":", "ignore-minus-n", "?", | |
258 | ":", "ignore-minus-q", "?", 0 }; | |
259 | bind_builtin( "UPDATE_NOW", | |
260 | builtin_update_now, 0, args ); | |
261 | } | |
262 | ||
263 | { | |
264 | char const * args[] = { "string", "pattern", "replacements", "+", 0 }; | |
265 | duplicate_rule( "subst", | |
266 | bind_builtin( "SUBST", | |
267 | builtin_subst, 0, args ) ); | |
268 | } | |
269 | ||
270 | { | |
271 | char const * args[] = { "module", "?", 0 }; | |
272 | bind_builtin( "RULENAMES", | |
273 | builtin_rulenames, 0, args ); | |
274 | } | |
275 | ||
276 | { | |
277 | char const * args[] = { "module", "?", 0 }; | |
278 | bind_builtin( "VARNAMES", | |
279 | builtin_varnames, 0, args ); | |
280 | } | |
281 | ||
282 | { | |
283 | char const * args[] = { "module", "?", 0 }; | |
284 | bind_builtin( "DELETE_MODULE", | |
285 | builtin_delete_module, 0, args ); | |
286 | } | |
287 | ||
288 | { | |
289 | char const * args[] = { "source_module", "?", | |
290 | ":", "source_rules", "*", | |
291 | ":", "target_module", "?", | |
292 | ":", "target_rules", "*", | |
293 | ":", "localize", "?", 0 }; | |
294 | bind_builtin( "IMPORT", | |
295 | builtin_import, 0, args ); | |
296 | } | |
297 | ||
298 | { | |
299 | char const * args[] = { "module", "?", ":", "rules", "*", 0 }; | |
300 | bind_builtin( "EXPORT", | |
301 | builtin_export, 0, args ); | |
302 | } | |
303 | ||
304 | { | |
305 | char const * args[] = { "levels", "?", 0 }; | |
306 | bind_builtin( "CALLER_MODULE", | |
307 | builtin_caller_module, 0, args ); | |
308 | } | |
309 | ||
310 | { | |
311 | char const * args[] = { "levels", "?", 0 }; | |
312 | bind_builtin( "BACKTRACE", | |
313 | builtin_backtrace, 0, args ); | |
314 | } | |
315 | ||
316 | { | |
317 | char const * args[] = { 0 }; | |
318 | bind_builtin( "PWD", | |
319 | builtin_pwd, 0, args ); | |
320 | } | |
321 | ||
322 | { | |
323 | char const * args[] = { "modules_to_import", "+", | |
324 | ":", "target_module", "?", 0 }; | |
325 | bind_builtin( "IMPORT_MODULE", | |
326 | builtin_import_module, 0, args ); | |
327 | } | |
328 | ||
329 | { | |
330 | char const * args[] = { "module", "?", 0 }; | |
331 | bind_builtin( "IMPORTED_MODULES", | |
332 | builtin_imported_modules, 0, args ); | |
333 | } | |
334 | ||
335 | { | |
336 | char const * args[] = { "instance_module", ":", "class_module", 0 }; | |
337 | bind_builtin( "INSTANCE", | |
338 | builtin_instance, 0, args ); | |
339 | } | |
340 | ||
341 | { | |
342 | char const * args[] = { "sequence", "*", 0 }; | |
343 | bind_builtin( "SORT", | |
344 | builtin_sort, 0, args ); | |
345 | } | |
346 | ||
347 | { | |
348 | char const * args[] = { "path_parts", "*", 0 }; | |
349 | bind_builtin( "NORMALIZE_PATH", | |
350 | builtin_normalize_path, 0, args ); | |
351 | } | |
352 | ||
353 | { | |
354 | char const * args[] = { "args", "*", 0 }; | |
355 | bind_builtin( "CALC", | |
356 | builtin_calc, 0, args ); | |
357 | } | |
358 | ||
359 | { | |
360 | char const * args[] = { "module", ":", "rule", 0 }; | |
361 | bind_builtin( "NATIVE_RULE", | |
362 | builtin_native_rule, 0, args ); | |
363 | } | |
364 | ||
365 | { | |
366 | char const * args[] = { "module", ":", "rule", ":", "version", 0 }; | |
367 | bind_builtin( "HAS_NATIVE_RULE", | |
368 | builtin_has_native_rule, 0, args ); | |
369 | } | |
370 | ||
371 | { | |
372 | char const * args[] = { "module", "*", 0 }; | |
373 | bind_builtin( "USER_MODULE", | |
374 | builtin_user_module, 0, args ); | |
375 | } | |
376 | ||
377 | { | |
378 | char const * args[] = { 0 }; | |
379 | bind_builtin( "NEAREST_USER_LOCATION", | |
380 | builtin_nearest_user_location, 0, args ); | |
381 | } | |
382 | ||
383 | { | |
384 | char const * args[] = { "file", 0 }; | |
385 | bind_builtin( "CHECK_IF_FILE", | |
386 | builtin_check_if_file, 0, args ); | |
387 | } | |
388 | ||
389 | #ifdef HAVE_PYTHON | |
390 | { | |
391 | char const * args[] = { "python-module", | |
392 | ":", "function", | |
393 | ":", "jam-module", | |
394 | ":", "rule-name", 0 }; | |
395 | bind_builtin( "PYTHON_IMPORT_RULE", | |
396 | builtin_python_import_rule, 0, args ); | |
397 | } | |
398 | #endif | |
399 | ||
400 | # if defined( OS_NT ) || defined( OS_CYGWIN ) | |
401 | { | |
402 | char const * args[] = { "key_path", ":", "data", "?", 0 }; | |
403 | bind_builtin( "W32_GETREG", | |
404 | builtin_system_registry, 0, args ); | |
405 | } | |
406 | ||
407 | { | |
408 | char const * args[] = { "key_path", ":", "result-type", 0 }; | |
409 | bind_builtin( "W32_GETREGNAMES", | |
410 | builtin_system_registry_names, 0, args ); | |
411 | } | |
412 | # endif | |
413 | ||
414 | { | |
415 | char const * args[] = { "command", ":", "*", 0 }; | |
416 | duplicate_rule( "SHELL", | |
417 | bind_builtin( "COMMAND", | |
418 | builtin_shell, 0, args ) ); | |
419 | } | |
420 | ||
421 | { | |
422 | char const * args[] = { "string", 0 }; | |
423 | bind_builtin( "MD5", | |
424 | builtin_md5, 0, args ); | |
425 | } | |
426 | ||
427 | { | |
428 | char const * args[] = { "name", ":", "mode", 0 }; | |
429 | bind_builtin( "FILE_OPEN", | |
430 | builtin_file_open, 0, args ); | |
431 | } | |
432 | ||
433 | { | |
434 | char const * args[] = { "string", ":", "width", 0 }; | |
435 | bind_builtin( "PAD", | |
436 | builtin_pad, 0, args ); | |
437 | } | |
438 | ||
439 | { | |
440 | char const * args[] = { "targets", "*", 0 }; | |
441 | bind_builtin( "PRECIOUS", | |
442 | builtin_precious, 0, args ); | |
443 | } | |
444 | ||
445 | { | |
446 | char const * args [] = { 0 }; | |
447 | bind_builtin( "SELF_PATH", builtin_self_path, 0, args ); | |
448 | } | |
449 | ||
450 | { | |
451 | char const * args [] = { "path", 0 }; | |
452 | bind_builtin( "MAKEDIR", builtin_makedir, 0, args ); | |
453 | } | |
b32b8144 | 454 | |
7c673cae FG |
455 | { |
456 | const char * args [] = { "path", 0 }; | |
457 | bind_builtin( "READLINK", builtin_readlink, 0, args ); | |
458 | } | |
b32b8144 | 459 | |
7c673cae FG |
460 | { |
461 | char const * args[] = { "archives", "*", | |
462 | ":", "member-patterns", "*", | |
463 | ":", "case-insensitive", "?", | |
464 | ":", "symbol-patterns", "*", 0 }; | |
465 | bind_builtin( "GLOB_ARCHIVE", builtin_glob_archive, 0, args ); | |
466 | } | |
467 | ||
b32b8144 FG |
468 | #ifdef JAM_DEBUGGER |
469 | ||
470 | { | |
471 | const char * args[] = { "list", "*", 0 }; | |
472 | bind_builtin("__DEBUG_PRINT_HELPER__", builtin_debug_print_helper, 0, args); | |
473 | } | |
474 | ||
475 | #endif | |
476 | ||
7c673cae FG |
477 | /* Initialize builtin modules. */ |
478 | init_set(); | |
479 | init_path(); | |
480 | init_regex(); | |
481 | init_property_set(); | |
482 | init_sequence(); | |
483 | init_order(); | |
484 | } | |
485 | ||
486 | ||
487 | /* | |
488 | * builtin_calc() - CALC rule | |
489 | * | |
490 | * Performs simple mathematical operations on two arguments. | |
491 | */ | |
492 | ||
493 | LIST * builtin_calc( FRAME * frame, int flags ) | |
494 | { | |
495 | LIST * arg = lol_get( frame->args, 0 ); | |
496 | ||
497 | LIST * result = L0; | |
498 | long lhs_value; | |
499 | long rhs_value; | |
500 | long result_value; | |
501 | char buffer[ 16 ]; | |
502 | char const * lhs; | |
503 | char const * op; | |
504 | char const * rhs; | |
505 | LISTITER iter = list_begin( arg ); | |
506 | LISTITER const end = list_end( arg ); | |
507 | ||
508 | if ( iter == end ) return L0; | |
509 | lhs = object_str( list_item( iter ) ); | |
510 | ||
511 | iter = list_next( iter ); | |
512 | if ( iter == end ) return L0; | |
513 | op = object_str( list_item( iter ) ); | |
514 | ||
515 | iter = list_next( iter ); | |
516 | if ( iter == end ) return L0; | |
517 | rhs = object_str( list_item( iter ) ); | |
518 | ||
519 | lhs_value = atoi( lhs ); | |
520 | rhs_value = atoi( rhs ); | |
521 | ||
522 | if ( !strcmp( "+", op ) ) | |
523 | result_value = lhs_value + rhs_value; | |
524 | else if ( !strcmp( "-", op ) ) | |
525 | result_value = lhs_value - rhs_value; | |
526 | else | |
527 | return L0; | |
528 | ||
529 | sprintf( buffer, "%ld", result_value ); | |
530 | result = list_push_back( result, object_new( buffer ) ); | |
531 | return result; | |
532 | } | |
533 | ||
534 | ||
535 | /* | |
536 | * builtin_depends() - DEPENDS/INCLUDES rule | |
537 | * | |
538 | * The DEPENDS/INCLUDES builtin rule appends each of the listed sources on the | |
539 | * dependency/includes list of each of the listed targets. It binds both the | |
540 | * targets and sources as TARGETs. | |
541 | */ | |
542 | ||
543 | LIST * builtin_depends( FRAME * frame, int flags ) | |
544 | { | |
545 | LIST * const targets = lol_get( frame->args, 0 ); | |
546 | LIST * const sources = lol_get( frame->args, 1 ); | |
547 | ||
548 | LISTITER iter = list_begin( targets ); | |
549 | LISTITER end = list_end( targets ); | |
550 | for ( ; iter != end; iter = list_next( iter ) ) | |
551 | { | |
552 | TARGET * const t = bindtarget( list_item( iter ) ); | |
553 | ||
554 | if ( flags ) | |
555 | target_include_many( t, sources ); | |
556 | else | |
557 | t->depends = targetlist( t->depends, sources ); | |
558 | } | |
559 | ||
560 | /* Enter reverse links */ | |
561 | iter = list_begin( sources ); | |
562 | end = list_end( sources ); | |
563 | for ( ; iter != end; iter = list_next( iter ) ) | |
564 | { | |
565 | TARGET * const s = bindtarget( list_item( iter ) ); | |
566 | if ( flags ) | |
567 | { | |
568 | LISTITER t_iter = list_begin( targets ); | |
569 | LISTITER const t_end = list_end( targets ); | |
570 | for ( ; t_iter != t_end; t_iter = list_next( t_iter ) ) | |
571 | s->dependants = targetentry( s->dependants, bindtarget( | |
572 | list_item( t_iter ) )->includes ); | |
573 | } | |
574 | else | |
575 | s->dependants = targetlist( s->dependants, targets ); | |
576 | } | |
577 | ||
578 | return L0; | |
579 | } | |
580 | ||
581 | ||
582 | /* | |
583 | * builtin_rebuilds() - REBUILDS rule | |
584 | * | |
585 | * Appends each of the rebuild-targets listed in its second argument to the | |
586 | * rebuilds list for each of the targets listed in its first argument. | |
587 | */ | |
588 | ||
589 | LIST * builtin_rebuilds( FRAME * frame, int flags ) | |
590 | { | |
591 | LIST * targets = lol_get( frame->args, 0 ); | |
592 | LIST * rebuilds = lol_get( frame->args, 1 ); | |
593 | LISTITER iter = list_begin( targets ); | |
594 | LISTITER const end = list_end( targets ); | |
595 | for ( ; iter != end; iter = list_next( iter ) ) | |
596 | { | |
597 | TARGET * const t = bindtarget( list_item( iter ) ); | |
598 | t->rebuilds = targetlist( t->rebuilds, rebuilds ); | |
599 | } | |
600 | return L0; | |
601 | } | |
602 | ||
603 | ||
604 | /* | |
605 | * builtin_echo() - ECHO rule | |
606 | * | |
607 | * Echoes the targets to the user. No other actions are taken. | |
608 | */ | |
609 | ||
610 | LIST * builtin_echo( FRAME * frame, int flags ) | |
611 | { | |
612 | list_print( lol_get( frame->args, 0 ) ); | |
613 | out_printf( "\n" ); | |
614 | out_flush(); | |
615 | return L0; | |
616 | } | |
617 | ||
618 | ||
619 | /* | |
620 | * builtin_exit() - EXIT rule | |
621 | * | |
622 | * Echoes the targets to the user and exits the program with a failure status. | |
623 | */ | |
624 | ||
625 | LIST * builtin_exit( FRAME * frame, int flags ) | |
626 | { | |
627 | LIST * const code = lol_get( frame->args, 1 ); | |
628 | list_print( lol_get( frame->args, 0 ) ); | |
629 | out_printf( "\n" ); | |
630 | if ( !list_empty( code ) ) | |
631 | { | |
632 | int status = atoi( object_str( list_front( code ) ) ); | |
633 | #ifdef OS_VMS | |
634 | switch( status ) | |
635 | { | |
636 | case 0: | |
637 | status = EXITOK; | |
638 | break; | |
639 | case 1: | |
640 | status = EXITBAD; | |
641 | break; | |
642 | } | |
643 | #endif | |
644 | exit( status ); | |
645 | } | |
646 | else | |
647 | exit( EXITBAD ); /* yeech */ | |
648 | return L0; | |
649 | } | |
650 | ||
651 | ||
652 | /* | |
653 | * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule | |
654 | * | |
655 | * Marks the target with the appropriate flag, for use by make0(). It binds each | |
656 | * target as a TARGET. | |
657 | */ | |
658 | ||
659 | LIST * builtin_flags( FRAME * frame, int flags ) | |
660 | { | |
661 | LIST * const targets = lol_get( frame->args, 0 ); | |
662 | LISTITER iter = list_begin( targets ); | |
663 | LISTITER const end = list_end( targets ); | |
664 | for ( ; iter != end; iter = list_next( iter ) ) | |
665 | bindtarget( list_item( iter ) )->flags |= flags; | |
666 | return L0; | |
667 | } | |
668 | ||
669 | ||
670 | /* | |
671 | * builtin_glob() - GLOB rule | |
672 | */ | |
673 | ||
674 | struct globbing | |
675 | { | |
676 | LIST * patterns; | |
677 | LIST * results; | |
678 | LIST * case_insensitive; | |
679 | }; | |
680 | ||
681 | ||
682 | static void downcase_inplace( char * p ) | |
683 | { | |
684 | for ( ; *p; ++p ) | |
685 | *p = tolower( *p ); | |
686 | } | |
687 | ||
688 | ||
689 | static void builtin_glob_back( void * closure, OBJECT * file, int status, | |
690 | timestamp const * const time ) | |
691 | { | |
692 | PROFILE_ENTER( BUILTIN_GLOB_BACK ); | |
693 | ||
694 | struct globbing * const globbing = (struct globbing *)closure; | |
695 | PATHNAME f; | |
696 | string buf[ 1 ]; | |
697 | LISTITER iter; | |
698 | LISTITER end; | |
699 | ||
700 | /* Null out directory for matching. We wish we had file_dirscan() pass up a | |
701 | * PATHNAME. | |
702 | */ | |
703 | path_parse( object_str( file ), &f ); | |
704 | f.f_dir.len = 0; | |
705 | ||
706 | /* For globbing, we unconditionally ignore current and parent directory | |
707 | * items. Since these items always exist, there is no reason why caller of | |
708 | * GLOB would want to see them. We could also change file_dirscan(), but | |
709 | * then paths with embedded "." and ".." would not work anywhere. | |
710 | */ | |
711 | if ( !strcmp( f.f_base.ptr, "." ) || !strcmp( f.f_base.ptr, ".." ) ) | |
712 | { | |
713 | PROFILE_EXIT( BUILTIN_GLOB_BACK ); | |
714 | return; | |
715 | } | |
716 | ||
717 | string_new( buf ); | |
718 | path_build( &f, buf ); | |
719 | ||
720 | if ( globbing->case_insensitive ) | |
721 | downcase_inplace( buf->value ); | |
722 | ||
723 | iter = list_begin( globbing->patterns ); | |
724 | end = list_end( globbing->patterns ); | |
725 | for ( ; iter != end; iter = list_next( iter ) ) | |
726 | { | |
727 | if ( !glob( object_str( list_item( iter ) ), buf->value ) ) | |
728 | { | |
729 | globbing->results = list_push_back( globbing->results, object_copy( | |
730 | file ) ); | |
731 | break; | |
732 | } | |
733 | } | |
734 | ||
735 | string_free( buf ); | |
736 | ||
737 | PROFILE_EXIT( BUILTIN_GLOB_BACK ); | |
738 | } | |
739 | ||
740 | ||
741 | static LIST * downcase_list( LIST * in ) | |
742 | { | |
743 | LIST * result = L0; | |
744 | LISTITER iter = list_begin( in ); | |
745 | LISTITER const end = list_end( in ); | |
746 | ||
747 | string s[ 1 ]; | |
748 | string_new( s ); | |
749 | ||
750 | for ( ; iter != end; iter = list_next( iter ) ) | |
751 | { | |
752 | string_append( s, object_str( list_item( iter ) ) ); | |
753 | downcase_inplace( s->value ); | |
754 | result = list_push_back( result, object_new( s->value ) ); | |
755 | string_truncate( s, 0 ); | |
756 | } | |
757 | ||
758 | string_free( s ); | |
759 | return result; | |
760 | } | |
761 | ||
762 | ||
763 | LIST * builtin_glob( FRAME * frame, int flags ) | |
764 | { | |
765 | LIST * const l = lol_get( frame->args, 0 ); | |
766 | LIST * const r = lol_get( frame->args, 1 ); | |
767 | ||
768 | LISTITER iter; | |
769 | LISTITER end; | |
770 | struct globbing globbing; | |
771 | ||
772 | globbing.results = L0; | |
773 | globbing.patterns = r; | |
774 | ||
775 | globbing.case_insensitive = | |
776 | # if defined( OS_NT ) || defined( OS_CYGWIN ) || defined( OS_VMS ) | |
777 | l; /* Always case-insensitive if any files can be found. */ | |
778 | # else | |
779 | lol_get( frame->args, 2 ); | |
780 | # endif | |
781 | ||
782 | if ( globbing.case_insensitive ) | |
783 | globbing.patterns = downcase_list( r ); | |
784 | ||
785 | iter = list_begin( l ); | |
786 | end = list_end( l ); | |
787 | for ( ; iter != end; iter = list_next( iter ) ) | |
788 | file_dirscan( list_item( iter ), builtin_glob_back, &globbing ); | |
789 | ||
790 | if ( globbing.case_insensitive ) | |
791 | list_free( globbing.patterns ); | |
792 | ||
793 | return globbing.results; | |
794 | } | |
795 | ||
796 | ||
797 | static int has_wildcards( char const * const str ) | |
798 | { | |
799 | return str[ strcspn( str, "[]*?" ) ] ? 1 : 0; | |
800 | } | |
801 | ||
802 | ||
803 | /* | |
804 | * append_if_exists() - if file exists, append it to the list | |
805 | */ | |
806 | ||
807 | static LIST * append_if_exists( LIST * list, OBJECT * file ) | |
808 | { | |
809 | file_info_t * info = file_query( file ); | |
810 | return info | |
811 | ? list_push_back( list, object_copy( info->name ) ) | |
812 | : list ; | |
813 | } | |
814 | ||
815 | ||
816 | LIST * glob1( OBJECT * dirname, OBJECT * pattern ) | |
817 | { | |
818 | LIST * const plist = list_new( object_copy( pattern ) ); | |
819 | struct globbing globbing; | |
820 | ||
821 | globbing.results = L0; | |
822 | globbing.patterns = plist; | |
823 | ||
824 | globbing.case_insensitive | |
825 | # if defined( OS_NT ) || defined( OS_CYGWIN ) || defined( OS_VMS ) | |
826 | = plist; /* always case-insensitive if any files can be found */ | |
827 | # else | |
828 | = L0; | |
829 | # endif | |
830 | ||
831 | if ( globbing.case_insensitive ) | |
832 | globbing.patterns = downcase_list( plist ); | |
833 | ||
834 | file_dirscan( dirname, builtin_glob_back, &globbing ); | |
835 | ||
836 | if ( globbing.case_insensitive ) | |
837 | list_free( globbing.patterns ); | |
838 | ||
839 | list_free( plist ); | |
840 | ||
841 | return globbing.results; | |
842 | } | |
843 | ||
844 | ||
845 | LIST * glob_recursive( char const * pattern ) | |
846 | { | |
847 | LIST * result = L0; | |
848 | ||
849 | /* Check if there's metacharacters in pattern */ | |
850 | if ( !has_wildcards( pattern ) ) | |
851 | { | |
852 | /* No metacharacters. Check if the path exists. */ | |
853 | OBJECT * const p = object_new( pattern ); | |
854 | result = append_if_exists( result, p ); | |
855 | object_free( p ); | |
856 | } | |
857 | else | |
858 | { | |
859 | /* Have metacharacters in the pattern. Split into dir/name. */ | |
860 | PATHNAME path[ 1 ]; | |
861 | path_parse( pattern, path ); | |
862 | ||
863 | if ( path->f_dir.ptr ) | |
864 | { | |
865 | LIST * dirs = L0; | |
866 | string dirname[ 1 ]; | |
867 | string basename[ 1 ]; | |
868 | string_new( dirname ); | |
869 | string_new( basename ); | |
870 | ||
871 | string_append_range( dirname, path->f_dir.ptr, | |
872 | path->f_dir.ptr + path->f_dir.len ); | |
873 | ||
874 | path->f_grist.ptr = 0; | |
875 | path->f_grist.len = 0; | |
876 | path->f_dir.ptr = 0; | |
877 | path->f_dir.len = 0; | |
878 | path_build( path, basename ); | |
879 | ||
880 | dirs = has_wildcards( dirname->value ) | |
881 | ? glob_recursive( dirname->value ) | |
882 | : list_push_back( dirs, object_new( dirname->value ) ); | |
883 | ||
884 | if ( has_wildcards( basename->value ) ) | |
885 | { | |
886 | OBJECT * const b = object_new( basename->value ); | |
887 | LISTITER iter = list_begin( dirs ); | |
888 | LISTITER const end = list_end( dirs ); | |
889 | for ( ; iter != end; iter = list_next( iter ) ) | |
890 | result = list_append( result, glob1( list_item( iter ), b ) | |
891 | ); | |
892 | object_free( b ); | |
893 | } | |
894 | else | |
895 | { | |
896 | LISTITER iter = list_begin( dirs ); | |
897 | LISTITER const end = list_end( dirs ); | |
898 | string file_string[ 1 ]; | |
899 | string_new( file_string ); | |
900 | ||
901 | /* No wildcard in basename. */ | |
902 | for ( ; iter != end; iter = list_next( iter ) ) | |
903 | { | |
904 | OBJECT * p; | |
905 | path->f_dir.ptr = object_str( list_item( iter ) ); | |
906 | path->f_dir.len = strlen( object_str( list_item( iter ) ) ); | |
907 | path_build( path, file_string ); | |
908 | ||
909 | p = object_new( file_string->value ); | |
910 | ||
911 | result = append_if_exists( result, p ); | |
912 | ||
913 | object_free( p ); | |
914 | ||
915 | string_truncate( file_string, 0 ); | |
916 | } | |
917 | ||
918 | string_free( file_string ); | |
919 | } | |
920 | ||
921 | string_free( dirname ); | |
922 | string_free( basename ); | |
923 | ||
924 | list_free( dirs ); | |
925 | } | |
926 | else | |
927 | { | |
928 | /* No directory, just a pattern. */ | |
929 | OBJECT * const p = object_new( pattern ); | |
930 | result = list_append( result, glob1( constant_dot, p ) ); | |
931 | object_free( p ); | |
932 | } | |
933 | } | |
934 | ||
935 | return result; | |
936 | } | |
937 | ||
938 | ||
939 | /* | |
940 | * builtin_glob_recursive() - ??? | |
941 | */ | |
942 | ||
943 | LIST * builtin_glob_recursive( FRAME * frame, int flags ) | |
944 | { | |
945 | LIST * result = L0; | |
946 | LIST * const l = lol_get( frame->args, 0 ); | |
947 | LISTITER iter = list_begin( l ); | |
948 | LISTITER const end = list_end( l ); | |
949 | for ( ; iter != end; iter = list_next( iter ) ) | |
950 | result = list_append( result, glob_recursive( object_str( list_item( | |
951 | iter ) ) ) ); | |
952 | return result; | |
953 | } | |
954 | ||
955 | ||
956 | /* | |
957 | * builtin_match() - MATCH rule, regexp matching | |
958 | */ | |
959 | ||
960 | LIST * builtin_match( FRAME * frame, int flags ) | |
961 | { | |
962 | LIST * l; | |
963 | LIST * r; | |
964 | LIST * result = L0; | |
965 | LISTITER l_iter; | |
966 | LISTITER l_end; | |
967 | LISTITER r_iter; | |
968 | LISTITER r_end; | |
969 | ||
970 | string buf[ 1 ]; | |
971 | string_new( buf ); | |
972 | ||
973 | /* For each pattern */ | |
974 | ||
975 | l = lol_get( frame->args, 0 ); | |
976 | l_iter = list_begin( l ); | |
977 | l_end = list_end( l ); | |
978 | for ( ; l_iter != l_end; l_iter = list_next( l_iter ) ) | |
979 | { | |
980 | /* Result is cached and intentionally never freed. */ | |
981 | regexp * re = regex_compile( list_item( l_iter ) ); | |
982 | ||
983 | /* For each string to match against. */ | |
984 | r = lol_get( frame->args, 1 ); | |
985 | r_iter = list_begin( r ); | |
986 | r_end = list_end( r ); | |
987 | for ( ; r_iter != r_end; r_iter = list_next( r_iter ) ) | |
988 | { | |
989 | if ( regexec( re, object_str( list_item( r_iter ) ) ) ) | |
990 | { | |
991 | int i; | |
992 | int top; | |
993 | ||
994 | /* Find highest parameter */ | |
995 | ||
996 | for ( top = NSUBEXP; top-- > 1; ) | |
997 | if ( re->startp[ top ] ) | |
998 | break; | |
999 | ||
1000 | /* And add all parameters up to highest onto list. */ | |
1001 | /* Must have parameters to have results! */ | |
1002 | for ( i = 1; i <= top; ++i ) | |
1003 | { | |
1004 | string_append_range( buf, re->startp[ i ], re->endp[ i ] ); | |
1005 | result = list_push_back( result, object_new( buf->value ) ); | |
1006 | string_truncate( buf, 0 ); | |
1007 | } | |
1008 | } | |
1009 | } | |
1010 | } | |
1011 | ||
1012 | string_free( buf ); | |
1013 | return result; | |
1014 | } | |
1015 | ||
1016 | ||
1017 | /* | |
1018 | * builtin_split_by_characters() - splits the given string into tokens | |
1019 | */ | |
1020 | ||
1021 | LIST * builtin_split_by_characters( FRAME * frame, int flags ) | |
1022 | { | |
1023 | LIST * l1 = lol_get( frame->args, 0 ); | |
1024 | LIST * l2 = lol_get( frame->args, 1 ); | |
1025 | ||
1026 | LIST * result = L0; | |
1027 | ||
1028 | string buf[ 1 ]; | |
1029 | ||
1030 | char const * delimiters = object_str( list_front( l2 ) ); | |
1031 | char * t; | |
1032 | ||
1033 | string_copy( buf, object_str( list_front( l1 ) ) ); | |
1034 | ||
1035 | t = strtok( buf->value, delimiters ); | |
1036 | while ( t ) | |
1037 | { | |
1038 | result = list_push_back( result, object_new( t ) ); | |
1039 | t = strtok( NULL, delimiters ); | |
1040 | } | |
1041 | ||
1042 | string_free( buf ); | |
1043 | ||
1044 | return result; | |
1045 | } | |
1046 | ||
1047 | ||
1048 | /* | |
1049 | * builtin_hdrmacro() - ??? | |
1050 | */ | |
1051 | ||
1052 | LIST * builtin_hdrmacro( FRAME * frame, int flags ) | |
1053 | { | |
1054 | LIST * const l = lol_get( frame->args, 0 ); | |
1055 | LISTITER iter = list_begin( l ); | |
1056 | LISTITER const end = list_end( l ); | |
1057 | ||
1058 | for ( ; iter != end; iter = list_next( iter ) ) | |
1059 | { | |
1060 | TARGET * const t = bindtarget( list_item( iter ) ); | |
1061 | ||
1062 | /* Scan file for header filename macro definitions. */ | |
1063 | if ( DEBUG_HEADER ) | |
1064 | out_printf( "scanning '%s' for header file macro definitions\n", | |
1065 | object_str( list_item( iter ) ) ); | |
1066 | ||
1067 | macro_headers( t ); | |
1068 | } | |
1069 | ||
1070 | return L0; | |
1071 | } | |
1072 | ||
1073 | ||
1074 | /* | |
1075 | * builtin_rulenames() - RULENAMES ( MODULE ? ) | |
1076 | * | |
1077 | * Returns a list of the non-local rule names in the given MODULE. If MODULE is | |
1078 | * not supplied, returns the list of rule names in the global module. | |
1079 | */ | |
1080 | ||
1081 | static void add_rule_name( void * r_, void * result_ ) | |
1082 | { | |
1083 | RULE * const r = (RULE *)r_; | |
1084 | LIST * * const result = (LIST * *)result_; | |
1085 | if ( r->exported ) | |
1086 | *result = list_push_back( *result, object_copy( r->name ) ); | |
1087 | } | |
1088 | ||
1089 | ||
1090 | LIST * builtin_rulenames( FRAME * frame, int flags ) | |
1091 | { | |
1092 | LIST * arg0 = lol_get( frame->args, 0 ); | |
1093 | LIST * result = L0; | |
1094 | module_t * const source_module = bindmodule( list_empty( arg0 ) | |
1095 | ? 0 | |
1096 | : list_front( arg0 ) ); | |
1097 | ||
1098 | if ( source_module->rules ) | |
1099 | hashenumerate( source_module->rules, add_rule_name, &result ); | |
1100 | return result; | |
1101 | } | |
1102 | ||
1103 | ||
1104 | /* | |
1105 | * builtin_varnames() - VARNAMES ( MODULE ? ) | |
1106 | * | |
1107 | * Returns a list of the variable names in the given MODULE. If MODULE is not | |
1108 | * supplied, returns the list of variable names in the global module. | |
1109 | */ | |
1110 | ||
1111 | /* helper function for builtin_varnames(), below. Used with hashenumerate, will | |
1112 | * prepend the key of each element to the list | |
1113 | */ | |
1114 | static void add_hash_key( void * np, void * result_ ) | |
1115 | { | |
1116 | LIST * * result = (LIST * *)result_; | |
1117 | *result = list_push_back( *result, object_copy( *(OBJECT * *)np ) ); | |
1118 | } | |
1119 | ||
1120 | ||
1121 | LIST * builtin_varnames( FRAME * frame, int flags ) | |
1122 | { | |
1123 | LIST * arg0 = lol_get( frame->args, 0 ); | |
1124 | LIST * result = L0; | |
1125 | module_t * source_module = bindmodule( list_empty( arg0 ) | |
1126 | ? 0 | |
1127 | : list_front( arg0 ) ); | |
1128 | ||
1129 | struct hash * const vars = source_module->variables; | |
1130 | if ( vars ) | |
1131 | hashenumerate( vars, add_hash_key, &result ); | |
1132 | return result; | |
1133 | } | |
1134 | ||
1135 | ||
1136 | /* | |
1137 | * builtin_delete_module() - DELETE_MODULE ( MODULE ? ) | |
1138 | * | |
1139 | * Clears all rules and variables from the given module. | |
1140 | */ | |
1141 | ||
1142 | LIST * builtin_delete_module( FRAME * frame, int flags ) | |
1143 | { | |
1144 | LIST * const arg0 = lol_get( frame->args, 0 ); | |
1145 | module_t * const source_module = bindmodule( list_empty( arg0 ) ? 0 : | |
1146 | list_front( arg0 ) ); | |
1147 | delete_module( source_module ); | |
1148 | return L0; | |
1149 | } | |
1150 | ||
1151 | ||
1152 | /* | |
1153 | * unknown_rule() - reports an unknown rule occurrence to the user and exits | |
1154 | */ | |
1155 | ||
1156 | void unknown_rule( FRAME * frame, char const * key, module_t * module, | |
1157 | OBJECT * rule_name ) | |
1158 | { | |
1159 | backtrace_line( frame->prev ); | |
1160 | if ( key ) | |
1161 | out_printf("%s error", key); | |
1162 | else | |
1163 | out_printf("ERROR"); | |
1164 | out_printf( ": rule \"%s\" unknown in ", object_str( rule_name ) ); | |
1165 | if ( module->name ) | |
1166 | out_printf( "module \"%s\".\n", object_str( module->name ) ); | |
1167 | else | |
1168 | out_printf( "root module.\n" ); | |
1169 | backtrace( frame->prev ); | |
1170 | exit( EXITBAD ); | |
1171 | } | |
1172 | ||
1173 | ||
1174 | /* | |
1175 | * builtin_import() - IMPORT rule | |
1176 | * | |
1177 | * IMPORT | |
1178 | * ( | |
1179 | * SOURCE_MODULE ? : | |
1180 | * SOURCE_RULES * : | |
1181 | * TARGET_MODULE ? : | |
1182 | * TARGET_RULES * : | |
1183 | * LOCALIZE ? | |
1184 | * ) | |
1185 | * | |
1186 | * Imports rules from the SOURCE_MODULE into the TARGET_MODULE as local rules. | |
1187 | * If either SOURCE_MODULE or TARGET_MODULE is not supplied, it refers to the | |
1188 | * global module. SOURCE_RULES specifies which rules from the SOURCE_MODULE to | |
1189 | * import; TARGET_RULES specifies the names to give those rules in | |
1190 | * TARGET_MODULE. If SOURCE_RULES contains a name that does not correspond to | |
1191 | * a rule in SOURCE_MODULE, or if it contains a different number of items than | |
1192 | * TARGET_RULES, an error is issued. If LOCALIZE is specified, the rules will be | |
1193 | * executed in TARGET_MODULE, with corresponding access to its module local | |
1194 | * variables. | |
1195 | */ | |
1196 | ||
1197 | LIST * builtin_import( FRAME * frame, int flags ) | |
1198 | { | |
1199 | LIST * source_module_list = lol_get( frame->args, 0 ); | |
1200 | LIST * source_rules = lol_get( frame->args, 1 ); | |
1201 | LIST * target_module_list = lol_get( frame->args, 2 ); | |
1202 | LIST * target_rules = lol_get( frame->args, 3 ); | |
1203 | LIST * localize = lol_get( frame->args, 4 ); | |
1204 | ||
1205 | module_t * target_module = bindmodule( list_empty( target_module_list ) | |
1206 | ? 0 | |
1207 | : list_front( target_module_list ) ); | |
1208 | module_t * source_module = bindmodule( list_empty( source_module_list ) | |
1209 | ? 0 | |
1210 | : list_front( source_module_list ) ); | |
1211 | ||
1212 | LISTITER source_iter = list_begin( source_rules ); | |
1213 | LISTITER const source_end = list_end( source_rules ); | |
1214 | LISTITER target_iter = list_begin( target_rules ); | |
1215 | LISTITER const target_end = list_end( target_rules ); | |
1216 | ||
1217 | for ( ; | |
1218 | source_iter != source_end && target_iter != target_end; | |
1219 | source_iter = list_next( source_iter ), | |
1220 | target_iter = list_next( target_iter ) ) | |
1221 | { | |
1222 | RULE * r; | |
1223 | RULE * imported; | |
1224 | ||
1225 | if ( !source_module->rules || !(r = (RULE *)hash_find( | |
1226 | source_module->rules, list_item( source_iter ) ) ) ) | |
1227 | unknown_rule( frame, "IMPORT", source_module, list_item( source_iter | |
1228 | ) ); | |
1229 | ||
1230 | imported = import_rule( r, target_module, list_item( target_iter ) ); | |
1231 | if ( !list_empty( localize ) ) | |
1232 | rule_localize( imported, target_module ); | |
1233 | /* This rule is really part of some other module. Just refer to it here, | |
1234 | * but do not let it out. | |
1235 | */ | |
1236 | imported->exported = 0; | |
1237 | } | |
1238 | ||
1239 | if ( source_iter != source_end || target_iter != target_end ) | |
1240 | { | |
1241 | backtrace_line( frame->prev ); | |
1242 | out_printf( "import error: length of source and target rule name lists " | |
1243 | "don't match!\n" ); | |
1244 | out_printf( " source: " ); | |
1245 | list_print( source_rules ); | |
1246 | out_printf( "\n target: " ); | |
1247 | list_print( target_rules ); | |
1248 | out_printf( "\n" ); | |
1249 | backtrace( frame->prev ); | |
1250 | exit( EXITBAD ); | |
1251 | } | |
1252 | ||
1253 | return L0; | |
1254 | } | |
1255 | ||
1256 | ||
1257 | /* | |
1258 | * builtin_export() - EXPORT ( MODULE ? : RULES * ) | |
1259 | * | |
1260 | * The EXPORT rule marks RULES from the SOURCE_MODULE as non-local (and thus | |
1261 | * exportable). If an element of RULES does not name a rule in MODULE, an error | |
1262 | * is issued. | |
1263 | */ | |
1264 | ||
1265 | LIST * builtin_export( FRAME * frame, int flags ) | |
1266 | { | |
1267 | LIST * const module_list = lol_get( frame->args, 0 ); | |
1268 | LIST * const rules = lol_get( frame->args, 1 ); | |
1269 | module_t * const m = bindmodule( list_empty( module_list ) ? 0 : list_front( | |
1270 | module_list ) ); | |
1271 | ||
1272 | LISTITER iter = list_begin( rules ); | |
1273 | LISTITER const end = list_end( rules ); | |
1274 | for ( ; iter != end; iter = list_next( iter ) ) | |
1275 | { | |
1276 | RULE * r; | |
1277 | if ( !m->rules || !( r = (RULE *)hash_find( m->rules, list_item( iter ) | |
1278 | ) ) ) | |
1279 | unknown_rule( frame, "EXPORT", m, list_item( iter ) ); | |
1280 | r->exported = 1; | |
1281 | } | |
1282 | return L0; | |
1283 | } | |
1284 | ||
1285 | ||
1286 | /* | |
1287 | * get_source_line() - get a frame's file and line number information | |
1288 | * | |
1289 | * This is the execution traceback information to be indicated for in debug | |
1290 | * output or an error backtrace. | |
1291 | */ | |
1292 | ||
1293 | static void get_source_line( FRAME * frame, char const * * file, int * line ) | |
1294 | { | |
1295 | if ( frame->file ) | |
1296 | { | |
1297 | char const * f = object_str( frame->file ); | |
1298 | int l = frame->line; | |
1299 | if ( !strcmp( f, "+" ) ) | |
1300 | { | |
1301 | f = "jambase.c"; | |
1302 | l += 3; | |
1303 | } | |
1304 | *file = f; | |
1305 | *line = l; | |
1306 | } | |
1307 | else | |
1308 | { | |
1309 | *file = "(builtin)"; | |
1310 | *line = -1; | |
1311 | } | |
1312 | } | |
1313 | ||
1314 | ||
1315 | void print_source_line( FRAME * frame ) | |
1316 | { | |
1317 | char const * file; | |
1318 | int line; | |
1319 | get_source_line( frame, &file, &line ); | |
1320 | if ( line < 0 ) | |
1321 | out_printf( "(builtin):" ); | |
1322 | else | |
1323 | out_printf( "%s:%d:", file, line ); | |
1324 | } | |
1325 | ||
1326 | ||
1327 | /* | |
1328 | * backtrace_line() - print a single line of error backtrace for the given | |
1329 | * frame. | |
1330 | */ | |
1331 | ||
1332 | void backtrace_line( FRAME * frame ) | |
1333 | { | |
1334 | if ( frame == 0 ) | |
1335 | { | |
1336 | out_printf( "(no frame):" ); | |
1337 | } | |
1338 | else | |
1339 | { | |
1340 | print_source_line( frame ); | |
1341 | out_printf( " in %s\n", frame->rulename ); | |
1342 | } | |
1343 | } | |
1344 | ||
1345 | ||
1346 | /* | |
1347 | * backtrace() - Print the entire backtrace from the given frame to the Jambase | |
1348 | * which invoked it. | |
1349 | */ | |
1350 | ||
1351 | void backtrace( FRAME * frame ) | |
1352 | { | |
1353 | if ( !frame ) return; | |
1354 | while ( ( frame = frame->prev ) ) | |
1355 | backtrace_line( frame ); | |
1356 | } | |
1357 | ||
1358 | ||
1359 | /* | |
1360 | * builtin_backtrace() - A Jam version of the backtrace function, taking no | |
1361 | * arguments and returning a list of quadruples: FILENAME LINE MODULE. RULENAME | |
1362 | * describing each frame. Note that the module-name is always followed by a | |
1363 | * period. | |
1364 | */ | |
1365 | ||
1366 | LIST * builtin_backtrace( FRAME * frame, int flags ) | |
1367 | { | |
1368 | LIST * const levels_arg = lol_get( frame->args, 0 ); | |
1369 | int levels = list_empty( levels_arg ) | |
1370 | ? (int)( (unsigned int)(-1) >> 1 ) | |
1371 | : atoi( object_str( list_front( levels_arg ) ) ); | |
1372 | ||
1373 | LIST * result = L0; | |
1374 | for ( ; ( frame = frame->prev ) && levels; --levels ) | |
1375 | { | |
1376 | char const * file; | |
1377 | int line; | |
1378 | char buf[ 32 ]; | |
1379 | string module_name[ 1 ]; | |
1380 | get_source_line( frame, &file, &line ); | |
1381 | sprintf( buf, "%d", line ); | |
1382 | string_new( module_name ); | |
1383 | if ( frame->module->name ) | |
1384 | { | |
1385 | string_append( module_name, object_str( frame->module->name ) ); | |
1386 | string_append( module_name, "." ); | |
1387 | } | |
1388 | result = list_push_back( result, object_new( file ) ); | |
1389 | result = list_push_back( result, object_new( buf ) ); | |
1390 | result = list_push_back( result, object_new( module_name->value ) ); | |
1391 | result = list_push_back( result, object_new( frame->rulename ) ); | |
1392 | string_free( module_name ); | |
1393 | } | |
1394 | return result; | |
1395 | } | |
1396 | ||
1397 | ||
1398 | /* | |
1399 | * builtin_caller_module() - CALLER_MODULE ( levels ? ) | |
1400 | * | |
1401 | * If levels is not supplied, returns the name of the module of the rule which | |
1402 | * called the one calling this one. If levels is supplied, it is interpreted as | |
1403 | * an integer specifying a number of additional levels of call stack to traverse | |
1404 | * in order to locate the module in question. If no such module exists, returns | |
1405 | * the empty list. Also returns the empty list when the module in question is | |
1406 | * the global module. This rule is needed for implementing module import | |
1407 | * behavior. | |
1408 | */ | |
1409 | ||
1410 | LIST * builtin_caller_module( FRAME * frame, int flags ) | |
1411 | { | |
1412 | LIST * const levels_arg = lol_get( frame->args, 0 ); | |
1413 | int const levels = list_empty( levels_arg ) | |
1414 | ? 0 | |
1415 | : atoi( object_str( list_front( levels_arg ) ) ); | |
1416 | ||
1417 | int i; | |
1418 | for ( i = 0; ( i < levels + 2 ) && frame->prev; ++i ) | |
1419 | frame = frame->prev; | |
1420 | ||
1421 | return frame->module == root_module() | |
1422 | ? L0 | |
1423 | : list_new( object_copy( frame->module->name ) ); | |
1424 | } | |
1425 | ||
1426 | ||
1427 | /* | |
1428 | * Return the current working directory. | |
1429 | * | |
1430 | * Usage: pwd = [ PWD ] ; | |
1431 | */ | |
1432 | ||
1433 | LIST * builtin_pwd( FRAME * frame, int flags ) | |
1434 | { | |
1435 | return list_new( object_copy( cwd() ) ); | |
1436 | } | |
1437 | ||
1438 | ||
1439 | /* | |
1440 | * Adds targets to the list of target that jam will attempt to update. | |
1441 | */ | |
1442 | ||
1443 | LIST * builtin_update( FRAME * frame, int flags ) | |
1444 | { | |
1445 | LIST * result = list_copy( targets_to_update() ); | |
1446 | LIST * arg1 = lol_get( frame->args, 0 ); | |
1447 | LISTITER iter = list_begin( arg1 ), end = list_end( arg1 ); | |
1448 | clear_targets_to_update(); | |
1449 | for ( ; iter != end; iter = list_next( iter ) ) | |
1450 | mark_target_for_updating( object_copy( list_item( iter ) ) ); | |
1451 | return result; | |
1452 | } | |
1453 | ||
1454 | extern int anyhow; | |
1455 | int last_update_now_status; | |
1456 | ||
1457 | /* Takes a list of target names and immediately updates them. | |
1458 | * | |
1459 | * Parameters: | |
1460 | * 1. Target list. | |
1461 | * 2. Optional file descriptor (converted to a string) for a log file where all | |
1462 | * the related build output should be redirected. | |
1463 | * 3. If specified, makes the build temporarily disable the -n option, i.e. | |
1464 | * forces all needed out-of-date targets to be rebuilt. | |
1465 | * 4. If specified, makes the build temporarily disable the -q option, i.e. | |
1466 | * forces the build to continue even if one of the targets fails to build. | |
1467 | */ | |
1468 | LIST * builtin_update_now( FRAME * frame, int flags ) | |
1469 | { | |
1470 | LIST * targets = lol_get( frame->args, 0 ); | |
1471 | LIST * log = lol_get( frame->args, 1 ); | |
1472 | LIST * force = lol_get( frame->args, 2 ); | |
1473 | LIST * continue_ = lol_get( frame->args, 3 ); | |
1474 | int status; | |
1475 | int original_stdout = 0; | |
1476 | int original_stderr = 0; | |
1477 | int original_noexec = 0; | |
1478 | int original_quitquick = 0; | |
1479 | ||
1480 | if ( !list_empty( log ) ) | |
1481 | { | |
1482 | /* Temporarily redirect stdout and stderr to the given log file. */ | |
1483 | int const fd = atoi( object_str( list_front( log ) ) ); | |
1484 | original_stdout = dup( 0 ); | |
1485 | original_stderr = dup( 1 ); | |
1486 | dup2( fd, 0 ); | |
1487 | dup2( fd, 1 ); | |
1488 | } | |
1489 | ||
1490 | if ( !list_empty( force ) ) | |
1491 | { | |
1492 | original_noexec = globs.noexec; | |
1493 | globs.noexec = 0; | |
1494 | } | |
1495 | ||
1496 | if ( !list_empty( continue_ ) ) | |
1497 | { | |
1498 | original_quitquick = globs.quitquick; | |
1499 | globs.quitquick = 0; | |
1500 | } | |
1501 | ||
1502 | status = make( targets, anyhow ); | |
1503 | ||
1504 | if ( !list_empty( force ) ) | |
1505 | { | |
1506 | globs.noexec = original_noexec; | |
1507 | } | |
1508 | ||
1509 | if ( !list_empty( continue_ ) ) | |
1510 | { | |
1511 | globs.quitquick = original_quitquick; | |
1512 | } | |
1513 | ||
1514 | if ( !list_empty( log ) ) | |
1515 | { | |
1516 | /* Flush whatever stdio might have buffered, while descriptions 0 and 1 | |
1517 | * still refer to the log file. | |
1518 | */ | |
1519 | out_flush( ); | |
1520 | err_flush( ); | |
1521 | dup2( original_stdout, 0 ); | |
1522 | dup2( original_stderr, 1 ); | |
1523 | close( original_stdout ); | |
1524 | close( original_stderr ); | |
1525 | } | |
1526 | ||
1527 | last_update_now_status = status; | |
1528 | ||
1529 | return status ? L0 : list_new( object_copy( constant_ok ) ); | |
1530 | } | |
1531 | ||
1532 | ||
1533 | LIST * builtin_import_module( FRAME * frame, int flags ) | |
1534 | { | |
1535 | LIST * const arg1 = lol_get( frame->args, 0 ); | |
1536 | LIST * const arg2 = lol_get( frame->args, 1 ); | |
1537 | module_t * const m = list_empty( arg2 ) | |
1538 | ? root_module() | |
1539 | : bindmodule( list_front( arg2 ) ); | |
1540 | import_module( arg1, m ); | |
1541 | return L0; | |
1542 | } | |
1543 | ||
1544 | ||
1545 | LIST * builtin_imported_modules( FRAME * frame, int flags ) | |
1546 | { | |
1547 | LIST * const arg0 = lol_get( frame->args, 0 ); | |
1548 | OBJECT * const module = list_empty( arg0 ) ? 0 : list_front( arg0 ); | |
1549 | return imported_modules( bindmodule( module ) ); | |
1550 | } | |
1551 | ||
1552 | ||
1553 | LIST * builtin_instance( FRAME * frame, int flags ) | |
1554 | { | |
1555 | LIST * arg1 = lol_get( frame->args, 0 ); | |
1556 | LIST * arg2 = lol_get( frame->args, 1 ); | |
1557 | module_t * const instance = bindmodule( list_front( arg1 ) ); | |
1558 | module_t * const class_module = bindmodule( list_front( arg2 ) ); | |
1559 | instance->class_module = class_module; | |
1560 | module_set_fixed_variables( instance, class_module->num_fixed_variables ); | |
1561 | return L0; | |
1562 | } | |
1563 | ||
1564 | ||
1565 | LIST * builtin_sort( FRAME * frame, int flags ) | |
1566 | { | |
1567 | return list_sort( lol_get( frame->args, 0 ) ); | |
1568 | } | |
1569 | ||
1570 | ||
1571 | LIST * builtin_normalize_path( FRAME * frame, int flags ) | |
1572 | { | |
1573 | LIST * arg = lol_get( frame->args, 0 ); | |
1574 | ||
1575 | /* First, we iterate over all '/'-separated elements, starting from the end | |
1576 | * of string. If we see a '..', we remove a preceeding path element. If we | |
1577 | * see '.', we remove it. Removal is done by overwriting data using '\1' | |
1578 | * characters. After the whole string has been processed, we do a second | |
1579 | * pass, removing any entered '\1' characters. | |
1580 | */ | |
1581 | ||
1582 | string in[ 1 ]; | |
1583 | string out[ 1 ]; | |
1584 | /* Last character of the part of string still to be processed. */ | |
1585 | char * end; | |
1586 | /* Working pointer. */ | |
1587 | char * current; | |
1588 | /* Number of '..' elements seen and not processed yet. */ | |
1589 | int dotdots = 0; | |
1590 | int rooted = 0; | |
1591 | OBJECT * result = 0; | |
1592 | LISTITER arg_iter = list_begin( arg ); | |
1593 | LISTITER arg_end = list_end( arg ); | |
1594 | ||
1595 | /* Make a copy of input: we should not change it. Prepend a '/' before it as | |
1596 | * a guard for the algorithm later on and remember whether it was originally | |
1597 | * rooted or not. | |
1598 | */ | |
1599 | string_new( in ); | |
1600 | string_push_back( in, '/' ); | |
1601 | for ( ; arg_iter != arg_end; arg_iter = list_next( arg_iter ) ) | |
1602 | { | |
1603 | if ( object_str( list_item( arg_iter ) )[ 0 ] != '\0' ) | |
1604 | { | |
1605 | if ( in->size == 1 ) | |
1606 | rooted = ( object_str( list_item( arg_iter ) )[ 0 ] == '/' ) || | |
1607 | ( object_str( list_item( arg_iter ) )[ 0 ] == '\\' ); | |
1608 | else | |
1609 | string_append( in, "/" ); | |
1610 | string_append( in, object_str( list_item( arg_iter ) ) ); | |
1611 | } | |
1612 | } | |
1613 | ||
1614 | /* Convert \ into /. On Windows, paths using / and \ are equivalent, and we | |
1615 | * want this function to obtain a canonic representation. | |
1616 | */ | |
1617 | for ( current = in->value, end = in->value + in->size; | |
1618 | current < end; ++current ) | |
1619 | if ( *current == '\\' ) | |
1620 | *current = '/'; | |
1621 | ||
1622 | /* Now we remove any extra path elements by overwriting them with '\1' | |
1623 | * characters and cound how many more unused '..' path elements there are | |
1624 | * remaining. Note that each remaining path element with always starts with | |
1625 | * a '/' character. | |
1626 | */ | |
1627 | for ( end = in->value + in->size - 1; end >= in->value; ) | |
1628 | { | |
1629 | /* Set 'current' to the next occurence of '/', which always exists. */ | |
1630 | for ( current = end; *current != '/'; --current ); | |
1631 | ||
1632 | if ( current == end ) | |
1633 | { | |
1634 | /* Found a trailing or duplicate '/'. Remove it. */ | |
1635 | *current = '\1'; | |
1636 | } | |
1637 | else if ( ( end - current == 1 ) && ( *( current + 1 ) == '.' ) ) | |
1638 | { | |
1639 | /* Found '/.'. Remove them all. */ | |
1640 | *current = '\1'; | |
1641 | *(current + 1) = '\1'; | |
1642 | } | |
1643 | else if ( ( end - current == 2 ) && ( *( current + 1 ) == '.' ) && | |
1644 | ( *( current + 2 ) == '.' ) ) | |
1645 | { | |
1646 | /* Found '/..'. Remove them all. */ | |
1647 | *current = '\1'; | |
1648 | *(current + 1) = '\1'; | |
1649 | *(current + 2) = '\1'; | |
1650 | ++dotdots; | |
1651 | } | |
1652 | else if ( dotdots ) | |
1653 | { | |
1654 | memset( current, '\1', end - current + 1 ); | |
1655 | --dotdots; | |
1656 | } | |
1657 | end = current - 1; | |
1658 | } | |
1659 | ||
1660 | string_new( out ); | |
1661 | ||
1662 | /* Now we know that we need to add exactly dotdots '..' path elements to the | |
1663 | * front and that our string is either empty or has a '/' as its first | |
1664 | * significant character. If we have any dotdots remaining then the passed | |
1665 | * path must not have been rooted or else it is invalid we return an empty | |
1666 | * list. | |
1667 | */ | |
1668 | if ( dotdots ) | |
1669 | { | |
1670 | if ( rooted ) | |
1671 | { | |
1672 | string_free( out ); | |
1673 | string_free( in ); | |
1674 | return L0; | |
1675 | } | |
1676 | do | |
1677 | string_append( out, "/.." ); | |
1678 | while ( --dotdots ); | |
1679 | } | |
1680 | ||
1681 | /* Now we actually remove all the path characters marked for removal. */ | |
1682 | for ( current = in->value; *current; ++current ) | |
1683 | if ( *current != '\1' ) | |
1684 | string_push_back( out, *current ); | |
1685 | ||
1686 | /* Here we know that our string contains no '\1' characters and is either | |
1687 | * empty or has a '/' as its initial character. If the original path was not | |
1688 | * rooted and we have a non-empty path we need to drop the initial '/'. If | |
1689 | * the original path was rooted and we have an empty path we need to add | |
1690 | * back the '/'. | |
1691 | */ | |
1692 | result = object_new( out->size | |
1693 | ? out->value + !rooted | |
1694 | : ( rooted ? "/" : "." ) ); | |
1695 | ||
1696 | string_free( out ); | |
1697 | string_free( in ); | |
1698 | ||
1699 | return list_new( result ); | |
1700 | } | |
1701 | ||
1702 | ||
1703 | LIST * builtin_native_rule( FRAME * frame, int flags ) | |
1704 | { | |
1705 | LIST * module_name = lol_get( frame->args, 0 ); | |
1706 | LIST * rule_name = lol_get( frame->args, 1 ); | |
1707 | ||
1708 | module_t * module = bindmodule( list_front( module_name ) ); | |
1709 | ||
1710 | native_rule_t * np; | |
1711 | if ( module->native_rules && (np = (native_rule_t *)hash_find( | |
1712 | module->native_rules, list_front( rule_name ) ) ) ) | |
1713 | { | |
1714 | new_rule_body( module, np->name, np->procedure, 1 ); | |
1715 | } | |
1716 | else | |
1717 | { | |
1718 | backtrace_line( frame->prev ); | |
1719 | out_printf( "error: no native rule \"%s\" defined in module \"%s.\"\n", | |
1720 | object_str( list_front( rule_name ) ), object_str( module->name ) ); | |
1721 | backtrace( frame->prev ); | |
1722 | exit( EXITBAD ); | |
1723 | } | |
1724 | return L0; | |
1725 | } | |
1726 | ||
1727 | ||
1728 | LIST * builtin_has_native_rule( FRAME * frame, int flags ) | |
1729 | { | |
1730 | LIST * module_name = lol_get( frame->args, 0 ); | |
1731 | LIST * rule_name = lol_get( frame->args, 1 ); | |
1732 | LIST * version = lol_get( frame->args, 2 ); | |
1733 | ||
1734 | module_t * module = bindmodule( list_front( module_name ) ); | |
1735 | ||
1736 | native_rule_t * np; | |
1737 | if ( module->native_rules && (np = (native_rule_t *)hash_find( | |
1738 | module->native_rules, list_front( rule_name ) ) ) ) | |
1739 | { | |
1740 | int expected_version = atoi( object_str( list_front( version ) ) ); | |
1741 | if ( np->version == expected_version ) | |
1742 | return list_new( object_copy( constant_true ) ); | |
1743 | } | |
1744 | return L0; | |
1745 | } | |
1746 | ||
1747 | ||
1748 | LIST * builtin_user_module( FRAME * frame, int flags ) | |
1749 | { | |
1750 | LIST * const module_name = lol_get( frame->args, 0 ); | |
1751 | LISTITER iter = list_begin( module_name ); | |
1752 | LISTITER const end = list_end( module_name ); | |
1753 | for ( ; iter != end; iter = list_next( iter ) ) | |
1754 | bindmodule( list_item( iter ) )->user_module = 1; | |
1755 | return L0; | |
1756 | } | |
1757 | ||
1758 | ||
1759 | LIST * builtin_nearest_user_location( FRAME * frame, int flags ) | |
1760 | { | |
1761 | FRAME * const nearest_user_frame = frame->module->user_module | |
1762 | ? frame | |
1763 | : frame->prev_user; | |
1764 | if ( !nearest_user_frame ) | |
1765 | return L0; | |
1766 | ||
1767 | { | |
1768 | LIST * result = L0; | |
1769 | char const * file; | |
1770 | int line; | |
1771 | char buf[ 32 ]; | |
1772 | ||
1773 | get_source_line( nearest_user_frame, &file, &line ); | |
1774 | sprintf( buf, "%d", line ); | |
1775 | result = list_push_back( result, object_new( file ) ); | |
1776 | result = list_push_back( result, object_new( buf ) ); | |
1777 | return result; | |
1778 | } | |
1779 | } | |
1780 | ||
1781 | ||
1782 | LIST * builtin_check_if_file( FRAME * frame, int flags ) | |
1783 | { | |
1784 | LIST * const name = lol_get( frame->args, 0 ); | |
1785 | return file_is_file( list_front( name ) ) == 1 | |
1786 | ? list_new( object_copy( constant_true ) ) | |
1787 | : L0; | |
1788 | } | |
1789 | ||
1790 | ||
1791 | LIST * builtin_md5( FRAME * frame, int flags ) | |
1792 | { | |
1793 | LIST * l = lol_get( frame->args, 0 ); | |
1794 | char const * s = object_str( list_front( l ) ); | |
1795 | ||
1796 | md5_state_t state; | |
1797 | md5_byte_t digest[ 16 ]; | |
1798 | char hex_output[ 16 * 2 + 1 ]; | |
1799 | ||
1800 | int di; | |
1801 | ||
1802 | md5_init( &state ); | |
1803 | md5_append( &state, (md5_byte_t const *)s, strlen( s ) ); | |
1804 | md5_finish( &state, digest ); | |
1805 | ||
1806 | for ( di = 0; di < 16; ++di ) | |
1807 | sprintf( hex_output + di * 2, "%02x", digest[ di ] ); | |
1808 | ||
1809 | return list_new( object_new( hex_output ) ); | |
1810 | } | |
1811 | ||
1812 | ||
1813 | LIST * builtin_file_open( FRAME * frame, int flags ) | |
1814 | { | |
1815 | char const * name = object_str( list_front( lol_get( frame->args, 0 ) ) ); | |
1816 | char const * mode = object_str( list_front( lol_get( frame->args, 1 ) ) ); | |
1817 | int fd; | |
1818 | char buffer[ sizeof( "4294967295" ) ]; | |
1819 | ||
1820 | if ( strcmp(mode, "w") == 0 ) | |
1821 | fd = open( name, O_WRONLY|O_CREAT|O_TRUNC, 0666 ); | |
1822 | else | |
1823 | fd = open( name, O_RDONLY ); | |
1824 | ||
1825 | if ( fd != -1 ) | |
1826 | { | |
1827 | sprintf( buffer, "%d", fd ); | |
1828 | return list_new( object_new( buffer ) ); | |
1829 | } | |
1830 | return L0; | |
1831 | } | |
1832 | ||
1833 | ||
1834 | LIST * builtin_pad( FRAME * frame, int flags ) | |
1835 | { | |
1836 | OBJECT * string = list_front( lol_get( frame->args, 0 ) ); | |
1837 | char const * width_s = object_str( list_front( lol_get( frame->args, 1 ) ) ); | |
1838 | ||
1839 | int current = strlen( object_str( string ) ); | |
1840 | int desired = atoi( width_s ); | |
1841 | if ( current >= desired ) | |
1842 | return list_new( object_copy( string ) ); | |
1843 | else | |
1844 | { | |
1845 | char * buffer = BJAM_MALLOC( desired + 1 ); | |
1846 | int i; | |
1847 | LIST * result; | |
1848 | ||
1849 | strcpy( buffer, object_str( string ) ); | |
1850 | for ( i = current; i < desired; ++i ) | |
1851 | buffer[ i ] = ' '; | |
1852 | buffer[ desired ] = '\0'; | |
1853 | result = list_new( object_new( buffer ) ); | |
1854 | BJAM_FREE( buffer ); | |
1855 | return result; | |
1856 | } | |
1857 | } | |
1858 | ||
1859 | ||
1860 | LIST * builtin_precious( FRAME * frame, int flags ) | |
1861 | { | |
1862 | LIST * targets = lol_get( frame->args, 0 ); | |
1863 | LISTITER iter = list_begin( targets ); | |
1864 | LISTITER const end = list_end( targets ); | |
1865 | for ( ; iter != end; iter = list_next( iter ) ) | |
1866 | bindtarget( list_item( iter ) )->flags |= T_FLAG_PRECIOUS; | |
1867 | return L0; | |
1868 | } | |
1869 | ||
1870 | ||
1871 | LIST * builtin_self_path( FRAME * frame, int flags ) | |
1872 | { | |
1873 | extern char const * saved_argv0; | |
1874 | char * p = executable_path( saved_argv0 ); | |
1875 | if ( p ) | |
1876 | { | |
1877 | LIST * const result = list_new( object_new( p ) ); | |
1878 | free( p ); | |
1879 | return result; | |
1880 | } | |
1881 | return L0; | |
1882 | } | |
1883 | ||
1884 | ||
1885 | LIST * builtin_makedir( FRAME * frame, int flags ) | |
1886 | { | |
1887 | LIST * const path = lol_get( frame->args, 0 ); | |
1888 | return file_mkdir( object_str( list_front( path ) ) ) | |
1889 | ? L0 | |
1890 | : list_new( object_copy( list_front( path ) ) ); | |
1891 | } | |
1892 | ||
1893 | LIST *builtin_readlink( FRAME * frame, int flags ) | |
1894 | { | |
1895 | const char * path = object_str( list_front( lol_get( frame->args, 0 ) ) ); | |
1896 | #ifdef OS_NT | |
1897 | ||
1898 | /* This struct is declared in ntifs.h which is | |
1899 | * part of the Windows Driver Kit. | |
1900 | */ | |
1901 | typedef struct _REPARSE_DATA_BUFFER { | |
1902 | ULONG ReparseTag; | |
1903 | USHORT ReparseDataLength; | |
1904 | USHORT Reserved; | |
1905 | union { | |
1906 | struct { | |
1907 | USHORT SubstituteNameOffset; | |
1908 | USHORT SubstituteNameLength; | |
1909 | USHORT PrintNameOffset; | |
1910 | USHORT PrintNameLength; | |
1911 | ULONG Flags; | |
1912 | WCHAR PathBuffer[ 1 ]; | |
1913 | } SymbolicLinkReparseBuffer; | |
1914 | struct { | |
1915 | USHORT SubstituteNameOffset; | |
1916 | USHORT SubstituteNameLength; | |
1917 | USHORT PrintNameOffset; | |
1918 | USHORT PrintNameLength; | |
1919 | WCHAR PathBuffer[ 1 ]; | |
1920 | } MountPointReparseBuffer; | |
1921 | struct { | |
1922 | UCHAR DataBuffer[ 1 ]; | |
1923 | } GenericReparseBuffer; | |
1924 | }; | |
1925 | } REPARSE_DATA_BUFFER; | |
1926 | ||
1927 | HANDLE hLink = CreateFileA( path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL ); | |
1928 | DWORD n; | |
1929 | union { | |
1930 | REPARSE_DATA_BUFFER reparse; | |
1931 | char data[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; | |
1932 | } buf; | |
1933 | int okay = DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, &buf, sizeof(buf), &n, NULL); | |
1934 | ||
1935 | CloseHandle( hLink ); | |
1936 | ||
1937 | if (okay && buf.reparse.ReparseTag == IO_REPARSE_TAG_SYMLINK ) | |
1938 | { | |
1939 | int index = buf.reparse.SymbolicLinkReparseBuffer.SubstituteNameOffset / 2; | |
1940 | int length = buf.reparse.SymbolicLinkReparseBuffer.SubstituteNameLength / 2; | |
1941 | char cbuf[MAX_PATH + 1]; | |
1942 | int numchars = WideCharToMultiByte( CP_ACP, 0, buf.reparse.SymbolicLinkReparseBuffer.PathBuffer + index, length, cbuf, sizeof(cbuf), NULL, NULL ); | |
1943 | if( numchars >= sizeof(cbuf) ) | |
1944 | { | |
1945 | return 0; | |
1946 | } | |
1947 | cbuf[numchars] = '\0'; | |
1948 | return list_new( object_new( cbuf ) ); | |
1949 | } | |
1950 | else if( okay && buf.reparse.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT ) | |
1951 | { | |
1952 | int index = buf.reparse.MountPointReparseBuffer.SubstituteNameOffset / 2; | |
1953 | int length = buf.reparse.MountPointReparseBuffer.SubstituteNameLength / 2; | |
1954 | char cbuf[MAX_PATH + 1]; | |
1955 | const char * result; | |
1956 | int numchars = WideCharToMultiByte( CP_ACP, 0, buf.reparse.MountPointReparseBuffer.PathBuffer + index, length, cbuf, sizeof(cbuf), NULL, NULL ); | |
1957 | if( numchars >= sizeof(cbuf) ) | |
1958 | { | |
1959 | return 0; | |
1960 | } | |
1961 | cbuf[numchars] = '\0'; | |
1962 | /* strip off the leading "\??\" */ | |
1963 | result = cbuf; | |
1964 | if ( cbuf[ 0 ] == '\\' && cbuf[ 1 ] == '?' && | |
1965 | cbuf[ 2 ] == '?' && cbuf[ 3 ] == '\\' && | |
1966 | cbuf[ 4 ] != '\0' && cbuf[ 5 ] == ':' ) | |
1967 | { | |
1968 | result += 4; | |
1969 | } | |
1970 | return list_new( object_new( result ) ); | |
1971 | } | |
1972 | return 0; | |
1973 | #else | |
1974 | char static_buf[256]; | |
1975 | char * buf = static_buf; | |
1976 | size_t bufsize = 256; | |
1977 | LIST * result = 0; | |
1978 | while (1) { | |
1979 | ssize_t len = readlink( path, buf, bufsize ); | |
1980 | if ( len < 0 ) | |
1981 | { | |
1982 | break; | |
1983 | } | |
1984 | else if ( len < bufsize ) | |
1985 | { | |
1986 | buf[ len ] = '\0'; | |
1987 | result = list_new( object_new( buf ) ); | |
1988 | break; | |
1989 | } | |
1990 | if ( buf != static_buf ) | |
1991 | BJAM_FREE( buf ); | |
1992 | bufsize *= 2; | |
1993 | buf = BJAM_MALLOC( bufsize ); | |
1994 | } | |
b32b8144 | 1995 | |
7c673cae FG |
1996 | if ( buf != static_buf ) |
1997 | BJAM_FREE( buf ); | |
1998 | ||
1999 | return result; | |
2000 | #endif | |
2001 | } | |
2002 | ||
b32b8144 FG |
2003 | #ifdef JAM_DEBUGGER |
2004 | ||
2005 | LIST *builtin_debug_print_helper( FRAME * frame, int flags ) | |
2006 | { | |
2007 | debug_print_result = list_copy( lol_get( frame->args, 0 ) ); | |
2008 | return L0; | |
2009 | } | |
2010 | ||
2011 | #endif | |
7c673cae FG |
2012 | |
2013 | #ifdef HAVE_PYTHON | |
2014 | ||
2015 | LIST * builtin_python_import_rule( FRAME * frame, int flags ) | |
2016 | { | |
2017 | static int first_time = 1; | |
2018 | char const * python_module = object_str( list_front( lol_get( frame->args, | |
2019 | 0 ) ) ); | |
2020 | char const * python_function = object_str( list_front( lol_get( frame->args, | |
2021 | 1 ) ) ); | |
2022 | OBJECT * jam_module = list_front( lol_get( frame->args, 2 ) ); | |
2023 | OBJECT * jam_rule = list_front( lol_get( frame->args, 3 ) ); | |
2024 | ||
2025 | PyObject * pName; | |
2026 | PyObject * pModule; | |
2027 | PyObject * pDict; | |
2028 | PyObject * pFunc; | |
2029 | ||
2030 | if ( first_time ) | |
2031 | { | |
2032 | /* At the first invocation, we add the value of the global | |
2033 | * EXTRA_PYTHONPATH to the sys.path Python variable. | |
2034 | */ | |
2035 | LIST * extra = 0; | |
2036 | module_t * outer_module = frame->module; | |
2037 | LISTITER iter, end; | |
2038 | ||
2039 | first_time = 0; | |
2040 | ||
2041 | extra = var_get( root_module(), constant_extra_pythonpath ); | |
2042 | ||
2043 | iter = list_begin( extra ), end = list_end( extra ); | |
2044 | for ( ; iter != end; iter = list_next( iter ) ) | |
2045 | { | |
2046 | string buf[ 1 ]; | |
2047 | string_new( buf ); | |
2048 | string_append( buf, "import sys\nsys.path.append(\"" ); | |
2049 | string_append( buf, object_str( list_item( iter ) ) ); | |
2050 | string_append( buf, "\")\n" ); | |
2051 | PyRun_SimpleString( buf->value ); | |
2052 | string_free( buf ); | |
2053 | } | |
2054 | } | |
2055 | ||
2056 | pName = PyString_FromString( python_module ); | |
2057 | pModule = PyImport_Import( pName ); | |
2058 | Py_DECREF( pName ); | |
2059 | ||
2060 | if ( pModule != NULL ) | |
2061 | { | |
2062 | pDict = PyModule_GetDict( pModule ); | |
2063 | pFunc = PyDict_GetItemString( pDict, python_function ); | |
2064 | ||
2065 | if ( pFunc && PyCallable_Check( pFunc ) ) | |
2066 | { | |
2067 | module_t * m = bindmodule( jam_module ); | |
2068 | new_rule_body( m, jam_rule, function_python( pFunc, 0 ), 0 ); | |
2069 | } | |
2070 | else | |
2071 | { | |
2072 | if ( PyErr_Occurred() ) | |
2073 | PyErr_Print(); | |
2074 | err_printf( "Cannot find function \"%s\"\n", python_function ); | |
2075 | } | |
2076 | Py_DECREF( pModule ); | |
2077 | } | |
2078 | else | |
2079 | { | |
2080 | PyErr_Print(); | |
2081 | err_printf( "Failed to load \"%s\"\n", python_module ); | |
2082 | } | |
2083 | return L0; | |
2084 | ||
2085 | } | |
2086 | ||
2087 | #endif /* #ifdef HAVE_PYTHON */ | |
2088 | ||
2089 | ||
2090 | void lol_build( LOL * lol, char const * * elements ) | |
2091 | { | |
2092 | LIST * l = L0; | |
2093 | lol_init( lol ); | |
2094 | ||
2095 | while ( elements && *elements ) | |
2096 | { | |
2097 | if ( !strcmp( *elements, ":" ) ) | |
2098 | { | |
2099 | lol_add( lol, l ); | |
2100 | l = L0; | |
2101 | } | |
2102 | else | |
2103 | { | |
2104 | l = list_push_back( l, object_new( *elements ) ); | |
2105 | } | |
2106 | ++elements; | |
2107 | } | |
2108 | ||
2109 | if ( l != L0 ) | |
2110 | lol_add( lol, l ); | |
2111 | } | |
2112 | ||
2113 | ||
2114 | #ifdef HAVE_PYTHON | |
2115 | ||
2116 | static LIST *jam_list_from_string(PyObject *a) | |
2117 | { | |
2118 | return list_new( object_new( PyString_AsString( a ) ) ); | |
2119 | } | |
2120 | ||
2121 | static LIST *jam_list_from_sequence(PyObject *a) | |
2122 | { | |
2123 | LIST * l = 0; | |
2124 | ||
2125 | int i = 0; | |
2126 | int s = PySequence_Size( a ); | |
2127 | ||
2128 | for ( ; i < s; ++i ) | |
2129 | { | |
2130 | /* PySequence_GetItem returns new reference. */ | |
2131 | PyObject * e = PySequence_GetItem( a, i ); | |
2132 | char * s = PyString_AsString( e ); | |
2133 | if ( !s ) | |
2134 | { | |
2135 | /* try to get the repr() on the object */ | |
2136 | PyObject *repr = PyObject_Repr(e); | |
2137 | if (repr) | |
2138 | { | |
2139 | const char *str = PyString_AsString(repr); | |
2140 | PyErr_Format(PyExc_TypeError, "expecting type <str> got %s", str); | |
2141 | } | |
2142 | /* fall back to a dumb error */ | |
2143 | else | |
2144 | { | |
2145 | PyErr_BadArgument(); | |
2146 | } | |
2147 | return NULL; | |
2148 | } | |
2149 | l = list_push_back( l, object_new( s ) ); | |
2150 | Py_DECREF( e ); | |
2151 | } | |
2152 | ||
2153 | return l; | |
2154 | } | |
2155 | ||
2156 | static void make_jam_arguments_from_python(FRAME* inner, PyObject *args) | |
2157 | { | |
2158 | int i; | |
2159 | int size; | |
2160 | ||
2161 | /* Build up the list of arg lists. */ | |
2162 | frame_init( inner ); | |
2163 | inner->prev = 0; | |
2164 | inner->prev_user = 0; | |
2165 | inner->module = bindmodule( constant_python_interface ); | |
2166 | ||
2167 | size = PyTuple_Size( args ); | |
2168 | for (i = 0 ; i < size; ++i) | |
2169 | { | |
2170 | PyObject * a = PyTuple_GetItem( args, i ); | |
2171 | if ( PyString_Check( a ) ) | |
2172 | { | |
2173 | lol_add( inner->args, jam_list_from_string(a) ); | |
2174 | } | |
2175 | else if ( PySequence_Check( a ) ) | |
2176 | { | |
2177 | lol_add( inner->args, jam_list_from_sequence(a) ); | |
2178 | } | |
2179 | } | |
2180 | } | |
2181 | ||
2182 | ||
2183 | /* | |
2184 | * Calls the bjam rule specified by name passed in 'args'. The name is looked up | |
2185 | * in the context of bjam's 'python_interface' module. Returns the list of | |
2186 | * strings returned by the rule. | |
2187 | */ | |
2188 | ||
2189 | PyObject * bjam_call( PyObject * self, PyObject * args ) | |
2190 | { | |
2191 | FRAME inner[ 1 ]; | |
2192 | LIST * result; | |
2193 | PARSE * p; | |
2194 | OBJECT * rulename; | |
2195 | PyObject *args_proper; | |
2196 | ||
2197 | /* PyTuple_GetItem returns borrowed reference. */ | |
2198 | rulename = object_new( PyString_AsString( PyTuple_GetItem( args, 0 ) ) ); | |
2199 | ||
2200 | args_proper = PyTuple_GetSlice(args, 1, PyTuple_Size(args)); | |
2201 | make_jam_arguments_from_python (inner, args_proper); | |
2202 | if ( PyErr_Occurred() ) | |
2203 | { | |
2204 | return NULL; | |
2205 | } | |
2206 | Py_DECREF(args_proper); | |
2207 | ||
2208 | result = evaluate_rule( bindrule( rulename, inner->module), rulename, inner ); | |
2209 | object_free( rulename ); | |
2210 | ||
2211 | frame_free( inner ); | |
2212 | ||
2213 | /* Convert the bjam list into a Python list result. */ | |
2214 | { | |
2215 | PyObject * const pyResult = PyList_New( list_length( result ) ); | |
2216 | int i = 0; | |
2217 | LISTITER iter = list_begin( result ); | |
2218 | LISTITER const end = list_end( result ); | |
2219 | for ( ; iter != end; iter = list_next( iter ) ) | |
2220 | { | |
2221 | PyList_SetItem( pyResult, i, PyString_FromString( object_str( | |
2222 | list_item( iter ) ) ) ); | |
2223 | i += 1; | |
2224 | } | |
2225 | list_free( result ); | |
2226 | return pyResult; | |
2227 | } | |
2228 | } | |
2229 | ||
2230 | ||
2231 | /* | |
2232 | * Accepts four arguments: | |
2233 | * - module name | |
2234 | * - rule name, | |
2235 | * - Python callable. | |
2236 | * - (optional) bjam language function signature. | |
2237 | * Creates a bjam rule with the specified name in the specified module, which | |
2238 | * will invoke the Python callable. | |
2239 | */ | |
2240 | ||
2241 | PyObject * bjam_import_rule( PyObject * self, PyObject * args ) | |
2242 | { | |
2243 | char * module; | |
2244 | char * rule; | |
2245 | PyObject * func; | |
2246 | PyObject * bjam_signature = NULL; | |
2247 | module_t * m; | |
2248 | RULE * r; | |
2249 | OBJECT * module_name; | |
2250 | OBJECT * rule_name; | |
2251 | ||
2252 | if ( !PyArg_ParseTuple( args, "ssO|O:import_rule", | |
2253 | &module, &rule, &func, &bjam_signature ) ) | |
2254 | return NULL; | |
2255 | ||
2256 | if ( !PyCallable_Check( func ) ) | |
2257 | { | |
2258 | PyErr_SetString( PyExc_RuntimeError, "Non-callable object passed to " | |
2259 | "bjam.import_rule" ); | |
2260 | return NULL; | |
2261 | } | |
2262 | ||
2263 | module_name = *module ? object_new( module ) : 0; | |
2264 | m = bindmodule( module_name ); | |
2265 | if ( module_name ) | |
2266 | object_free( module_name ); | |
2267 | rule_name = object_new( rule ); | |
2268 | new_rule_body( m, rule_name, function_python( func, bjam_signature ), 0 ); | |
2269 | object_free( rule_name ); | |
2270 | ||
2271 | Py_INCREF( Py_None ); | |
2272 | return Py_None; | |
2273 | } | |
2274 | ||
2275 | ||
2276 | /* | |
2277 | * Accepts four arguments: | |
2278 | * - an action name | |
2279 | * - an action body | |
2280 | * - a list of variable that will be bound inside the action | |
2281 | * - integer flags. | |
2282 | * Defines an action on bjam side. | |
2283 | */ | |
2284 | ||
2285 | PyObject * bjam_define_action( PyObject * self, PyObject * args ) | |
2286 | { | |
2287 | char * name; | |
2288 | char * body; | |
2289 | module_t * m; | |
2290 | PyObject * bindlist_python; | |
2291 | int flags; | |
2292 | LIST * bindlist = L0; | |
2293 | int n; | |
2294 | int i; | |
2295 | OBJECT * name_str; | |
2296 | FUNCTION * body_func; | |
2297 | ||
2298 | if ( !PyArg_ParseTuple( args, "ssO!i:define_action", &name, &body, | |
2299 | &PyList_Type, &bindlist_python, &flags ) ) | |
2300 | return NULL; | |
2301 | ||
2302 | n = PyList_Size( bindlist_python ); | |
2303 | for ( i = 0; i < n; ++i ) | |
2304 | { | |
2305 | PyObject * next = PyList_GetItem( bindlist_python, i ); | |
2306 | if ( !PyString_Check( next ) ) | |
2307 | { | |
2308 | PyErr_SetString( PyExc_RuntimeError, "bind list has non-string " | |
2309 | "type" ); | |
2310 | return NULL; | |
2311 | } | |
2312 | bindlist = list_push_back( bindlist, object_new( PyString_AsString( next | |
2313 | ) ) ); | |
2314 | } | |
2315 | ||
2316 | name_str = object_new( name ); | |
2317 | body_func = function_compile_actions( body, constant_builtin, -1 ); | |
2318 | new_rule_actions( root_module(), name_str, body_func, bindlist, flags ); | |
2319 | function_free( body_func ); | |
2320 | object_free( name_str ); | |
2321 | ||
2322 | Py_INCREF( Py_None ); | |
2323 | return Py_None; | |
2324 | } | |
2325 | ||
2326 | ||
2327 | /* | |
2328 | * Returns the value of a variable in root Jam module. | |
2329 | */ | |
2330 | ||
2331 | PyObject * bjam_variable( PyObject * self, PyObject * args ) | |
2332 | { | |
2333 | char * name; | |
2334 | LIST * value; | |
2335 | PyObject * result; | |
2336 | int i; | |
2337 | OBJECT * varname; | |
2338 | LISTITER iter; | |
2339 | LISTITER end; | |
2340 | ||
2341 | if ( !PyArg_ParseTuple( args, "s", &name ) ) | |
2342 | return NULL; | |
2343 | ||
2344 | varname = object_new( name ); | |
2345 | value = var_get( root_module(), varname ); | |
2346 | object_free( varname ); | |
2347 | iter = list_begin( value ); | |
2348 | end = list_end( value ); | |
2349 | ||
2350 | result = PyList_New( list_length( value ) ); | |
2351 | for ( i = 0; iter != end; iter = list_next( iter ), ++i ) | |
2352 | PyList_SetItem( result, i, PyString_FromString( object_str( list_item( | |
2353 | iter ) ) ) ); | |
2354 | ||
2355 | return result; | |
2356 | } | |
2357 | ||
2358 | ||
2359 | PyObject * bjam_backtrace( PyObject * self, PyObject * args ) | |
2360 | { | |
2361 | PyObject * result = PyList_New( 0 ); | |
2362 | struct frame * f = frame_before_python_call; | |
2363 | ||
2364 | for ( ; f = f->prev; ) | |
2365 | { | |
2366 | PyObject * tuple = PyTuple_New( 4 ); | |
2367 | char const * file; | |
2368 | int line; | |
2369 | char buf[ 32 ]; | |
2370 | string module_name[ 1 ]; | |
2371 | ||
2372 | get_source_line( f, &file, &line ); | |
2373 | sprintf( buf, "%d", line ); | |
2374 | string_new( module_name ); | |
2375 | if ( f->module->name ) | |
2376 | { | |
2377 | string_append( module_name, object_str( f->module->name ) ); | |
2378 | string_append( module_name, "." ); | |
2379 | } | |
2380 | ||
2381 | /* PyTuple_SetItem steals reference. */ | |
2382 | PyTuple_SetItem( tuple, 0, PyString_FromString( file ) ); | |
2383 | PyTuple_SetItem( tuple, 1, PyString_FromString( buf ) ); | |
2384 | PyTuple_SetItem( tuple, 2, PyString_FromString( module_name->value ) ); | |
2385 | PyTuple_SetItem( tuple, 3, PyString_FromString( f->rulename ) ); | |
2386 | ||
2387 | string_free( module_name ); | |
2388 | ||
2389 | PyList_Append( result, tuple ); | |
2390 | Py_DECREF( tuple ); | |
2391 | } | |
2392 | return result; | |
2393 | } | |
2394 | ||
2395 | PyObject * bjam_caller( PyObject * self, PyObject * args ) | |
2396 | { | |
2397 | return PyString_FromString( frame_before_python_call->prev->module->name ? | |
2398 | object_str( frame_before_python_call->prev->module->name ) : "" ); | |
2399 | } | |
2400 | ||
2401 | #endif /* #ifdef HAVE_PYTHON */ | |
2402 | ||
2403 | ||
2404 | #ifdef HAVE_POPEN | |
2405 | ||
2406 | #if defined(_MSC_VER) || defined(__BORLANDC__) | |
2407 | #define popen windows_popen_wrapper | |
2408 | #define pclose _pclose | |
2409 | ||
2410 | /* | |
2411 | * This wrapper is a workaround for a funny _popen() feature on Windows | |
2412 | * where it eats external quotes in some cases. The bug seems to be related | |
2413 | * to the quote stripping functionality used by the Windows cmd.exe | |
2414 | * interpreter when its /S is not specified. | |
2415 | * | |
2416 | * Cleaned up quote from the cmd.exe help screen as displayed on Windows XP | |
2417 | * SP3: | |
2418 | * | |
2419 | * 1. If all of the following conditions are met, then quote characters on | |
2420 | * the command line are preserved: | |
2421 | * | |
2422 | * - no /S switch | |
2423 | * - exactly two quote characters | |
2424 | * - no special characters between the two quote characters, where | |
2425 | * special is one of: &<>()@^| | |
2426 | * - there are one or more whitespace characters between the two quote | |
2427 | * characters | |
2428 | * - the string between the two quote characters is the name of an | |
2429 | * executable file. | |
2430 | * | |
2431 | * 2. Otherwise, old behavior is to see if the first character is a quote | |
2432 | * character and if so, strip the leading character and remove the last | |
2433 | * quote character on the command line, preserving any text after the | |
2434 | * last quote character. | |
2435 | * | |
2436 | * This causes some commands containing quotes not to be executed correctly. | |
2437 | * For example: | |
2438 | * | |
2439 | * "\Long folder name\aaa.exe" --name="Jurko" --no-surname | |
2440 | * | |
2441 | * would get its outermost quotes stripped and would be executed as: | |
2442 | * | |
2443 | * \Long folder name\aaa.exe" --name="Jurko --no-surname | |
2444 | * | |
2445 | * which would report an error about '\Long' not being a valid command. | |
2446 | * | |
2447 | * cmd.exe help seems to indicate it would be enough to add an extra space | |
2448 | * character in front of the command to avoid this but this does not work, | |
2449 | * most likely due to the shell first stripping all leading whitespace | |
2450 | * characters from the command. | |
2451 | * | |
2452 | * Solution implemented here is to quote the whole command in case it | |
2453 | * contains any quote characters. Note thought this will not work correctly | |
2454 | * should Windows ever 'fix' this feature. | |
2455 | * (03.06.2008.) (Jurko) | |
2456 | */ | |
2457 | static FILE * windows_popen_wrapper( char const * command, | |
2458 | char const * mode ) | |
2459 | { | |
2460 | int const extra_command_quotes_needed = !!strchr( command, '"' ); | |
2461 | string quoted_command; | |
2462 | FILE * result; | |
2463 | ||
2464 | if ( extra_command_quotes_needed ) | |
2465 | { | |
2466 | string_new( "ed_command ); | |
2467 | string_append( "ed_command, "\"" ); | |
2468 | string_append( "ed_command, command ); | |
2469 | string_append( "ed_command, "\"" ); | |
2470 | command = quoted_command.value; | |
2471 | } | |
2472 | ||
2473 | result = _popen( command, "r" ); | |
2474 | ||
2475 | if ( extra_command_quotes_needed ) | |
2476 | string_free( "ed_command ); | |
2477 | ||
2478 | return result; | |
2479 | } | |
2480 | #endif /* defined(_MSC_VER) || defined(__BORLANDC__) */ | |
2481 | ||
2482 | ||
7c673cae FG |
2483 | LIST * builtin_shell( FRAME * frame, int flags ) |
2484 | { | |
2485 | LIST * command = lol_get( frame->args, 0 ); | |
2486 | LIST * result = L0; | |
2487 | string s; | |
2488 | int ret; | |
2489 | char buffer[ 1024 ]; | |
2490 | FILE * p = NULL; | |
2491 | int exit_status = -1; | |
2492 | int exit_status_opt = 0; | |
2493 | int no_output_opt = 0; | |
2494 | int strip_eol_opt = 0; | |
2495 | ||
2496 | /* Process the variable args options. */ | |
2497 | { | |
2498 | int a = 1; | |
2499 | LIST * arg = lol_get( frame->args, a ); | |
2500 | for ( ; !list_empty( arg ); arg = lol_get( frame->args, ++a ) ) | |
2501 | { | |
2502 | if ( !strcmp( "exit-status", object_str( list_front( arg ) ) ) ) | |
2503 | exit_status_opt = 1; | |
2504 | else if ( !strcmp( "no-output", object_str( list_front( arg ) ) ) ) | |
2505 | no_output_opt = 1; | |
2506 | else if ( !strcmp("strip-eol", object_str( list_front( arg ) ) ) ) | |
2507 | strip_eol_opt = 1; | |
2508 | } | |
2509 | } | |
2510 | ||
2511 | /* The following fflush() call seems to be indicated as a workaround for a | |
2512 | * popen() bug on POSIX implementations related to synhronizing input | |
2513 | * stream positions for the called and the calling process. | |
2514 | */ | |
2515 | fflush( NULL ); | |
2516 | ||
2517 | p = popen( object_str( list_front( command ) ), "r" ); | |
2518 | if ( p == NULL ) | |
2519 | return L0; | |
2520 | ||
2521 | string_new( &s ); | |
2522 | ||
2523 | while ( ( ret = fread( buffer, sizeof( char ), sizeof( buffer ) - 1, p ) ) > | |
2524 | 0 ) | |
2525 | { | |
2526 | buffer[ ret ] = 0; | |
2527 | if ( !no_output_opt ) | |
2528 | { | |
7c673cae FG |
2529 | string_append( &s, buffer ); |
2530 | } | |
2531 | ||
2532 | /* Explicit EOF check for systems with broken fread */ | |
2533 | if ( feof( p ) ) break; | |
2534 | } | |
2535 | ||
b32b8144 FG |
2536 | if ( strip_eol_opt ) |
2537 | string_rtrim( &s ); | |
2538 | ||
7c673cae FG |
2539 | exit_status = pclose( p ); |
2540 | ||
2541 | /* The command output is returned first. */ | |
2542 | result = list_new( object_new( s.value ) ); | |
2543 | string_free( &s ); | |
2544 | ||
2545 | /* The command exit result next. */ | |
2546 | if ( exit_status_opt ) | |
2547 | { | |
2548 | if ( WIFEXITED( exit_status ) ) | |
2549 | exit_status = WEXITSTATUS( exit_status ); | |
2550 | else | |
2551 | exit_status = -1; | |
2552 | ||
2553 | #ifdef OS_VMS | |
2554 | /* Harmonize VMS success status with POSIX */ | |
2555 | if ( exit_status == 1 ) exit_status = EXIT_SUCCESS; | |
2556 | #endif | |
2557 | sprintf( buffer, "%d", exit_status ); | |
2558 | result = list_push_back( result, object_new( buffer ) ); | |
2559 | } | |
2560 | ||
2561 | return result; | |
2562 | } | |
2563 | ||
2564 | #else /* #ifdef HAVE_POPEN */ | |
2565 | ||
2566 | LIST * builtin_shell( FRAME * frame, int flags ) | |
2567 | { | |
2568 | return L0; | |
2569 | } | |
2570 | ||
2571 | #endif /* #ifdef HAVE_POPEN */ | |
2572 | ||
2573 | ||
2574 | /* | |
2575 | * builtin_glob_archive() - GLOB_ARCHIVE rule | |
2576 | */ | |
2577 | ||
2578 | struct globbing2 | |
2579 | { | |
2580 | LIST * patterns[ 2 ]; | |
2581 | LIST * results; | |
2582 | LIST * case_insensitive; | |
2583 | }; | |
2584 | ||
2585 | ||
2586 | static void builtin_glob_archive_back( void * closure, OBJECT * member, | |
2587 | LIST * symbols, int status, timestamp const * const time ) | |
2588 | { | |
2589 | PROFILE_ENTER( BUILTIN_GLOB_ARCHIVE_BACK ); | |
2590 | ||
2591 | struct globbing2 * const globbing = (struct globbing2 *)closure; | |
2592 | PATHNAME f; | |
2593 | string buf[ 1 ]; | |
2594 | LISTITER iter; | |
2595 | LISTITER end; | |
2596 | LISTITER iter_symbols; | |
2597 | LISTITER end_symbols; | |
2598 | int matched = 0; | |
2599 | ||
2600 | /* Match member name. | |
2601 | */ | |
2602 | path_parse( object_str( member ), &f ); | |
2603 | ||
2604 | if ( !strcmp( f.f_member.ptr, "" ) ) | |
2605 | { | |
2606 | PROFILE_EXIT( BUILTIN_GLOB_ARCHIVE_BACK ); | |
2607 | return; | |
2608 | } | |
2609 | ||
2610 | string_new( buf ); | |
2611 | string_append_range( buf, f.f_member.ptr, f.f_member.ptr + f.f_member.len ); | |
2612 | ||
2613 | if ( globbing->case_insensitive ) | |
2614 | downcase_inplace( buf->value ); | |
2615 | ||
2616 | /* Glob with member patterns. If not matched, then match symbols. | |
2617 | */ | |
2618 | matched = 0; | |
2619 | iter = list_begin( globbing->patterns[ 0 ] ); | |
2620 | end = list_end( globbing->patterns[ 0 ] ); | |
2621 | for ( ; !matched && iter != end; | |
2622 | iter = list_next( iter ) ) | |
2623 | { | |
2624 | const char * pattern = object_str( list_item( iter ) ); | |
2625 | int match_exact = ( !has_wildcards( pattern ) ); | |
2626 | matched = ( match_exact ? | |
2627 | ( !strcmp( pattern, buf->value ) ) : | |
2628 | ( !glob( pattern, buf->value ) ) ); | |
2629 | } | |
2630 | ||
2631 | ||
2632 | /* Glob with symbol patterns, if requested. | |
2633 | */ | |
2634 | iter = list_begin( globbing->patterns[ 1 ] ); | |
2635 | end = list_end( globbing->patterns[ 1 ] ); | |
2636 | ||
2637 | if ( iter != end ) matched = 0; | |
2638 | ||
2639 | for ( ; !matched && iter != end; | |
2640 | iter = list_next( iter ) ) | |
2641 | { | |
2642 | const char * pattern = object_str( list_item( iter ) ); | |
2643 | int match_exact = ( !has_wildcards( pattern ) ); | |
2644 | ||
2645 | iter_symbols = list_begin( symbols ); | |
2646 | end_symbols = list_end( symbols ); | |
2647 | ||
2648 | for ( ; !matched && iter_symbols != end_symbols; | |
2649 | iter_symbols = list_next( iter_symbols ) ) | |
2650 | { | |
2651 | const char * symbol = object_str( list_item( iter_symbols ) ); | |
2652 | ||
2653 | string_copy( buf, symbol ); | |
2654 | if ( globbing->case_insensitive ) | |
2655 | downcase_inplace( buf->value ); | |
2656 | ||
2657 | matched = ( match_exact ? | |
2658 | ( !strcmp( pattern, buf->value ) ) : | |
2659 | ( !glob( pattern, buf->value ) ) ); | |
2660 | } | |
2661 | } | |
2662 | ||
2663 | if ( matched ) | |
2664 | { | |
2665 | globbing->results = list_push_back( globbing->results, | |
2666 | object_copy( member ) ); | |
2667 | } | |
2668 | ||
2669 | string_free( buf ); | |
2670 | ||
2671 | PROFILE_EXIT( BUILTIN_GLOB_ARCHIVE_BACK ); | |
2672 | } | |
2673 | ||
2674 | ||
2675 | LIST * builtin_glob_archive( FRAME * frame, int flags ) | |
2676 | { | |
2677 | LIST * const l = lol_get( frame->args, 0 ); | |
2678 | LIST * const r1 = lol_get( frame->args, 1 ); | |
2679 | LIST * const r2 = lol_get( frame->args, 2 ); | |
2680 | LIST * const r3 = lol_get( frame->args, 3 ); | |
2681 | ||
2682 | LISTITER iter; | |
2683 | LISTITER end; | |
2684 | struct globbing2 globbing; | |
2685 | ||
2686 | globbing.results = L0; | |
2687 | globbing.patterns[ 0 ] = r1; | |
2688 | globbing.patterns[ 1 ] = r3; | |
2689 | ||
2690 | globbing.case_insensitive = | |
2691 | # if defined( OS_NT ) || defined( OS_CYGWIN ) || defined( OS_VMS ) | |
2692 | l; /* Always case-insensitive. */ | |
2693 | # else | |
2694 | r2; | |
2695 | # endif | |
2696 | ||
2697 | if ( globbing.case_insensitive ) | |
2698 | { | |
2699 | globbing.patterns[ 0 ] = downcase_list( globbing.patterns[ 0 ] ); | |
2700 | globbing.patterns[ 1 ] = downcase_list( globbing.patterns[ 1 ] ); | |
2701 | } | |
2702 | ||
2703 | iter = list_begin( l ); | |
2704 | end = list_end( l ); | |
2705 | for ( ; iter != end; iter = list_next( iter ) ) | |
2706 | file_archivescan( list_item( iter ), builtin_glob_archive_back, &globbing ); | |
2707 | ||
2708 | if ( globbing.case_insensitive ) | |
2709 | { | |
2710 | list_free( globbing.patterns[ 0 ] ); | |
2711 | list_free( globbing.patterns[ 1 ] ); | |
2712 | } | |
2713 | ||
2714 | return globbing.results; | |
2715 | } |