1 /*
2 * /+\
3 * +\ Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
4 * \+/
5 *
6 * This file is part of jam.
7 *
8 * License is hereby granted to use this software and distribute it freely, as
9 * long as this copyright notice is retained and modifications are clearly
10 * marked.
11 *
12 * ALL WARRANTIES ARE HEREBY DISCLAIMED.
13 */
14
15 /* This file is ALSO:
16 * Copyright 2001-2004 David Abrahams.
17 * Copyright 2018 Rene Rivera
18 * Distributed under the Boost Software License, Version 1.0.
19 * (See accompanying file LICENSE_1_0.txt or copy at
20 * http://www.boost.org/LICENSE_1_0.txt)
21 */
22
23 /*
24 * jam.c - make redux
25 *
26 * See Jam.html for usage information.
27 *
28 * These comments document the code.
29 *
30 * The top half of the code is structured such:
31 *
32 * jam
33 * / | \
34 * +---+ | \
35 * / | \
36 * jamgram option \
37 * / | \ \
38 * / | \ \
39 * / | \ |
40 * scan | compile make
41 * | | / | \ / | \
42 * | | / | \ / | \
43 * | | / | \ / | \
44 * jambase parse | rules search make1
45 * | | | \
46 * | | | \
47 * | | | \
48 * builtins timestamp command execute
49 * |
50 * |
51 * |
52 * filesys
53 *
54 *
55 * The support routines are called by all of the above, but themselves are
56 * layered thus:
57 *
58 * variable|expand
59 * / | |
60 * / | |
61 * / | |
62 * lists | pathsys
63 * \ |
64 * \ hash
65 * \ |
66 * \ |
67 * \ |
68 * \ |
69 * \ |
70 * object
71 *
72 * Roughly, the modules are:
73 *
74 * builtins.c - jam's built-in rules
75 * command.c - maintain lists of commands
76 * compile.c - compile parsed jam statements
77 * exec*.c - execute a shell script on a specific OS
78 * file*.c - scan directories and archives on a specific OS
79 * hash.c - simple in-memory hashing routines
80 * hdrmacro.c - handle header file parsing for filename macro definitions
81 * headers.c - handle #includes in source files
82 * jamgram.y - jam grammar
83 * lists.c - maintain lists of strings
84 * make.c - bring a target up to date, once rules are in place
85 * make1.c - execute command to bring targets up to date
86 * object.c - string manipulation routines
87 * option.c - command line option processing
88 * parse.c - make and destroy parse trees as driven by the parser
89 * path*.c - manipulate file names on a specific OS
90 * hash.c - simple in-memory hashing routines
91 * regexp.c - Henry Spencer's regexp
92 * rules.c - access to RULEs, TARGETs, and ACTIONs
93 * scan.c - the jam yacc scanner
94 * search.c - find a target along $(SEARCH) or $(LOCATE)
95 * timestamp.c - get the timestamp of a file or archive member
96 * variable.c - handle jam multi-element variables
97 */
98
99
100 #include "jam.h"
101
102 #include "patchlevel.h"
103
104 /* Keep JAMVERSYM in sync with VERSION. */
105 /* It can be accessed as $(JAMVERSION) in the Jamfile. */
106 #define JAM_STRINGIZE(X) JAM_DO_STRINGIZE(X)
107 #define JAM_DO_STRINGIZE(X) #X
108 #define VERSION_MAJOR_SYM JAM_STRINGIZE(VERSION_MAJOR)
109 #define VERSION_MINOR_SYM JAM_STRINGIZE(VERSION_MINOR)
110 #define VERSION_PATCH_SYM JAM_STRINGIZE(VERSION_PATCH)
111 #define VERSION VERSION_MAJOR_SYM "." VERSION_MINOR_SYM
112 #define JAMVERSYM "JAMVERSION=" VERSION
113
114 #include "builtins.h"
115 #include "class.h"
116 #include "compile.h"
117 #include "constants.h"
118 #include "debugger.h"
119 #include "filesys.h"
120 #include "function.h"
121 #include "hcache.h"
122 #include "lists.h"
123 #include "make.h"
124 #include "object.h"
125 #include "option.h"
126 #include "output.h"
127 #include "parse.h"
128 #include "cwd.h"
129 #include "rules.h"
130 #include "scan.h"
131 #include "search.h"
132 #include "startup.h"
133 #include "jam_strings.h"
134 #include "timestamp.h"
135 #include "variable.h"
136 #include "execcmd.h"
137 #include "sysinfo.h"
138
139 #include <errno.h>
140 #include <string.h>
141
142 /* Macintosh is "special" */
143 #ifdef OS_MAC
144 # include <QuickDraw.h>
145 #endif
146
147 /* And UNIX for this. */
148 #ifdef unix
149 # include <sys/utsname.h>
150 # include <signal.h>
151 #endif
152
153 struct globs globs =
154 {
155 0, /* noexec */
156 1, /* jobs */
157 0, /* quitquick */
158 0, /* newestfirst */
159 0, /* pipes action stdout and stderr merged to action output */
160 #ifdef OS_MAC
161 { 0, 0 }, /* debug - suppress tracing output */
162 #else
163 { 0, 1 }, /* debug ... */
164 #endif
165 0, /* output commands, not run them */
166 0, /* action timeout */
167 0 /* maximum buffer size zero is all output */
168 };
169
170 /* Symbols to be defined as true for use in Jambase. */
171 static const char * othersyms[] = { OSMAJOR, OSMINOR, OSPLAT, JAMVERSYM, 0 };
172
173
174 /* on Win32-LCC */
175 #if defined( OS_NT ) && defined( __LCC__ )
176 # define use_environ _environ
177 #endif
178
179 #if defined( __MWERKS__)
180 # define use_environ _environ
181 extern char * * _environ;
182 #endif
183
184 #ifndef use_environ
185 # define use_environ environ
186 # if !defined( __WATCOM__ ) && !defined( OS_OS2 ) && !defined( OS_NT )
187 extern char **environ;
188 # endif
189 #endif
190
191 #if YYDEBUG != 0
192 extern int yydebug;
193 #endif
194
195 #ifndef NDEBUG
run_unit_tests()196 static void run_unit_tests()
197 {
198 # if defined( USE_EXECNT )
199 extern void execnt_unit_test();
200 execnt_unit_test();
201 # endif
202 string_unit_test();
203 }
204 #endif
205
206 int anyhow = 0;
207
208 #ifdef HAVE_PYTHON
209 extern PyObject * bjam_call ( PyObject * self, PyObject * args );
210 extern PyObject * bjam_import_rule ( PyObject * self, PyObject * args );
211 extern PyObject * bjam_define_action( PyObject * self, PyObject * args );
212 extern PyObject * bjam_variable ( PyObject * self, PyObject * args );
213 extern PyObject * bjam_backtrace ( PyObject * self, PyObject * args );
214 extern PyObject * bjam_caller ( PyObject * self, PyObject * args );
215 int python_optimize = 1; /* Set Python optimzation on by default */
216 #endif
217
218 void regex_done();
219
220 char const * saved_argv0;
221
usage(const char * progname)222 static void usage( const char * progname )
223 {
224 err_printf("\nusage: %s [ options ] targets...\n\n", progname);
225
226 err_printf("-a Build all targets, even if they are current.\n");
227 err_printf("-dx Set the debug level to x (0-13,console,mi).\n");
228 err_printf("-fx Read x instead of bootstrap.\n");
229 /* err_printf( "-g Build from newest sources first.\n" ); */
230 err_printf("-jx Run up to x shell commands concurrently.\n");
231 err_printf("-lx Limit actions to x number of seconds after which they are stopped.\n");
232 err_printf("-mx Maximum target output saved (kb), default is to save all output.\n");
233 err_printf("-n Don't actually execute the updating actions.\n");
234 err_printf("-ox Mirror all output to file x.\n");
235 err_printf("-px x=0, pipes action stdout and stderr merged into action output.\n");
236 err_printf("-q Quit quickly as soon as a target fails.\n");
237 err_printf("-sx=y Set variable x=y, overriding environment.\n");
238 err_printf("-tx Rebuild x, even if it is up-to-date.\n");
239 err_printf("-v Print the version of jam and exit.\n");
240 #ifdef HAVE_PYTHON
241 err_printf("-z Disable Python Optimization and enable asserts\n");
242 #endif
243 err_printf("--x Option is ignored.\n\n");
244
245 exit( EXITBAD );
246 }
247
main(int argc,char ** argv)248 int main( int argc, char * * argv )
249 {
250 int n;
251 char * s;
252 struct bjam_option optv[ N_OPTS ];
253 int status = 0;
254 int arg_c = argc;
255 char * * arg_v = argv;
256 char const * progname = argv[ 0 ];
257 module_t * environ_module;
258 int is_debugger;
259 b2::system_info sys_info;
260
261 saved_argv0 = argv[ 0 ];
262 last_update_now_status = 0;
263
264 BJAM_MEM_INIT();
265
266 #ifdef OS_MAC
267 InitGraf( &qd.thePort );
268 #endif
269
270 cwd_init();
271 constants_init();
272
273 #ifdef JAM_DEBUGGER
274
275 is_debugger = 0;
276
277 if ( getoptions( argc - 1, argv + 1, "-:l:m:d:j:p:f:gs:t:ano:qv", optv ) < 0 )
278 usage( progname );
279
280 if ( ( s = getoptval( optv, 'd', 0 ) ) )
281 {
282 if ( strcmp( s, "mi" ) == 0 )
283 {
284 debug_interface = DEBUG_INTERFACE_MI;
285 is_debugger = 1;
286 }
287 else if ( strcmp( s, "console" ) == 0 )
288 {
289 debug_interface = DEBUG_INTERFACE_CONSOLE;
290 is_debugger = 1;
291 }
292 }
293
294 #if NT
295
296 if ( argc >= 3 )
297 {
298 /* Check whether this instance is being run by the debugger. */
299 size_t opt_len = strlen( debugger_opt );
300 if ( strncmp( argv[ 1 ], debugger_opt, opt_len ) == 0 &&
301 strncmp( argv[ 2 ], debugger_opt, opt_len ) == 0 )
302 {
303 debug_init_handles( argv[ 1 ] + opt_len, argv[ 2 ] + opt_len );
304 /* Fix up argc/argv to hide the internal options */
305 arg_c = argc = (argc - 2);
306 argv[ 2 ] = argv[ 0 ];
307 arg_v = argv = (argv + 2);
308 debug_interface = DEBUG_INTERFACE_CHILD;
309 }
310 }
311
312 if ( is_debugger )
313 {
314 return debugger();
315 }
316
317 #else
318
319 if ( is_debugger )
320 {
321 if ( setjmp( debug_child_data.jmp ) != 0 )
322 {
323 arg_c = argc = debug_child_data.argc;
324 arg_v = argv = (char * *)debug_child_data.argv;
325 debug_interface = DEBUG_INTERFACE_CHILD;
326 }
327 else
328 {
329 return debugger();
330 }
331 }
332
333 #endif
334
335 #endif
336
337 --argc;
338 ++argv;
339
340 #ifdef HAVE_PYTHON
341 #define OPTSTRING "-:l:m:d:j:p:f:gs:t:ano:qvz"
342 #else
343 #define OPTSTRING "-:l:m:d:j:p:f:gs:t:ano:qv"
344 #endif
345
346 if ( getoptions( argc, argv, OPTSTRING, optv ) < 0 )
347 {
348 usage( progname );
349 }
350
351 /* Set default parallel jobs to match cpu threads. This can be overridden
352 the usual way with -jX or PARALLELISM env var. */
353 globs.jobs = sys_info.cpu_thread_count();
354
355 /* Version info. */
356 if ( ( s = getoptval( optv, 'v', 0 ) ) )
357 {
358 out_printf( "B2 Version %s. %s.\n", VERSION, OSMINOR );
359 out_printf( " Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.\n" );
360 out_printf( " Copyright 2001 David Turner.\n" );
361 out_printf( " Copyright 2001-2004 David Abrahams.\n" );
362 out_printf( " Copyright 2002-2019 Rene Rivera.\n" );
363 out_printf( " Copyright 2003-2015 Vladimir Prus.\n" );
364 out_printf( "\n DEFAULTS: jobs = %i\n", globs.jobs);
365 return EXITOK;
366 }
367
368 /* Pick up interesting options. */
369 if ( ( s = getoptval( optv, 'n', 0 ) ) )
370 {
371 ++globs.noexec;
372 globs.debug[ 2 ] = 1;
373 }
374
375 if ( ( s = getoptval( optv, 'p', 0 ) ) )
376 {
377 /* Undocumented -p3 (acts like both -p1 -p2) means separate pipe action
378 * stdout and stderr.
379 */
380 globs.pipe_action = atoi( s );
381 if ( globs.pipe_action < 0 || 3 < globs.pipe_action )
382 {
383 err_printf( "Invalid pipe descriptor '%d', valid values are -p[0..3]."
384 "\n", globs.pipe_action );
385 exit( EXITBAD );
386 }
387 }
388
389 if ( ( s = getoptval( optv, 'q', 0 ) ) )
390 globs.quitquick = 1;
391
392 if ( ( s = getoptval( optv, 'a', 0 ) ) )
393 anyhow++;
394
395 if ( ( s = getoptval( optv, 'j', 0 ) ) )
396 {
397 globs.jobs = atoi( s );
398 if ( globs.jobs < 1 )
399 {
400 err_printf( "Invalid value for the '-j' option.\n" );
401 exit( EXITBAD );
402 }
403 }
404
405 if ( ( s = getoptval( optv, 'g', 0 ) ) )
406 globs.newestfirst = 1;
407
408 if ( ( s = getoptval( optv, 'l', 0 ) ) )
409 globs.timeout = atoi( s );
410
411 if ( ( s = getoptval( optv, 'm', 0 ) ) )
412 globs.max_buf = atoi( s ) * 1024; /* convert to kb */
413
414 #ifdef HAVE_PYTHON
415 if ( ( s = getoptval( optv, 'z', 0 ) ) )
416 python_optimize = 0; /* disable python optimization */
417 #endif
418
419 /* Turn on/off debugging */
420 for ( n = 0; ( s = getoptval( optv, 'd', n ) ); ++n )
421 {
422 int i;
423
424 /* First -d, turn off defaults. */
425 if ( !n )
426 for ( i = 0; i < DEBUG_MAX; ++i )
427 globs.debug[i] = 0;
428
429 i = atoi( s );
430
431 if ( ( i < 0 ) || ( i >= DEBUG_MAX ) )
432 {
433 out_printf( "Invalid debug level '%s'.\n", s );
434 continue;
435 }
436
437 /* n turns on levels 1-n. */
438 /* +n turns on level n. */
439 if ( *s == '+' )
440 globs.debug[ i ] = 1;
441 else while ( i )
442 globs.debug[ i-- ] = 1;
443 }
444
445 /* If an output file is specified, set globs.out to that. */
446 if ( ( s = getoptval( optv, 'o', 0 ) ) )
447 {
448 if ( !( globs.out = fopen( s, "w" ) ) )
449 {
450 err_printf( "[errno %d] failed to write output file '%s': %s",
451 errno, s, strerror(errno) );
452 exit( EXITBAD );
453 }
454 /* ++globs.noexec; */
455 }
456
457 {
458 PROFILE_ENTER( MAIN );
459
460 #ifdef HAVE_PYTHON
461 {
462 PROFILE_ENTER( MAIN_PYTHON );
463 Py_OptimizeFlag = python_optimize;
464 Py_Initialize();
465 {
466 static PyMethodDef BjamMethods[] = {
467 {"call", bjam_call, METH_VARARGS,
468 "Call the specified bjam rule."},
469 {"import_rule", bjam_import_rule, METH_VARARGS,
470 "Imports Python callable to bjam."},
471 {"define_action", bjam_define_action, METH_VARARGS,
472 "Defines a command line action."},
473 {"variable", bjam_variable, METH_VARARGS,
474 "Obtains a variable from bjam's global module."},
475 {"backtrace", bjam_backtrace, METH_VARARGS,
476 "Returns bjam backtrace from the last call into Python."},
477 {"caller", bjam_caller, METH_VARARGS,
478 "Returns the module from which the last call into Python is made."},
479 {NULL, NULL, 0, NULL}
480 };
481
482 Py_InitModule( "bjam", BjamMethods );
483 }
484 PROFILE_EXIT( MAIN_PYTHON );
485 }
486 #endif
487
488 #ifndef NDEBUG
489 run_unit_tests();
490 #endif
491 #if YYDEBUG != 0
492 if ( DEBUG_PARSE )
493 yydebug = 1;
494 #endif
495
496 /* Set JAMDATE. */
497 {
498 timestamp current;
499 timestamp_current( ¤t );
500 var_set( root_module(), constant_JAMDATE, list_new( outf_time(
501 ¤t ) ), VAR_SET );
502 }
503
504 /* Set JAM_VERSION. */
505 var_set( root_module(), constant_JAM_VERSION,
506 list_push_back( list_push_back( list_new(
507 object_new( VERSION_MAJOR_SYM ) ),
508 object_new( VERSION_MINOR_SYM ) ),
509 object_new( VERSION_PATCH_SYM ) ),
510 VAR_SET );
511
512 /* Set JAMUNAME. */
513 #ifdef unix
514 {
515 struct utsname u;
516
517 if ( uname( &u ) >= 0 )
518 {
519 var_set( root_module(), constant_JAMUNAME,
520 list_push_back(
521 list_push_back(
522 list_push_back(
523 list_push_back(
524 list_new(
525 object_new( u.sysname ) ),
526 object_new( u.nodename ) ),
527 object_new( u.release ) ),
528 object_new( u.version ) ),
529 object_new( u.machine ) ), VAR_SET );
530 }
531 }
532 #endif /* unix */
533
534 /* Set JAM_TIMESTAMP_RESOLUTION. */
535 {
536 timestamp fmt_resolution[ 1 ];
537 file_supported_fmt_resolution( fmt_resolution );
538 var_set( root_module(), constant_JAM_TIMESTAMP_RESOLUTION, list_new(
539 object_new( timestamp_timestr( fmt_resolution ) ) ), VAR_SET );
540 }
541
542 /* Load up environment variables. */
543
544 /* First into the global module, with splitting, for backward
545 * compatibility.
546 */
547 var_defines( root_module(), use_environ, 1 );
548
549 environ_module = bindmodule( constant_ENVIRON );
550 /* Then into .ENVIRON, without splitting. */
551 var_defines( environ_module, use_environ, 0 );
552
553 /*
554 * Jam defined variables OS & OSPLAT. We load them after environment, so
555 * that setting OS in environment does not change Jam's notion of the
556 * current platform.
557 */
558 var_defines( root_module(), othersyms, 1 );
559
560 /* Load up variables set on command line. */
561 for ( n = 0; ( s = getoptval( optv, 's', n ) ); ++n )
562 {
563 char * symv[ 2 ];
564 symv[ 0 ] = s;
565 symv[ 1 ] = 0;
566 var_defines( root_module(), symv, 1 );
567 var_defines( environ_module, symv, 0 );
568 }
569
570 /* Set the ARGV to reflect the complete list of arguments of invocation.
571 */
572 for ( n = 0; n < arg_c; ++n )
573 var_set( root_module(), constant_ARGV, list_new( object_new(
574 arg_v[ n ] ) ), VAR_APPEND );
575
576 /* Initialize built-in rules. */
577 load_builtins();
578 b2::startup::load_builtins();
579
580 /* Add the targets in the command line to the update list. */
581 for ( n = 1; n < arg_c; ++n )
582 {
583 if ( arg_v[ n ][ 0 ] == '-' )
584 {
585 const char * f = "-:l:d:j:f:gs:t:ano:qv";
586 for ( ; *f; ++f ) if ( *f == arg_v[ n ][ 1 ] ) break;
587 if ( f[0] && f[1] && ( f[ 1 ] == ':' ) && ( arg_v[ n ][ 2 ] == '\0' ) ) ++n;
588 }
589 else
590 {
591 OBJECT * const target = object_new( arg_v[ n ] );
592 mark_target_for_updating( target );
593 object_free( target );
594 }
595 }
596
597 /* The build system may set the PARALLELISM variable to override -j
598 * options.
599 */
600 {
601 LIST * const p = var_get( root_module(), constant_PARALLELISM );
602 if ( !list_empty( p ) )
603 {
604 int const j = atoi( object_str( list_front( p ) ) );
605 if ( j < 1 )
606 out_printf( "Invalid value of PARALLELISM: %s.\n",
607 object_str( list_front( p ) ) );
608 else
609 globs.jobs = j;
610 }
611 }
612
613 /* KEEP_GOING overrides -q option. */
614 {
615 LIST * const p = var_get( root_module(), constant_KEEP_GOING );
616 if ( !list_empty( p ) )
617 globs.quitquick = atoi( object_str( list_front( p ) ) ) ? 0 : 1;
618 }
619
620
621 if ( list_empty( targets_to_update() ) )
622 mark_target_for_updating( constant_all );
623
624 /* Parse ruleset. */
625 {
626 FRAME frame[ 1 ];
627 frame_init( frame );
628 for ( n = 0; ( s = getoptval( optv, 'f', n ) ); ++n )
629 {
630 OBJECT * const filename = object_new( s );
631 parse_file( filename, frame );
632 object_free( filename );
633 }
634
635 if ( !n )
636 status = b2::startup::bootstrap(frame) ? 0 : 13;
637 }
638
639 /* FIXME: What shall we do if builtin_update_now,
640 * the sole place setting last_update_now_status,
641 * failed earlier?
642 */
643
644 if ( status == 0 )
645 status = yyanyerrors();
646 if ( status == 0 )
647 {
648 /* Manually touch -t targets. */
649 for ( n = 0; ( s = getoptval( optv, 't', n ) ); ++n )
650 {
651 OBJECT * const target = object_new( s );
652 touch_target( target );
653 object_free( target );
654 }
655
656 /* Now make target. */
657 {
658 PROFILE_ENTER( MAIN_MAKE );
659 LIST * const targets = targets_to_update();
660 if ( !list_empty( targets ) )
661 status |= make( targets, anyhow );
662 else
663 status = last_update_now_status;
664 PROFILE_EXIT( MAIN_MAKE );
665 }
666 }
667
668 PROFILE_EXIT( MAIN );
669 }
670
671 if ( DEBUG_PROFILE )
672 profile_dump();
673
674
675 #ifdef OPT_HEADER_CACHE_EXT
676 hcache_done();
677 #endif
678
679 clear_targets_to_update();
680
681 /* Widely scattered cleanup. */
682 property_set_done();
683 exec_done();
684 file_done();
685 rules_done();
686 timestamp_done();
687 search_done();
688 class_done();
689 modules_done();
690 regex_done();
691 cwd_done();
692 path_done();
693 function_done();
694 list_done();
695 constants_done();
696 object_done();
697
698 /* Close log out. */
699 if ( globs.out )
700 fclose( globs.out );
701
702 #ifdef HAVE_PYTHON
703 Py_Finalize();
704 #endif
705
706 BJAM_MEM_CLOSE();
707
708 return status ? EXITBAD : EXITOK;
709 }
710
711
712 /*
713 * executable_path()
714 */
715
716 #if defined(_WIN32)
717 # define WIN32_LEAN_AND_MEAN
718 # include <windows.h>
executable_path(char const * argv0)719 char * executable_path( char const * argv0 )
720 {
721 char buf[ 1024 ];
722 DWORD const ret = GetModuleFileNameA( NULL, buf, sizeof( buf ) );
723 return ( !ret || ret == sizeof( buf ) ) ? NULL : strdup( buf );
724 }
725 #elif defined(__APPLE__) /* Not tested */
726 # include <mach-o/dyld.h>
executable_path(char const * argv0)727 char *executable_path( char const * argv0 )
728 {
729 char buf[ 1024 ];
730 uint32_t size = sizeof( buf );
731 return _NSGetExecutablePath( buf, &size ) ? NULL : strdup( buf );
732 }
733 #elif defined(sun) || defined(__sun) /* Not tested */
734 # include <stdlib.h>
executable_path(char const * argv0)735 char * executable_path( char const * argv0 )
736 {
737 const char * execname = getexecname();
738 return execname ? strdup( execname ) : NULL;
739 }
740 #elif defined(__FreeBSD__)
741 # include <sys/sysctl.h>
executable_path(char const * argv0)742 char * executable_path( char const * argv0 )
743 {
744 int mib[ 4 ] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
745 char buf[ 1024 ];
746 size_t size = sizeof( buf );
747 sysctl( mib, 4, buf, &size, NULL, 0 );
748 return ( !size || size == sizeof( buf ) ) ? NULL : strndup( buf, size );
749 }
750 #elif defined(__linux__)
751 # include <unistd.h>
executable_path(char const * argv0)752 char * executable_path( char const * argv0 )
753 {
754 char buf[ 1024 ];
755 ssize_t const ret = readlink( "/proc/self/exe", buf, sizeof( buf ) );
756 return ( !ret || ret == sizeof( buf ) ) ? NULL : strndup( buf, ret );
757 }
758 #elif defined(OS_VMS)
759 # include <unixlib.h>
executable_path(char const * argv0)760 char * executable_path( char const * argv0 )
761 {
762 char * vms_path = NULL;
763 char * posix_path = NULL;
764 char * p;
765
766 /* On VMS argv[0] shows absolute path to the image file.
767 * So, just remove VMS file version and translate path to POSIX-style.
768 */
769 vms_path = strdup( argv0 );
770 if ( vms_path && ( p = strchr( vms_path, ';') ) ) *p = '\0';
771 posix_path = decc$translate_vms( vms_path );
772 if ( vms_path ) free( vms_path );
773
774 return posix_path > 0 ? strdup( posix_path ) : NULL;
775 }
776 #else
executable_path(char const * argv0)777 char * executable_path( char const * argv0 )
778 {
779 /* If argv0 is an absolute path, assume it is the right absolute path. */
780 return argv0[ 0 ] == '/' ? strdup( argv0 ) : NULL;
781 }
782 #endif
783