1 /* LIBGIMP - The GIMP Library
2  * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
3  *
4  * gimputils.c
5  * Copyright (C) 2003  Sven Neumann <sven@gimp.org>
6  *
7  * This library is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 3 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library.  If not, see
19  * <https://www.gnu.org/licenses/>.
20  */
21 
22 #include "config.h"
23 
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdio.h>
27 
28 #ifdef PLATFORM_OSX
29 #include <AppKit/AppKit.h>
30 #endif
31 
32 #ifdef HAVE_EXECINFO_H
33 /* Allowing backtrace() API. */
34 #include <execinfo.h>
35 #endif
36 
37 #include <gio/gio.h>
38 #include <glib/gprintf.h>
39 
40 #if defined(G_OS_WIN32)
41 # include <windows.h>
42 # include <shlobj.h>
43 
44 #else /* G_OS_WIN32 */
45 
46 /* For waitpid() */
47 #include <sys/wait.h>
48 #include <unistd.h>
49 #include <errno.h>
50 
51 /* For thread IDs. */
52 #include <sys/types.h>
53 #include <sys/syscall.h>
54 
55 #ifdef HAVE_SYS_PRCTL_H
56 #include <sys/prctl.h>
57 #endif
58 
59 #ifdef HAVE_SYS_THR_H
60 #include <sys/thr.h>
61 #endif
62 
63 #endif /* G_OS_WIN32 */
64 
65 #include "gimpbasetypes.h"
66 #include "gimputils.h"
67 
68 #include "libgimp/libgimp-intl.h"
69 
70 
71 /**
72  * SECTION: gimputils
73  * @title: gimputils
74  * @short_description: Utilities of general interest
75  *
76  * Utilities of general interest
77  **/
78 
79 static gboolean gimp_utils_generic_available (const gchar *program,
80                                               gint         major,
81                                               gint         minor);
82 static gboolean gimp_utils_gdb_available     (gint         major,
83                                               gint         minor);
84 
85 /**
86  * gimp_utf8_strtrim:
87  * @str: an UTF-8 encoded string (or %NULL)
88  * @max_chars: the maximum number of characters before the string get
89  * trimmed
90  *
91  * Creates a (possibly trimmed) copy of @str. The string is cut if it
92  * exceeds @max_chars characters or on the first newline. The fact
93  * that the string was trimmed is indicated by appending an ellipsis.
94  *
95  * Returns: A (possibly trimmed) copy of @str which should be freed
96  * using g_free() when it is not needed any longer.
97  **/
98 gchar *
gimp_utf8_strtrim(const gchar * str,gint max_chars)99 gimp_utf8_strtrim (const gchar *str,
100                    gint         max_chars)
101 {
102   /* FIXME: should we make this translatable? */
103   const gchar ellipsis[] = "...";
104   const gint  e_len      = strlen (ellipsis);
105 
106   if (str)
107     {
108       const gchar *p;
109       const gchar *newline = NULL;
110       gint         chars   = 0;
111       gunichar     unichar;
112 
113       for (p = str; *p; p = g_utf8_next_char (p))
114         {
115           if (++chars > max_chars)
116             break;
117 
118           unichar = g_utf8_get_char (p);
119 
120           switch (g_unichar_break_type (unichar))
121             {
122             case G_UNICODE_BREAK_MANDATORY:
123             case G_UNICODE_BREAK_LINE_FEED:
124               newline = p;
125               break;
126             default:
127               continue;
128             }
129 
130           break;
131         }
132 
133       if (*p)
134         {
135           gsize  len     = p - str;
136           gchar *trimmed = g_new (gchar, len + e_len + 2);
137 
138           memcpy (trimmed, str, len);
139           if (newline)
140             trimmed[len++] = ' ';
141 
142           g_strlcpy (trimmed + len, ellipsis, e_len + 1);
143 
144           return trimmed;
145         }
146 
147       return g_strdup (str);
148     }
149 
150   return NULL;
151 }
152 
153 /**
154  * gimp_any_to_utf8:
155  * @str:            The string to be converted to UTF-8.
156  * @len:            The length of the string, or -1 if the string
157  *                  is nul-terminated.
158  * @warning_format: The message format for the warning message if conversion
159  *                  to UTF-8 fails. See the <function>printf()</function>
160  *                  documentation.
161  * @...:            The parameters to insert into the format string.
162  *
163  * This function takes any string (UTF-8 or not) and always returns a valid
164  * UTF-8 string.
165  *
166  * If @str is valid UTF-8, a copy of the string is returned.
167  *
168  * If UTF-8 validation fails, g_locale_to_utf8() is tried and if it
169  * succeeds the resulting string is returned.
170  *
171  * Otherwise, the portion of @str that is UTF-8, concatenated
172  * with "(invalid UTF-8 string)" is returned. If not even the start
173  * of @str is valid UTF-8, only "(invalid UTF-8 string)" is returned.
174  *
175  * Return value: The UTF-8 string as described above.
176  **/
177 gchar *
gimp_any_to_utf8(const gchar * str,gssize len,const gchar * warning_format,...)178 gimp_any_to_utf8 (const gchar  *str,
179                   gssize        len,
180                   const gchar  *warning_format,
181                   ...)
182 {
183   const gchar *start_invalid;
184   gchar       *utf8;
185 
186   g_return_val_if_fail (str != NULL, NULL);
187 
188   if (g_utf8_validate (str, len, &start_invalid))
189     {
190       if (len < 0)
191         utf8 = g_strdup (str);
192       else
193         utf8 = g_strndup (str, len);
194     }
195   else
196     {
197       utf8 = g_locale_to_utf8 (str, len, NULL, NULL, NULL);
198     }
199 
200   if (! utf8)
201     {
202       if (warning_format)
203         {
204           va_list warning_args;
205 
206           va_start (warning_args, warning_format);
207 
208           g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE,
209                   warning_format, warning_args);
210 
211           va_end (warning_args);
212         }
213 
214       if (start_invalid > str)
215         {
216           gchar *tmp;
217 
218           tmp = g_strndup (str, start_invalid - str);
219           utf8 = g_strconcat (tmp, " ", _("(invalid UTF-8 string)"), NULL);
220           g_free (tmp);
221         }
222       else
223         {
224           utf8 = g_strdup (_("(invalid UTF-8 string)"));
225         }
226     }
227 
228   return utf8;
229 }
230 
231 /**
232  * gimp_filename_to_utf8:
233  * @filename: The filename to be converted to UTF-8.
234  *
235  * Convert a filename in the filesystem's encoding to UTF-8
236  * temporarily.  The return value is a pointer to a string that is
237  * guaranteed to be valid only during the current iteration of the
238  * main loop or until the next call to gimp_filename_to_utf8().
239  *
240  * The only purpose of this function is to provide an easy way to pass
241  * a filename in the filesystem encoding to a function that expects an
242  * UTF-8 encoded filename.
243  *
244  * Return value: A temporarily valid UTF-8 representation of @filename.
245  *               This string must not be changed or freed.
246  **/
247 const gchar *
gimp_filename_to_utf8(const gchar * filename)248 gimp_filename_to_utf8 (const gchar *filename)
249 {
250   /* Simpleminded implementation, but at least allocates just one copy
251    * of each translation. Could check if already UTF-8, and if so
252    * return filename as is. Could perhaps (re)use a suitably large
253    * cyclic buffer, but then would have to verify that all calls
254    * really need the return value just for a "short" time.
255    */
256 
257   static GHashTable *ht = NULL;
258   gchar             *filename_utf8;
259 
260   if (! filename)
261     return NULL;
262 
263   if (! ht)
264     ht = g_hash_table_new (g_str_hash, g_str_equal);
265 
266   filename_utf8 = g_hash_table_lookup (ht, filename);
267 
268   if (! filename_utf8)
269     {
270       filename_utf8 = g_filename_display_name (filename);
271       g_hash_table_insert (ht, g_strdup (filename), filename_utf8);
272     }
273 
274   return filename_utf8;
275 }
276 
277 /**
278  * gimp_file_get_utf8_name:
279  * @file: a #GFile
280  *
281  * This function works like gimp_filename_to_utf8() and returns
282  * a UTF-8 encoded string that does not need to be freed.
283  *
284  * It converts a #GFile's path or uri to UTF-8 temporarily.  The
285  * return value is a pointer to a string that is guaranteed to be
286  * valid only during the current iteration of the main loop or until
287  * the next call to gimp_file_get_utf8_name().
288  *
289  * The only purpose of this function is to provide an easy way to pass
290  * a #GFile's name to a function that expects an UTF-8 encoded string.
291  *
292  * See g_file_get_parse_name().
293  *
294  * Since: 2.10
295  *
296  * Return value: A temporarily valid UTF-8 representation of @file's name.
297  *               This string must not be changed or freed.
298  **/
299 const gchar *
gimp_file_get_utf8_name(GFile * file)300 gimp_file_get_utf8_name (GFile *file)
301 {
302   gchar *name;
303 
304   g_return_val_if_fail (G_IS_FILE (file), NULL);
305 
306   name = g_file_get_parse_name (file);
307 
308   g_object_set_data_full (G_OBJECT (file), "gimp-parse-name", name,
309                           (GDestroyNotify) g_free);
310 
311   return name;
312 }
313 
314 /**
315  * gimp_file_has_extension:
316  * @file:      a #GFile
317  * @extension: an ASCII extension
318  *
319  * This function checks if @file's URI ends with @extension. It behaves
320  * like g_str_has_suffix() on g_file_get_uri(), except that the string
321  * comparison is done case-insensitively using g_ascii_strcasecmp().
322  *
323  * Since: 2.10
324  *
325  * Return value: %TRUE if @file's URI ends with @extension,
326  *               %FALSE otherwise.
327  **/
328 gboolean
gimp_file_has_extension(GFile * file,const gchar * extension)329 gimp_file_has_extension (GFile       *file,
330                          const gchar *extension)
331 {
332   gchar    *uri;
333   gint      uri_len;
334   gint      ext_len;
335   gboolean  result = FALSE;
336 
337   g_return_val_if_fail (G_IS_FILE (file), FALSE);
338   g_return_val_if_fail (extension != NULL, FALSE);
339 
340   uri = g_file_get_uri (file);
341 
342   uri_len = strlen (uri);
343   ext_len = strlen (extension);
344 
345   if (uri_len && ext_len && (uri_len > ext_len))
346     {
347       if (g_ascii_strcasecmp (uri + uri_len - ext_len, extension) == 0)
348         result = TRUE;
349     }
350 
351   g_free (uri);
352 
353   return result;
354 }
355 
356 /**
357  * gimp_file_show_in_file_manager:
358  * @file:  a #GFile
359  * @error: return location for a #GError
360  *
361  * Shows @file in the system file manager.
362  *
363  * Since: 2.10
364  *
365  * Return value: %TRUE on success, %FALSE otherwise. On %FALSE, @error
366  *               is set.
367  **/
368 gboolean
gimp_file_show_in_file_manager(GFile * file,GError ** error)369 gimp_file_show_in_file_manager (GFile   *file,
370                                 GError **error)
371 {
372   g_return_val_if_fail (G_IS_FILE (file), FALSE);
373   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
374 
375 #if defined(G_OS_WIN32)
376 
377   {
378     gboolean ret;
379     char *filename;
380     int n;
381     LPWSTR w_filename = NULL;
382     ITEMIDLIST *pidl = NULL;
383 
384     ret = FALSE;
385 
386     /* Calling this function multiple times should do no harm, but it is
387        easier to put this here as it needs linking against ole32. */
388     CoInitialize (NULL);
389 
390     filename = g_file_get_path (file);
391     if (!filename)
392       {
393         g_set_error_literal (error, G_FILE_ERROR, 0,
394                              _("File path is NULL"));
395         goto out;
396       }
397 
398     n = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS,
399                              filename, -1, NULL, 0);
400     if (n == 0)
401       {
402         g_set_error_literal (error, G_FILE_ERROR, 0,
403                              _("Error converting UTF-8 filename to wide char"));
404         goto out;
405       }
406 
407     w_filename = g_malloc_n (n + 1, sizeof (wchar_t));
408     n = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS,
409                              filename, -1,
410                              w_filename, (n + 1) * sizeof (wchar_t));
411     if (n == 0)
412       {
413         g_set_error_literal (error, G_FILE_ERROR, 0,
414                              _("Error converting UTF-8 filename to wide char"));
415         goto out;
416       }
417 
418     pidl = ILCreateFromPathW (w_filename);
419     if (!pidl)
420       {
421         g_set_error_literal (error, G_FILE_ERROR, 0,
422                              _("ILCreateFromPath() failed"));
423         goto out;
424       }
425 
426     SHOpenFolderAndSelectItems (pidl, 0, NULL, 0);
427     ret = TRUE;
428 
429   out:
430     if (pidl)
431       ILFree (pidl);
432     g_free (w_filename);
433     g_free (filename);
434 
435     return ret;
436   }
437 
438 #elif defined(PLATFORM_OSX)
439 
440   {
441     gchar    *uri;
442     NSString *filename;
443     NSURL    *url;
444     gboolean  retval = TRUE;
445 
446     uri = g_file_get_uri (file);
447     filename = [NSString stringWithUTF8String:uri];
448 
449     url = [NSURL URLWithString:filename];
450     if (url)
451       {
452         NSArray *url_array = [NSArray arrayWithObject:url];
453 
454         [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:url_array];
455       }
456     else
457       {
458         g_set_error (error, G_FILE_ERROR, 0,
459                      _("Cannot convert '%s' into a valid NSURL."), uri);
460         retval = FALSE;
461       }
462 
463     g_free (uri);
464 
465     return retval;
466   }
467 
468 #else /* UNIX */
469 
470   {
471     GDBusProxy      *proxy;
472     GVariant        *retval;
473     GVariantBuilder *builder;
474     gchar           *uri;
475 
476     proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
477                                            G_DBUS_PROXY_FLAGS_NONE,
478                                            NULL,
479                                            "org.freedesktop.FileManager1",
480                                            "/org/freedesktop/FileManager1",
481                                            "org.freedesktop.FileManager1",
482                                            NULL, error);
483 
484     if (! proxy)
485       {
486         g_prefix_error (error,
487                         _("Connecting to org.freedesktop.FileManager1 failed: "));
488         return FALSE;
489       }
490 
491     uri = g_file_get_uri (file);
492 
493     builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
494     g_variant_builder_add (builder, "s", uri);
495 
496     g_free (uri);
497 
498     retval = g_dbus_proxy_call_sync (proxy,
499                                      "ShowItems",
500                                      g_variant_new ("(ass)",
501                                                     builder,
502                                                     ""),
503                                      G_DBUS_CALL_FLAGS_NONE,
504                                      -1, NULL, error);
505 
506     g_variant_builder_unref (builder);
507     g_object_unref (proxy);
508 
509     if (! retval)
510       {
511         g_prefix_error (error, _("Calling ShowItems failed: "));
512         return FALSE;
513       }
514 
515     g_variant_unref (retval);
516 
517     return TRUE;
518   }
519 
520 #endif
521 }
522 
523 /**
524  * gimp_strip_uline:
525  * @str: underline infested string (or %NULL)
526  *
527  * This function returns a copy of @str stripped of underline
528  * characters. This comes in handy when needing to strip mnemonics
529  * from menu paths etc.
530  *
531  * In some languages, mnemonics are handled by adding the mnemonic
532  * character in brackets (like "File (_F)"). This function recognizes
533  * this construct and removes the whole bracket construction to get
534  * rid of the mnemonic (see bug 157561).
535  *
536  * Return value: A (possibly stripped) copy of @str which should be
537  *               freed using g_free() when it is not needed any longer.
538  **/
539 gchar *
gimp_strip_uline(const gchar * str)540 gimp_strip_uline (const gchar *str)
541 {
542   gchar    *escaped;
543   gchar    *p;
544   gboolean  past_bracket = FALSE;
545 
546   if (! str)
547     return NULL;
548 
549   p = escaped = g_strdup (str);
550 
551   while (*str)
552     {
553       if (*str == '_')
554         {
555           /*  "__" means a literal "_" in the menu path  */
556           if (str[1] == '_')
557             {
558              *p++ = *str++;
559              str++;
560              continue;
561             }
562 
563           /*  find the "(_X)" construct and remove it entirely  */
564           if (past_bracket && str[1] && *(g_utf8_next_char (str + 1)) == ')')
565             {
566               str = g_utf8_next_char (str + 1) + 1;
567               p--;
568             }
569           else
570             {
571               str++;
572             }
573         }
574       else
575         {
576           past_bracket = (*str == '(');
577 
578           *p++ = *str++;
579         }
580     }
581 
582   *p = '\0';
583 
584   return escaped;
585 }
586 
587 /**
588  * gimp_escape_uline:
589  * @str: Underline infested string (or %NULL)
590  *
591  * This function returns a copy of @str with all underline converted
592  * to two adjacent underlines. This comes in handy when needing to display
593  * strings with underlines (like filenames) in a place that would convert
594  * them to mnemonics.
595  *
596  * Return value: A (possibly escaped) copy of @str which should be
597  * freed using g_free() when it is not needed any longer.
598  *
599  * Since: 2.2
600  **/
601 gchar *
gimp_escape_uline(const gchar * str)602 gimp_escape_uline (const gchar *str)
603 {
604   gchar *escaped;
605   gchar *p;
606   gint   n_ulines = 0;
607 
608   if (! str)
609     return NULL;
610 
611   for (p = (gchar *) str; *p; p++)
612     if (*p == '_')
613       n_ulines++;
614 
615   p = escaped = g_malloc (strlen (str) + n_ulines + 1);
616 
617   while (*str)
618     {
619       if (*str == '_')
620         *p++ = '_';
621 
622       *p++ = *str++;
623     }
624 
625   *p = '\0';
626 
627   return escaped;
628 }
629 
630 /**
631  * gimp_canonicalize_identifier:
632  * @identifier: The identifier string to canonicalize.
633  *
634  * Turns any input string into a canonicalized string.
635  *
636  * Canonical identifiers are e.g. expected by the PDB for procedure
637  * and parameter names. Every character of the input string that is
638  * not either '-', 'a-z', 'A-Z' or '0-9' will be replaced by a '-'.
639  *
640  * Return value: The canonicalized identifier. This is a newly
641  *               allocated string that should be freed with g_free()
642  *               when no longer needed.
643  *
644  * Since: 2.4
645  **/
646 gchar *
gimp_canonicalize_identifier(const gchar * identifier)647 gimp_canonicalize_identifier (const gchar *identifier)
648 {
649   gchar *canonicalized = NULL;
650 
651   if (identifier)
652     {
653       gchar *p;
654 
655       canonicalized = g_strdup (identifier);
656 
657       for (p = canonicalized; *p != 0; p++)
658         {
659           gchar c = *p;
660 
661           if (c != '-' &&
662               (c < '0' || c > '9') &&
663               (c < 'A' || c > 'Z') &&
664               (c < 'a' || c > 'z'))
665             *p = '-';
666         }
667     }
668 
669   return canonicalized;
670 }
671 
672 /**
673  * gimp_enum_get_desc:
674  * @enum_class: a #GEnumClass
675  * @value:      a value from @enum_class
676  *
677  * Retrieves #GimpEnumDesc associated with the given value, or %NULL.
678  *
679  * Return value: the value's #GimpEnumDesc.
680  *
681  * Since: 2.2
682  **/
683 GimpEnumDesc *
gimp_enum_get_desc(GEnumClass * enum_class,gint value)684 gimp_enum_get_desc (GEnumClass *enum_class,
685                     gint        value)
686 {
687   const GimpEnumDesc *value_desc;
688 
689   g_return_val_if_fail (G_IS_ENUM_CLASS (enum_class), NULL);
690 
691   value_desc =
692     gimp_enum_get_value_descriptions (G_TYPE_FROM_CLASS (enum_class));
693 
694   if (value_desc)
695     {
696       while (value_desc->value_desc)
697         {
698           if (value_desc->value == value)
699             return (GimpEnumDesc *) value_desc;
700 
701           value_desc++;
702         }
703     }
704 
705   return NULL;
706 }
707 
708 /**
709  * gimp_enum_get_value:
710  * @enum_type:  the #GType of a registered enum
711  * @value:      an integer value
712  * @value_name: return location for the value's name (or %NULL)
713  * @value_nick: return location for the value's nick (or %NULL)
714  * @value_desc: return location for the value's translated description (or %NULL)
715  * @value_help: return location for the value's translated help (or %NULL)
716  *
717  * Checks if @value is valid for the enum registered as @enum_type.
718  * If the value exists in that enum, its name, nick and its translated
719  * description and help are returned (if @value_name, @value_nick,
720  * @value_desc and @value_help are not %NULL).
721  *
722  * Return value: %TRUE if @value is valid for the @enum_type,
723  *               %FALSE otherwise
724  *
725  * Since: 2.2
726  **/
727 gboolean
gimp_enum_get_value(GType enum_type,gint value,const gchar ** value_name,const gchar ** value_nick,const gchar ** value_desc,const gchar ** value_help)728 gimp_enum_get_value (GType         enum_type,
729                      gint          value,
730                      const gchar **value_name,
731                      const gchar **value_nick,
732                      const gchar **value_desc,
733                      const gchar **value_help)
734 {
735   GEnumClass *enum_class;
736   GEnumValue *enum_value;
737   gboolean    success = FALSE;
738 
739   g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), FALSE);
740 
741   enum_class = g_type_class_ref (enum_type);
742   enum_value = g_enum_get_value (enum_class, value);
743 
744   if (enum_value)
745     {
746       if (value_name)
747         *value_name = enum_value->value_name;
748 
749       if (value_nick)
750         *value_nick = enum_value->value_nick;
751 
752       if (value_desc || value_help)
753         {
754           GimpEnumDesc *enum_desc;
755 
756           enum_desc = gimp_enum_get_desc (enum_class, value);
757 
758           if (value_desc)
759             {
760               if (enum_desc && enum_desc->value_desc)
761                 {
762                   const gchar *context;
763 
764                   context = gimp_type_get_translation_context (enum_type);
765 
766                   if (context)  /*  the new way, using NC_()    */
767                     *value_desc = g_dpgettext2 (gimp_type_get_translation_domain (enum_type),
768                                                 context,
769                                                 enum_desc->value_desc);
770                   else          /*  for backward compatibility  */
771                     *value_desc = g_strip_context (enum_desc->value_desc,
772                                                    dgettext (gimp_type_get_translation_domain (enum_type),
773                                                              enum_desc->value_desc));
774                 }
775               else
776                 {
777                   *value_desc = NULL;
778                 }
779             }
780 
781           if (value_help)
782             {
783               *value_help = ((enum_desc && enum_desc->value_help) ?
784                              dgettext (gimp_type_get_translation_domain (enum_type),
785                                        enum_desc->value_help) :
786                              NULL);
787             }
788         }
789 
790       success = TRUE;
791     }
792 
793   g_type_class_unref (enum_class);
794 
795   return success;
796 }
797 
798 /**
799  * gimp_enum_value_get_desc:
800  * @enum_class: a #GEnumClass
801  * @enum_value: a #GEnumValue from @enum_class
802  *
803  * Retrieves the translated description for a given @enum_value.
804  *
805  * Return value: the translated description of the enum value
806  *
807  * Since: 2.2
808  **/
809 const gchar *
gimp_enum_value_get_desc(GEnumClass * enum_class,GEnumValue * enum_value)810 gimp_enum_value_get_desc (GEnumClass *enum_class,
811                           GEnumValue *enum_value)
812 {
813   GType         type = G_TYPE_FROM_CLASS (enum_class);
814   GimpEnumDesc *enum_desc;
815 
816   enum_desc = gimp_enum_get_desc (enum_class, enum_value->value);
817 
818   if (enum_desc && enum_desc->value_desc)
819     {
820       const gchar *context;
821 
822       context = gimp_type_get_translation_context (type);
823 
824       if (context)  /*  the new way, using NC_()    */
825         return g_dpgettext2 (gimp_type_get_translation_domain (type),
826                              context,
827                              enum_desc->value_desc);
828       else          /*  for backward compatibility  */
829         return g_strip_context (enum_desc->value_desc,
830                                 dgettext (gimp_type_get_translation_domain (type),
831                                           enum_desc->value_desc));
832     }
833 
834   return enum_value->value_name;
835 }
836 
837 /**
838  * gimp_enum_value_get_help:
839  * @enum_class: a #GEnumClass
840  * @enum_value: a #GEnumValue from @enum_class
841  *
842  * Retrieves the translated help for a given @enum_value.
843  *
844  * Return value: the translated help of the enum value
845  *
846  * Since: 2.2
847  **/
848 const gchar *
gimp_enum_value_get_help(GEnumClass * enum_class,GEnumValue * enum_value)849 gimp_enum_value_get_help (GEnumClass *enum_class,
850                           GEnumValue *enum_value)
851 {
852   GType         type = G_TYPE_FROM_CLASS (enum_class);
853   GimpEnumDesc *enum_desc;
854 
855   enum_desc = gimp_enum_get_desc (enum_class, enum_value->value);
856 
857   if (enum_desc && enum_desc->value_help)
858     return dgettext (gimp_type_get_translation_domain (type),
859                      enum_desc->value_help);
860 
861   return NULL;
862 }
863 
864 /**
865  * gimp_enum_value_get_abbrev:
866  * @enum_class: a #GEnumClass
867  * @enum_value: a #GEnumValue from @enum_class
868  *
869  * Retrieves the translated abbreviation for a given @enum_value.
870  *
871  * Return value: the translated abbreviation of the enum value
872  *
873  * Since: 2.10
874  **/
875 const gchar *
gimp_enum_value_get_abbrev(GEnumClass * enum_class,GEnumValue * enum_value)876 gimp_enum_value_get_abbrev (GEnumClass *enum_class,
877                             GEnumValue *enum_value)
878 {
879   GType         type = G_TYPE_FROM_CLASS (enum_class);
880   GimpEnumDesc *enum_desc;
881 
882   enum_desc = gimp_enum_get_desc (enum_class, enum_value->value);
883 
884   if (enum_desc                              &&
885       enum_desc[1].value == enum_desc->value &&
886       enum_desc[1].value_desc)
887     {
888       return g_dpgettext2 (gimp_type_get_translation_domain (type),
889                            gimp_type_get_translation_context (type),
890                            enum_desc[1].value_desc);
891     }
892 
893   return NULL;
894 }
895 
896 /**
897  * gimp_flags_get_first_desc:
898  * @flags_class: a #GFlagsClass
899  * @value:       a value from @flags_class
900  *
901  * Retrieves the first #GimpFlagsDesc that matches the given value, or %NULL.
902  *
903  * Return value: the value's #GimpFlagsDesc.
904  *
905  * Since: 2.2
906  **/
907 GimpFlagsDesc *
gimp_flags_get_first_desc(GFlagsClass * flags_class,guint value)908 gimp_flags_get_first_desc (GFlagsClass *flags_class,
909                            guint        value)
910 {
911   const GimpFlagsDesc *value_desc;
912 
913   g_return_val_if_fail (G_IS_FLAGS_CLASS (flags_class), NULL);
914 
915   value_desc =
916     gimp_flags_get_value_descriptions (G_TYPE_FROM_CLASS (flags_class));
917 
918   if (value_desc)
919     {
920       while (value_desc->value_desc)
921         {
922           if ((value_desc->value & value) == value_desc->value)
923             return (GimpFlagsDesc *) value_desc;
924 
925           value_desc++;
926         }
927     }
928 
929   return NULL;
930 }
931 
932 /**
933  * gimp_flags_get_first_value:
934  * @flags_type: the #GType of registered flags
935  * @value:      an integer value
936  * @value_name: return location for the value's name (or %NULL)
937  * @value_nick: return location for the value's nick (or %NULL)
938  * @value_desc: return location for the value's translated description (or %NULL)
939  * @value_help: return location for the value's translated help (or %NULL)
940  *
941  * Checks if @value is valid for the flags registered as @flags_type.
942  * If the value exists in that flags, its name, nick and its
943  * translated description and help are returned (if @value_name,
944  * @value_nick, @value_desc and @value_help are not %NULL).
945  *
946  * Return value: %TRUE if @value is valid for the @flags_type,
947  *               %FALSE otherwise
948  *
949  * Since: 2.2
950  **/
951 gboolean
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)952 gimp_flags_get_first_value (GType         flags_type,
953                             guint         value,
954                             const gchar **value_name,
955                             const gchar **value_nick,
956                             const gchar **value_desc,
957                             const gchar **value_help)
958 {
959   GFlagsClass *flags_class;
960   GFlagsValue *flags_value;
961 
962   g_return_val_if_fail (G_TYPE_IS_FLAGS (flags_type), FALSE);
963 
964   flags_class = g_type_class_peek (flags_type);
965   flags_value = g_flags_get_first_value (flags_class, value);
966 
967   if (flags_value)
968     {
969       if (value_name)
970         *value_name = flags_value->value_name;
971 
972       if (value_nick)
973         *value_nick = flags_value->value_nick;
974 
975       if (value_desc || value_help)
976         {
977           GimpFlagsDesc *flags_desc;
978 
979           flags_desc = gimp_flags_get_first_desc (flags_class, value);
980 
981           if (value_desc)
982             *value_desc = ((flags_desc && flags_desc->value_desc) ?
983                            dgettext (gimp_type_get_translation_domain (flags_type),
984                                      flags_desc->value_desc) :
985                            NULL);
986 
987           if (value_help)
988             *value_help = ((flags_desc && flags_desc->value_desc) ?
989                            dgettext (gimp_type_get_translation_domain (flags_type),
990                                      flags_desc->value_help) :
991                            NULL);
992         }
993 
994       return TRUE;
995     }
996 
997   return FALSE;
998 }
999 
1000 /**
1001  * gimp_flags_value_get_desc:
1002  * @flags_class: a #GFlagsClass
1003  * @flags_value: a #GFlagsValue from @flags_class
1004  *
1005  * Retrieves the translated description for a given @flags_value.
1006  *
1007  * Return value: the translated description of the flags value
1008  *
1009  * Since: 2.2
1010  **/
1011 const gchar *
gimp_flags_value_get_desc(GFlagsClass * flags_class,GFlagsValue * flags_value)1012 gimp_flags_value_get_desc (GFlagsClass *flags_class,
1013                            GFlagsValue *flags_value)
1014 {
1015   GType         type = G_TYPE_FROM_CLASS (flags_class);
1016   GimpFlagsDesc *flags_desc;
1017 
1018   flags_desc = gimp_flags_get_first_desc (flags_class, flags_value->value);
1019 
1020   if (flags_desc->value_desc)
1021     {
1022       const gchar *context;
1023 
1024       context = gimp_type_get_translation_context (type);
1025 
1026       if (context)  /*  the new way, using NC_()    */
1027         return g_dpgettext2 (gimp_type_get_translation_domain (type),
1028                              context,
1029                              flags_desc->value_desc);
1030       else          /*  for backward compatibility  */
1031         return g_strip_context (flags_desc->value_desc,
1032                                 dgettext (gimp_type_get_translation_domain (type),
1033                                           flags_desc->value_desc));
1034     }
1035 
1036   return flags_value->value_name;
1037 }
1038 
1039 /**
1040  * gimp_flags_value_get_help:
1041  * @flags_class: a #GFlagsClass
1042  * @flags_value: a #GFlagsValue from @flags_class
1043  *
1044  * Retrieves the translated help for a given @flags_value.
1045  *
1046  * Return value: the translated help of the flags value
1047  *
1048  * Since: 2.2
1049  **/
1050 const gchar *
gimp_flags_value_get_help(GFlagsClass * flags_class,GFlagsValue * flags_value)1051 gimp_flags_value_get_help (GFlagsClass *flags_class,
1052                            GFlagsValue *flags_value)
1053 {
1054   GType         type = G_TYPE_FROM_CLASS (flags_class);
1055   GimpFlagsDesc *flags_desc;
1056 
1057   flags_desc = gimp_flags_get_first_desc (flags_class, flags_value->value);
1058 
1059   if (flags_desc->value_help)
1060     return dgettext (gimp_type_get_translation_domain (type),
1061                      flags_desc->value_help);
1062 
1063   return NULL;
1064 }
1065 
1066 /**
1067  * gimp_flags_value_get_abbrev:
1068  * @flags_class: a #GFlagsClass
1069  * @flags_value: a #GFlagsValue from @flags_class
1070  *
1071  * Retrieves the translated abbreviation for a given @flags_value.
1072  *
1073  * Return value: the translated abbreviation of the flags value
1074  *
1075  * Since: 2.10
1076  **/
1077 const gchar *
gimp_flags_value_get_abbrev(GFlagsClass * flags_class,GFlagsValue * flags_value)1078 gimp_flags_value_get_abbrev (GFlagsClass *flags_class,
1079                              GFlagsValue *flags_value)
1080 {
1081   GType          type = G_TYPE_FROM_CLASS (flags_class);
1082   GimpFlagsDesc *flags_desc;
1083 
1084   flags_desc = gimp_flags_get_first_desc (flags_class, flags_value->value);
1085 
1086   if (flags_desc                               &&
1087       flags_desc[1].value == flags_desc->value &&
1088       flags_desc[1].value_desc)
1089     {
1090       return g_dpgettext2 (gimp_type_get_translation_domain (type),
1091                            gimp_type_get_translation_context (type),
1092                            flags_desc[1].value_desc);
1093     }
1094 
1095   return NULL;
1096 }
1097 
1098 /**
1099  * gimp_stack_trace_available:
1100  * @optimal: whether we get optimal traces.
1101  *
1102  * Returns #TRUE if we have dependencies to generate backtraces. If
1103  * @optimal is #TRUE, the function will return #TRUE only when we
1104  * are able to generate optimal traces (i.e. with GDB or LLDB);
1105  * otherwise we return #TRUE even if only backtrace() API is available.
1106  *
1107  * On Win32, we return TRUE if Dr. Mingw is built-in, FALSE otherwise.
1108  *
1109  * Note: this function is not crash-safe, i.e. you should not try to use
1110  * it in a callback when the program is already crashing. In such a
1111  * case, call gimp_stack_trace_print() or gimp_stack_trace_query()
1112  * directly.
1113  *
1114  * Since: 2.10
1115  **/
1116 gboolean
gimp_stack_trace_available(gboolean optimal)1117 gimp_stack_trace_available (gboolean optimal)
1118 {
1119 #ifndef G_OS_WIN32
1120   gchar    *lld_path = NULL;
1121   gboolean  has_lldb = FALSE;
1122 
1123   /* Similarly to gdb, we could check for lldb by calling:
1124    * gimp_utils_generic_available ("lldb", major, minor).
1125    * We don't do so on purpose because on macOS, when lldb is absent, it
1126    * triggers a popup asking to install Xcode. So instead, we just
1127    * search for the executable in path.
1128    * This is the reason why this function is not crash-safe, since
1129    * g_find_program_in_path() allocates memory.
1130    * See issue #1999.
1131    */
1132   lld_path = g_find_program_in_path ("lldb");
1133   if (lld_path)
1134     {
1135       has_lldb = TRUE;
1136       g_free (lld_path);
1137     }
1138 
1139   if (gimp_utils_gdb_available (7, 0) || has_lldb)
1140     return TRUE;
1141 #ifdef HAVE_EXECINFO_H
1142   if (! optimal)
1143     return TRUE;
1144 #endif
1145 #else /* G_OS_WIN32 */
1146 #ifdef HAVE_EXCHNDL
1147   return TRUE;
1148 #endif
1149 #endif /* G_OS_WIN32 */
1150   return FALSE;
1151 }
1152 
1153 /**
1154  * gimp_stack_trace_print:
1155  * @prog_name: the program to attach to.
1156  * @stream: a #FILE * stream.
1157  * @trace: location to store a newly allocated string of the trace.
1158  *
1159  * Attempts to generate a stack trace at current code position in
1160  * @prog_name. @prog_name is mostly a helper and can be set to NULL.
1161  * Nevertheless if set, it has to be the current program name (argv[0]).
1162  * This function is not meant to generate stack trace for third-party
1163  * programs, and will attach the current process id only.
1164  * Internally, this function uses `gdb` or `lldb` if they are available,
1165  * or the stacktrace() API on platforms where it is available. It always
1166  * fails on Win32.
1167  *
1168  * The stack trace, once generated, will either be printed to @stream or
1169  * returned as a newly allocated string in @trace, if not #NULL.
1170  *
1171  * In some error cases (e.g. segmentation fault), trying to allocate
1172  * more memory will trigger more segmentation faults and therefore loop
1173  * our error handling (which is just wrong). Therefore printing to a
1174  * file description is an implementation without any memory allocation.
1175 
1176  * Return value: #TRUE if a stack trace could be generated, #FALSE
1177  * otherwise.
1178  *
1179  * Since: 2.10
1180  **/
1181 gboolean
gimp_stack_trace_print(const gchar * prog_name,gpointer stream,gchar ** trace)1182 gimp_stack_trace_print (const gchar   *prog_name,
1183                         gpointer      stream,
1184                         gchar       **trace)
1185 {
1186   gboolean stack_printed = FALSE;
1187 
1188   /* This works only on UNIX systems. */
1189 #ifndef G_OS_WIN32
1190   GString *gtrace = NULL;
1191   gchar    gimp_pid[16];
1192   gchar    buffer[256];
1193   ssize_t  read_n;
1194   int      sync_fd[2];
1195   int      out_fd[2];
1196   pid_t    fork_pid;
1197   pid_t    pid = getpid();
1198   gint     eintr_count = 0;
1199 #if defined(G_OS_WIN32)
1200   DWORD    tid = GetCurrentThreadId ();
1201 #elif defined(PLATFORM_OSX)
1202   uint64   tid64;
1203   long     tid;
1204 
1205   pthread_threadid_np (NULL, &tid64);
1206   tid = (long) tid64;
1207 #elif defined(SYS_gettid)
1208   long     tid = syscall (SYS_gettid);
1209 #elif defined(HAVE_THR_SELF)
1210   long     tid = 0;
1211   thr_self (&tid);
1212 #endif
1213 
1214   g_snprintf (gimp_pid, 16, "%u", (guint) pid);
1215 
1216   if (pipe (sync_fd) == -1)
1217     {
1218       return FALSE;
1219     }
1220 
1221   if (pipe (out_fd) == -1)
1222     {
1223       close (sync_fd[0]);
1224       close (sync_fd[1]);
1225 
1226       return FALSE;
1227     }
1228 
1229   fork_pid = fork ();
1230   if (fork_pid == 0)
1231     {
1232       /* Child process. */
1233       gchar *args[9] = { "gdb", "-batch",
1234                          "-ex", "info threads",
1235                          "-ex", "thread apply all backtrace full",
1236                          (gchar *) prog_name, NULL, NULL };
1237 
1238       if (prog_name == NULL)
1239         args[6] = "-p";
1240 
1241       args[7] = gimp_pid;
1242 
1243       /* Wait until the parent enabled us to ptrace it. */
1244       {
1245         gchar dummy;
1246 
1247         close (sync_fd[1]);
1248         while (read (sync_fd[0], &dummy, 1) < 0 && errno == EINTR);
1249         close (sync_fd[0]);
1250       }
1251 
1252       /* Redirect the debugger output. */
1253       dup2 (out_fd[1], STDOUT_FILENO);
1254       close (out_fd[0]);
1255       close (out_fd[1]);
1256 
1257       /* Run GDB if version 7.0 or over. Why I do such a check is that
1258        * it turns out older versions may not only fail, but also have
1259        * very undesirable side effects like terminating the debugged
1260        * program, at least on FreeBSD where GDB 6.1 is apparently
1261        * installed by default on the stable release at day of writing.
1262        * See bug 793514. */
1263       if (! gimp_utils_gdb_available (7, 0) ||
1264           execvp (args[0], args) == -1)
1265         {
1266           /* LLDB as alternative if the GDB call failed or if it was in
1267            * a too-old version. */
1268           gchar *args_lldb[15] = { "lldb", "--attach-pid", NULL, "--batch",
1269                                    "--one-line", "thread list",
1270                                    "--one-line", "thread backtrace all",
1271                                    "--one-line", "bt all",
1272                                    "--one-line-on-crash", "bt",
1273                                    "--one-line-on-crash", "quit", NULL };
1274 
1275           args_lldb[2] = gimp_pid;
1276 
1277           execvp (args_lldb[0], args_lldb);
1278         }
1279 
1280       _exit (0);
1281     }
1282   else if (fork_pid > 0)
1283     {
1284       /* Main process */
1285       int status;
1286 
1287       /* Allow the child to ptrace us, and signal it to start. */
1288       close (sync_fd[0]);
1289 #ifdef PR_SET_PTRACER
1290       prctl (PR_SET_PTRACER, fork_pid, 0, 0, 0);
1291 #endif
1292       close (sync_fd[1]);
1293 
1294       /* It is important to close the writing side of the pipe, otherwise
1295        * the read() will wait forever without getting the information that
1296        * writing is finished.
1297        */
1298       close (out_fd[1]);
1299 
1300       while ((read_n = read (out_fd[0], buffer, 256)) != 0)
1301         {
1302           if (read_n < 0)
1303             {
1304               /* LLDB on macOS seems to trigger a few EINTR error (see
1305                * !13), though read() finally ends up working later. So
1306                * let's not make this error fatal, and instead try again.
1307                * Yet to avoid infinite loop (in case the error really
1308                * happens at every call), we abandon after a few
1309                * consecutive errors.
1310                */
1311               if (errno == EINTR && eintr_count <= 5)
1312                 {
1313                   eintr_count++;
1314                   continue;
1315                 }
1316               break;
1317             }
1318           eintr_count = 0;
1319           if (! stack_printed)
1320             {
1321 #if defined(PLATFORM_OSX)
1322               if (stream)
1323                 g_fprintf (stream,
1324                            "\n# Stack traces obtained from PID %d - Thread 0x%lx #\n\n",
1325                            pid, tid);
1326 #elif defined(G_OS_WIN32) || defined(SYS_gettid) || defined(HAVE_THR_SELF)
1327               if (stream)
1328                 g_fprintf (stream,
1329                            "\n# Stack traces obtained from PID %d - Thread %lu #\n\n",
1330                            pid, tid);
1331 #endif
1332               if (trace)
1333                 {
1334                   gtrace = g_string_new (NULL);
1335 #if defined(PLATFORM_OSX)
1336                   g_string_printf (gtrace,
1337                                    "\n# Stack traces obtained from PID %d - Thread 0x%lx #\n\n",
1338                                    pid, tid);
1339 #elif defined(G_OS_WIN32) || defined(SYS_gettid) || defined(HAVE_THR_SELF)
1340                   g_string_printf (gtrace,
1341                                    "\n# Stack traces obtained from PID %d - Thread %lu #\n\n",
1342                                    pid, tid);
1343 #endif
1344                 }
1345             }
1346           /* It's hard to know if the debugger was found since it
1347            * happened in the child. Let's just assume that any output
1348            * means it succeeded.
1349            */
1350           stack_printed = TRUE;
1351 
1352           buffer[read_n] = '\0';
1353           if (stream)
1354             g_fprintf (stream, "%s", buffer);
1355           if (trace)
1356             g_string_append (gtrace, (const gchar *) buffer);
1357         }
1358       close (out_fd[0]);
1359 
1360 #ifdef PR_SET_PTRACER
1361       /* Clear ptrace permission set above */
1362       prctl (PR_SET_PTRACER, 0, 0, 0, 0);
1363 #endif
1364 
1365       waitpid (fork_pid, &status, 0);
1366     }
1367   /* else if (fork_pid == (pid_t) -1)
1368    * Fork failed!
1369    * Just continue, maybe the backtrace() API will succeed.
1370    */
1371 
1372 #ifdef HAVE_EXECINFO_H
1373   if (! stack_printed)
1374     {
1375       /* As a last resort, try using the backtrace() Linux API. It is a bit
1376        * less fancy than gdb or lldb, which is why it is not given priority.
1377        */
1378       void *bt_buf[100];
1379       int   n_symbols;
1380 
1381       n_symbols = backtrace (bt_buf, 100);
1382       if (trace && n_symbols)
1383         {
1384           char **symbols;
1385           int    i;
1386 
1387           symbols = backtrace_symbols (bt_buf, n_symbols);
1388           if (symbols)
1389             {
1390               for (i = 0; i < n_symbols; i++)
1391                 {
1392                   if (stream)
1393                     g_fprintf (stream, "%s\n", (const gchar *) symbols[i]);
1394                   if (trace)
1395                     {
1396                       if (! gtrace)
1397                         gtrace = g_string_new (NULL);
1398                       g_string_append (gtrace,
1399                                        (const gchar *) symbols[i]);
1400                       g_string_append_c (gtrace, '\n');
1401                     }
1402                 }
1403               free (symbols);
1404             }
1405         }
1406       else if (n_symbols)
1407         {
1408           /* This allows to generate traces without memory allocation.
1409            * In some cases, this is necessary, especially during
1410            * segfault-type crashes.
1411            */
1412           backtrace_symbols_fd (bt_buf, n_symbols, fileno ((FILE *) stream));
1413         }
1414       stack_printed = (n_symbols > 0);
1415     }
1416 #endif /* HAVE_EXECINFO_H */
1417 
1418   if (trace)
1419     {
1420       if (gtrace)
1421         *trace = g_string_free (gtrace, FALSE);
1422       else
1423         *trace = NULL;
1424     }
1425 #endif /* G_OS_WIN32 */
1426 
1427   return stack_printed;
1428 }
1429 
1430 /**
1431  * gimp_stack_trace_query:
1432  * @prog_name: the program to attach to.
1433  *
1434  * This is mostly the same as g_on_error_query() except that we use our
1435  * own backtrace function, much more complete.
1436  * @prog_name must be the current program name (argv[0]).
1437  * It does nothing on Win32.
1438  *
1439  * Since: 2.10
1440  **/
1441 void
gimp_stack_trace_query(const gchar * prog_name)1442 gimp_stack_trace_query (const gchar *prog_name)
1443 {
1444 #ifndef G_OS_WIN32
1445   gchar buf[16];
1446 
1447  retry:
1448 
1449   g_fprintf (stdout,
1450              "%s (pid:%u): %s: ",
1451              prog_name,
1452              (guint) getpid (),
1453              "[E]xit, show [S]tack trace or [P]roceed");
1454   fflush (stdout);
1455 
1456   if (isatty(0) && isatty(1))
1457     fgets (buf, 8, stdin);
1458   else
1459     strcpy (buf, "E\n");
1460 
1461   if ((buf[0] == 'E' || buf[0] == 'e')
1462       && buf[1] == '\n')
1463     _exit (0);
1464   else if ((buf[0] == 'P' || buf[0] == 'p')
1465            && buf[1] == '\n')
1466     return;
1467   else if ((buf[0] == 'S' || buf[0] == 's')
1468            && buf[1] == '\n')
1469     {
1470       if (! gimp_stack_trace_print (prog_name, stdout, NULL))
1471         g_fprintf (stderr, "%s\n", "Stack trace not available on your system.");
1472       goto retry;
1473     }
1474   else
1475     goto retry;
1476 #endif
1477 }
1478 
1479 
1480 /* Private functions. */
1481 
1482 static gboolean
gimp_utils_generic_available(const gchar * program,gint major,gint minor)1483 gimp_utils_generic_available (const gchar *program,
1484                               gint         major,
1485                               gint         minor)
1486 {
1487 #ifndef G_OS_WIN32
1488   pid_t pid;
1489   int   out_fd[2];
1490 
1491   if (pipe (out_fd) == -1)
1492     {
1493       return FALSE;
1494     }
1495 
1496   /* XXX: I don't use g_spawn_sync() or similar glib functions because
1497    * to read the contents of the stdout, these functions would allocate
1498    * memory dynamically. As we know, when debugging crashes, this is a
1499    * definite blocker. So instead I simply use a buffer on the stack
1500    * with a lower level fork() call.
1501    */
1502   pid = fork ();
1503   if (pid == 0)
1504     {
1505       /* Child process. */
1506       gchar *args[3] = { (gchar *) program, "--version", NULL };
1507 
1508       /* Redirect the debugger output. */
1509       dup2 (out_fd[1], STDOUT_FILENO);
1510       close (out_fd[0]);
1511       close (out_fd[1]);
1512 
1513       /* Run version check. */
1514       execvp (args[0], args);
1515       _exit (-1);
1516     }
1517   else if (pid > 0)
1518     {
1519       /* Main process */
1520       gchar    buffer[256];
1521       ssize_t  read_n;
1522       int      status;
1523       gint     installed_major = 0;
1524       gint     installed_minor = 0;
1525       gboolean major_reading = FALSE;
1526       gboolean minor_reading = FALSE;
1527       gint     i;
1528       gchar    c;
1529 
1530       waitpid (pid, &status, 0);
1531 
1532       if (! WIFEXITED (status) || WEXITSTATUS (status) != 0)
1533         return FALSE;
1534 
1535       /* It is important to close the writing side of the pipe, otherwise
1536        * the read() will wait forever without getting the information that
1537        * writing is finished.
1538        */
1539       close (out_fd[1]);
1540 
1541       /* I could loop forever until EOL, but I am pretty sure the
1542        * version information is stored on the first line and one call to
1543        * read() with 256 characters should be more than enough.
1544        */
1545       read_n = read (out_fd[0], buffer, 256);
1546 
1547       /* This is quite a very stupid parser. I only look for the first
1548        * numbers and consider them as version information. This works
1549        * fine for both GDB and LLDB as far as I can see for the output
1550        * of `${program} --version` but this should obviously not be
1551        * considered as a *really* generic version test.
1552        */
1553       for (i = 0; i < read_n; i++)
1554         {
1555           c = buffer[i];
1556           if (c >= '0' && c <= '9')
1557             {
1558               if (minor_reading)
1559                 {
1560                   installed_minor = 10 * installed_minor + (c - '0');
1561                 }
1562               else
1563                 {
1564                   major_reading = TRUE;
1565                   installed_major = 10 * installed_major + (c - '0');
1566                 }
1567             }
1568           else if (c == '.')
1569             {
1570               if (major_reading)
1571                 {
1572                   minor_reading = TRUE;
1573                   major_reading = FALSE;
1574                 }
1575               else if (minor_reading)
1576                 {
1577                   break;
1578                 }
1579             }
1580           else if (c == '\n')
1581             {
1582               /* Version information should be in the first line. */
1583               break;
1584             }
1585         }
1586       close (out_fd[0]);
1587 
1588       return (installed_major > 0 &&
1589               (installed_major > major ||
1590                (installed_major == major && installed_minor >= minor)));
1591     }
1592 #endif
1593 
1594   /* Fork failed, or Win32. */
1595   return FALSE;
1596 }
1597 
1598 static gboolean
gimp_utils_gdb_available(gint major,gint minor)1599 gimp_utils_gdb_available (gint major,
1600                           gint minor)
1601 {
1602   return gimp_utils_generic_available ("gdb", major, minor);
1603 }
1604