#include #include 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 char * path_absolute( const char *path ) { char cwd[PATH_MAX]; char *result; if( path == NULL || *path == 0 ) { errno = EINVAL; return( NULL ); } if( path[0] == '/' ) return( strdup(path) ); if( getcwd(cwd, sizeof(cwd)) == NULL ) return( NULL ); result = path_join( cwd, path ); 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 ); } int mcpp_runtime_paths_set_root( mcpp_runtime_paths *paths, const char *root ) { char *installation_root; char *config_file; char *system_include_path; if( paths == NULL || root == NULL || *root == 0 ) { errno = EINVAL; return( -1 ); } installation_root = path_absolute( root ); if( installation_root == NULL ) return( -1 ); 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; free( paths->installation_root ); free( paths->config_file ); free( paths->system_include_path ); paths->installation_root = installation_root; paths->config_file = config_file; paths->system_include_path = system_include_path; return( 0 ); fail: free( installation_root ); free( config_file ); return( -1 ); }