using Invercargill; namespace Usm { public class ResourceFinder { public Paths paths { get; set; } public ResourceFinder(Paths? paths = null) { this.paths = paths ?? new Paths(); } public bool has_resource(ResourceRef resource_ref) { return locate_resource(resource_ref) != null; } public string? locate_resource(ResourceRef resource_ref) { switch (resource_ref.resource_type) { case ResourceType.ROOT_PATH: return locate_rootpath(resource_ref); case ResourceType.PATH: return locate_path(resource_ref); case ResourceType.OPTIONAL: return locate_opt(resource_ref); case ResourceType.RESOURCE: return locate_res(resource_ref); case ResourceType.CONFIGURATION: return locate_cfg(resource_ref); case ResourceType.BINARY: return locate_bin(resource_ref); case ResourceType.SUPER_BINARY: return locate_sbin(resource_ref); case ResourceType.LIBRARY: return locate_lib(resource_ref); case ResourceType.LIBRARY_EXECUTABLE: return locate_libexec(resource_ref); case ResourceType.LIBRARY_RESOURCE: return locate_libres(resource_ref); case ResourceType.CANONICAL_LIBRARY: return locate_canonlib(resource_ref); case ResourceType.CANONICAL_LIBRARY_RESOURCE: return locate_canonlibres(resource_ref); case ResourceType.GIO_MODULE: return locate_gio(resource_ref); case ResourceType.INFO_PAGE: return locate_info(resource_ref); case ResourceType.MANUAL_PAGE: return locate_man(resource_ref); case ResourceType.LOCALE: return locate_locale(resource_ref); case ResourceType.APPLICATION: return locate_app(resource_ref); case ResourceType.INCLUDE: return locate_inc(resource_ref); case ResourceType.PKG_CONFIG: return locate_pc(resource_ref); case ResourceType.VALA_API: return locate_vapi(resource_ref); case ResourceType.GOBJECT_IR: return locate_gir(resource_ref); case ResourceType.TYPELIB: return locate_typelib(resource_ref); case ResourceType.TAG: return locate_tag(resource_ref); default: assert_not_reached(); } } private string? locate_tag(ResourceRef resource) { if(Tag.read(resource.resource) != null) { return paths.get_tag_file_path(resource.resource); } return null; } private string? locate_lib(ResourceRef resource) { var via_ldconfig = locate_lib_ldconfig(resource); if(via_ldconfig != null) { return via_ldconfig; } // musl systems have no `ldconfig -p` output to consult: fall // back to the conventional library directories foreach(var directory in new string[] { "/lib", "/usr/lib", "/lib64", "/usr/lib64" }) { var file = File.new_build_filename(directory, resource.resource); if(file.query_exists()) { return file.get_path(); } } return null; } private string? locate_lib_ldconfig(ResourceRef resource) { try { var proc = new Subprocess.newv(new string[] { "ldconfig", "-p" }, SubprocessFlags.STDOUT_PIPE | SubprocessFlags.STDERR_SILENCE); var pipe = new DataInputStream(proc.get_stdout_pipe()); string line = null; while((line = pipe.read_line()) != null) { if(line.has_prefix("\t")) { var name = line.substring(1).split(" ", 2)[0]; if(resource.resource == name) { return line.substring(1).split("=>", 2)[1].chomp().chug(); } } } proc.wait_check(); } catch(Error e) { // busybox/musl ldconfig doesn't support -p; the fallback in // locate_lib handles it — suppress the noise } return null; } private string? locate_canonlib(ResourceRef resource) { try { var proc = new Subprocess.newv(new string[] { "ldconfig", "-p" }, SubprocessFlags.STDOUT_PIPE | SubprocessFlags.STDERR_SILENCE); var pipe = new DataInputStream(proc.get_stdout_pipe()); string line = null; while((line = pipe.read_line()) != null) { if(line.has_prefix("\t")) { var name = line.substring(1).split(" ", 2)[0]; // Only return true for libraries found in the canonical lib directory (e.g. not in /lib64) if(resource.resource == name && (name.has_prefix("/usr/lib") || name.has_prefix("/lib"))) { return line.substring(1).split("=>", 2)[1].chomp().chug(); } } } proc.wait_check(); } catch(Error e) { warning(@"Error checking for resource $resource: $(e.message)"); } return null; } private string? locate_bin(ResourceRef resource) { var env_paths = Environment.get_variable("PATH").split(":"); foreach (var path in env_paths) { var file = File.new_build_filename(paths.destination, path, resource.resource); if(file.query_exists()) { return file.get_path(); } } return null; } private string? locate_path(ResourceRef resource) { var file = File.new_build_filename(paths.destination, paths.prefix, resource.resource); return file.query_exists() ? file.get_path() : null; } private string? locate_rootpath(ResourceRef resource) { var file = File.new_build_filename(paths.destination, resource.resource); return file.query_exists() ? file.get_path() : null; } private string? locate_res(ResourceRef resource) { var file = File.new_build_filename(paths.destination, paths.prefix, paths.data, resource.resource); return file.query_exists() ? file.get_path() : null; } private string? locate_opt(ResourceRef resource) { var file = File.new_build_filename(paths.destination, "opt", resource.resource); return file.query_exists() ? file.get_path() : null; } private string? locate_cfg(ResourceRef resource) { var file = File.new_build_filename(paths.destination, paths.sys_config, resource.resource); return file.query_exists() ? file.get_path() : null; } private string? locate_libres(ResourceRef resource) { var search = new string[] { "/usr/lib", "/usr/lib64", "/lib", "/lib64" }; foreach (var path in search) { var file = File.new_build_filename(paths.destination, path, resource.resource); if(file.query_exists()) { return file.get_path(); } } return null; } private string? locate_canonlibres(ResourceRef resource) { var search = new string[] { "/usr/lib", "/lib" }; foreach (var path in search) { var file = File.new_build_filename(paths.destination, path, resource.resource); if(file.query_exists()) { return file.get_path(); } } return null; } private string? locate_gio(ResourceRef resource) { var search = Iterate.these("/usr/lib64/gio/modules", "/usr/lib/gio/modules"); // Debian-style multiarch keeps GIO modules in // /usr/lib//gio/modules, which no fixed path covers: // enumerate whatever multiarch directories exist try { foreach(var entry in Iterate.directory("/usr/lib")) { if(entry.contains("-linux-")) { search = search.concat(Wrap.array(new string[] { @"/usr/lib/$entry/gio/modules" })); } } } catch(FileError e) { } foreach (var path in search) { var file = File.new_build_filename(paths.destination, path, resource.resource); if(file.query_exists()) { return file.get_path(); } } return null; } private string? locate_app(ResourceRef resource) { var file = File.new_build_filename("/usr/share/applications", resource.resource); return file.query_exists() ? file.get_path() : null; } private string? locate_inc(ResourceRef resource) { var file = File.new_build_filename("/usr/include", resource.resource); return file.query_exists() ? file.get_path() : null; } private string? locate_sbin(ResourceRef resource) { var search = new string[] { "/usr/sbin", "/sbin" }; foreach (var path in search) { var file = File.new_build_filename(paths.destination, path, resource.resource); if(file.query_exists()) { return file.get_path(); } } return null; } private string? locate_libexec(ResourceRef resource) { var file = File.new_build_filename("/usr/libexec", resource.resource); return file.query_exists() ? file.get_path() : null; } private string? locate_info(ResourceRef resource) { var file = File.new_build_filename("/usr/share/info", resource.resource); return file.query_exists() ? file.get_path() : null; } private string? locate_man(ResourceRef resource) { var file = File.new_build_filename("/usr/share/man", resource.resource); return file.query_exists() ? file.get_path() : null; } private string? locate_locale(ResourceRef resource) { var file = File.new_build_filename("/usr/share/locale", resource.resource); return file.query_exists() ? file.get_path() : null; } private string? locate_vapi(ResourceRef resource) { var search = new string[] { "/usr/share/vala/vapi", "/usr/share/vala-0.56/vapi" }; foreach (var path in search) { var file = File.new_build_filename(paths.destination, path, resource.resource); if(file.query_exists()) { return file.get_path(); } } return null; } private string? locate_pc(ResourceRef resource) { var search = Iterate.these("/usr/lib/pkgconfig", "/usr/lib64/pkgconfig", "/usr/share/pkgconfig"); // Debian-style multiarch keeps pkg-config files in // /usr/lib//pkgconfig, which no fixed path covers: // enumerate whatever multiarch directories exist try { foreach(var entry in Iterate.directory("/usr/lib")) { if(entry.contains("-linux-")) { search = search.concat(Wrap.array(new string[] { @"/usr/lib/$entry/pkgconfig" })); } } } catch(FileError e) { } var env = Environment.get_variable("PKG_CONFIG_PATH"); if(env != null) { search = search.concat(Wrap.array(env.split(":"))); } foreach (var path in search) { var file = File.new_build_filename(paths.destination, path, resource.resource); if(file.query_exists()) { return file.get_path(); } } return null; } private string? locate_gir(ResourceRef resource) { var file = File.new_build_filename("/usr/share/gir", resource.resource); return file.query_exists() ? file.get_path() : null; } private string? locate_typelib(ResourceRef resource) { var search = new string[] { "/usr/lib64/girepository-1.0", "/usr/lib/girepository-1.0", "/lib64/girepository-1.0", "/lib/girepository-1.0" }; foreach (var path in search) { var file = File.new_build_filename(paths.destination, path, resource.resource); if(file.query_exists()) { return file.get_path(); } } return null; } } }