# [email protected] / 2010-02-12 17:03:00 +0100:
> # [email protected] / 2010-02-12 15:25:48 +0100:
> > Maybe there's a BSD specific C API for getting current executable path ?
> 
> I'm investigating that.  The first thing that occurred to me is that this 
> could
> be made more universal if it matched the behavior prescribed by the SUS:
> 
> if argv[0] begins with "/", the full path == argv[0]; else
> if argv[0] contains "/", the full path == $PWD/argv[0]; else
> foreach dir in $PATH:
>   if $dir/argv[0] exists, it's the full path, you're done
> end
> if you got here, the full path is unknown
> 
> found this on google: http://stackoverflow.com/questions/1023306/

> I'll be back with a patch.

Ok, here I am.  In the end I've opted for the argv[0] approach.
It turned out sysctl(3) wouldn't give better results, and the code
wouldn't stay the same among the various BSDs, let alone apply to
other unices.

I should mention that I'm not really a C programmer, so you'll want
to doublecheck the changes.  That said, valgrind's memcheck is happy
with all branches of find_exe().  Again, if you're not happy with
something, yell, I'll fix it.

Here's the patch description for exposition in list archives (mentions
only nekoml, but the same probably applies to nekoc, maybe others?):

nekoml working on FreeBSD, probably any unix(ish) OS

the Linux-specific #else branch of executable_path() now
branches into another #if/#else, the #if is for Linux,
#else covers any other unix(ish) OS (at least I think it's
completely OS-agnostic (within POSIX).

* vm/main.c

  executable_path(): takes const char *argv0, which is
   expected to be argv[0] as received by main()

  find_exe(), concat(): the OS-agnostic branch implemented
   using two static functions since it's quite a bit longer
   than the other branches

  neko_has_embedded_module(): grows second parameter,
   const char *argv0, passes it into executable_path()

  main(): calls neko_has_embedded_module with argv[0]

-- 
Roman Neuhauser
# HG changeset patch
# User neuhauser
# Date 1266239796 -3600
# Node ID 6617034002635fbf2defa96586d8b74460d02092
# Parent  35c92addaa580132ee87871ff33cb521f2b7407b
nekoml working on FreeBSD, probably any unix(ish) OS

the Linux-specific #else branch of executable_path() now
branches into another #if/#else, the #if is for Linux,
#else covers any other unix(ish) OS (at least I think it's
completely OS-agnostic (within POSIX).

* vm/main.c

  executable_path(): takes const char *argv0, which is
   expected to be argv[0] as received by main()

  find_exe(), concat(): the OS-agnostic branch implemented
   using two static functions since it's quite a bit longer
   than the other branches

  neko_has_embedded_module(): grows second parameter,
   const char *argv0, passes it into executable_path()

  main(): calls neko_has_embedded_module with argv[0]

diff -r 35c92addaa58 -r 661703400263 vm/main.c
--- a/vm/main.c	Sun Feb 14 22:53:32 2010 +0100
+++ b/vm/main.c	Mon Feb 15 14:16:36 2010 +0100
@@ -44,8 +44,77 @@
 extern void neko_stats_measure( neko_vm *vm, const char *kind, int start );
 extern value neko_stats_build( neko_vm *vm );
 
+#if !defined(NEKO_WINDOWS) && !defined(NEKO_MAC) && !defined(NEKO_LINUX)
 
-static char *executable_path() {
+static int concat( char* dst, size_t dstsz, const char* dir, const char* base ) {
+	if( dstsz >= (size_t)snprintf(dst, dstsz, "%s/%s", dir, base) ) {
+		return 0;
+	} else {
+		return -1;
+	}
+}
+
+static int find_exe( const char* exe, char* resolved, size_t rsize ) {
+#define BAILOUT(rv) \
+	free(cwd); \
+	free(tmp); \
+	free(path); \
+	return rv;
+
+#define RETURN_IF_FOUND(x) \
+	if (0 == access(x, X_OK)) { \
+		if (rsize > strlen(x)) { \
+			strcpy(resolved, x); \
+			BAILOUT(0); \
+		} else { \
+			BAILOUT(-1); \
+		} \
+	}
+
+	char *cwd = NULL, *dir = NULL, *path = NULL, *tmp = malloc(rsize);
+
+	if('/' == exe[0]) {
+		RETURN_IF_FOUND(exe);
+	}
+
+	if(NULL != strchr(exe,'/')) {
+		cwd = malloc(rsize);
+		if(NULL == getcwd(cwd,rsize)) {
+			BAILOUT(-1);
+		}
+
+		if( concat(tmp,rsize,cwd,exe) ) {
+			BAILOUT(-1);
+		};
+		RETURN_IF_FOUND(tmp);
+	}
+
+	path = getenv("PATH");
+	if(NULL == path)
+		return -1;
+	path = strdup(path);
+
+	*tmp = 0;
+	for (
+		dir = strtok(path, ":");
+		dir != NULL;
+		dir = strtok(NULL, ":")
+	) {
+		if( concat(tmp,rsize,dir,exe) ) {
+			BAILOUT(-1);
+		}
+		RETURN_IF_FOUND(tmp);
+	}
+
+	BAILOUT(-1);
+
+#undef BAILOUT
+#undef RETURN_IF_FOUND
+}
+
+#endif // !defined(NEKO_WINDOWS) && !defined(NEKO_MAC) && !defined(NEKO_LINUX)
+
+static char *executable_path( const char *argv0 ) {
 #if defined(NEKO_WINDOWS)
 	static char path[MAX_PATH];
 	if( GetModuleFileName(NULL,path,MAX_PATH) == 0 )
@@ -59,6 +128,7 @@
 	return path;
 #else
 	static char path[200];
+#	if defined(NEKO_LINUX)
 	int length = readlink("/proc/self/exe", path, sizeof(path));
 	if( length < 0 || length >= 200 ) {
 		char *p = getenv("   "); // for upx
@@ -67,12 +137,16 @@
 		return p;
 	}
 	path[length] = '\0';
+#	else
+	if( find_exe(argv0, path, sizeof(path)) )
+		return NULL;
+#	endif
 	return path;
 #endif
 }
 
-int neko_has_embedded_module( neko_vm *vm ) {
-	char *exe = executable_path();
+int neko_has_embedded_module( neko_vm *vm, char *argv0 ) {
+	char *exe = executable_path(argv0);
 	unsigned char id[8];
 	int pos;
 	if( exe == NULL )
@@ -204,7 +278,7 @@
 #	ifdef NEKO_STANDALONE
 	neko_standalone_init();
 #	endif
-	if( !neko_has_embedded_module(vm) ) {
+	if( !neko_has_embedded_module(vm, argv[0]) ) {
 		int jit = 1;
 		int stats = 0;
 		while( argc > 1 ) {
-- 
Neko : One VM to run them all
(http://nekovm.org)

Reply via email to