• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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( &current );
500             var_set( root_module(), constant_JAMDATE, list_new( outf_time(
501                 &current ) ), 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