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
run_unit_tests()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 
usage(const char * progname)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 
main(int argc,char ** argv,char ** arg_environ)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>
executable_path(char const * argv0)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>
executable_path(char const * argv0)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>
executable_path(char const * argv0)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>
executable_path(char const * argv0)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>
executable_path(char const * argv0)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>
executable_path(char const * argv0)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
executable_path(char const * argv0)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
789