-
Simon McVittie authored
A lot of the code added here is #if 0. Normally we don't commit commented-out or #ifdef'd out code, but in this case it helps tools like gvimdiff to have enough context to figure out which parts of the file in Flatpak correspond to which parts of the file in pressure-vessel. Signed-off-by:
Simon McVittie <smcv@collabora.com>
Simon McVittie authoredA lot of the code added here is #if 0. Normally we don't commit commented-out or #ifdef'd out code, but in this case it helps tools like gvimdiff to have enough context to figure out which parts of the file in Flatpak correspond to which parts of the file in pressure-vessel. Signed-off-by:
Simon McVittie <smcv@collabora.com>
glib-backports.c 14.38 KiB
/*
* Backports from GLib
*
* Copyright 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
* Copyright 1997-2000 GLib team
* Copyright 2000 Red Hat, Inc.
* Copyright 2013-2019 Collabora Ltd.
* Copyright 2018 Georges Basile Stavracas Neto
* Copyright 2018 Philip Withnall
* g_execvpe implementation based on GNU libc execvp:
* Copyright 1991, 92, 95, 96, 97, 98, 99 Free Software Foundation, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "glib-backports.h"
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include <glib-object.h>
#if !GLIB_CHECK_VERSION (2, 34, 0)
G_DEFINE_QUARK (g-spawn-exit-error-quark, my_g_spawn_exit_error)
#endif
#if !GLIB_CHECK_VERSION (2, 36, 0)
/**
* g_close:
* @fd: A file descriptor
* @error: a #GError
*
* This wraps the close() call; in case of error, %errno will be
* preserved, but the error will also be stored as a #GError in @error.
*
* Besides using #GError, there is another major reason to prefer this
* function over the call provided by the system; on Unix, it will
* attempt to correctly handle %EINTR, which has platform-specific
* semantics.
*
* Returns: %TRUE on success, %FALSE if there was an error.
*
* Since: 2.36
*/
gboolean
my_g_close (gint fd,
GError **error)
{
int res;
res = close (fd);
/* Just ignore EINTR for now; a retry loop is the wrong thing to do
* on Linux at least. Anyone who wants to add a conditional check
* for e.g. HP-UX is welcome to do so later...
*
* http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
* https://bugzilla.gnome.org/show_bug.cgi?id=682819
* http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR
* https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain
*/
if (G_UNLIKELY (res == -1 && errno == EINTR))
return TRUE;
else if (res == -1)
{
int errsv = errno;
g_set_error_literal (error, G_FILE_ERROR,
g_file_error_from_errno (errsv),
g_strerror (errsv));
errno = errsv;
return FALSE;
}
return TRUE;
}
#endif
#if !GLIB_CHECK_VERSION (2, 34, 0)
/**
* g_spawn_check_exit_status:
* @exit_status: An exit code as returned from g_spawn_sync()
* @error: a #GError
*
* Set @error if @exit_status indicates the child exited abnormally
* (e.g. with a nonzero exit code, or via a fatal signal).
*
* The g_spawn_sync() and g_child_watch_add() family of APIs return an
* exit status for subprocesses encoded in a platform-specific way.
* On Unix, this is guaranteed to be in the same format waitpid() returns,
* and on Windows it is guaranteed to be the result of GetExitCodeProcess().
*
* Prior to the introduction of this function in GLib 2.34, interpreting
* @exit_status required use of platform-specific APIs, which is problematic
* for software using GLib as a cross-platform layer.
*
* Additionally, many programs simply want to determine whether or not
* the child exited successfully, and either propagate a #GError or
* print a message to standard error. In that common case, this function
* can be used. Note that the error message in @error will contain
* human-readable information about the exit status.
*
* The @domain and @code of @error have special semantics in the case
* where the process has an "exit code", as opposed to being killed by
* a signal. On Unix, this happens if WIFEXITED() would be true of
* @exit_status. On Windows, it is always the case.
*
* The special semantics are that the actual exit code will be the
* code set in @error, and the domain will be %G_SPAWN_EXIT_ERROR.
* This allows you to differentiate between different exit codes.
*
* If the process was terminated by some means other than an exit
* status, the domain will be %G_SPAWN_ERROR, and the code will be
* %G_SPAWN_ERROR_FAILED.
*
* This function just offers convenience; you can of course also check
* the available platform via a macro such as %G_OS_UNIX, and use
* WIFEXITED() and WEXITSTATUS() on @exit_status directly. Do not attempt
* to scan or parse the error message string; it may be translated and/or
* change in future versions of GLib.
*
* Returns: %TRUE if child exited successfully, %FALSE otherwise (and
* @error will be set)
*
* Since: 2.34
*/
gboolean
my_g_spawn_check_exit_status (gint exit_status,
GError **error)
{
gboolean ret = FALSE;
if (WIFEXITED (exit_status))
{
if (WEXITSTATUS (exit_status) != 0)
{
g_set_error (error, G_SPAWN_EXIT_ERROR, WEXITSTATUS (exit_status),
_("Child process exited with code %ld"),
(long) WEXITSTATUS (exit_status));
goto out;
}
}
else if (WIFSIGNALED (exit_status))
{
g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
_("Child process killed by signal %ld"),
(long) WTERMSIG (exit_status));
goto out;
}
else if (WIFSTOPPED (exit_status))
{
g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
_("Child process stopped by signal %ld"),
(long) WSTOPSIG (exit_status));
goto out;
}
else
{
g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
_("Child process exited abnormally"));
goto out;
}
ret = TRUE;
out:
return ret;
}
#endif
#if !GLIB_CHECK_VERSION (2, 40, 0)
/*
* A reimplementation of g_ptr_array_insert()
*/
void
my_g_ptr_array_insert (GPtrArray *arr,
gint index_,
gpointer data)
{
g_return_if_fail (arr != NULL);
g_return_if_fail (index_ >= -1);
g_return_if_fail (index_ <= (gint) arr->len);
if (index_ < 0)
index_ = arr->len;
if ((guint) index_ == arr->len)
{
g_ptr_array_add (arr, data);
}
else
{
g_ptr_array_add (arr, NULL);
memmove (&(arr->pdata[index_ + 1]),
&(arr->pdata[index_]),
(arr->len - 1 - index_) * sizeof (gpointer));
arr->pdata[index_] = data;
}
}
#endif
#if !GLIB_CHECK_VERSION (2, 36, 0)
/**
* g_dbus_address_escape_value:
* @string: an unescaped string to be included in a D-Bus address
* as the value in a key-value pair
*
* Escape @string so it can appear in a D-Bus address as the value
* part of a key-value pair.
*
* For instance, if @string is `/run/bus-for-:0`,
* this function would return `/run/bus-for-%3A0`,
* which could be used in a D-Bus address like
* `unix:nonce-tcp:host=127.0.0.1,port=42,noncefile=/run/bus-for-%3A0`.
*
* Returns: (transfer full): a copy of @string with all
* non-optionally-escaped bytes escaped
*
* Since: 2.36
*/
gchar *
my_g_dbus_address_escape_value (const gchar *string)
{
GString *s;
gsize i;
g_return_val_if_fail (string != NULL, NULL);
/* There will often not be anything needing escaping at all. */
s = g_string_sized_new (strlen (string));
/* D-Bus address escaping is mostly the same as URI escaping... */
g_string_append_uri_escaped (s, string, "\\/", FALSE);
/* ... but '~' is an unreserved character in URIs, but a
* non-optionally-escaped character in D-Bus addresses. */
for (i = 0; i < s->len; i++)
{
if (G_UNLIKELY (s->str[i] == '~'))
{
s->str[i] = '%';
g_string_insert (s, i + 1, "7E");
i += 2;
}
}
return g_string_free (s, FALSE);
}
#endif
#if !GLIB_CHECK_VERSION(2, 36, 0)
/*
* Reimplement GUnixFDSourceFunc API in terms of older GLib versions
*/
typedef struct
{
GSource parent;
GPollFD pollfd;
} MyUnixFDSource;
static gboolean
my_unix_fd_source_prepare (GSource *source,
int *timeout_out)
{
if (timeout_out != NULL)
*timeout_out = -1;
/* We don't know we're ready to be dispatched until we have polled. */
return FALSE;
}
static gboolean
my_unix_fd_source_check (GSource *source)
{
MyUnixFDSource *self = (MyUnixFDSource *) source;
/* We're ready to be dispatched if an event occurred. */
return (self->pollfd.revents != 0);
}
static gboolean
my_unix_fd_source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
MyUnixFDSource *self = (MyUnixFDSource *) source;
MyGUnixFDSourceFunc func = (MyGUnixFDSourceFunc) G_CALLBACK (callback);
g_return_val_if_fail (func != NULL, G_SOURCE_REMOVE);
return func (self->pollfd.fd, self->pollfd.revents, user_data);
}
static void
my_unix_fd_source_finalize (GSource *source)
{
}
static GSourceFuncs my_unix_fd_source_funcs =
{
my_unix_fd_source_prepare,
my_unix_fd_source_check,
my_unix_fd_source_dispatch,
my_unix_fd_source_finalize
};
GSource *
my_g_unix_fd_source_new (int fd,
GIOCondition condition)
{
GSource *source = g_source_new (&my_unix_fd_source_funcs,
sizeof (MyUnixFDSource));
MyUnixFDSource *self = (MyUnixFDSource *) source;
self->pollfd.fd = fd;
self->pollfd.events = condition;
g_source_add_poll (source, &self->pollfd);
return source;
}
guint
my_g_unix_fd_add_full (int priority,
int fd,
GIOCondition condition,
MyGUnixFDSourceFunc func,
gpointer user_data,
GDestroyNotify destroy)
{
GSource *source;
guint ret;
g_return_val_if_fail (fd >= 0, 0);
g_return_val_if_fail (func != NULL, 0);
source = my_g_unix_fd_source_new (fd, condition);
g_source_set_callback (source, (GSourceFunc) G_CALLBACK (func),
user_data, destroy);
g_source_set_priority (source, priority);
ret = g_source_attach (source, NULL);
g_source_unref (source);
return ret;
}
#endif
#if !GLIB_CHECK_VERSION(2, 58, 0)
/**
* g_canonicalize_filename:
* @filename: (type filename): the name of the file
* @relative_to: (type filename) (nullable): the relative directory, or %NULL
* to use the current working directory
*
* Gets the canonical file name from @filename. All triple slashes are turned into
* single slashes, and all `..` and `.`s resolved against @relative_to.
*
* Symlinks are not followed, and the returned path is guaranteed to be absolute.
*
* If @filename is an absolute path, @relative_to is ignored. Otherwise,
* @relative_to will be prepended to @filename to make it absolute. @relative_to
* must be an absolute path, or %NULL. If @relative_to is %NULL, it'll fallback
* to g_get_current_dir().
*
* This function never fails, and will canonicalize file paths even if they don't
* exist.
*
* No file system I/O is done.
*
* Returns: (type filename) (transfer full): a newly allocated string with the
* canonical file path
* Since: 2.58
*/
gchar *
my_g_canonicalize_filename (const gchar *filename,
const gchar *relative_to)
{
gchar *canon, *start, *p, *q;
guint i;
g_return_val_if_fail (relative_to == NULL || g_path_is_absolute (relative_to), NULL);
if (!g_path_is_absolute (filename))
{
gchar *cwd_allocated = NULL;
const gchar *cwd;
if (relative_to != NULL)
cwd = relative_to;
else
cwd = cwd_allocated = g_get_current_dir ();
canon = g_build_filename (cwd, filename, NULL);
g_free (cwd_allocated);
}
else
{
canon = g_strdup (filename);
}
start = (char *)g_path_skip_root (canon);
if (start == NULL)
{
/* This shouldn't really happen, as g_get_current_dir() should
return an absolute pathname, but bug 573843 shows this is
not always happening */
g_free (canon);
return g_build_filename (G_DIR_SEPARATOR_S, filename, NULL);
}
/* POSIX allows double slashes at the start to
* mean something special (as does windows too).
* So, "//" != "/", but more than two slashes
* is treated as "/".
*/
i = 0;
for (p = start - 1;
(p >= canon) &&
G_IS_DIR_SEPARATOR (*p);
p--)
i++;
if (i > 2)
{
i -= 1;
start -= i;
memmove (start, start+i, strlen (start+i) + 1);
}
/* Make sure we're using the canonical dir separator */
p++;
while (p < start && G_IS_DIR_SEPARATOR (*p))
*p++ = G_DIR_SEPARATOR;
p = start;
while (*p != 0)
{
if (p[0] == '.' && (p[1] == 0 || G_IS_DIR_SEPARATOR (p[1])))
{
memmove (p, p+1, strlen (p+1)+1);
}
else if (p[0] == '.' && p[1] == '.' && (p[2] == 0 || G_IS_DIR_SEPARATOR (p[2])))
{
q = p + 2;
/* Skip previous separator */
p = p - 2;
if (p < start)
p = start;
while (p > start && !G_IS_DIR_SEPARATOR (*p))
p--;
if (G_IS_DIR_SEPARATOR (*p))
*p++ = G_DIR_SEPARATOR;
memmove (p, q, strlen (q)+1);
}
else
{
/* Skip until next separator */
while (*p != 0 && !G_IS_DIR_SEPARATOR (*p))
p++;
if (*p != 0)
{
/* Canonicalize one separator */
*p++ = G_DIR_SEPARATOR;
}
}
/* Remove additional separators */
q = p;
while (*q && G_IS_DIR_SEPARATOR (*q))
q++;
if (p != q)
memmove (p, q, strlen (q) + 1);
}
/* Remove trailing slashes */
if (p > start && G_IS_DIR_SEPARATOR (*(p-1)))
*(p-1) = 0;
return canon;
}
#endif
#if !GLIB_CHECK_VERSION(2, 40, 0)
gpointer *
my_g_hash_table_get_keys_as_array (GHashTable *hash,
guint *len)
{
GPtrArray *arr = g_ptr_array_sized_new (g_hash_table_size (hash));
GHashTableIter iter;
gpointer k;
g_hash_table_iter_init (&iter, hash);
while (g_hash_table_iter_next (&iter, &k, NULL))
g_ptr_array_add (arr, k);
if (len != NULL)
*len = arr->len;
g_ptr_array_add (arr, NULL);
return g_ptr_array_free (arr, FALSE);
}
#endif