#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "mcpu-library-search.h" 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 ); } /* End of path_join() */ static int regular_file( const char *path ) { struct stat st; if( stat(path, &st) != 0 ) return( 0 ); return( S_ISREG(st.st_mode) ); } /* End of regular_file() */ void mcpu_library_search_init( mcpu_library_search *search ) { if( search == NULL ) return; search->directory = NULL; search->count = 0; search->capacity = 0; } /* End of mcpu_library_search_init() */ void mcpu_library_search_free( mcpu_library_search *search ) { size_t i; if( search == NULL ) return; for( i = 0; i < search->count; ++i ) free( search->directory[i] ); free( search->directory ); mcpu_library_search_init( search ); } /* End of mcpu_library_search_free() */ int mcpu_library_search_add_directory( mcpu_library_search *search, const char *directory ) { char **p; char *copy; size_t capacity; if( search == NULL || directory == NULL || *directory == 0 ) { errno = EINVAL; return( -1 ); } if( search->count == search->capacity ) { capacity = search->capacity ? search->capacity * 2U : 4U; if( capacity < search->capacity || capacity > SIZE_MAX / sizeof(*p) ) { errno = EOVERFLOW; return( -1 ); } p = (char **)realloc( search->directory, capacity * sizeof(*p) ); if( p == NULL ) return( -1 ); search->directory = p; search->capacity = capacity; } copy = strdup( directory ); if( copy == NULL ) return( -1 ); search->directory[search->count++] = copy; return( 0 ); } /* End of mcpu_library_search_add_directory() */ int mcpu_library_search_add_list( mcpu_library_search *search, const char *list ) { const char *begin; if( search == NULL || list == NULL || *list == 0 ) { errno = EINVAL; return( -1 ); } begin = list; for( ;; ) { const char *colon = strchr( begin, ':' ); size_t length = colon ? (size_t)(colon - begin) : strlen(begin); char *directory; int rc; if( length == 0 ) { errno = EINVAL; return( -1 ); } directory = (char *)malloc( length + 1 ); if( directory == NULL ) return( -1 ); memcpy( directory, begin, length ); directory[length] = 0; rc = mcpu_library_search_add_directory( search, directory ); free( directory ); if( rc != 0 ) return( -1 ); if( colon == NULL ) break; begin = colon + 1; } return( 0 ); } /* End of mcpu_library_search_add_list() */ static int try_directory( const char *directory, const char *name, char **resolved ) { char *candidate; if( directory == NULL || *directory == 0 ) return( 0 ); candidate = path_join( directory, name ); if( candidate == NULL ) return( -1 ); if( regular_file(candidate) ) { *resolved = candidate; return( 1 ); } free( candidate ); return( 0 ); } /* End of try_directory() */ int mcpu_library_search_resolve( const mcpu_library_search *search, const char *system_directory, const char *name, char **resolved ) { size_t i; int rc; if( search == NULL || name == NULL || *name == 0 || resolved == NULL ) { errno = EINVAL; return( -1 ); } *resolved = NULL; if( strchr(name, '/') != NULL ) { *resolved = strdup( name ); return( *resolved == NULL ? -1 : 0 ); } for( i = 0; i < search->count; ++i ) { rc = try_directory( search->directory[i], name, resolved ); if( rc < 0 ) return( -1 ); if( rc > 0 ) return( 0 ); } rc = try_directory( system_directory, name, resolved ); if( rc < 0 ) return( -1 ); if( rc > 0 ) return( 0 ); errno = ENOENT; return( -1 ); } /* End of mcpu_library_search_resolve() */ int mcpu_library_search_resolve_alternatives( const mcpu_library_search *search, const char *system_directory, const char *const *names, size_t name_count, char **resolved, size_t *selected_index ) { size_t i, j; int rc; if( search == NULL || names == NULL || name_count == 0 || resolved == NULL ) { errno = EINVAL; return( -1 ); } for( j = 0; j < name_count; ++j ) if( names[j] == NULL || names[j][0] == 0 || strchr(names[j], '/') != NULL ) { errno = EINVAL; return( -1 ); } *resolved = NULL; if( selected_index ) *selected_index = 0; for( i = 0; i < search->count; ++i ) for( j = 0; j < name_count; ++j ) { rc = try_directory( search->directory[i], names[j], resolved ); if( rc < 0 ) return( -1 ); if( rc > 0 ) { if( selected_index ) *selected_index = j; return( 0 ); } } for( j = 0; j < name_count; ++j ) { rc = try_directory( system_directory, names[j], resolved ); if( rc < 0 ) return( -1 ); if( rc > 0 ) { if( selected_index ) *selected_index = j; return( 0 ); } } errno = ENOENT; return( -1 ); } /* End of mcpu_library_search_resolve_alternatives() */