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