diff options
Diffstat (limited to 'utils/mcpu-library-search.c')
| -rw-r--r-- | utils/mcpu-library-search.c | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/utils/mcpu-library-search.c b/utils/mcpu-library-search.c new file mode 100644 index 0000000..dc877b9 --- /dev/null +++ b/utils/mcpu-library-search.c @@ -0,0 +1,307 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#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() */ |
