summaryrefslogtreecommitdiff
path: root/utils/mcpu-library-search.c
diff options
context:
space:
mode:
authorkx <kx@radix-linux.su>2026-10-09 18:00:15 +0300
committerkx <kx@radix-linux.su>2026-10-09 18:00:15 +0300
commit065da3509019b15dd0e678072e6be629e5330bef (patch)
tree69c934c9e786ac048b4fbe8f4c4c5571661521f4 /utils/mcpu-library-search.c
parentd130fe27fa753be02ed76fd9b277aa3c3d928eb9 (diff)
downloadlibmcpu-0.4.0.tar.xz
Version 0.4.00.4.0
Diffstat (limited to 'utils/mcpu-library-search.c')
-rw-r--r--utils/mcpu-library-search.c307
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() */