mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-04-01 21:33:09 +02:00
gutils: Split g_find_program_path() to make it more flexible and testable
Split g_find_program_path() in g_find_program_for_path() that supports passing path arguments and providing a custom working directory. Adding tests to cover the cases we were not doing before.
This commit is contained in:
parent
bbb3453c82
commit
7bac92a2bb
@ -275,7 +275,44 @@ gchar*
|
|||||||
g_find_program_in_path (const gchar *program)
|
g_find_program_in_path (const gchar *program)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
const gchar *path, *p;
|
return g_find_program_for_path (program, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_find_program_for_path:
|
||||||
|
* @program: (type filename): a program name in the GLib file name encoding
|
||||||
|
* @path: (type filename) (nullable): the current dir where to search program
|
||||||
|
* @working_dir: (type filename) (nullable): the working dir where to search
|
||||||
|
* program
|
||||||
|
*
|
||||||
|
* Locates the first executable named @program in @path, in the
|
||||||
|
* same way that execvp() would locate it. Returns an allocated string
|
||||||
|
* with the absolute path name (taking in account the @working_dir), or
|
||||||
|
* %NULL if the program is not found in @path. If @program is already an
|
||||||
|
* absolute path, returns a copy of @program if @program exists and is
|
||||||
|
* executable, and %NULL otherwise.
|
||||||
|
*
|
||||||
|
* On Windows, if @path is %NULL, it looks for the file in the same way as
|
||||||
|
* CreateProcess() would. This means first in the directory where the
|
||||||
|
* executing program was loaded from, then in the current directory, then in
|
||||||
|
* the Windows 32-bit system directory, then in the Windows directory, and
|
||||||
|
* finally in the directories in the `PATH` environment variable. If
|
||||||
|
* the program is found, the return value contains the full name
|
||||||
|
* including the type suffix.
|
||||||
|
*
|
||||||
|
* Returns: (type filename) (transfer full) (nullable): a newly-allocated
|
||||||
|
* string with the absolute path, or %NULL
|
||||||
|
* Since: 2.76
|
||||||
|
**/
|
||||||
|
char *
|
||||||
|
g_find_program_for_path (const char *program,
|
||||||
|
const char *path,
|
||||||
|
const char *working_dir)
|
||||||
|
{
|
||||||
|
const char *original_path = path;
|
||||||
|
const char *original_program = program;
|
||||||
|
char *program_path = NULL;
|
||||||
|
const gchar *p;
|
||||||
gchar *name, *freeme;
|
gchar *name, *freeme;
|
||||||
#ifdef G_OS_WIN32
|
#ifdef G_OS_WIN32
|
||||||
const gchar *path_copy;
|
const gchar *path_copy;
|
||||||
@ -290,20 +327,28 @@ g_find_program_in_path (const gchar *program)
|
|||||||
|
|
||||||
g_return_val_if_fail (program != NULL, NULL);
|
g_return_val_if_fail (program != NULL, NULL);
|
||||||
|
|
||||||
|
/* Use the working dir as program path if provided */
|
||||||
|
if (working_dir && !g_path_is_absolute (program))
|
||||||
|
{
|
||||||
|
program_path = g_build_filename (working_dir, program, NULL);
|
||||||
|
program = program_path;
|
||||||
|
}
|
||||||
|
|
||||||
/* If it is an absolute path, or a relative path including subdirectories,
|
/* If it is an absolute path, or a relative path including subdirectories,
|
||||||
* don't look in PATH.
|
* don't look in PATH.
|
||||||
*/
|
*/
|
||||||
if (g_path_is_absolute (program)
|
if (g_path_is_absolute (program)
|
||||||
|| strchr (program, G_DIR_SEPARATOR) != NULL
|
|| strchr (original_program, G_DIR_SEPARATOR) != NULL
|
||||||
#ifdef G_OS_WIN32
|
#ifdef G_OS_WIN32
|
||||||
|| strchr (program, '/') != NULL
|
|| strchr (original_program, '/') != NULL
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (g_file_test (program, G_FILE_TEST_IS_EXECUTABLE) &&
|
if (g_file_test (program, G_FILE_TEST_IS_EXECUTABLE) &&
|
||||||
!g_file_test (program, G_FILE_TEST_IS_DIR))
|
!g_file_test (program, G_FILE_TEST_IS_DIR))
|
||||||
{
|
{
|
||||||
gchar *out = NULL, *cwd = NULL;
|
gchar *out = NULL;
|
||||||
|
char *cwd;
|
||||||
|
|
||||||
if (g_path_is_absolute (program))
|
if (g_path_is_absolute (program))
|
||||||
return g_strdup (program);
|
return g_strdup (program);
|
||||||
@ -311,13 +356,26 @@ g_find_program_in_path (const gchar *program)
|
|||||||
cwd = g_get_current_dir ();
|
cwd = g_get_current_dir ();
|
||||||
out = g_build_filename (cwd, program, NULL);
|
out = g_build_filename (cwd, program, NULL);
|
||||||
g_free (cwd);
|
g_free (cwd);
|
||||||
|
g_free (program_path);
|
||||||
|
|
||||||
return g_steal_pointer (&out);
|
return g_steal_pointer (&out);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
g_clear_pointer (&program_path, g_free);
|
||||||
|
|
||||||
|
if (g_path_is_absolute (original_program))
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
program = original_program;
|
||||||
|
|
||||||
|
if G_LIKELY (original_path == NULL)
|
||||||
path = g_getenv ("PATH");
|
path = g_getenv ("PATH");
|
||||||
|
else
|
||||||
|
path = original_path;
|
||||||
|
|
||||||
#if defined(G_OS_UNIX)
|
#if defined(G_OS_UNIX)
|
||||||
if (path == NULL)
|
if (path == NULL)
|
||||||
{
|
{
|
||||||
@ -334,6 +392,8 @@ g_find_program_in_path (const gchar *program)
|
|||||||
path = "/bin:/usr/bin:.";
|
path = "/bin:/usr/bin:.";
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
if G_LIKELY (original_path == NULL)
|
||||||
|
{
|
||||||
n = GetModuleFileNameW (NULL, wfilename, MAXPATHLEN);
|
n = GetModuleFileNameW (NULL, wfilename, MAXPATHLEN);
|
||||||
if (n > 0 && n < MAXPATHLEN)
|
if (n > 0 && n < MAXPATHLEN)
|
||||||
filename = g_utf16_to_utf8 (wfilename, -1, NULL, NULL, NULL);
|
filename = g_utf16_to_utf8 (wfilename, -1, NULL, NULL, NULL);
|
||||||
@ -385,6 +445,12 @@ g_find_program_in_path (const gchar *program)
|
|||||||
}
|
}
|
||||||
|
|
||||||
path_copy = path;
|
path_copy = path;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
path_copy = g_strdup (path);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
len = strlen (program) + 1;
|
len = strlen (program) + 1;
|
||||||
@ -401,6 +467,7 @@ g_find_program_in_path (const gchar *program)
|
|||||||
do
|
do
|
||||||
{
|
{
|
||||||
char *startp;
|
char *startp;
|
||||||
|
char *startp_path = NULL;
|
||||||
|
|
||||||
path = p;
|
path = p;
|
||||||
p = my_strchrnul (path, G_SEARCHPATH_SEPARATOR);
|
p = my_strchrnul (path, G_SEARCHPATH_SEPARATOR);
|
||||||
@ -413,6 +480,13 @@ g_find_program_in_path (const gchar *program)
|
|||||||
else
|
else
|
||||||
startp = memcpy (name - (p - path), path, p - path);
|
startp = memcpy (name - (p - path), path, p - path);
|
||||||
|
|
||||||
|
/* Use the working dir as program path if provided */
|
||||||
|
if (working_dir && !g_path_is_absolute (startp))
|
||||||
|
{
|
||||||
|
startp_path = g_build_filename (working_dir, startp, NULL);
|
||||||
|
startp = startp_path;
|
||||||
|
}
|
||||||
|
|
||||||
if (g_file_test (startp, G_FILE_TEST_IS_EXECUTABLE) &&
|
if (g_file_test (startp, G_FILE_TEST_IS_EXECUTABLE) &&
|
||||||
!g_file_test (startp, G_FILE_TEST_IS_DIR))
|
!g_file_test (startp, G_FILE_TEST_IS_DIR))
|
||||||
{
|
{
|
||||||
@ -425,12 +499,15 @@ g_find_program_in_path (const gchar *program)
|
|||||||
ret = g_build_filename (cwd, startp, NULL);
|
ret = g_build_filename (cwd, startp, NULL);
|
||||||
g_free (cwd);
|
g_free (cwd);
|
||||||
}
|
}
|
||||||
|
g_free (startp_path);
|
||||||
g_free (freeme);
|
g_free (freeme);
|
||||||
#ifdef G_OS_WIN32
|
#ifdef G_OS_WIN32
|
||||||
g_free ((gchar *) path_copy);
|
g_free ((gchar *) path_copy);
|
||||||
#endif
|
#endif
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_free (startp_path);
|
||||||
}
|
}
|
||||||
while (*p++ != '\0');
|
while (*p++ != '\0');
|
||||||
|
|
||||||
|
@ -32,6 +32,11 @@ GLIB_AVAILABLE_IN_2_60
|
|||||||
void g_set_user_dirs (const gchar *first_dir_type,
|
void g_set_user_dirs (const gchar *first_dir_type,
|
||||||
...) G_GNUC_NULL_TERMINATED;
|
...) G_GNUC_NULL_TERMINATED;
|
||||||
|
|
||||||
|
GLIB_AVAILABLE_IN_2_76
|
||||||
|
char * g_find_program_for_path (const char *program,
|
||||||
|
const char *path,
|
||||||
|
const char *working_dir);
|
||||||
|
|
||||||
/* Returns the smallest power of 2 greater than or equal to n,
|
/* Returns the smallest power of 2 greater than or equal to n,
|
||||||
* or 0 if such power does not fit in a gsize
|
* or 0 if such power does not fit in a gsize
|
||||||
*/
|
*/
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
|
|
||||||
#include "glib.h"
|
#include "glib.h"
|
||||||
#include "glib-private.h"
|
#include "glib-private.h"
|
||||||
|
#include "gutilsprivate.h"
|
||||||
|
#include "glib/gstdio.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -485,6 +487,145 @@ test_find_program (void)
|
|||||||
g_assert (res == NULL);
|
g_assert (res == NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_find_program_for_path (void)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
/* Using .cmd extension to make windows to consider it an executable */
|
||||||
|
const char *command_to_find = "just-an-exe-file.cmd";
|
||||||
|
char *path;
|
||||||
|
char *exe_path;
|
||||||
|
char *found_path;
|
||||||
|
char *old_cwd;
|
||||||
|
char *tmp;
|
||||||
|
|
||||||
|
tmp = g_dir_make_tmp ("find_program_for_path_XXXXXXX", &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
|
||||||
|
path = g_build_filename (tmp, "sub-path", NULL);
|
||||||
|
g_mkdir (path, 0700);
|
||||||
|
g_assert_true (g_file_test (path, G_FILE_TEST_IS_DIR));
|
||||||
|
|
||||||
|
exe_path = g_build_filename (path, command_to_find, NULL);
|
||||||
|
g_file_set_contents (exe_path, "", -1, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_true (g_file_test (exe_path, G_FILE_TEST_EXISTS));
|
||||||
|
|
||||||
|
#ifdef G_OS_UNIX
|
||||||
|
g_assert_no_errno (g_chmod (exe_path, 0500));
|
||||||
|
#endif
|
||||||
|
g_assert_true (g_file_test (exe_path, G_FILE_TEST_IS_EXECUTABLE));
|
||||||
|
|
||||||
|
g_assert_null (g_find_program_in_path (command_to_find));
|
||||||
|
g_assert_null (g_find_program_for_path (command_to_find, NULL, NULL));
|
||||||
|
|
||||||
|
found_path = g_find_program_for_path (command_to_find, path, NULL);
|
||||||
|
#ifdef __APPLE__
|
||||||
|
g_assert_nonnull (found_path);
|
||||||
|
g_assert_true (g_str_has_suffix (found_path, exe_path));
|
||||||
|
#else
|
||||||
|
g_assert_cmpstr (exe_path, ==, found_path);
|
||||||
|
#endif
|
||||||
|
g_clear_pointer (&found_path, g_free);
|
||||||
|
|
||||||
|
found_path = g_find_program_for_path (command_to_find, path, path);
|
||||||
|
#ifdef __APPLE__
|
||||||
|
g_assert_nonnull (found_path);
|
||||||
|
g_assert_true (g_str_has_suffix (found_path, exe_path));
|
||||||
|
#else
|
||||||
|
g_assert_cmpstr (exe_path, ==, found_path);
|
||||||
|
#endif
|
||||||
|
g_clear_pointer (&found_path, g_free);
|
||||||
|
|
||||||
|
found_path = g_find_program_for_path (command_to_find, NULL, path);
|
||||||
|
#ifdef __APPLE__
|
||||||
|
g_assert_nonnull (found_path);
|
||||||
|
g_assert_true (g_str_has_suffix (found_path, exe_path));
|
||||||
|
#else
|
||||||
|
g_assert_cmpstr (exe_path, ==, found_path);
|
||||||
|
#endif
|
||||||
|
g_clear_pointer (&found_path, g_free);
|
||||||
|
|
||||||
|
found_path = g_find_program_for_path (command_to_find, "::", path);
|
||||||
|
#ifdef __APPLE__
|
||||||
|
g_assert_nonnull (found_path);
|
||||||
|
g_assert_true (g_str_has_suffix (found_path, exe_path));
|
||||||
|
#else
|
||||||
|
g_assert_cmpstr (exe_path, ==, found_path);
|
||||||
|
#endif
|
||||||
|
g_clear_pointer (&found_path, g_free);
|
||||||
|
|
||||||
|
old_cwd = g_get_current_dir ();
|
||||||
|
g_chdir (path);
|
||||||
|
found_path =
|
||||||
|
g_find_program_for_path (command_to_find,
|
||||||
|
G_SEARCHPATH_SEPARATOR_S G_SEARCHPATH_SEPARATOR_S, NULL);
|
||||||
|
g_chdir (old_cwd);
|
||||||
|
g_clear_pointer (&old_cwd, g_free);
|
||||||
|
#ifdef __APPLE__
|
||||||
|
g_assert_nonnull (found_path);
|
||||||
|
g_assert_true (g_str_has_suffix (found_path, exe_path));
|
||||||
|
#else
|
||||||
|
g_assert_cmpstr (exe_path, ==, found_path);
|
||||||
|
#endif
|
||||||
|
g_clear_pointer (&found_path, g_free);
|
||||||
|
|
||||||
|
old_cwd = g_get_current_dir ();
|
||||||
|
g_chdir (tmp);
|
||||||
|
found_path =
|
||||||
|
g_find_program_for_path (command_to_find,
|
||||||
|
G_SEARCHPATH_SEPARATOR_S G_SEARCHPATH_SEPARATOR_S, "sub-path");
|
||||||
|
g_chdir (old_cwd);
|
||||||
|
g_clear_pointer (&old_cwd, g_free);
|
||||||
|
#ifdef __APPLE__
|
||||||
|
g_assert_nonnull (found_path);
|
||||||
|
g_assert_true (g_str_has_suffix (found_path, exe_path));
|
||||||
|
#else
|
||||||
|
g_assert_cmpstr (exe_path, ==, found_path);
|
||||||
|
#endif
|
||||||
|
g_clear_pointer (&found_path, g_free);
|
||||||
|
|
||||||
|
g_assert_null (
|
||||||
|
g_find_program_for_path (command_to_find,
|
||||||
|
G_SEARCHPATH_SEPARATOR_S G_SEARCHPATH_SEPARATOR_S, "other-sub-path"));
|
||||||
|
|
||||||
|
found_path = g_find_program_for_path (command_to_find,
|
||||||
|
G_SEARCHPATH_SEPARATOR_S "sub-path" G_SEARCHPATH_SEPARATOR_S, tmp);
|
||||||
|
#ifdef __APPLE__
|
||||||
|
g_assert_nonnull (found_path);
|
||||||
|
g_assert_true (g_str_has_suffix (found_path, exe_path));
|
||||||
|
#else
|
||||||
|
g_assert_cmpstr (exe_path, ==, found_path);
|
||||||
|
#endif
|
||||||
|
g_clear_pointer (&found_path, g_free);
|
||||||
|
|
||||||
|
g_assert_null (g_find_program_for_path (command_to_find,
|
||||||
|
G_SEARCHPATH_SEPARATOR_S "other-sub-path" G_SEARCHPATH_SEPARATOR_S, tmp));
|
||||||
|
|
||||||
|
#ifdef G_OS_UNIX
|
||||||
|
found_path = g_find_program_for_path ("sh", NULL, tmp);
|
||||||
|
g_assert_nonnull (found_path);
|
||||||
|
g_clear_pointer (&found_path, g_free);
|
||||||
|
|
||||||
|
old_cwd = g_get_current_dir ();
|
||||||
|
g_chdir ("/");
|
||||||
|
found_path = g_find_program_for_path ("sh", "sbin:bin:usr/bin:usr/sbin", NULL);
|
||||||
|
g_chdir (old_cwd);
|
||||||
|
g_clear_pointer (&old_cwd, g_free);
|
||||||
|
g_assert_nonnull (found_path);
|
||||||
|
g_clear_pointer (&found_path, g_free);
|
||||||
|
|
||||||
|
found_path = g_find_program_for_path ("sh", "sbin:bin:usr/bin:usr/sbin", "/");
|
||||||
|
g_assert_nonnull (found_path);
|
||||||
|
g_clear_pointer (&found_path, g_free);
|
||||||
|
#endif /* G_OS_UNIX */
|
||||||
|
|
||||||
|
g_clear_pointer (&exe_path, g_free);
|
||||||
|
g_clear_pointer (&path, g_free);
|
||||||
|
g_clear_pointer (&tmp, g_free);
|
||||||
|
g_clear_error (&error);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_debug (void)
|
test_debug (void)
|
||||||
{
|
{
|
||||||
@ -1149,6 +1290,7 @@ main (int argc,
|
|||||||
g_test_add_func ("/utils/bits", test_bits);
|
g_test_add_func ("/utils/bits", test_bits);
|
||||||
g_test_add_func ("/utils/swap", test_swap);
|
g_test_add_func ("/utils/swap", test_swap);
|
||||||
g_test_add_func ("/utils/find-program", test_find_program);
|
g_test_add_func ("/utils/find-program", test_find_program);
|
||||||
|
g_test_add_func ("/utils/find-program-for-path", test_find_program_for_path);
|
||||||
g_test_add_func ("/utils/debug", test_debug);
|
g_test_add_func ("/utils/debug", test_debug);
|
||||||
g_test_add_func ("/utils/codeset", test_codeset);
|
g_test_add_func ("/utils/codeset", test_codeset);
|
||||||
g_test_add_func ("/utils/codeset2", test_codeset2);
|
g_test_add_func ("/utils/codeset2", test_codeset2);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user