summaryrefslogtreecommitdiff
path: root/src/mcpp-runtime.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mcpp-runtime.c')
-rw-r--r--src/mcpp-runtime.c268
1 files changed, 268 insertions, 0 deletions
diff --git a/src/mcpp-runtime.c b/src/mcpp-runtime.c
new file mode 100644
index 0000000..f498b20
--- /dev/null
+++ b/src/mcpp-runtime.c
@@ -0,0 +1,268 @@
+#include <defs.h>
+
+#include <unistd.h>
+
+
+static char *
+path_dirname( const char *path )
+{
+ const char *slash;
+ size_t length;
+ char *result;
+
+ if( path == NULL || *path == 0 )
+ {
+ errno = EINVAL;
+ return( NULL );
+ }
+
+ slash = strrchr( path, '/' );
+ if( slash == NULL )
+ {
+ errno = EINVAL;
+ return( NULL );
+ }
+
+ if( slash == path )
+ length = 1;
+ else
+ length = (size_t)(slash - path);
+
+ result = (char *)malloc( length + 1 );
+ if( result == NULL )
+ return( NULL );
+
+ memcpy( result, path, length );
+ result[length] = 0;
+ return( result );
+}
+
+
+static char *
+path_join( const char *directory, const char *name )
+{
+ size_t directory_length;
+ size_t name_length;
+ int need_slash;
+ char *result;
+
+ if( directory == NULL || name == NULL )
+ {
+ errno = EINVAL;
+ return( NULL );
+ }
+
+ directory_length = strlen( directory );
+ name_length = strlen( name );
+ need_slash = directory_length != 0 && directory[directory_length - 1] != '/';
+
+ if( directory_length > SIZE_MAX - name_length - (size_t)need_slash - 1 )
+ {
+ errno = EOVERFLOW;
+ return( NULL );
+ }
+
+ result = (char *)malloc( directory_length + name_length +
+ (size_t)need_slash + 1 );
+ if( result == NULL )
+ return( NULL );
+
+ memcpy( result, directory, directory_length );
+ if( need_slash )
+ result[directory_length++] = '/';
+ memcpy( result + directory_length, name, name_length );
+ result[directory_length + name_length] = 0;
+ return( result );
+}
+
+
+static int
+resolve_proc_self_exe( char path[PATH_MAX] )
+{
+ ssize_t length;
+
+ length = readlink( "/proc/self/exe", path, PATH_MAX - 1 );
+ if( length < 0 )
+ return( -1 );
+
+ if( length >= PATH_MAX - 1 )
+ {
+ errno = ENAMETOOLONG;
+ return( -1 );
+ }
+
+ path[length] = 0;
+ return( 0 );
+}
+
+
+static int
+resolve_candidate( const char *candidate, char path[PATH_MAX] )
+{
+ if( candidate == NULL || *candidate == 0 )
+ {
+ errno = EINVAL;
+ return( -1 );
+ }
+
+ if( realpath(candidate, path) == NULL )
+ return( -1 );
+
+ if( access(path, X_OK) != 0 )
+ return( -1 );
+
+ return( 0 );
+}
+
+
+static int
+resolve_argv0( const char *argv0, char path[PATH_MAX] )
+{
+ const char *search;
+ const char *entry;
+
+ if( argv0 == NULL || *argv0 == 0 )
+ {
+ errno = EINVAL;
+ return( -1 );
+ }
+
+ if( strchr(argv0, '/') != NULL )
+ return( resolve_candidate(argv0, path) );
+
+ search = getenv( "PATH" );
+ if( search == NULL )
+ {
+ errno = ENOENT;
+ return( -1 );
+ }
+
+ entry = search;
+ for( ;; )
+ {
+ const char *colon = strchr( entry, ':' );
+ size_t length = colon ? (size_t)(colon - entry) : strlen(entry);
+ const char *directory = entry;
+ char directory_buffer[PATH_MAX];
+ char *candidate;
+ int rc;
+
+ if( length == 0 )
+ directory = ".";
+ else
+ {
+ if( length >= sizeof(directory_buffer) )
+ {
+ errno = ENAMETOOLONG;
+ return( -1 );
+ }
+ memcpy( directory_buffer, entry, length );
+ directory_buffer[length] = 0;
+ directory = directory_buffer;
+ }
+
+ candidate = path_join( directory, argv0 );
+ if( candidate == NULL )
+ return( -1 );
+ rc = resolve_candidate( candidate, path );
+ free( candidate );
+ if( rc == 0 )
+ return( 0 );
+
+ if( colon == NULL )
+ break;
+ entry = colon + 1;
+ }
+
+ errno = ENOENT;
+ return( -1 );
+}
+
+
+void
+mcpp_runtime_paths_init( mcpp_runtime_paths *paths )
+{
+ if( paths == NULL )
+ return;
+
+ paths->executable_path = NULL;
+ paths->executable_dir = NULL;
+ paths->installation_root = NULL;
+ paths->config_file = NULL;
+ paths->system_include_path = NULL;
+}
+
+
+void
+mcpp_runtime_paths_free( mcpp_runtime_paths *paths )
+{
+ if( paths == NULL )
+ return;
+
+ free( paths->executable_path );
+ free( paths->executable_dir );
+ free( paths->installation_root );
+ free( paths->config_file );
+ free( paths->system_include_path );
+ mcpp_runtime_paths_init( paths );
+}
+
+
+int
+mcpp_runtime_paths_discover( mcpp_runtime_paths *paths, const char *argv0 )
+{
+ char resolved[PATH_MAX];
+ char *executable_path = NULL;
+ char *executable_dir = NULL;
+ char *installation_root = NULL;
+ char *config_file = NULL;
+ char *system_include_path = NULL;
+
+ if( paths == NULL )
+ {
+ errno = EINVAL;
+ return( -1 );
+ }
+
+ if( resolve_proc_self_exe(resolved) != 0 )
+ {
+ if( resolve_argv0(argv0, resolved) != 0 )
+ return( -1 );
+ }
+
+ executable_path = strdup( resolved );
+ if( executable_path == NULL )
+ goto fail;
+
+ executable_dir = path_dirname( executable_path );
+ if( executable_dir == NULL )
+ goto fail;
+
+ installation_root = path_dirname( executable_dir );
+ if( installation_root == NULL )
+ goto fail;
+
+ config_file = path_join( installation_root, "etc/mcpu-cpp.conf" );
+ if( config_file == NULL )
+ goto fail;
+
+ system_include_path = path_join( installation_root, "include" );
+ if( system_include_path == NULL )
+ goto fail;
+
+ mcpp_runtime_paths_free( paths );
+ paths->executable_path = executable_path;
+ paths->executable_dir = executable_dir;
+ paths->installation_root = installation_root;
+ paths->config_file = config_file;
+ paths->system_include_path = system_include_path;
+ return( 0 );
+
+fail:
+ free( executable_path );
+ free( executable_dir );
+ free( installation_root );
+ free( config_file );
+ free( system_include_path );
+ return( -1 );
+}