diff options
Diffstat (limited to 'src/mcpp-runtime.c')
| -rw-r--r-- | src/mcpp-runtime.c | 268 |
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 ); +} |
