2003-02-06 06:15:39 +08:00
|
|
|
/* LIBGIMP - The GIMP Library
|
|
|
|
* Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
|
|
|
|
*
|
|
|
|
* gimputils.c
|
|
|
|
* Copyright (C) 2003 Sven Neumann <sven@gimp.org>
|
|
|
|
*
|
2009-01-18 06:28:01 +08:00
|
|
|
* This library is free software: you can redistribute it and/or
|
2003-11-23 23:35:27 +08:00
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
2009-01-18 06:28:01 +08:00
|
|
|
* version 3 of the License, or (at your option) any later version.
|
2003-02-06 06:15:39 +08:00
|
|
|
*
|
2003-11-23 23:35:27 +08:00
|
|
|
* This library is distributed in the hope that it will be useful,
|
2003-02-06 06:15:39 +08:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2003-11-23 23:35:27 +08:00
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
2003-02-06 06:15:39 +08:00
|
|
|
*
|
2003-11-23 23:35:27 +08:00
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2009-01-18 06:28:01 +08:00
|
|
|
* License along with this library. If not, see
|
|
|
|
* <http://www.gnu.org/licenses/>.
|
2003-02-06 06:15:39 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2018-02-09 08:57:03 +08:00
|
|
|
#include <stdlib.h>
|
2003-02-06 06:15:39 +08:00
|
|
|
#include <string.h>
|
2006-06-28 04:00:03 +08:00
|
|
|
#include <stdio.h>
|
2003-02-06 06:15:39 +08:00
|
|
|
|
2015-02-20 04:39:05 +08:00
|
|
|
#ifdef PLATFORM_OSX
|
|
|
|
#include <AppKit/AppKit.h>
|
|
|
|
#endif
|
|
|
|
|
2018-02-09 08:57:03 +08:00
|
|
|
#ifdef HAVE_EXECINFO_H
|
|
|
|
/* Allowing backtrace() API. */
|
|
|
|
#include <execinfo.h>
|
|
|
|
#endif
|
|
|
|
|
2013-11-28 07:29:43 +08:00
|
|
|
#include <gio/gio.h>
|
2018-02-09 08:57:03 +08:00
|
|
|
#include <glib/gprintf.h>
|
2003-02-06 06:15:39 +08:00
|
|
|
|
2015-02-27 09:48:29 +08:00
|
|
|
#if defined(G_OS_WIN32)
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
|
2015-02-27 09:48:29 +08:00
|
|
|
/* This is a hack for Windows known directory support.
|
|
|
|
* DATADIR (autotools-generated constant) is a type defined in objidl.h
|
|
|
|
* so we must #undef it before including shlobj.h in order to avoid a
|
|
|
|
* name clash. */
|
|
|
|
#undef DATADIR
|
|
|
|
#include <windows.h>
|
|
|
|
#include <shlobj.h>
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
|
2018-02-13 01:08:03 +08:00
|
|
|
#else /* G_OS_WIN32 */
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
|
2018-02-13 01:08:03 +08:00
|
|
|
/* For waitpid() */
|
|
|
|
#include <sys/wait.h>
|
2018-03-05 12:26:13 +08:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
/* For thread IDs. */
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
|
2018-03-05 12:26:13 +08:00
|
|
|
#ifdef HAVE_SYS_PRCTL_H
|
|
|
|
#include <sys/prctl.h>
|
|
|
|
#endif
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
|
2018-04-28 20:57:46 +08:00
|
|
|
#ifdef HAVE_SYS_THR_H
|
|
|
|
#include <sys/thr.h>
|
|
|
|
#endif
|
|
|
|
|
2018-02-13 01:08:03 +08:00
|
|
|
#endif /* G_OS_WIN32 */
|
2015-02-27 09:48:29 +08:00
|
|
|
|
2004-07-29 20:33:15 +08:00
|
|
|
#include "gimpbasetypes.h"
|
2003-02-06 06:15:39 +08:00
|
|
|
#include "gimputils.h"
|
|
|
|
|
2003-04-04 23:11:30 +08:00
|
|
|
#include "libgimp/libgimp-intl.h"
|
|
|
|
|
2003-02-06 06:15:39 +08:00
|
|
|
|
2010-06-30 01:13:40 +08:00
|
|
|
/**
|
|
|
|
* SECTION: gimputils
|
|
|
|
* @title: gimputils
|
|
|
|
* @short_description: Utilities of general interest
|
|
|
|
*
|
|
|
|
* Utilities of general interest
|
|
|
|
**/
|
|
|
|
|
2018-02-22 12:05:24 +08:00
|
|
|
static gboolean gimp_utils_generic_available (const gchar *program,
|
|
|
|
gint major,
|
|
|
|
gint minor);
|
|
|
|
static gboolean gimp_utils_gdb_available (gint major,
|
|
|
|
gint minor);
|
|
|
|
static gboolean gimp_utils_lldb_available (gint major,
|
|
|
|
gint minor);
|
2010-06-30 01:13:40 +08:00
|
|
|
|
2003-02-06 06:15:39 +08:00
|
|
|
/**
|
|
|
|
* gimp_utf8_strtrim:
|
|
|
|
* @str: an UTF-8 encoded string (or %NULL)
|
2003-02-21 00:11:23 +08:00
|
|
|
* @max_chars: the maximum number of characters before the string get
|
|
|
|
* trimmed
|
2003-02-06 06:15:39 +08:00
|
|
|
*
|
2003-02-21 00:11:23 +08:00
|
|
|
* Creates a (possibly trimmed) copy of @str. The string is cut if it
|
|
|
|
* exceeds @max_chars characters or on the first newline. The fact
|
|
|
|
* that the string was trimmed is indicated by appending an ellipsis.
|
2003-10-16 20:24:58 +08:00
|
|
|
*
|
2003-02-21 00:11:23 +08:00
|
|
|
* Returns: A (possibly trimmed) copy of @str which should be freed
|
|
|
|
* using g_free() when it is not needed any longer.
|
2003-02-06 06:15:39 +08:00
|
|
|
**/
|
|
|
|
gchar *
|
|
|
|
gimp_utf8_strtrim (const gchar *str,
|
2006-04-12 18:53:28 +08:00
|
|
|
gint max_chars)
|
2003-02-06 06:15:39 +08:00
|
|
|
{
|
|
|
|
/* FIXME: should we make this translatable? */
|
2006-04-20 15:15:15 +08:00
|
|
|
const gchar ellipsis[] = "...";
|
2006-04-21 17:14:55 +08:00
|
|
|
const gint e_len = strlen (ellipsis);
|
2003-02-06 06:15:39 +08:00
|
|
|
|
|
|
|
if (str)
|
|
|
|
{
|
|
|
|
const gchar *p;
|
|
|
|
const gchar *newline = NULL;
|
|
|
|
gint chars = 0;
|
|
|
|
gunichar unichar;
|
|
|
|
|
2003-02-09 06:04:59 +08:00
|
|
|
for (p = str; *p; p = g_utf8_next_char (p))
|
2003-02-06 06:15:39 +08:00
|
|
|
{
|
|
|
|
if (++chars > max_chars)
|
|
|
|
break;
|
|
|
|
|
|
|
|
unichar = g_utf8_get_char (p);
|
|
|
|
|
|
|
|
switch (g_unichar_break_type (unichar))
|
|
|
|
{
|
|
|
|
case G_UNICODE_BREAK_MANDATORY:
|
|
|
|
case G_UNICODE_BREAK_LINE_FEED:
|
|
|
|
newline = p;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2006-04-12 18:53:28 +08:00
|
|
|
break;
|
2003-02-06 06:15:39 +08:00
|
|
|
}
|
|
|
|
|
2003-02-21 00:11:23 +08:00
|
|
|
if (*p)
|
2003-02-06 06:15:39 +08:00
|
|
|
{
|
|
|
|
gsize len = p - str;
|
|
|
|
gchar *trimmed = g_new (gchar, len + e_len + 2);
|
|
|
|
|
|
|
|
memcpy (trimmed, str, len);
|
|
|
|
if (newline)
|
|
|
|
trimmed[len++] = ' ';
|
|
|
|
|
|
|
|
g_strlcpy (trimmed + len, ellipsis, e_len + 1);
|
|
|
|
|
|
|
|
return trimmed;
|
|
|
|
}
|
|
|
|
|
|
|
|
return g_strdup (str);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2003-04-04 23:11:30 +08:00
|
|
|
|
2003-10-16 20:24:58 +08:00
|
|
|
/**
|
|
|
|
* gimp_any_to_utf8:
|
|
|
|
* @str: The string to be converted to UTF-8.
|
|
|
|
* @len: The length of the string, or -1 if the string
|
|
|
|
* is nul-terminated.
|
|
|
|
* @warning_format: The message format for the warning message if conversion
|
|
|
|
* to UTF-8 fails. See the <function>printf()</function>
|
|
|
|
* documentation.
|
2011-11-26 04:39:55 +08:00
|
|
|
* @...: The parameters to insert into the format string.
|
2003-10-16 20:24:58 +08:00
|
|
|
*
|
|
|
|
* This function takes any string (UTF-8 or not) and always returns a valid
|
|
|
|
* UTF-8 string.
|
|
|
|
*
|
|
|
|
* If @str is valid UTF-8, a copy of the string is returned.
|
|
|
|
*
|
|
|
|
* If UTF-8 validation fails, g_locale_to_utf8() is tried and if it
|
|
|
|
* succeeds the resulting string is returned.
|
|
|
|
*
|
|
|
|
* Otherwise, the portion of @str that is UTF-8, concatenated
|
|
|
|
* with "(invalid UTF-8 string)" is returned. If not even the start
|
|
|
|
* of @str is valid UTF-8, only "(invalid UTF-8 string)" is returned.
|
|
|
|
*
|
|
|
|
* Return value: The UTF-8 string as described above.
|
|
|
|
**/
|
|
|
|
gchar *
|
|
|
|
gimp_any_to_utf8 (const gchar *str,
|
|
|
|
gssize len,
|
|
|
|
const gchar *warning_format,
|
|
|
|
...)
|
|
|
|
{
|
|
|
|
const gchar *start_invalid;
|
|
|
|
gchar *utf8;
|
|
|
|
|
|
|
|
g_return_val_if_fail (str != NULL, NULL);
|
|
|
|
|
|
|
|
if (g_utf8_validate (str, len, &start_invalid))
|
2004-11-22 23:25:50 +08:00
|
|
|
{
|
|
|
|
if (len < 0)
|
|
|
|
utf8 = g_strdup (str);
|
|
|
|
else
|
|
|
|
utf8 = g_strndup (str, len);
|
|
|
|
}
|
2003-10-16 20:24:58 +08:00
|
|
|
else
|
2004-11-22 23:25:50 +08:00
|
|
|
{
|
|
|
|
utf8 = g_locale_to_utf8 (str, len, NULL, NULL, NULL);
|
|
|
|
}
|
2003-10-16 20:24:58 +08:00
|
|
|
|
|
|
|
if (! utf8)
|
|
|
|
{
|
|
|
|
if (warning_format)
|
|
|
|
{
|
|
|
|
va_list warning_args;
|
|
|
|
|
|
|
|
va_start (warning_args, warning_format);
|
|
|
|
|
|
|
|
g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE,
|
|
|
|
warning_format, warning_args);
|
|
|
|
|
|
|
|
va_end (warning_args);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (start_invalid > str)
|
|
|
|
{
|
|
|
|
gchar *tmp;
|
|
|
|
|
|
|
|
tmp = g_strndup (str, start_invalid - str);
|
2007-07-06 18:02:31 +08:00
|
|
|
utf8 = g_strconcat (tmp, " ", _("(invalid UTF-8 string)"), NULL);
|
2003-10-16 20:24:58 +08:00
|
|
|
g_free (tmp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
utf8 = g_strdup (_("(invalid UTF-8 string)"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return utf8;
|
|
|
|
}
|
|
|
|
|
2004-01-19 09:08:43 +08:00
|
|
|
/**
|
|
|
|
* gimp_filename_to_utf8:
|
|
|
|
* @filename: The filename to be converted to UTF-8.
|
|
|
|
*
|
|
|
|
* Convert a filename in the filesystem's encoding to UTF-8
|
|
|
|
* temporarily. The return value is a pointer to a string that is
|
|
|
|
* guaranteed to be valid only during the current iteration of the
|
|
|
|
* main loop or until the next call to gimp_filename_to_utf8().
|
|
|
|
*
|
|
|
|
* The only purpose of this function is to provide an easy way to pass
|
|
|
|
* a filename in the filesystem encoding to a function that expects an
|
|
|
|
* UTF-8 encoded filename.
|
|
|
|
*
|
|
|
|
* Return value: A temporarily valid UTF-8 representation of @filename.
|
|
|
|
* This string must not be changed or freed.
|
|
|
|
**/
|
|
|
|
const gchar *
|
|
|
|
gimp_filename_to_utf8 (const gchar *filename)
|
|
|
|
{
|
|
|
|
/* Simpleminded implementation, but at least allocates just one copy
|
|
|
|
* of each translation. Could check if already UTF-8, and if so
|
|
|
|
* return filename as is. Could perhaps (re)use a suitably large
|
|
|
|
* cyclic buffer, but then would have to verify that all calls
|
|
|
|
* really need the return value just for a "short" time.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static GHashTable *ht = NULL;
|
|
|
|
gchar *filename_utf8;
|
|
|
|
|
|
|
|
if (! filename)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (! ht)
|
|
|
|
ht = g_hash_table_new (g_str_hash, g_str_equal);
|
|
|
|
|
|
|
|
filename_utf8 = g_hash_table_lookup (ht, filename);
|
|
|
|
|
|
|
|
if (! filename_utf8)
|
|
|
|
{
|
2005-02-21 05:49:53 +08:00
|
|
|
filename_utf8 = g_filename_display_name (filename);
|
2004-01-19 09:08:43 +08:00
|
|
|
g_hash_table_insert (ht, g_strdup (filename), filename_utf8);
|
|
|
|
}
|
|
|
|
|
|
|
|
return filename_utf8;
|
|
|
|
}
|
|
|
|
|
2014-07-01 19:18:14 +08:00
|
|
|
/**
|
|
|
|
* gimp_file_get_utf8_name:
|
|
|
|
* @file: a #GFile
|
|
|
|
*
|
|
|
|
* This function works like gimp_filename_to_utf8() and returns
|
|
|
|
* a UTF-8 encoded string that does not need to be freed.
|
|
|
|
*
|
|
|
|
* It converts a #GFile's path or uri to UTF-8 temporarily. The
|
|
|
|
* return value is a pointer to a string that is guaranteed to be
|
|
|
|
* valid only during the current iteration of the main loop or until
|
|
|
|
* the next call to gimp_file_get_utf8_name().
|
|
|
|
*
|
|
|
|
* The only purpose of this function is to provide an easy way to pass
|
|
|
|
* a #GFile's name to a function that expects an UTF-8 encoded string.
|
|
|
|
*
|
|
|
|
* See g_file_get_parse_name().
|
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.10
|
2014-07-01 19:18:14 +08:00
|
|
|
*
|
|
|
|
* Return value: A temporarily valid UTF-8 representation of @file's name.
|
|
|
|
* This string must not be changed or freed.
|
|
|
|
**/
|
|
|
|
const gchar *
|
|
|
|
gimp_file_get_utf8_name (GFile *file)
|
|
|
|
{
|
|
|
|
gchar *name;
|
|
|
|
|
|
|
|
g_return_val_if_fail (G_IS_FILE (file), NULL);
|
|
|
|
|
|
|
|
name = g_file_get_parse_name (file);
|
|
|
|
|
|
|
|
g_object_set_data_full (G_OBJECT (file), "gimp-parse-name", name,
|
|
|
|
(GDestroyNotify) g_free);
|
|
|
|
|
|
|
|
return name;
|
|
|
|
}
|
2003-08-12 01:14:32 +08:00
|
|
|
|
2014-08-04 02:46:28 +08:00
|
|
|
/**
|
|
|
|
* gimp_file_has_extension:
|
|
|
|
* @file: a #GFile
|
|
|
|
* @extension: an ASCII extension
|
|
|
|
*
|
|
|
|
* This function checks if @file's URI ends with @extension. It behaves
|
|
|
|
* like g_str_has_suffix() on g_file_get_uri(), except that the string
|
|
|
|
* comparison is done case-insensitively using g_ascii_strcasecmp().
|
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.10
|
2014-08-04 02:46:28 +08:00
|
|
|
*
|
|
|
|
* Return value: %TRUE if @file's URI ends with @extension,
|
|
|
|
* %FALSE otherwise.
|
|
|
|
**/
|
|
|
|
gboolean
|
|
|
|
gimp_file_has_extension (GFile *file,
|
|
|
|
const gchar *extension)
|
|
|
|
{
|
|
|
|
gchar *uri;
|
|
|
|
gint uri_len;
|
|
|
|
gint ext_len;
|
|
|
|
gboolean result = FALSE;
|
|
|
|
|
|
|
|
g_return_val_if_fail (G_IS_FILE (file), FALSE);
|
|
|
|
g_return_val_if_fail (extension != NULL, FALSE);
|
|
|
|
|
|
|
|
uri = g_file_get_uri (file);
|
|
|
|
|
|
|
|
uri_len = strlen (uri);
|
|
|
|
ext_len = strlen (extension);
|
|
|
|
|
|
|
|
if (uri_len && ext_len && (uri_len > ext_len))
|
|
|
|
{
|
|
|
|
if (g_ascii_strcasecmp (uri + uri_len - ext_len, extension) == 0)
|
|
|
|
result = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free (uri);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-02-17 01:40:26 +08:00
|
|
|
/**
|
|
|
|
* gimp_file_show_in_file_manager:
|
|
|
|
* @file: a #GFile
|
|
|
|
* @error: return location for a #GError
|
|
|
|
*
|
|
|
|
* Shows @file in the system file manager.
|
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.10
|
2015-02-17 01:40:26 +08:00
|
|
|
*
|
2015-06-01 01:44:39 +08:00
|
|
|
* Return value: %TRUE on success, %FALSE otherwise. On %FALSE, @error
|
2015-02-17 01:40:26 +08:00
|
|
|
* is set.
|
|
|
|
**/
|
|
|
|
gboolean
|
|
|
|
gimp_file_show_in_file_manager (GFile *file,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (G_IS_FILE (file), FALSE);
|
|
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
|
|
|
|
#if defined(G_OS_WIN32)
|
|
|
|
|
|
|
|
{
|
2015-02-27 09:48:29 +08:00
|
|
|
gboolean ret;
|
|
|
|
char *filename;
|
|
|
|
int n;
|
|
|
|
LPWSTR w_filename = NULL;
|
|
|
|
ITEMIDLIST *pidl = NULL;
|
|
|
|
|
|
|
|
ret = FALSE;
|
2015-02-17 01:40:26 +08:00
|
|
|
|
2016-06-26 04:54:10 +08:00
|
|
|
/* Calling this function multiple times should do no harm, but it is
|
2015-02-27 09:48:29 +08:00
|
|
|
easier to put this here as it needs linking against ole32. */
|
|
|
|
CoInitialize (NULL);
|
|
|
|
|
|
|
|
filename = g_file_get_path (file);
|
|
|
|
if (!filename)
|
|
|
|
{
|
|
|
|
g_set_error_literal (error, G_FILE_ERROR, 0,
|
2015-02-27 14:46:02 +08:00
|
|
|
_("File path is NULL"));
|
2015-02-27 09:48:29 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
n = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS,
|
|
|
|
filename, -1, NULL, 0);
|
|
|
|
if (n == 0)
|
|
|
|
{
|
|
|
|
g_set_error_literal (error, G_FILE_ERROR, 0,
|
2015-02-27 14:46:02 +08:00
|
|
|
_("Error converting UTF-8 filename to wide char"));
|
2015-02-27 09:48:29 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
w_filename = g_malloc_n (n + 1, sizeof (wchar_t));
|
|
|
|
n = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS,
|
|
|
|
filename, -1,
|
|
|
|
w_filename, (n + 1) * sizeof (wchar_t));
|
|
|
|
if (n == 0)
|
|
|
|
{
|
|
|
|
g_set_error_literal (error, G_FILE_ERROR, 0,
|
2015-02-27 14:46:02 +08:00
|
|
|
_("Error converting UTF-8 filename to wide char"));
|
2015-02-27 09:48:29 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
pidl = ILCreateFromPathW (w_filename);
|
|
|
|
if (!pidl)
|
|
|
|
{
|
|
|
|
g_set_error_literal (error, G_FILE_ERROR, 0,
|
2015-02-27 14:46:02 +08:00
|
|
|
_("ILCreateFromPath() failed"));
|
2015-02-27 09:48:29 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
SHOpenFolderAndSelectItems (pidl, 0, NULL, 0);
|
|
|
|
ret = TRUE;
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (pidl)
|
|
|
|
ILFree (pidl);
|
|
|
|
g_free (w_filename);
|
|
|
|
g_free (filename);
|
|
|
|
|
|
|
|
return ret;
|
2015-02-17 01:40:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#elif defined(PLATFORM_OSX)
|
|
|
|
|
|
|
|
{
|
2015-02-20 04:39:05 +08:00
|
|
|
gchar *uri;
|
|
|
|
NSString *filename;
|
|
|
|
NSURL *url;
|
|
|
|
gboolean retval = TRUE;
|
2015-02-17 01:40:26 +08:00
|
|
|
|
2015-02-20 04:39:05 +08:00
|
|
|
uri = g_file_get_uri (file);
|
|
|
|
filename = [NSString stringWithUTF8String:uri];
|
2015-02-17 01:40:26 +08:00
|
|
|
|
2015-02-20 04:39:05 +08:00
|
|
|
url = [NSURL URLWithString:filename];
|
|
|
|
if (url)
|
|
|
|
{
|
|
|
|
NSArray *url_array = [NSArray arrayWithObject:url];
|
|
|
|
|
|
|
|
[[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:url_array];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_set_error (error, G_FILE_ERROR, 0,
|
|
|
|
_("Cannot convert '%s' into a valid NSURL."), uri);
|
|
|
|
retval = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free (uri);
|
|
|
|
|
|
|
|
return retval;
|
2015-02-17 01:40:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#else /* UNIX */
|
|
|
|
|
|
|
|
{
|
|
|
|
GDBusProxy *proxy;
|
|
|
|
GVariant *retval;
|
|
|
|
GVariantBuilder *builder;
|
|
|
|
gchar *uri;
|
|
|
|
|
|
|
|
proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
|
|
|
|
G_DBUS_PROXY_FLAGS_NONE,
|
|
|
|
NULL,
|
|
|
|
"org.freedesktop.FileManager1",
|
|
|
|
"/org/freedesktop/FileManager1",
|
|
|
|
"org.freedesktop.FileManager1",
|
|
|
|
NULL, error);
|
|
|
|
|
|
|
|
if (! proxy)
|
|
|
|
{
|
|
|
|
g_prefix_error (error,
|
|
|
|
_("Connecting to org.freedesktop.FileManager1 failed: "));
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
uri = g_file_get_uri (file);
|
|
|
|
|
|
|
|
builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
|
|
|
|
g_variant_builder_add (builder, "s", uri);
|
|
|
|
|
|
|
|
g_free (uri);
|
|
|
|
|
|
|
|
retval = g_dbus_proxy_call_sync (proxy,
|
|
|
|
"ShowItems",
|
|
|
|
g_variant_new ("(ass)",
|
|
|
|
builder,
|
|
|
|
""),
|
|
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
|
|
-1, NULL, error);
|
|
|
|
|
|
|
|
g_variant_builder_unref (builder);
|
|
|
|
g_object_unref (proxy);
|
|
|
|
|
|
|
|
if (! retval)
|
|
|
|
{
|
|
|
|
g_prefix_error (error, _("Calling ShowItems failed: "));
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_variant_unref (retval);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2003-08-12 01:14:32 +08:00
|
|
|
/**
|
|
|
|
* gimp_strip_uline:
|
2004-11-13 20:32:01 +08:00
|
|
|
* @str: underline infested string (or %NULL)
|
2003-08-12 01:14:32 +08:00
|
|
|
*
|
|
|
|
* This function returns a copy of @str stripped of underline
|
|
|
|
* characters. This comes in handy when needing to strip mnemonics
|
|
|
|
* from menu paths etc.
|
|
|
|
*
|
2004-11-13 20:32:01 +08:00
|
|
|
* In some languages, mnemonics are handled by adding the mnemonic
|
|
|
|
* character in brackets (like "File (_F)"). This function recognizes
|
|
|
|
* this construct and removes the whole bracket construction to get
|
2010-06-30 01:13:40 +08:00
|
|
|
* rid of the mnemonic (see bug 157561).
|
2004-11-13 20:32:01 +08:00
|
|
|
*
|
2003-08-12 01:14:32 +08:00
|
|
|
* Return value: A (possibly stripped) copy of @str which should be
|
2004-11-13 20:32:01 +08:00
|
|
|
* freed using g_free() when it is not needed any longer.
|
2003-08-12 01:14:32 +08:00
|
|
|
**/
|
|
|
|
gchar *
|
|
|
|
gimp_strip_uline (const gchar *str)
|
|
|
|
{
|
2004-11-13 20:32:01 +08:00
|
|
|
gchar *escaped;
|
|
|
|
gchar *p;
|
|
|
|
gboolean past_bracket = FALSE;
|
2003-08-12 01:14:32 +08:00
|
|
|
|
|
|
|
if (! str)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
p = escaped = g_strdup (str);
|
|
|
|
|
|
|
|
while (*str)
|
|
|
|
{
|
|
|
|
if (*str == '_')
|
|
|
|
{
|
|
|
|
/* "__" means a literal "_" in the menu path */
|
|
|
|
if (str[1] == '_')
|
2004-11-13 20:32:01 +08:00
|
|
|
{
|
|
|
|
*p++ = *str++;
|
2006-08-06 21:12:43 +08:00
|
|
|
str++;
|
|
|
|
continue;
|
2004-11-13 20:32:01 +08:00
|
|
|
}
|
2003-08-12 01:14:32 +08:00
|
|
|
|
2004-11-13 20:32:01 +08:00
|
|
|
/* find the "(_X)" construct and remove it entirely */
|
|
|
|
if (past_bracket && str[1] && *(g_utf8_next_char (str + 1)) == ')')
|
|
|
|
{
|
|
|
|
str = g_utf8_next_char (str + 1) + 1;
|
|
|
|
p--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
str++;
|
|
|
|
}
|
2003-08-12 01:14:32 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-11-13 20:32:01 +08:00
|
|
|
past_bracket = (*str == '(');
|
|
|
|
|
2003-08-12 01:14:32 +08:00
|
|
|
*p++ = *str++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*p = '\0';
|
|
|
|
|
|
|
|
return escaped;
|
|
|
|
}
|
2004-04-30 00:47:53 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* gimp_escape_uline:
|
|
|
|
* @str: Underline infested string (or %NULL)
|
|
|
|
*
|
|
|
|
* This function returns a copy of @str with all underline converted
|
|
|
|
* to two adjacent underlines. This comes in handy when needing to display
|
|
|
|
* strings with underlines (like filenames) in a place that would convert
|
|
|
|
* them to mnemonics.
|
|
|
|
*
|
|
|
|
* Return value: A (possibly escaped) copy of @str which should be
|
|
|
|
* freed using g_free() when it is not needed any longer.
|
2004-11-10 21:55:12 +08:00
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.2
|
2004-04-30 00:47:53 +08:00
|
|
|
**/
|
|
|
|
gchar *
|
|
|
|
gimp_escape_uline (const gchar *str)
|
|
|
|
{
|
|
|
|
gchar *escaped;
|
|
|
|
gchar *p;
|
|
|
|
gint n_ulines = 0;
|
|
|
|
|
|
|
|
if (! str)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (p = (gchar *) str; *p; p++)
|
|
|
|
if (*p == '_')
|
|
|
|
n_ulines++;
|
|
|
|
|
|
|
|
p = escaped = g_malloc (strlen (str) + n_ulines + 1);
|
|
|
|
|
|
|
|
while (*str)
|
|
|
|
{
|
|
|
|
if (*str == '_')
|
|
|
|
*p++ = '_';
|
|
|
|
|
|
|
|
*p++ = *str++;
|
|
|
|
}
|
|
|
|
|
|
|
|
*p = '\0';
|
|
|
|
|
|
|
|
return escaped;
|
|
|
|
}
|
2004-07-29 20:33:15 +08:00
|
|
|
|
2005-08-03 06:52:23 +08:00
|
|
|
/**
|
|
|
|
* gimp_canonicalize_identifier:
|
|
|
|
* @identifier: The identifier string to canonicalize.
|
|
|
|
*
|
|
|
|
* Turns any input string into a canonicalized string.
|
|
|
|
*
|
|
|
|
* Canonical identifiers are e.g. expected by the PDB for procedure
|
2005-10-06 07:49:17 +08:00
|
|
|
* and parameter names. Every character of the input string that is
|
|
|
|
* not either '-', 'a-z', 'A-Z' or '0-9' will be replaced by a '-'.
|
2005-08-03 06:52:23 +08:00
|
|
|
*
|
|
|
|
* Return value: The canonicalized identifier. This is a newly
|
|
|
|
* allocated string that should be freed with g_free()
|
|
|
|
* when no longer needed.
|
2005-10-06 07:49:17 +08:00
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.4
|
2005-08-03 06:52:23 +08:00
|
|
|
**/
|
|
|
|
gchar *
|
|
|
|
gimp_canonicalize_identifier (const gchar *identifier)
|
|
|
|
{
|
|
|
|
gchar *canonicalized = NULL;
|
|
|
|
|
|
|
|
if (identifier)
|
|
|
|
{
|
|
|
|
gchar *p;
|
|
|
|
|
|
|
|
canonicalized = g_strdup (identifier);
|
|
|
|
|
|
|
|
for (p = canonicalized; *p != 0; p++)
|
|
|
|
{
|
|
|
|
gchar c = *p;
|
|
|
|
|
|
|
|
if (c != '-' &&
|
|
|
|
(c < '0' || c > '9') &&
|
|
|
|
(c < 'A' || c > 'Z') &&
|
|
|
|
(c < 'a' || c > 'z'))
|
|
|
|
*p = '-';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return canonicalized;
|
|
|
|
}
|
|
|
|
|
2004-11-10 20:44:13 +08:00
|
|
|
/**
|
|
|
|
* gimp_enum_get_desc:
|
|
|
|
* @enum_class: a #GEnumClass
|
|
|
|
* @value: a value from @enum_class
|
|
|
|
*
|
|
|
|
* Retrieves #GimpEnumDesc associated with the given value, or %NULL.
|
|
|
|
*
|
|
|
|
* Return value: the value's #GimpEnumDesc.
|
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.2
|
2004-11-10 20:44:13 +08:00
|
|
|
**/
|
2004-10-26 01:55:25 +08:00
|
|
|
GimpEnumDesc *
|
|
|
|
gimp_enum_get_desc (GEnumClass *enum_class,
|
|
|
|
gint value)
|
|
|
|
{
|
|
|
|
const GimpEnumDesc *value_desc;
|
|
|
|
|
|
|
|
g_return_val_if_fail (G_IS_ENUM_CLASS (enum_class), NULL);
|
|
|
|
|
2007-07-06 18:02:31 +08:00
|
|
|
value_desc =
|
|
|
|
gimp_enum_get_value_descriptions (G_TYPE_FROM_CLASS (enum_class));
|
2004-10-26 01:55:25 +08:00
|
|
|
|
|
|
|
if (value_desc)
|
|
|
|
{
|
|
|
|
while (value_desc->value_desc)
|
|
|
|
{
|
|
|
|
if (value_desc->value == value)
|
|
|
|
return (GimpEnumDesc *) value_desc;
|
|
|
|
|
|
|
|
value_desc++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-07-29 20:33:15 +08:00
|
|
|
/**
|
|
|
|
* gimp_enum_get_value:
|
|
|
|
* @enum_type: the #GType of a registered enum
|
|
|
|
* @value: an integer value
|
2004-10-26 01:55:25 +08:00
|
|
|
* @value_name: return location for the value's name (or %NULL)
|
2004-07-29 20:33:15 +08:00
|
|
|
* @value_nick: return location for the value's nick (or %NULL)
|
2008-11-06 17:29:32 +08:00
|
|
|
* @value_desc: return location for the value's translated description (or %NULL)
|
2004-10-26 01:55:25 +08:00
|
|
|
* @value_help: return location for the value's translated help (or %NULL)
|
2004-07-29 20:33:15 +08:00
|
|
|
*
|
|
|
|
* Checks if @value is valid for the enum registered as @enum_type.
|
2004-10-26 01:55:25 +08:00
|
|
|
* If the value exists in that enum, its name, nick and its translated
|
2008-11-06 17:29:32 +08:00
|
|
|
* description and help are returned (if @value_name, @value_nick,
|
|
|
|
* @value_desc and @value_help are not %NULL).
|
2004-07-29 20:33:15 +08:00
|
|
|
*
|
|
|
|
* Return value: %TRUE if @value is valid for the @enum_type,
|
|
|
|
* %FALSE otherwise
|
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.2
|
2004-07-29 20:33:15 +08:00
|
|
|
**/
|
|
|
|
gboolean
|
|
|
|
gimp_enum_get_value (GType enum_type,
|
|
|
|
gint value,
|
2004-10-26 01:55:25 +08:00
|
|
|
const gchar **value_name,
|
2004-07-29 20:33:15 +08:00
|
|
|
const gchar **value_nick,
|
2004-10-26 01:55:25 +08:00
|
|
|
const gchar **value_desc,
|
|
|
|
const gchar **value_help)
|
2004-07-29 20:33:15 +08:00
|
|
|
{
|
|
|
|
GEnumClass *enum_class;
|
|
|
|
GEnumValue *enum_value;
|
2005-09-28 19:02:02 +08:00
|
|
|
gboolean success = FALSE;
|
2004-07-29 20:33:15 +08:00
|
|
|
|
|
|
|
g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), FALSE);
|
|
|
|
|
2005-09-28 19:02:02 +08:00
|
|
|
enum_class = g_type_class_ref (enum_type);
|
2004-07-29 20:33:15 +08:00
|
|
|
enum_value = g_enum_get_value (enum_class, value);
|
|
|
|
|
|
|
|
if (enum_value)
|
|
|
|
{
|
2004-10-26 01:55:25 +08:00
|
|
|
if (value_name)
|
|
|
|
*value_name = enum_value->value_name;
|
|
|
|
|
2004-07-29 20:33:15 +08:00
|
|
|
if (value_nick)
|
|
|
|
*value_nick = enum_value->value_nick;
|
|
|
|
|
2004-10-26 01:55:25 +08:00
|
|
|
if (value_desc || value_help)
|
|
|
|
{
|
|
|
|
GimpEnumDesc *enum_desc;
|
|
|
|
|
|
|
|
enum_desc = gimp_enum_get_desc (enum_class, value);
|
|
|
|
|
2008-11-06 16:28:28 +08:00
|
|
|
if (value_desc)
|
|
|
|
{
|
|
|
|
if (enum_desc && enum_desc->value_desc)
|
|
|
|
{
|
|
|
|
const gchar *context;
|
|
|
|
|
|
|
|
context = gimp_type_get_translation_context (enum_type);
|
|
|
|
|
|
|
|
if (context) /* the new way, using NC_() */
|
|
|
|
*value_desc = g_dpgettext2 (gimp_type_get_translation_domain (enum_type),
|
|
|
|
context,
|
|
|
|
enum_desc->value_desc);
|
|
|
|
else /* for backward compatibility */
|
|
|
|
*value_desc = g_strip_context (enum_desc->value_desc,
|
|
|
|
dgettext (gimp_type_get_translation_domain (enum_type),
|
|
|
|
enum_desc->value_desc));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*value_desc = NULL;
|
|
|
|
}
|
|
|
|
}
|
2004-10-26 01:55:25 +08:00
|
|
|
|
|
|
|
if (value_help)
|
2008-11-06 16:28:28 +08:00
|
|
|
{
|
|
|
|
*value_help = ((enum_desc && enum_desc->value_help) ?
|
|
|
|
dgettext (gimp_type_get_translation_domain (enum_type),
|
|
|
|
enum_desc->value_help) :
|
|
|
|
NULL);
|
|
|
|
}
|
2004-10-26 01:55:25 +08:00
|
|
|
}
|
|
|
|
|
2005-09-28 19:02:02 +08:00
|
|
|
success = TRUE;
|
2004-10-26 01:55:25 +08:00
|
|
|
}
|
|
|
|
|
2005-09-28 19:02:02 +08:00
|
|
|
g_type_class_unref (enum_class);
|
|
|
|
|
|
|
|
return success;
|
2004-10-26 01:55:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2004-11-10 20:44:13 +08:00
|
|
|
* gimp_enum_value_get_desc:
|
|
|
|
* @enum_class: a #GEnumClass
|
|
|
|
* @enum_value: a #GEnumValue from @enum_class
|
|
|
|
*
|
2008-11-06 16:28:28 +08:00
|
|
|
* Retrieves the translated description for a given @enum_value.
|
2004-11-10 20:44:13 +08:00
|
|
|
*
|
2008-11-06 16:28:28 +08:00
|
|
|
* Return value: the translated description of the enum value
|
2004-11-10 20:44:13 +08:00
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.2
|
2004-11-10 20:44:13 +08:00
|
|
|
**/
|
|
|
|
const gchar *
|
|
|
|
gimp_enum_value_get_desc (GEnumClass *enum_class,
|
|
|
|
GEnumValue *enum_value)
|
|
|
|
{
|
|
|
|
GType type = G_TYPE_FROM_CLASS (enum_class);
|
|
|
|
GimpEnumDesc *enum_desc;
|
|
|
|
|
|
|
|
enum_desc = gimp_enum_get_desc (enum_class, enum_value->value);
|
|
|
|
|
2005-01-22 08:08:49 +08:00
|
|
|
if (enum_desc && enum_desc->value_desc)
|
2008-11-06 16:28:28 +08:00
|
|
|
{
|
|
|
|
const gchar *context;
|
|
|
|
|
|
|
|
context = gimp_type_get_translation_context (type);
|
|
|
|
|
|
|
|
if (context) /* the new way, using NC_() */
|
|
|
|
return g_dpgettext2 (gimp_type_get_translation_domain (type),
|
|
|
|
context,
|
|
|
|
enum_desc->value_desc);
|
|
|
|
else /* for backward compatibility */
|
|
|
|
return g_strip_context (enum_desc->value_desc,
|
|
|
|
dgettext (gimp_type_get_translation_domain (type),
|
|
|
|
enum_desc->value_desc));
|
|
|
|
}
|
2004-11-10 20:44:13 +08:00
|
|
|
|
|
|
|
return enum_value->value_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gimp_enum_value_get_help:
|
|
|
|
* @enum_class: a #GEnumClass
|
|
|
|
* @enum_value: a #GEnumValue from @enum_class
|
|
|
|
*
|
|
|
|
* Retrieves the translated help for a given @enum_value.
|
|
|
|
*
|
|
|
|
* Return value: the translated help of the enum value
|
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.2
|
2004-11-10 20:44:13 +08:00
|
|
|
**/
|
|
|
|
const gchar *
|
|
|
|
gimp_enum_value_get_help (GEnumClass *enum_class,
|
|
|
|
GEnumValue *enum_value)
|
|
|
|
{
|
|
|
|
GType type = G_TYPE_FROM_CLASS (enum_class);
|
|
|
|
GimpEnumDesc *enum_desc;
|
|
|
|
|
|
|
|
enum_desc = gimp_enum_get_desc (enum_class, enum_value->value);
|
|
|
|
|
2005-01-22 09:06:52 +08:00
|
|
|
if (enum_desc && enum_desc->value_help)
|
2004-11-10 20:44:13 +08:00
|
|
|
return dgettext (gimp_type_get_translation_domain (type),
|
|
|
|
enum_desc->value_help);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
libgimpbase: add gimp_{enum,flags}_value_get_abbrev()
Add support for specifying an abbreviated description for enum/
flags values, which can be used in contexts where the full
description is too long.
Since the exact layout and size of Gimp{Enum,Flags}Desc is part of
the ABI, we can't simply add a field to these structs to hold the
abbreviated description. Instead, we use the fact that entries
with a repeated value in the value descriptions array are ignored,
and that the array is NULL terminated (in particular, that all non-
NULL entries are followed by at least one additional entry), and
specify the abbreviation in the "value_desc" field of the entry
that immediately follows the initial entry for a given value,
setting the "value" field of both entries to the same value.
Right now this behavior is undocumented, so there is no proper way
to specify abbreviated descriptions in the API, and is only meant
to be used in generated enum files.
2017-11-30 04:33:32 +08:00
|
|
|
/**
|
|
|
|
* gimp_enum_value_get_abbrev:
|
|
|
|
* @enum_class: a #GEnumClass
|
|
|
|
* @enum_value: a #GEnumValue from @enum_class
|
|
|
|
*
|
|
|
|
* Retrieves the translated abbreviation for a given @enum_value.
|
|
|
|
*
|
|
|
|
* Return value: the translated abbreviation of the enum value
|
|
|
|
*
|
|
|
|
* Since: 2.10
|
|
|
|
**/
|
|
|
|
const gchar *
|
|
|
|
gimp_enum_value_get_abbrev (GEnumClass *enum_class,
|
|
|
|
GEnumValue *enum_value)
|
|
|
|
{
|
|
|
|
GType type = G_TYPE_FROM_CLASS (enum_class);
|
|
|
|
GimpEnumDesc *enum_desc;
|
|
|
|
|
|
|
|
enum_desc = gimp_enum_get_desc (enum_class, enum_value->value);
|
|
|
|
|
|
|
|
if (enum_desc &&
|
|
|
|
enum_desc[1].value == enum_desc->value &&
|
|
|
|
enum_desc[1].value_desc)
|
|
|
|
{
|
2017-12-04 22:49:24 +08:00
|
|
|
return g_dpgettext2 (gimp_type_get_translation_domain (type),
|
|
|
|
gimp_type_get_translation_context (type),
|
|
|
|
enum_desc[1].value_desc);
|
libgimpbase: add gimp_{enum,flags}_value_get_abbrev()
Add support for specifying an abbreviated description for enum/
flags values, which can be used in contexts where the full
description is too long.
Since the exact layout and size of Gimp{Enum,Flags}Desc is part of
the ABI, we can't simply add a field to these structs to hold the
abbreviated description. Instead, we use the fact that entries
with a repeated value in the value descriptions array are ignored,
and that the array is NULL terminated (in particular, that all non-
NULL entries are followed by at least one additional entry), and
specify the abbreviation in the "value_desc" field of the entry
that immediately follows the initial entry for a given value,
setting the "value" field of both entries to the same value.
Right now this behavior is undocumented, so there is no proper way
to specify abbreviated descriptions in the API, and is only meant
to be used in generated enum files.
2017-11-30 04:33:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-11-10 20:44:13 +08:00
|
|
|
/**
|
|
|
|
* gimp_flags_get_first_desc:
|
|
|
|
* @flags_class: a #GFlagsClass
|
|
|
|
* @value: a value from @flags_class
|
|
|
|
*
|
|
|
|
* Retrieves the first #GimpFlagsDesc that matches the given value, or %NULL.
|
|
|
|
*
|
|
|
|
* Return value: the value's #GimpFlagsDesc.
|
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.2
|
2004-11-10 20:44:13 +08:00
|
|
|
**/
|
|
|
|
GimpFlagsDesc *
|
|
|
|
gimp_flags_get_first_desc (GFlagsClass *flags_class,
|
|
|
|
guint value)
|
|
|
|
{
|
|
|
|
const GimpFlagsDesc *value_desc;
|
|
|
|
|
|
|
|
g_return_val_if_fail (G_IS_FLAGS_CLASS (flags_class), NULL);
|
|
|
|
|
2007-07-06 18:02:31 +08:00
|
|
|
value_desc =
|
|
|
|
gimp_flags_get_value_descriptions (G_TYPE_FROM_CLASS (flags_class));
|
2004-11-10 20:44:13 +08:00
|
|
|
|
|
|
|
if (value_desc)
|
|
|
|
{
|
|
|
|
while (value_desc->value_desc)
|
|
|
|
{
|
|
|
|
if ((value_desc->value & value) == value_desc->value)
|
|
|
|
return (GimpFlagsDesc *) value_desc;
|
|
|
|
|
|
|
|
value_desc++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gimp_flags_get_first_value:
|
2004-10-26 01:55:25 +08:00
|
|
|
* @flags_type: the #GType of registered flags
|
|
|
|
* @value: an integer value
|
|
|
|
* @value_name: return location for the value's name (or %NULL)
|
|
|
|
* @value_nick: return location for the value's nick (or %NULL)
|
2008-11-06 17:29:32 +08:00
|
|
|
* @value_desc: return location for the value's translated description (or %NULL)
|
2004-10-26 01:55:25 +08:00
|
|
|
* @value_help: return location for the value's translated help (or %NULL)
|
|
|
|
*
|
|
|
|
* Checks if @value is valid for the flags registered as @flags_type.
|
2008-11-06 17:29:32 +08:00
|
|
|
* If the value exists in that flags, its name, nick and its
|
|
|
|
* translated description and help are returned (if @value_name,
|
|
|
|
* @value_nick, @value_desc and @value_help are not %NULL).
|
2004-10-26 01:55:25 +08:00
|
|
|
*
|
|
|
|
* Return value: %TRUE if @value is valid for the @flags_type,
|
|
|
|
* %FALSE otherwise
|
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.2
|
2004-10-26 01:55:25 +08:00
|
|
|
**/
|
|
|
|
gboolean
|
2004-11-10 20:44:13 +08:00
|
|
|
gimp_flags_get_first_value (GType flags_type,
|
|
|
|
guint value,
|
|
|
|
const gchar **value_name,
|
|
|
|
const gchar **value_nick,
|
|
|
|
const gchar **value_desc,
|
|
|
|
const gchar **value_help)
|
2004-10-26 01:55:25 +08:00
|
|
|
{
|
|
|
|
GFlagsClass *flags_class;
|
|
|
|
GFlagsValue *flags_value;
|
|
|
|
|
|
|
|
g_return_val_if_fail (G_TYPE_IS_FLAGS (flags_type), FALSE);
|
|
|
|
|
|
|
|
flags_class = g_type_class_peek (flags_type);
|
|
|
|
flags_value = g_flags_get_first_value (flags_class, value);
|
|
|
|
|
|
|
|
if (flags_value)
|
|
|
|
{
|
2004-07-29 20:33:15 +08:00
|
|
|
if (value_name)
|
2004-10-26 01:55:25 +08:00
|
|
|
*value_name = flags_value->value_name;
|
|
|
|
|
|
|
|
if (value_nick)
|
|
|
|
*value_nick = flags_value->value_nick;
|
|
|
|
|
|
|
|
if (value_desc || value_help)
|
|
|
|
{
|
|
|
|
GimpFlagsDesc *flags_desc;
|
|
|
|
|
|
|
|
flags_desc = gimp_flags_get_first_desc (flags_class, value);
|
|
|
|
|
|
|
|
if (value_desc)
|
|
|
|
*value_desc = ((flags_desc && flags_desc->value_desc) ?
|
|
|
|
dgettext (gimp_type_get_translation_domain (flags_type),
|
|
|
|
flags_desc->value_desc) :
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
if (value_help)
|
|
|
|
*value_help = ((flags_desc && flags_desc->value_desc) ?
|
|
|
|
dgettext (gimp_type_get_translation_domain (flags_type),
|
|
|
|
flags_desc->value_help) :
|
|
|
|
NULL);
|
|
|
|
}
|
2004-07-29 20:33:15 +08:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2004-10-26 01:55:25 +08:00
|
|
|
/**
|
|
|
|
* gimp_flags_value_get_desc:
|
|
|
|
* @flags_class: a #GFlagsClass
|
|
|
|
* @flags_value: a #GFlagsValue from @flags_class
|
|
|
|
*
|
2008-11-06 16:28:28 +08:00
|
|
|
* Retrieves the translated description for a given @flags_value.
|
2004-10-26 01:55:25 +08:00
|
|
|
*
|
2008-11-06 16:28:28 +08:00
|
|
|
* Return value: the translated description of the flags value
|
2004-10-26 01:55:25 +08:00
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.2
|
2004-10-26 01:55:25 +08:00
|
|
|
**/
|
|
|
|
const gchar *
|
|
|
|
gimp_flags_value_get_desc (GFlagsClass *flags_class,
|
|
|
|
GFlagsValue *flags_value)
|
|
|
|
{
|
|
|
|
GType type = G_TYPE_FROM_CLASS (flags_class);
|
|
|
|
GimpFlagsDesc *flags_desc;
|
|
|
|
|
|
|
|
flags_desc = gimp_flags_get_first_desc (flags_class, flags_value->value);
|
|
|
|
|
|
|
|
if (flags_desc->value_desc)
|
2017-12-04 22:49:24 +08:00
|
|
|
{
|
|
|
|
const gchar *context;
|
|
|
|
|
|
|
|
context = gimp_type_get_translation_context (type);
|
|
|
|
|
|
|
|
if (context) /* the new way, using NC_() */
|
|
|
|
return g_dpgettext2 (gimp_type_get_translation_domain (type),
|
|
|
|
context,
|
|
|
|
flags_desc->value_desc);
|
|
|
|
else /* for backward compatibility */
|
|
|
|
return g_strip_context (flags_desc->value_desc,
|
|
|
|
dgettext (gimp_type_get_translation_domain (type),
|
|
|
|
flags_desc->value_desc));
|
|
|
|
}
|
2004-10-26 01:55:25 +08:00
|
|
|
|
|
|
|
return flags_value->value_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gimp_flags_value_get_help:
|
|
|
|
* @flags_class: a #GFlagsClass
|
|
|
|
* @flags_value: a #GFlagsValue from @flags_class
|
|
|
|
*
|
|
|
|
* Retrieves the translated help for a given @flags_value.
|
|
|
|
*
|
|
|
|
* Return value: the translated help of the flags value
|
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.2
|
2004-10-26 01:55:25 +08:00
|
|
|
**/
|
|
|
|
const gchar *
|
|
|
|
gimp_flags_value_get_help (GFlagsClass *flags_class,
|
|
|
|
GFlagsValue *flags_value)
|
|
|
|
{
|
|
|
|
GType type = G_TYPE_FROM_CLASS (flags_class);
|
|
|
|
GimpFlagsDesc *flags_desc;
|
|
|
|
|
|
|
|
flags_desc = gimp_flags_get_first_desc (flags_class, flags_value->value);
|
2004-07-29 20:33:15 +08:00
|
|
|
|
2004-10-26 01:55:25 +08:00
|
|
|
if (flags_desc->value_help)
|
2004-07-29 20:33:15 +08:00
|
|
|
return dgettext (gimp_type_get_translation_domain (type),
|
2004-10-26 01:55:25 +08:00
|
|
|
flags_desc->value_help);
|
2004-07-29 20:33:15 +08:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
libgimpbase: add gimp_{enum,flags}_value_get_abbrev()
Add support for specifying an abbreviated description for enum/
flags values, which can be used in contexts where the full
description is too long.
Since the exact layout and size of Gimp{Enum,Flags}Desc is part of
the ABI, we can't simply add a field to these structs to hold the
abbreviated description. Instead, we use the fact that entries
with a repeated value in the value descriptions array are ignored,
and that the array is NULL terminated (in particular, that all non-
NULL entries are followed by at least one additional entry), and
specify the abbreviation in the "value_desc" field of the entry
that immediately follows the initial entry for a given value,
setting the "value" field of both entries to the same value.
Right now this behavior is undocumented, so there is no proper way
to specify abbreviated descriptions in the API, and is only meant
to be used in generated enum files.
2017-11-30 04:33:32 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* gimp_flags_value_get_abbrev:
|
|
|
|
* @flags_class: a #GFlagsClass
|
|
|
|
* @flags_value: a #GFlagsValue from @flags_class
|
|
|
|
*
|
|
|
|
* Retrieves the translated abbreviation for a given @flags_value.
|
|
|
|
*
|
|
|
|
* Return value: the translated abbreviation of the flags value
|
|
|
|
*
|
|
|
|
* Since: 2.10
|
|
|
|
**/
|
|
|
|
const gchar *
|
|
|
|
gimp_flags_value_get_abbrev (GFlagsClass *flags_class,
|
|
|
|
GFlagsValue *flags_value)
|
|
|
|
{
|
|
|
|
GType type = G_TYPE_FROM_CLASS (flags_class);
|
|
|
|
GimpFlagsDesc *flags_desc;
|
|
|
|
|
|
|
|
flags_desc = gimp_flags_get_first_desc (flags_class, flags_value->value);
|
|
|
|
|
|
|
|
if (flags_desc &&
|
|
|
|
flags_desc[1].value == flags_desc->value &&
|
|
|
|
flags_desc[1].value_desc)
|
|
|
|
{
|
2017-12-04 22:49:24 +08:00
|
|
|
return g_dpgettext2 (gimp_type_get_translation_domain (type),
|
|
|
|
gimp_type_get_translation_context (type),
|
|
|
|
flags_desc[1].value_desc);
|
libgimpbase: add gimp_{enum,flags}_value_get_abbrev()
Add support for specifying an abbreviated description for enum/
flags values, which can be used in contexts where the full
description is too long.
Since the exact layout and size of Gimp{Enum,Flags}Desc is part of
the ABI, we can't simply add a field to these structs to hold the
abbreviated description. Instead, we use the fact that entries
with a repeated value in the value descriptions array are ignored,
and that the array is NULL terminated (in particular, that all non-
NULL entries are followed by at least one additional entry), and
specify the abbreviation in the "value_desc" field of the entry
that immediately follows the initial entry for a given value,
setting the "value" field of both entries to the same value.
Right now this behavior is undocumented, so there is no proper way
to specify abbreviated descriptions in the API, and is only meant
to be used in generated enum files.
2017-11-30 04:33:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-02-09 08:57:03 +08:00
|
|
|
|
|
|
|
/**
|
2018-02-22 19:35:43 +08:00
|
|
|
* gimp_stack_trace_available:
|
|
|
|
* @optimal: whether we get optimal traces.
|
|
|
|
*
|
|
|
|
* Returns #TRUE if we have dependencies to generate backtraces. If
|
|
|
|
* @optimal is #TRUE, the function will return #TRUE only when we
|
|
|
|
* are able to generate optimal traces (i.e. with GDB or LLDB);
|
|
|
|
* otherwise we return #TRUE even if only backtrace() API is available.
|
|
|
|
*
|
|
|
|
* On Win32, we return TRUE if Dr. Mingw is built-in, FALSE otherwise.
|
|
|
|
*
|
|
|
|
* Since: 2.10
|
|
|
|
**/
|
|
|
|
gboolean
|
|
|
|
gimp_stack_trace_available (gboolean optimal)
|
|
|
|
{
|
|
|
|
#ifndef G_OS_WIN32
|
|
|
|
if (gimp_utils_gdb_available (7, 0) ||
|
|
|
|
gimp_utils_lldb_available (0, 0))
|
|
|
|
return TRUE;
|
|
|
|
#ifdef HAVE_EXECINFO_H
|
|
|
|
if (! optimal)
|
|
|
|
return TRUE;
|
|
|
|
#endif
|
|
|
|
#else /* G_OS_WIN32 */
|
|
|
|
#ifdef HAVE_EXCHNDL
|
|
|
|
return TRUE;
|
|
|
|
#endif
|
|
|
|
#endif /* G_OS_WIN32 */
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gimp_stack_trace_print:
|
2018-02-09 08:57:03 +08:00
|
|
|
* @prog_name: the program to attach to.
|
2018-02-16 04:32:58 +08:00
|
|
|
* @stream: a #FILE * stream.
|
2018-02-09 08:57:03 +08:00
|
|
|
* @trace: location to store a newly allocated string of the trace.
|
|
|
|
*
|
|
|
|
* Attempts to generate a stack trace at current code position in
|
2018-02-12 08:50:19 +08:00
|
|
|
* @prog_name. @prog_name is mostly a helper and can be set to NULL.
|
|
|
|
* Nevertheless if set, it has to be the current program name (argv[0]).
|
|
|
|
* This function is not meant to generate stack trace for third-party
|
|
|
|
* programs, and will attach the current process id only.
|
2018-02-09 08:57:03 +08:00
|
|
|
* Internally, this function uses `gdb` or `lldb` if they are available,
|
|
|
|
* or the stacktrace() API on platforms where it is available. It always
|
|
|
|
* fails on Win32.
|
|
|
|
*
|
2018-02-16 04:32:58 +08:00
|
|
|
* The stack trace, once generated, will either be printed to @stream or
|
2018-02-09 08:57:03 +08:00
|
|
|
* returned as a newly allocated string in @trace, if not #NULL.
|
|
|
|
*
|
|
|
|
* In some error cases (e.g. segmentation fault), trying to allocate
|
|
|
|
* more memory will trigger more segmentation faults and therefore loop
|
|
|
|
* our error handling (which is just wrong). Therefore printing to a
|
|
|
|
* file description is an implementation without any memory allocation.
|
|
|
|
|
|
|
|
* Return value: #TRUE if a stack trace could be generated, #FALSE
|
|
|
|
* otherwise.
|
|
|
|
*
|
|
|
|
* Since: 2.10
|
|
|
|
**/
|
|
|
|
gboolean
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
gimp_stack_trace_print (const gchar *prog_name,
|
|
|
|
gpointer stream,
|
|
|
|
gchar **trace)
|
2018-02-09 08:57:03 +08:00
|
|
|
{
|
2018-02-12 06:20:17 +08:00
|
|
|
gboolean stack_printed = FALSE;
|
2018-02-09 08:57:03 +08:00
|
|
|
|
|
|
|
/* This works only on UNIX systems. */
|
|
|
|
#ifndef G_OS_WIN32
|
|
|
|
GString *gtrace = NULL;
|
|
|
|
gchar gimp_pid[16];
|
|
|
|
gchar buffer[256];
|
|
|
|
ssize_t read_n;
|
2018-03-05 12:26:13 +08:00
|
|
|
int sync_fd[2];
|
2018-02-09 08:57:03 +08:00
|
|
|
int out_fd[2];
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
pid_t fork_pid;
|
|
|
|
pid_t pid = getpid();
|
|
|
|
#if defined(G_OS_WIN32)
|
|
|
|
DWORD tid = GetCurrentThreadId ();
|
2018-04-25 06:19:37 +08:00
|
|
|
#elif defined(SYS_gettid)
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
long tid = syscall (SYS_gettid);
|
2018-04-28 20:57:46 +08:00
|
|
|
#elif defined(HAVE_THR_SELF)
|
|
|
|
long tid = 0;
|
|
|
|
thr_self (&tid);
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
#endif
|
2018-02-09 08:57:03 +08:00
|
|
|
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
g_snprintf (gimp_pid, 16, "%u", (guint) pid);
|
2018-02-09 08:57:03 +08:00
|
|
|
|
2018-03-05 12:26:13 +08:00
|
|
|
if (pipe (sync_fd) == -1)
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2018-02-09 08:57:03 +08:00
|
|
|
if (pipe (out_fd) == -1)
|
|
|
|
{
|
2018-03-05 12:26:13 +08:00
|
|
|
close (sync_fd[0]);
|
|
|
|
close (sync_fd[1]);
|
|
|
|
|
2018-02-09 08:57:03 +08:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
fork_pid = fork ();
|
|
|
|
if (fork_pid == 0)
|
2018-02-09 08:57:03 +08:00
|
|
|
{
|
|
|
|
/* Child process. */
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
gchar *args[9] = { "gdb", "-batch",
|
|
|
|
"-ex", "info threads",
|
|
|
|
"-ex", "thread apply all backtrace full",
|
2018-02-09 08:57:03 +08:00
|
|
|
(gchar *) prog_name, NULL, NULL };
|
|
|
|
|
2018-02-12 08:50:19 +08:00
|
|
|
if (prog_name == NULL)
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
args[6] = "-p";
|
2018-02-12 08:50:19 +08:00
|
|
|
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
args[7] = gimp_pid;
|
2018-02-09 08:57:03 +08:00
|
|
|
|
2018-03-05 12:26:13 +08:00
|
|
|
/* Wait until the parent enabled us to ptrace it. */
|
|
|
|
{
|
|
|
|
gchar dummy;
|
|
|
|
|
|
|
|
close (sync_fd[1]);
|
|
|
|
while (read (sync_fd[0], &dummy, 1) < 0 && errno == EINTR);
|
|
|
|
close (sync_fd[0]);
|
|
|
|
}
|
|
|
|
|
2018-02-09 08:57:03 +08:00
|
|
|
/* Redirect the debugger output. */
|
|
|
|
dup2 (out_fd[1], STDOUT_FILENO);
|
|
|
|
close (out_fd[0]);
|
|
|
|
close (out_fd[1]);
|
|
|
|
|
2018-02-22 12:05:24 +08:00
|
|
|
/* Run GDB if version 7.0 or over. Why I do such a check is that
|
|
|
|
* it turns out older versions may not only fail, but also have
|
|
|
|
* very undesirable side effects like terminating the debugged
|
|
|
|
* program, at least on FreeBSD where GDB 6.1 is apparently
|
|
|
|
* installed by default on the stable release at day of writing.
|
|
|
|
* See bug 793514. */
|
|
|
|
if (! gimp_utils_gdb_available (7, 0) ||
|
|
|
|
execvp (args[0], args) == -1)
|
2018-02-09 08:57:03 +08:00
|
|
|
{
|
2018-02-22 12:05:24 +08:00
|
|
|
/* LLDB as alternative if the GDB call failed or if it was in
|
|
|
|
* a too-old version. */
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
gchar *args_lldb[15] = { "lldb", "--attach-pid", NULL, "--batch",
|
|
|
|
"--one-line", "thread list",
|
|
|
|
"--one-line", "thread backtrace all",
|
|
|
|
"--one-line", "bt all",
|
2018-02-09 08:57:03 +08:00
|
|
|
"--one-line-on-crash", "bt",
|
|
|
|
"--one-line-on-crash", "quit", NULL };
|
|
|
|
|
|
|
|
args_lldb[2] = gimp_pid;
|
|
|
|
|
|
|
|
execvp (args_lldb[0], args_lldb);
|
|
|
|
}
|
|
|
|
|
|
|
|
_exit (0);
|
|
|
|
}
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
else if (fork_pid > 0)
|
2018-02-09 08:57:03 +08:00
|
|
|
{
|
|
|
|
/* Main process */
|
|
|
|
int status;
|
|
|
|
|
2018-03-05 12:26:13 +08:00
|
|
|
/* Allow the child to ptrace us, and signal it to start. */
|
|
|
|
close (sync_fd[0]);
|
|
|
|
#ifdef PR_SET_PTRACER
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
prctl (PR_SET_PTRACER, fork_pid, 0, 0, 0);
|
2018-03-05 12:26:13 +08:00
|
|
|
#endif
|
|
|
|
close (sync_fd[1]);
|
2018-02-09 08:57:03 +08:00
|
|
|
|
|
|
|
/* It is important to close the writing side of the pipe, otherwise
|
|
|
|
* the read() will wait forever without getting the information that
|
|
|
|
* writing is finished.
|
|
|
|
*/
|
|
|
|
close (out_fd[1]);
|
|
|
|
|
|
|
|
while ((read_n = read (out_fd[0], buffer, 256)) > 0)
|
|
|
|
{
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
if (! stack_printed)
|
|
|
|
{
|
2018-04-28 20:57:46 +08:00
|
|
|
#if defined(G_OS_WIN32) || defined(SYS_gettid) || defined(HAVE_THR_SELF)
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
if (stream)
|
|
|
|
g_fprintf (stream,
|
|
|
|
"\n# Stack traces obtained from PID %d - Thread %lu #\n\n",
|
|
|
|
pid, tid);
|
2018-04-25 06:19:37 +08:00
|
|
|
#endif
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
if (trace)
|
|
|
|
{
|
|
|
|
gtrace = g_string_new (NULL);
|
2018-04-28 20:57:46 +08:00
|
|
|
#if defined(G_OS_WIN32) || defined(SYS_gettid) || defined(HAVE_THR_SELF)
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
g_string_printf (gtrace,
|
|
|
|
"\n# Stack traces obtained from PID %d - Thread %lu #\n\n",
|
|
|
|
pid, tid);
|
2018-04-25 06:19:37 +08:00
|
|
|
#endif
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
}
|
|
|
|
}
|
2018-02-09 08:57:03 +08:00
|
|
|
/* It's hard to know if the debugger was found since it
|
|
|
|
* happened in the child. Let's just assume that any output
|
|
|
|
* means it succeeded.
|
|
|
|
*/
|
|
|
|
stack_printed = TRUE;
|
|
|
|
|
|
|
|
buffer[read_n] = '\0';
|
2018-02-16 04:32:58 +08:00
|
|
|
if (stream)
|
|
|
|
g_fprintf (stream, "%s", buffer);
|
2018-02-09 08:57:03 +08:00
|
|
|
if (trace)
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
g_string_append (gtrace, (const gchar *) buffer);
|
2018-02-09 08:57:03 +08:00
|
|
|
}
|
|
|
|
close (out_fd[0]);
|
2018-03-05 15:31:00 +08:00
|
|
|
|
|
|
|
#ifdef PR_SET_PTRACER
|
|
|
|
/* Clear ptrace permission set above */
|
|
|
|
prctl (PR_SET_PTRACER, 0, 0, 0, 0);
|
|
|
|
#endif
|
|
|
|
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
waitpid (fork_pid, &status, 0);
|
2018-02-09 08:57:03 +08:00
|
|
|
}
|
libgimpbase: improve multi-threaded stack traces.
Since commit bb52431cdd, we get multi-thread traces in functions
gimp_stack_trace_*(). Adding now the LLDB equivalent improvement.
Also adding the process and thread id information, from which the trace
order was made, atop the listing, as well as the thread list. This would
allow to easily find and associate the threads.
The problem is that sometimes the thread where we got a trace from may
not matter (for instance signals, even such as SIGABRT or SIGSEGV, seem
to sent a bit randomly to either the thread which provoked them or the
main thread; there is a bit of contradictory info on this when reading
on the topic, in my case I experienced this), in such case, getting all
thread stack is important to find the origin of the signal.
Other times it will highly matter, in particular when getting a trace
for a WARNING or CRITICAL. This information will help to discriminate
between thread traces.
2018-04-10 20:36:51 +08:00
|
|
|
/* else if (fork_pid == (pid_t) -1)
|
2018-02-12 06:20:17 +08:00
|
|
|
* Fork failed!
|
|
|
|
* Just continue, maybe the backtrace() API will succeed.
|
|
|
|
*/
|
2018-02-09 08:57:03 +08:00
|
|
|
|
|
|
|
#ifdef HAVE_EXECINFO_H
|
|
|
|
if (! stack_printed)
|
|
|
|
{
|
|
|
|
/* As a last resort, try using the backtrace() Linux API. It is a bit
|
|
|
|
* less fancy than gdb or lldb, which is why it is not given priority.
|
|
|
|
*/
|
2018-02-12 06:20:17 +08:00
|
|
|
void *bt_buf[100];
|
|
|
|
int n_symbols;
|
2018-02-09 08:57:03 +08:00
|
|
|
|
|
|
|
n_symbols = backtrace (bt_buf, 100);
|
2018-02-12 06:20:17 +08:00
|
|
|
if (trace && n_symbols)
|
2018-02-09 08:57:03 +08:00
|
|
|
{
|
2018-02-12 06:20:17 +08:00
|
|
|
char **symbols;
|
|
|
|
int i;
|
2018-02-09 08:57:03 +08:00
|
|
|
|
2018-02-12 06:20:17 +08:00
|
|
|
symbols = backtrace_symbols (bt_buf, n_symbols);
|
|
|
|
if (symbols)
|
2018-02-09 08:57:03 +08:00
|
|
|
{
|
2018-02-12 06:20:17 +08:00
|
|
|
for (i = 0; i < n_symbols; i++)
|
2018-02-09 08:57:03 +08:00
|
|
|
{
|
2018-02-16 04:32:58 +08:00
|
|
|
if (stream)
|
|
|
|
g_fprintf (stream, "%s\n", (const gchar *) symbols[i]);
|
2018-02-12 06:20:17 +08:00
|
|
|
if (trace)
|
|
|
|
{
|
|
|
|
if (! gtrace)
|
|
|
|
gtrace = g_string_new (NULL);
|
|
|
|
g_string_append (gtrace,
|
|
|
|
(const gchar *) symbols[i]);
|
|
|
|
g_string_append_c (gtrace, '\n');
|
|
|
|
}
|
2018-02-09 08:57:03 +08:00
|
|
|
}
|
2018-02-12 06:20:17 +08:00
|
|
|
free (symbols);
|
2018-02-09 08:57:03 +08:00
|
|
|
}
|
|
|
|
}
|
2018-02-12 06:20:17 +08:00
|
|
|
else if (n_symbols)
|
|
|
|
{
|
|
|
|
/* This allows to generate traces without memory allocation.
|
|
|
|
* In some cases, this is necessary, especially during
|
|
|
|
* segfault-type crashes.
|
|
|
|
*/
|
2018-02-16 04:32:58 +08:00
|
|
|
backtrace_symbols_fd (bt_buf, n_symbols, fileno ((FILE *) stream));
|
2018-02-12 06:20:17 +08:00
|
|
|
}
|
|
|
|
stack_printed = (n_symbols > 0);
|
2018-02-09 08:57:03 +08:00
|
|
|
}
|
|
|
|
#endif /* HAVE_EXECINFO_H */
|
|
|
|
|
|
|
|
if (trace)
|
|
|
|
{
|
|
|
|
if (gtrace)
|
|
|
|
*trace = g_string_free (gtrace, FALSE);
|
|
|
|
else
|
|
|
|
*trace = NULL;
|
|
|
|
}
|
|
|
|
#endif /* G_OS_WIN32 */
|
|
|
|
|
|
|
|
return stack_printed;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-02-22 19:35:43 +08:00
|
|
|
* gimp_stack_trace_query:
|
2018-02-09 08:57:03 +08:00
|
|
|
* @prog_name: the program to attach to.
|
|
|
|
*
|
|
|
|
* This is mostly the same as g_on_error_query() except that we use our
|
|
|
|
* own backtrace function, much more complete.
|
2018-02-12 08:50:19 +08:00
|
|
|
* @prog_name must be the current program name (argv[0]).
|
2018-02-09 08:57:03 +08:00
|
|
|
* It does nothing on Win32.
|
|
|
|
*
|
|
|
|
* Since: 2.10
|
|
|
|
**/
|
|
|
|
void
|
2018-02-22 19:35:43 +08:00
|
|
|
gimp_stack_trace_query (const gchar *prog_name)
|
2018-02-09 08:57:03 +08:00
|
|
|
{
|
|
|
|
#ifndef G_OS_WIN32
|
|
|
|
gchar buf[16];
|
|
|
|
|
|
|
|
retry:
|
|
|
|
|
|
|
|
g_fprintf (stdout,
|
|
|
|
"%s (pid:%u): %s: ",
|
|
|
|
prog_name,
|
|
|
|
(guint) getpid (),
|
|
|
|
"[E]xit, show [S]tack trace or [P]roceed");
|
|
|
|
fflush (stdout);
|
|
|
|
|
|
|
|
if (isatty(0) && isatty(1))
|
|
|
|
fgets (buf, 8, stdin);
|
|
|
|
else
|
|
|
|
strcpy (buf, "E\n");
|
|
|
|
|
|
|
|
if ((buf[0] == 'E' || buf[0] == 'e')
|
|
|
|
&& buf[1] == '\n')
|
|
|
|
_exit (0);
|
|
|
|
else if ((buf[0] == 'P' || buf[0] == 'p')
|
|
|
|
&& buf[1] == '\n')
|
|
|
|
return;
|
|
|
|
else if ((buf[0] == 'S' || buf[0] == 's')
|
|
|
|
&& buf[1] == '\n')
|
|
|
|
{
|
2018-02-22 19:35:43 +08:00
|
|
|
if (! gimp_stack_trace_print (prog_name, stdout, NULL))
|
2018-02-09 08:57:03 +08:00
|
|
|
g_fprintf (stderr, "%s\n", "Stack trace not available on your system.");
|
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
goto retry;
|
|
|
|
#endif
|
|
|
|
}
|
2018-02-22 12:05:24 +08:00
|
|
|
|
|
|
|
|
|
|
|
/* Private functions. */
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gimp_utils_generic_available (const gchar *program,
|
|
|
|
gint major,
|
|
|
|
gint minor)
|
|
|
|
{
|
|
|
|
#ifndef G_OS_WIN32
|
|
|
|
pid_t pid;
|
|
|
|
int out_fd[2];
|
|
|
|
|
|
|
|
if (pipe (out_fd) == -1)
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX: I don't use g_spawn_sync() or similar glib functions because
|
|
|
|
* to read the contents of the stdout, these functions would allocate
|
|
|
|
* memory dynamically. As we know, when debugging crashes, this is a
|
|
|
|
* definite blocker. So instead I simply use a buffer on the stack
|
|
|
|
* with a lower level fork() call.
|
|
|
|
*/
|
|
|
|
pid = fork ();
|
|
|
|
if (pid == 0)
|
|
|
|
{
|
|
|
|
/* Child process. */
|
|
|
|
gchar *args[3] = { (gchar *) program, "--version", NULL };
|
|
|
|
|
|
|
|
/* Redirect the debugger output. */
|
|
|
|
dup2 (out_fd[1], STDOUT_FILENO);
|
|
|
|
close (out_fd[0]);
|
|
|
|
close (out_fd[1]);
|
|
|
|
|
|
|
|
/* Run version check. */
|
|
|
|
execvp (args[0], args);
|
|
|
|
_exit (-1);
|
|
|
|
}
|
|
|
|
else if (pid > 0)
|
|
|
|
{
|
|
|
|
/* Main process */
|
|
|
|
gchar buffer[256];
|
|
|
|
ssize_t read_n;
|
|
|
|
int status;
|
|
|
|
gint installed_major = 0;
|
|
|
|
gint installed_minor = 0;
|
|
|
|
gboolean major_reading = FALSE;
|
|
|
|
gboolean minor_reading = FALSE;
|
|
|
|
gint i;
|
|
|
|
gchar c;
|
|
|
|
|
|
|
|
waitpid (pid, &status, 0);
|
|
|
|
|
|
|
|
if (! WIFEXITED (status) || WEXITSTATUS (status) != 0)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
/* It is important to close the writing side of the pipe, otherwise
|
|
|
|
* the read() will wait forever without getting the information that
|
|
|
|
* writing is finished.
|
|
|
|
*/
|
|
|
|
close (out_fd[1]);
|
|
|
|
|
|
|
|
/* I could loop forever until EOL, but I am pretty sure the
|
|
|
|
* version information is stored on the first line and one call to
|
|
|
|
* read() with 256 characters should be more than enough.
|
|
|
|
*/
|
|
|
|
read_n = read (out_fd[0], buffer, 256);
|
|
|
|
|
|
|
|
/* This is quite a very stupid parser. I only look for the first
|
|
|
|
* numbers and consider them as version information. This works
|
|
|
|
* fine for both GDB and LLDB as far as I can see for the output
|
|
|
|
* of `${program} --version` but this should obviously not be
|
|
|
|
* considered as a *really* generic version test.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < read_n; i++)
|
|
|
|
{
|
|
|
|
c = buffer[i];
|
|
|
|
if (c >= '0' && c <= '9')
|
|
|
|
{
|
|
|
|
if (minor_reading)
|
|
|
|
{
|
|
|
|
installed_minor = 10 * installed_minor + (c - '0');
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
major_reading = TRUE;
|
|
|
|
installed_major = 10 * installed_major + (c - '0');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (c == '.')
|
|
|
|
{
|
|
|
|
if (major_reading)
|
|
|
|
{
|
|
|
|
minor_reading = TRUE;
|
|
|
|
major_reading = FALSE;
|
|
|
|
}
|
|
|
|
else if (minor_reading)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (c == '\n')
|
|
|
|
{
|
|
|
|
/* Version information should be in the first line. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close (out_fd[0]);
|
|
|
|
|
|
|
|
return (installed_major > 0 &&
|
|
|
|
(installed_major > major ||
|
|
|
|
(installed_major == major && installed_minor >= minor)));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Fork failed, or Win32. */
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gimp_utils_gdb_available (gint major,
|
|
|
|
gint minor)
|
|
|
|
{
|
|
|
|
return gimp_utils_generic_available ("gdb", major, minor);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gimp_utils_lldb_available (gint major,
|
|
|
|
gint minor)
|
|
|
|
{
|
|
|
|
return gimp_utils_generic_available ("lldb", major, minor);
|
|
|
|
}
|