summaryrefslogtreecommitdiffstats
path: root/app/plug-in
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:23:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:23:22 +0000
commite42129241681dde7adae7d20697e7b421682fbb4 (patch)
treeaf1fe815a5e639e68e59fabd8395ec69458b3e5e /app/plug-in
parentInitial commit. (diff)
downloadgimp-e42129241681dde7adae7d20697e7b421682fbb4.tar.xz
gimp-e42129241681dde7adae7d20697e7b421682fbb4.zip
Adding upstream version 2.10.22.upstream/2.10.22upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'app/plug-in')
-rw-r--r--app/plug-in/Makefile.am106
-rw-r--r--app/plug-in/Makefile.in1125
-rw-r--r--app/plug-in/gimpenvirontable.c518
-rw-r--r--app/plug-in/gimpenvirontable.h72
-rw-r--r--app/plug-in/gimpinterpreterdb.c874
-rw-r--r--app/plug-in/gimpinterpreterdb.h70
-rw-r--r--app/plug-in/gimpplugin-cleanup.c578
-rw-r--r--app/plug-in/gimpplugin-cleanup.h53
-rw-r--r--app/plug-in/gimpplugin-context.c81
-rw-r--r--app/plug-in/gimpplugin-context.h28
-rw-r--r--app/plug-in/gimpplugin-message.c1063
-rw-r--r--app/plug-in/gimpplugin-message.h28
-rw-r--r--app/plug-in/gimpplugin-progress.c366
-rw-r--r--app/plug-in/gimpplugin-progress.h47
-rw-r--r--app/plug-in/gimpplugin.c1060
-rw-r--r--app/plug-in/gimpplugin.h127
-rw-r--r--app/plug-in/gimpplugindebug.c142
-rw-r--r--app/plug-in/gimpplugindebug.h43
-rw-r--r--app/plug-in/gimpplugindef.c235
-rw-r--r--app/plug-in/gimpplugindef.h82
-rw-r--r--app/plug-in/gimppluginerror.c36
-rw-r--r--app/plug-in/gimppluginerror.h35
-rw-r--r--app/plug-in/gimppluginmanager-call.c375
-rw-r--r--app/plug-in/gimppluginmanager-call.h59
-rw-r--r--app/plug-in/gimppluginmanager-data.c133
-rw-r--r--app/plug-in/gimppluginmanager-data.h35
-rw-r--r--app/plug-in/gimppluginmanager-file-procedure.c716
-rw-r--r--app/plug-in/gimppluginmanager-file-procedure.h37
-rw-r--r--app/plug-in/gimppluginmanager-file.c451
-rw-r--r--app/plug-in/gimppluginmanager-file.h75
-rw-r--r--app/plug-in/gimppluginmanager-help-domain.c160
-rw-r--r--app/plug-in/gimppluginmanager-help-domain.h43
-rw-r--r--app/plug-in/gimppluginmanager-locale-domain.c180
-rw-r--r--app/plug-in/gimppluginmanager-locale-domain.h43
-rw-r--r--app/plug-in/gimppluginmanager-menu-branch.c92
-rw-r--r--app/plug-in/gimppluginmanager-menu-branch.h42
-rw-r--r--app/plug-in/gimppluginmanager-query.c162
-rw-r--r--app/plug-in/gimppluginmanager-query.h34
-rw-r--r--app/plug-in/gimppluginmanager-restore.c1065
-rw-r--r--app/plug-in/gimppluginmanager-restore.h29
-rw-r--r--app/plug-in/gimppluginmanager.c426
-rw-r--r--app/plug-in/gimppluginmanager.h118
-rw-r--r--app/plug-in/gimppluginprocedure.c1293
-rw-r--r--app/plug-in/gimppluginprocedure.h140
-rw-r--r--app/plug-in/gimppluginprocframe.c200
-rw-r--r--app/plug-in/gimppluginprocframe.h67
-rw-r--r--app/plug-in/gimppluginshm.c301
-rw-r--r--app/plug-in/gimppluginshm.h31
-rw-r--r--app/plug-in/gimptemporaryprocedure.c156
-rw-r--r--app/plug-in/gimptemporaryprocedure.h55
-rw-r--r--app/plug-in/plug-in-enums.c118
-rw-r--r--app/plug-in/plug-in-enums.h64
-rw-r--r--app/plug-in/plug-in-menu-path.c141
-rw-r--r--app/plug-in/plug-in-menu-path.h28
-rw-r--r--app/plug-in/plug-in-params.c442
-rw-r--r--app/plug-in/plug-in-params.h32
-rw-r--r--app/plug-in/plug-in-rc.c1137
-rw-r--r--app/plug-in/plug-in-rc.h33
-rw-r--r--app/plug-in/plug-in-types.h40
59 files changed, 15322 insertions, 0 deletions
diff --git a/app/plug-in/Makefile.am b/app/plug-in/Makefile.am
new file mode 100644
index 0000000..852fdbb
--- /dev/null
+++ b/app/plug-in/Makefile.am
@@ -0,0 +1,106 @@
+## Process this file with automake to produce Makefile.in
+
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"Gimp-Plug-In\" \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_builddir)/app \
+ -I$(top_srcdir)/app \
+ $(CAIRO_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ -I$(includedir)
+
+noinst_LIBRARIES = libappplug-in.a
+
+libappplug_in_a_SOURCES = \
+ plug-in-enums.c \
+ plug-in-enums.h \
+ plug-in-types.h \
+ \
+ gimpenvirontable.c \
+ gimpenvirontable.h \
+ gimpinterpreterdb.c \
+ gimpinterpreterdb.h \
+ gimpplugindebug.c \
+ gimpplugindebug.h \
+ gimpplugin.c \
+ gimpplugin.h \
+ gimpplugin-cleanup.c \
+ gimpplugin-cleanup.h \
+ gimpplugin-context.c \
+ gimpplugin-context.h \
+ gimpplugin-message.c \
+ gimpplugin-message.h \
+ gimpplugin-progress.c \
+ gimpplugin-progress.h \
+ gimpplugindef.c \
+ gimpplugindef.h \
+ gimppluginerror.c \
+ gimppluginerror.h \
+ gimppluginmanager.c \
+ gimppluginmanager.h \
+ gimppluginmanager-call.c \
+ gimppluginmanager-call.h \
+ gimppluginmanager-data.c \
+ gimppluginmanager-data.h \
+ gimppluginmanager-file.c \
+ gimppluginmanager-file.h \
+ gimppluginmanager-file-procedure.c \
+ gimppluginmanager-file-procedure.h \
+ gimppluginmanager-help-domain.c \
+ gimppluginmanager-help-domain.h \
+ gimppluginmanager-locale-domain.c \
+ gimppluginmanager-locale-domain.h \
+ gimppluginmanager-menu-branch.c \
+ gimppluginmanager-menu-branch.h \
+ gimppluginmanager-query.c \
+ gimppluginmanager-query.h \
+ gimppluginmanager-restore.c \
+ gimppluginmanager-restore.h \
+ gimppluginprocedure.c \
+ gimppluginprocedure.h \
+ gimppluginprocframe.c \
+ gimppluginprocframe.h \
+ gimppluginshm.c \
+ gimppluginshm.h \
+ gimptemporaryprocedure.c \
+ gimptemporaryprocedure.h \
+ \
+ plug-in-menu-path.c \
+ plug-in-menu-path.h \
+ plug-in-params.c \
+ plug-in-params.h \
+ plug-in-rc.c \
+ plug-in-rc.h
+
+#
+# rules to generate built sources
+#
+# setup autogeneration dependencies
+gen_sources = xgen-pec
+CLEANFILES = $(EXTRA_PROGRAMS) $(gen_sources)
+
+xgen-pec: $(srcdir)/plug-in-enums.h $(GIMP_MKENUMS) Makefile.am
+ $(AM_V_GEN) $(GIMP_MKENUMS) \
+ --fhead "#include \"config.h\"\n#include <gio/gio.h>\n#include \"libgimpbase/gimpbase.h\"\n#include \"plug-in-enums.h\"\n#include \"gimp-intl.h\"" \
+ --fprod "\n/* enumerations from \"@basename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n" \
+ --dhead " static const Gimp@Type@Desc descs[] =\n {" \
+ --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \
+ --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \
+ $< > $@
+
+# copy the generated enum file back to the source directory only if it's
+# changed; otherwise, only update its timestamp, so that the recipe isn't
+# executed again on the next build, however, allow this to (harmlessly) fail,
+# to support building from a read-only source tree.
+$(srcdir)/plug-in-enums.c: xgen-pec
+ $(AM_V_GEN) if ! cmp -s $< $@; then \
+ cp $< $@; \
+ else \
+ touch $@ 2> /dev/null \
+ || true; \
+ fi
diff --git a/app/plug-in/Makefile.in b/app/plug-in/Makefile.in
new file mode 100644
index 0000000..20f9a6e
--- /dev/null
+++ b/app/plug-in/Makefile.in
@@ -0,0 +1,1125 @@
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = app/plug-in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.m4 \
+ $(top_srcdir)/m4macros/intltool.m4 \
+ $(top_srcdir)/m4macros/libtool.m4 \
+ $(top_srcdir)/m4macros/ltoptions.m4 \
+ $(top_srcdir)/m4macros/ltsugar.m4 \
+ $(top_srcdir)/m4macros/ltversion.m4 \
+ $(top_srcdir)/m4macros/lt~obsolete.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+AM_V_AR = $(am__v_AR_@AM_V@)
+am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
+am__v_AR_0 = @echo " AR " $@;
+am__v_AR_1 =
+libappplug_in_a_AR = $(AR) $(ARFLAGS)
+libappplug_in_a_LIBADD =
+am_libappplug_in_a_OBJECTS = plug-in-enums.$(OBJEXT) \
+ gimpenvirontable.$(OBJEXT) gimpinterpreterdb.$(OBJEXT) \
+ gimpplugindebug.$(OBJEXT) gimpplugin.$(OBJEXT) \
+ gimpplugin-cleanup.$(OBJEXT) gimpplugin-context.$(OBJEXT) \
+ gimpplugin-message.$(OBJEXT) gimpplugin-progress.$(OBJEXT) \
+ gimpplugindef.$(OBJEXT) gimppluginerror.$(OBJEXT) \
+ gimppluginmanager.$(OBJEXT) gimppluginmanager-call.$(OBJEXT) \
+ gimppluginmanager-data.$(OBJEXT) \
+ gimppluginmanager-file.$(OBJEXT) \
+ gimppluginmanager-file-procedure.$(OBJEXT) \
+ gimppluginmanager-help-domain.$(OBJEXT) \
+ gimppluginmanager-locale-domain.$(OBJEXT) \
+ gimppluginmanager-menu-branch.$(OBJEXT) \
+ gimppluginmanager-query.$(OBJEXT) \
+ gimppluginmanager-restore.$(OBJEXT) \
+ gimppluginprocedure.$(OBJEXT) gimppluginprocframe.$(OBJEXT) \
+ gimppluginshm.$(OBJEXT) gimptemporaryprocedure.$(OBJEXT) \
+ plug-in-menu-path.$(OBJEXT) plug-in-params.$(OBJEXT) \
+ plug-in-rc.$(OBJEXT)
+libappplug_in_a_OBJECTS = $(am_libappplug_in_a_OBJECTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gimpenvirontable.Po \
+ ./$(DEPDIR)/gimpinterpreterdb.Po \
+ ./$(DEPDIR)/gimpplugin-cleanup.Po \
+ ./$(DEPDIR)/gimpplugin-context.Po \
+ ./$(DEPDIR)/gimpplugin-message.Po \
+ ./$(DEPDIR)/gimpplugin-progress.Po ./$(DEPDIR)/gimpplugin.Po \
+ ./$(DEPDIR)/gimpplugindebug.Po ./$(DEPDIR)/gimpplugindef.Po \
+ ./$(DEPDIR)/gimppluginerror.Po \
+ ./$(DEPDIR)/gimppluginmanager-call.Po \
+ ./$(DEPDIR)/gimppluginmanager-data.Po \
+ ./$(DEPDIR)/gimppluginmanager-file-procedure.Po \
+ ./$(DEPDIR)/gimppluginmanager-file.Po \
+ ./$(DEPDIR)/gimppluginmanager-help-domain.Po \
+ ./$(DEPDIR)/gimppluginmanager-locale-domain.Po \
+ ./$(DEPDIR)/gimppluginmanager-menu-branch.Po \
+ ./$(DEPDIR)/gimppluginmanager-query.Po \
+ ./$(DEPDIR)/gimppluginmanager-restore.Po \
+ ./$(DEPDIR)/gimppluginmanager.Po \
+ ./$(DEPDIR)/gimppluginprocedure.Po \
+ ./$(DEPDIR)/gimppluginprocframe.Po \
+ ./$(DEPDIR)/gimppluginshm.Po \
+ ./$(DEPDIR)/gimptemporaryprocedure.Po \
+ ./$(DEPDIR)/plug-in-enums.Po ./$(DEPDIR)/plug-in-menu-path.Po \
+ ./$(DEPDIR)/plug-in-params.Po ./$(DEPDIR)/plug-in-rc.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libappplug_in_a_SOURCES)
+DIST_SOURCES = $(libappplug_in_a_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"Gimp-Plug-In\" \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_builddir)/app \
+ -I$(top_srcdir)/app \
+ $(CAIRO_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ -I$(includedir)
+
+noinst_LIBRARIES = libappplug-in.a
+libappplug_in_a_SOURCES = \
+ plug-in-enums.c \
+ plug-in-enums.h \
+ plug-in-types.h \
+ \
+ gimpenvirontable.c \
+ gimpenvirontable.h \
+ gimpinterpreterdb.c \
+ gimpinterpreterdb.h \
+ gimpplugindebug.c \
+ gimpplugindebug.h \
+ gimpplugin.c \
+ gimpplugin.h \
+ gimpplugin-cleanup.c \
+ gimpplugin-cleanup.h \
+ gimpplugin-context.c \
+ gimpplugin-context.h \
+ gimpplugin-message.c \
+ gimpplugin-message.h \
+ gimpplugin-progress.c \
+ gimpplugin-progress.h \
+ gimpplugindef.c \
+ gimpplugindef.h \
+ gimppluginerror.c \
+ gimppluginerror.h \
+ gimppluginmanager.c \
+ gimppluginmanager.h \
+ gimppluginmanager-call.c \
+ gimppluginmanager-call.h \
+ gimppluginmanager-data.c \
+ gimppluginmanager-data.h \
+ gimppluginmanager-file.c \
+ gimppluginmanager-file.h \
+ gimppluginmanager-file-procedure.c \
+ gimppluginmanager-file-procedure.h \
+ gimppluginmanager-help-domain.c \
+ gimppluginmanager-help-domain.h \
+ gimppluginmanager-locale-domain.c \
+ gimppluginmanager-locale-domain.h \
+ gimppluginmanager-menu-branch.c \
+ gimppluginmanager-menu-branch.h \
+ gimppluginmanager-query.c \
+ gimppluginmanager-query.h \
+ gimppluginmanager-restore.c \
+ gimppluginmanager-restore.h \
+ gimppluginprocedure.c \
+ gimppluginprocedure.h \
+ gimppluginprocframe.c \
+ gimppluginprocframe.h \
+ gimppluginshm.c \
+ gimppluginshm.h \
+ gimptemporaryprocedure.c \
+ gimptemporaryprocedure.h \
+ \
+ plug-in-menu-path.c \
+ plug-in-menu-path.h \
+ plug-in-params.c \
+ plug-in-params.h \
+ plug-in-rc.c \
+ plug-in-rc.h
+
+
+#
+# rules to generate built sources
+#
+# setup autogeneration dependencies
+gen_sources = xgen-pec
+CLEANFILES = $(EXTRA_PROGRAMS) $(gen_sources)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu app/plug-in/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu app/plug-in/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+
+libappplug-in.a: $(libappplug_in_a_OBJECTS) $(libappplug_in_a_DEPENDENCIES) $(EXTRA_libappplug_in_a_DEPENDENCIES)
+ $(AM_V_at)-rm -f libappplug-in.a
+ $(AM_V_AR)$(libappplug_in_a_AR) libappplug-in.a $(libappplug_in_a_OBJECTS) $(libappplug_in_a_LIBADD)
+ $(AM_V_at)$(RANLIB) libappplug-in.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpenvirontable.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpinterpreterdb.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpplugin-cleanup.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpplugin-context.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpplugin-message.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpplugin-progress.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpplugin.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpplugindebug.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpplugindef.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppluginerror.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppluginmanager-call.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppluginmanager-data.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppluginmanager-file-procedure.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppluginmanager-file.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppluginmanager-help-domain.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppluginmanager-locale-domain.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppluginmanager-menu-branch.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppluginmanager-query.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppluginmanager-restore.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppluginmanager.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppluginprocedure.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppluginprocframe.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimppluginshm.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimptemporaryprocedure.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plug-in-enums.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plug-in-menu-path.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plug-in-params.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plug-in-rc.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/gimpenvirontable.Po
+ -rm -f ./$(DEPDIR)/gimpinterpreterdb.Po
+ -rm -f ./$(DEPDIR)/gimpplugin-cleanup.Po
+ -rm -f ./$(DEPDIR)/gimpplugin-context.Po
+ -rm -f ./$(DEPDIR)/gimpplugin-message.Po
+ -rm -f ./$(DEPDIR)/gimpplugin-progress.Po
+ -rm -f ./$(DEPDIR)/gimpplugin.Po
+ -rm -f ./$(DEPDIR)/gimpplugindebug.Po
+ -rm -f ./$(DEPDIR)/gimpplugindef.Po
+ -rm -f ./$(DEPDIR)/gimppluginerror.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager-call.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager-data.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager-file-procedure.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager-file.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager-help-domain.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager-locale-domain.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager-menu-branch.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager-query.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager-restore.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager.Po
+ -rm -f ./$(DEPDIR)/gimppluginprocedure.Po
+ -rm -f ./$(DEPDIR)/gimppluginprocframe.Po
+ -rm -f ./$(DEPDIR)/gimppluginshm.Po
+ -rm -f ./$(DEPDIR)/gimptemporaryprocedure.Po
+ -rm -f ./$(DEPDIR)/plug-in-enums.Po
+ -rm -f ./$(DEPDIR)/plug-in-menu-path.Po
+ -rm -f ./$(DEPDIR)/plug-in-params.Po
+ -rm -f ./$(DEPDIR)/plug-in-rc.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/gimpenvirontable.Po
+ -rm -f ./$(DEPDIR)/gimpinterpreterdb.Po
+ -rm -f ./$(DEPDIR)/gimpplugin-cleanup.Po
+ -rm -f ./$(DEPDIR)/gimpplugin-context.Po
+ -rm -f ./$(DEPDIR)/gimpplugin-message.Po
+ -rm -f ./$(DEPDIR)/gimpplugin-progress.Po
+ -rm -f ./$(DEPDIR)/gimpplugin.Po
+ -rm -f ./$(DEPDIR)/gimpplugindebug.Po
+ -rm -f ./$(DEPDIR)/gimpplugindef.Po
+ -rm -f ./$(DEPDIR)/gimppluginerror.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager-call.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager-data.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager-file-procedure.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager-file.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager-help-domain.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager-locale-domain.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager-menu-branch.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager-query.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager-restore.Po
+ -rm -f ./$(DEPDIR)/gimppluginmanager.Po
+ -rm -f ./$(DEPDIR)/gimppluginprocedure.Po
+ -rm -f ./$(DEPDIR)/gimppluginprocframe.Po
+ -rm -f ./$(DEPDIR)/gimppluginshm.Po
+ -rm -f ./$(DEPDIR)/gimptemporaryprocedure.Po
+ -rm -f ./$(DEPDIR)/plug-in-enums.Po
+ -rm -f ./$(DEPDIR)/plug-in-menu-path.Po
+ -rm -f ./$(DEPDIR)/plug-in-params.Po
+ -rm -f ./$(DEPDIR)/plug-in-rc.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+xgen-pec: $(srcdir)/plug-in-enums.h $(GIMP_MKENUMS) Makefile.am
+ $(AM_V_GEN) $(GIMP_MKENUMS) \
+ --fhead "#include \"config.h\"\n#include <gio/gio.h>\n#include \"libgimpbase/gimpbase.h\"\n#include \"plug-in-enums.h\"\n#include \"gimp-intl.h\"" \
+ --fprod "\n/* enumerations from \"@basename@\" */" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static const G@Type@Value values[] =\n {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n" \
+ --dhead " static const Gimp@Type@Desc descs[] =\n {" \
+ --dprod " { @VALUENAME@, @valuedesc@, @valuehelp@ },@if ('@valueabbrev@' ne 'NULL')@\n /* Translators: this is an abbreviated version of @valueudesc@.\n Keep it short. */\n { @VALUENAME@, @valueabbrev@, NULL },@endif@" \
+ --dtail " { 0, NULL, NULL }\n };\n\n static GType type = 0;\n\n if (G_UNLIKELY (! type))\n {\n type = g_@type@_register_static (\"@EnumName@\", values);\n gimp_type_set_translation_context (type, \"@enumnick@\");\n gimp_@type@_set_value_descriptions (type, descs);\n }\n\n return type;\n}\n" \
+ $< > $@
+
+# copy the generated enum file back to the source directory only if it's
+# changed; otherwise, only update its timestamp, so that the recipe isn't
+# executed again on the next build, however, allow this to (harmlessly) fail,
+# to support building from a read-only source tree.
+$(srcdir)/plug-in-enums.c: xgen-pec
+ $(AM_V_GEN) if ! cmp -s $< $@; then \
+ cp $< $@; \
+ else \
+ touch $@ 2> /dev/null \
+ || true; \
+ fi
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/app/plug-in/gimpenvirontable.c b/app/plug-in/gimpenvirontable.c
new file mode 100644
index 0000000..82f2840
--- /dev/null
+++ b/app/plug-in/gimpenvirontable.c
@@ -0,0 +1,518 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpenvirontable.c
+ * (C) 2002 Manish Singh <yosh@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gio/gio.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "plug-in-types.h"
+
+#include "gimpenvirontable.h"
+
+#include "gimp-intl.h"
+
+
+typedef struct _GimpEnvironValue GimpEnvironValue;
+
+struct _GimpEnvironValue
+{
+ gchar *value;
+ gchar *separator;
+};
+
+
+static void gimp_environ_table_finalize (GObject *object);
+
+static void gimp_environ_table_load_env_file (GimpEnvironTable *environ_table,
+ GFile *file);
+static gboolean gimp_environ_table_legal_name (gchar *name);
+
+static void gimp_environ_table_populate (GimpEnvironTable *environ_table);
+static void gimp_environ_table_populate_one (const gchar *name,
+ GimpEnvironValue *val,
+ GPtrArray *env_array);
+static gboolean gimp_environ_table_pass_through (GimpEnvironTable *environ_table,
+ const gchar *name);
+
+static void gimp_environ_table_clear_vars (GimpEnvironTable *environ_table);
+static void gimp_environ_table_clear_internal (GimpEnvironTable *environ_table);
+static void gimp_environ_table_clear_envp (GimpEnvironTable *environ_table);
+
+static void gimp_environ_table_free_value (GimpEnvironValue *val);
+
+
+G_DEFINE_TYPE (GimpEnvironTable, gimp_environ_table, G_TYPE_OBJECT)
+
+#define parent_class gimp_environ_table_parent_class
+
+
+static void
+gimp_environ_table_class_init (GimpEnvironTableClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->finalize = gimp_environ_table_finalize;
+}
+
+static void
+gimp_environ_table_init (GimpEnvironTable *environ_table)
+{
+}
+
+static void
+gimp_environ_table_finalize (GObject *object)
+{
+ GimpEnvironTable *environ_table = GIMP_ENVIRON_TABLE (object);
+
+ gimp_environ_table_clear_all (environ_table);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+GimpEnvironTable *
+gimp_environ_table_new (gboolean verbose)
+{
+ GimpEnvironTable *table = g_object_new (GIMP_TYPE_ENVIRON_TABLE, NULL);
+
+ table->verbose = verbose;
+
+ return table;
+}
+
+static guint
+gimp_environ_table_str_hash (gconstpointer v)
+{
+#ifdef G_OS_WIN32
+ gchar *p = g_ascii_strup ((const gchar *) v, -1);
+ guint retval = g_str_hash (p);
+
+ g_free (p);
+
+ return retval;
+#else
+ return g_str_hash (v);
+#endif
+}
+
+static gboolean
+gimp_environ_table_str_equal (gconstpointer v1,
+ gconstpointer v2)
+{
+#ifdef G_OS_WIN32
+ gchar *string1 = g_ascii_strup ((const gchar *) v1, -1);
+ gchar *string2 = g_ascii_strup ((const gchar *) v2, -1);
+ gboolean retval = g_str_equal (string1, string2);
+
+ g_free (string1);
+ g_free (string2);
+
+ return retval;
+#else
+ return g_str_equal (v1, v2);
+#endif
+}
+
+void
+gimp_environ_table_load (GimpEnvironTable *environ_table,
+ GList *path)
+{
+ GList *list;
+
+ g_return_if_fail (GIMP_IS_ENVIRON_TABLE (environ_table));
+
+ gimp_environ_table_clear (environ_table);
+
+ environ_table->vars =
+ g_hash_table_new_full (gimp_environ_table_str_hash,
+ gimp_environ_table_str_equal,
+ g_free,
+ (GDestroyNotify) gimp_environ_table_free_value);
+
+ for (list = path; list; list = g_list_next (list))
+ {
+ GFile *dir = list->data;
+ GFileEnumerator *enumerator;
+
+ enumerator =
+ g_file_enumerate_children (dir,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+
+ if (enumerator)
+ {
+ GFileInfo *info;
+
+ while ((info = g_file_enumerator_next_file (enumerator,
+ NULL, NULL)))
+ {
+ if (! g_file_info_get_is_hidden (info) &&
+ g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR)
+ {
+ GFile *file = g_file_enumerator_get_child (enumerator, info);
+
+ gimp_environ_table_load_env_file (environ_table, file);
+
+ g_object_unref (file);
+ }
+
+ g_object_unref (info);
+ }
+
+ g_object_unref (enumerator);
+ }
+ }
+}
+
+void
+gimp_environ_table_add (GimpEnvironTable *environ_table,
+ const gchar *name,
+ const gchar *value,
+ const gchar *separator)
+{
+ GimpEnvironValue *val;
+
+ g_return_if_fail (GIMP_IS_ENVIRON_TABLE (environ_table));
+
+ gimp_environ_table_clear_envp (environ_table);
+
+ if (! environ_table->internal)
+ environ_table->internal =
+ g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify) gimp_environ_table_free_value);
+
+ val = g_slice_new (GimpEnvironValue);
+
+ val->value = g_strdup (value);
+ val->separator = g_strdup (separator);
+
+ g_hash_table_insert (environ_table->internal, g_strdup (name), val);
+}
+
+void
+gimp_environ_table_remove (GimpEnvironTable *environ_table,
+ const gchar *name)
+{
+ g_return_if_fail (GIMP_IS_ENVIRON_TABLE (environ_table));
+
+ if (! environ_table->internal)
+ return;
+
+ gimp_environ_table_clear_envp (environ_table);
+
+ g_hash_table_remove (environ_table->internal, name);
+
+ if (g_hash_table_size (environ_table->internal) == 0)
+ gimp_environ_table_clear_internal (environ_table);
+}
+
+void
+gimp_environ_table_clear (GimpEnvironTable *environ_table)
+{
+ g_return_if_fail (GIMP_IS_ENVIRON_TABLE (environ_table));
+
+ gimp_environ_table_clear_envp (environ_table);
+
+ gimp_environ_table_clear_vars (environ_table);
+}
+
+void
+gimp_environ_table_clear_all (GimpEnvironTable *environ_table)
+{
+ g_return_if_fail (GIMP_IS_ENVIRON_TABLE (environ_table));
+
+ gimp_environ_table_clear_envp (environ_table);
+
+ gimp_environ_table_clear_vars (environ_table);
+ gimp_environ_table_clear_internal (environ_table);
+}
+
+gchar **
+gimp_environ_table_get_envp (GimpEnvironTable *environ_table)
+{
+ g_return_val_if_fail (GIMP_IS_ENVIRON_TABLE (environ_table), NULL);
+
+ /* Hmm.. should we return a copy here in the future? Not thread safe atm,
+ * but the rest of it isn't either.
+ */
+
+ if (! environ_table->envp)
+ gimp_environ_table_populate (environ_table);
+
+ return environ_table->envp;
+}
+
+
+/* private */
+
+static void
+gimp_environ_table_load_env_file (GimpEnvironTable *environ_table,
+ GFile *file)
+{
+ GInputStream *input;
+ GDataInputStream *data_input;
+ gchar *buffer;
+ gsize buffer_len;
+ GError *error = NULL;
+
+ if (environ_table->verbose)
+ g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file));
+
+ input = G_INPUT_STREAM (g_file_read (file, NULL, &error));
+ if (! input)
+ {
+ g_message (_("Could not open '%s' for reading: %s"),
+ gimp_file_get_utf8_name (file),
+ error->message);
+ g_clear_error (&error);
+ return;
+ }
+
+ data_input = g_data_input_stream_new (input);
+ g_object_unref (input);
+
+ while ((buffer = g_data_input_stream_read_line (data_input, &buffer_len,
+ NULL, &error)))
+ {
+ gchar *name;
+ gchar *value;
+ gchar *separator;
+ gchar *p;
+ gchar *q;
+
+ /* Skip comments */
+ if (buffer[0] == '#')
+ {
+ g_free (buffer);
+ continue;
+ }
+
+ p = strchr (buffer, '=');
+ if (! p)
+ {
+ g_free (buffer);
+ continue;
+ }
+
+ *p = '\0';
+
+ name = buffer;
+ value = p + 1;
+
+ if (name[0] == '\0')
+ {
+ g_message (_("Empty variable name in environment file %s"),
+ gimp_file_get_utf8_name (file));
+ g_free (buffer);
+ continue;
+ }
+
+ separator = NULL;
+
+ q = strchr (name, ' ');
+ if (q)
+ {
+ *q = '\0';
+
+ separator = name;
+ name = q + 1;
+ }
+
+ if (! gimp_environ_table_legal_name (name))
+ {
+ g_message (_("Illegal variable name in environment file %s: %s"),
+ gimp_file_get_utf8_name (file), name);
+ g_free (buffer);
+ continue;
+ }
+
+ if (! g_hash_table_lookup (environ_table->vars, name))
+ {
+ GimpEnvironValue *val = g_slice_new (GimpEnvironValue);
+
+ val->value = gimp_config_path_expand (value, FALSE, NULL);
+ val->separator = g_strdup (separator);
+
+ g_hash_table_insert (environ_table->vars, g_strdup (name), val);
+ }
+
+ g_free (buffer);
+ }
+
+ if (error)
+ {
+ g_message (_("Error reading '%s': %s"),
+ gimp_file_get_utf8_name (file),
+ error->message);
+ g_clear_error (&error);
+ }
+
+ g_object_unref (data_input);
+}
+
+static gboolean
+gimp_environ_table_legal_name (gchar *name)
+{
+ gchar *s;
+
+ if (! g_ascii_isalpha (*name) && (*name != '_'))
+ return FALSE;
+
+ for (s = name + 1; *s; s++)
+ if (! g_ascii_isalnum (*s) && (*s != '_'))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+gimp_environ_table_populate (GimpEnvironTable *environ_table)
+{
+ gchar **env = g_listenv ();
+ gchar **var;
+ GPtrArray *env_array;
+
+ var = env;
+ env_array = g_ptr_array_new ();
+
+ while (*var)
+ {
+ /* g_listenv() only returns the names of environment variables
+ * that are correctly specified (name=value) in the environ
+ * array (Unix) or the process environment string table (Win32).
+ */
+
+ if (gimp_environ_table_pass_through (environ_table, *var))
+ g_ptr_array_add (env_array, g_strconcat (*var, "=", g_getenv (*var), NULL));
+
+ var++;
+ }
+
+ g_strfreev (env);
+
+ if (environ_table->vars)
+ g_hash_table_foreach (environ_table->vars,
+ (GHFunc) gimp_environ_table_populate_one,
+ env_array);
+
+ if (environ_table->internal)
+ g_hash_table_foreach (environ_table->internal,
+ (GHFunc) gimp_environ_table_populate_one,
+ env_array);
+
+ g_ptr_array_add (env_array, NULL);
+
+ environ_table->envp = (gchar **) g_ptr_array_free (env_array, FALSE);
+
+#ifdef ENVP_DEBUG
+ var = environ_table->envp;
+
+ g_print ("GimpEnvironTable:\n");
+ while (*var)
+ {
+ g_print ("%s\n", *var);
+ var++;
+ }
+#endif /* ENVP_DEBUG */
+}
+
+static void
+gimp_environ_table_populate_one (const gchar *name,
+ GimpEnvironValue *val,
+ GPtrArray *env_array)
+{
+ const gchar *old;
+ gchar *var = NULL;
+
+ if (val->separator)
+ {
+ old = g_getenv (name);
+
+ if (old)
+ var = g_strconcat (name, "=", val->value, val->separator, old, NULL);
+ }
+
+ if (! var)
+ var = g_strconcat (name, "=", val->value, NULL);
+
+ g_ptr_array_add (env_array, var);
+}
+
+static gboolean
+gimp_environ_table_pass_through (GimpEnvironTable *environ_table,
+ const gchar *name)
+{
+ gboolean vars, internal;
+
+ vars = environ_table->vars &&
+ g_hash_table_lookup (environ_table->vars, name);
+
+ internal = environ_table->internal &&
+ g_hash_table_lookup (environ_table->internal, name);
+
+ return (!vars && !internal);
+}
+
+static void
+gimp_environ_table_clear_vars (GimpEnvironTable *environ_table)
+{
+ if (environ_table->vars)
+ {
+ g_hash_table_destroy (environ_table->vars);
+ environ_table->vars = NULL;
+ }
+}
+
+static void
+gimp_environ_table_clear_internal (GimpEnvironTable *environ_table)
+{
+ if (environ_table->internal)
+ {
+ g_hash_table_destroy (environ_table->internal);
+ environ_table->internal = NULL;
+ }
+}
+
+static void
+gimp_environ_table_clear_envp (GimpEnvironTable *environ_table)
+{
+ if (environ_table->envp)
+ {
+ g_strfreev (environ_table->envp);
+ environ_table->envp = NULL;
+ }
+}
+
+static void
+gimp_environ_table_free_value (GimpEnvironValue *val)
+{
+ g_free (val->value);
+ g_free (val->separator);
+
+ g_slice_free (GimpEnvironValue, val);
+}
diff --git a/app/plug-in/gimpenvirontable.h b/app/plug-in/gimpenvirontable.h
new file mode 100644
index 0000000..c77710d
--- /dev/null
+++ b/app/plug-in/gimpenvirontable.h
@@ -0,0 +1,72 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpenvirontable.h
+ * (C) 2002 Manish Singh <yosh@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_ENVIRON_TABLE_H__
+#define __GIMP_ENVIRON_TABLE_H__
+
+
+#define GIMP_TYPE_ENVIRON_TABLE (gimp_environ_table_get_type ())
+#define GIMP_ENVIRON_TABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_ENVIRON_TABLE, GimpEnvironTable))
+#define GIMP_ENVIRON_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_ENVIRON_TABLE, GimpEnvironTableClass))
+#define GIMP_IS_ENVIRON_TABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_ENVIRON_TABLE))
+#define GIMP_IS_ENVIRON_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_ENVIRON_TABLE))
+#define GIMP_ENVIRON_TABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_ENVIRON_TABLE, GimpEnvironTableClass))
+
+
+typedef struct _GimpEnvironTableClass GimpEnvironTableClass;
+
+struct _GimpEnvironTable
+{
+ GObject parent_instance;
+
+ gboolean verbose;
+
+ GHashTable *vars;
+ GHashTable *internal;
+
+ gchar **envp;
+};
+
+struct _GimpEnvironTableClass
+{
+ GObjectClass parent_class;
+};
+
+
+GType gimp_environ_table_get_type (void) G_GNUC_CONST;
+GimpEnvironTable * gimp_environ_table_new (gboolean verbose);
+
+void gimp_environ_table_load (GimpEnvironTable *environ_table,
+ GList *path);
+
+void gimp_environ_table_add (GimpEnvironTable *environ_table,
+ const gchar *name,
+ const gchar *value,
+ const gchar *separator);
+void gimp_environ_table_remove (GimpEnvironTable *environ_table,
+ const gchar *name);
+
+void gimp_environ_table_clear (GimpEnvironTable *environ_table);
+void gimp_environ_table_clear_all (GimpEnvironTable *environ_table);
+
+gchar ** gimp_environ_table_get_envp (GimpEnvironTable *environ_table);
+
+
+#endif /* __GIMP_ENVIRON_TABLE_H__ */
diff --git a/app/plug-in/gimpinterpreterdb.c b/app/plug-in/gimpinterpreterdb.c
new file mode 100644
index 0000000..14b1f79
--- /dev/null
+++ b/app/plug-in/gimpinterpreterdb.c
@@ -0,0 +1,874 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpinterpreterdb.c
+ * (C) 2005 Manish Singh <yosh@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * The binfmt_misc bits are derived from linux/fs/binfmt_misc.c
+ * Copyright (C) 1997 Richard Günther
+ */
+
+/*
+ * The sh-bang code is derived from linux/fs/binfmt_script.c
+ * Copyright (C) 1996 Martin von Löwis
+ * original #!-checking implemented by tytso.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gio/gio.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "plug-in-types.h"
+
+#include "gimpinterpreterdb.h"
+
+#include "gimp-intl.h"
+
+
+#define BUFSIZE 4096
+
+
+typedef struct _GimpInterpreterMagic GimpInterpreterMagic;
+
+struct _GimpInterpreterMagic
+{
+ gulong offset;
+ gchar *magic;
+ gchar *mask;
+ guint size;
+ gchar *program;
+};
+
+
+static void gimp_interpreter_db_finalize (GObject *object);
+
+static void gimp_interpreter_db_load_interp_file (GimpInterpreterDB *db,
+ GFile *file);
+
+static void gimp_interpreter_db_add_program (GimpInterpreterDB *db,
+ GFile *file,
+ gchar *buffer);
+static void gimp_interpreter_db_add_binfmt_misc (GimpInterpreterDB *db,
+ GFile *file,
+ gchar *buffer);
+
+static gboolean gimp_interpreter_db_add_extension (GFile *file,
+ GimpInterpreterDB *db,
+ gchar **tokens);
+static gboolean gimp_interpreter_db_add_magic (GimpInterpreterDB *db,
+ gchar **tokens);
+
+static void gimp_interpreter_db_clear_magics (GimpInterpreterDB *db);
+
+static void gimp_interpreter_db_resolve_programs (GimpInterpreterDB *db);
+
+
+G_DEFINE_TYPE (GimpInterpreterDB, gimp_interpreter_db, G_TYPE_OBJECT)
+
+#define parent_class gimp_interpreter_db_parent_class
+
+
+static void
+gimp_interpreter_db_class_init (GimpInterpreterDBClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->finalize = gimp_interpreter_db_finalize;
+}
+
+static void
+gimp_interpreter_db_init (GimpInterpreterDB *db)
+{
+}
+
+static void
+gimp_interpreter_db_finalize (GObject *object)
+{
+ GimpInterpreterDB *db = GIMP_INTERPRETER_DB (object);
+
+ gimp_interpreter_db_clear (db);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+GimpInterpreterDB *
+gimp_interpreter_db_new (gboolean verbose)
+{
+ GimpInterpreterDB *db = g_object_new (GIMP_TYPE_INTERPRETER_DB, NULL);
+
+ db->verbose = verbose;
+
+ return db;
+}
+
+void
+gimp_interpreter_db_load (GimpInterpreterDB *db,
+ GList *path)
+{
+ GList *list;
+
+ g_return_if_fail (GIMP_IS_INTERPRETER_DB (db));
+
+ gimp_interpreter_db_clear (db);
+
+ db->programs = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_free);
+
+ db->extensions = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_free);
+
+ db->magic_names = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, NULL);
+
+ db->extension_names = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, NULL);
+
+ for (list = path; list; list = g_list_next (list))
+ {
+ GFile *dir = list->data;
+ GFileEnumerator *enumerator;
+
+ enumerator =
+ g_file_enumerate_children (dir,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+
+ if (enumerator)
+ {
+ GFileInfo *info;
+
+ while ((info = g_file_enumerator_next_file (enumerator,
+ NULL, NULL)))
+ {
+ if (! g_file_info_get_is_hidden (info) &&
+ g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR)
+ {
+ GFile *file = g_file_enumerator_get_child (enumerator, info);
+
+ gimp_interpreter_db_load_interp_file (db, file);
+
+ g_object_unref (file);
+ }
+
+ g_object_unref (info);
+ }
+
+ g_object_unref (enumerator);
+ }
+ }
+
+ gimp_interpreter_db_resolve_programs (db);
+}
+
+void
+gimp_interpreter_db_clear (GimpInterpreterDB *db)
+{
+ g_return_if_fail (GIMP_IS_INTERPRETER_DB (db));
+
+ if (db->magic_names)
+ {
+ g_hash_table_destroy (db->magic_names);
+ db->magic_names = NULL;
+ }
+
+ if (db->extension_names)
+ {
+ g_hash_table_destroy (db->extension_names);
+ db->extension_names = NULL;
+ }
+
+ if (db->programs)
+ {
+ g_hash_table_destroy (db->programs);
+ db->programs = NULL;
+ }
+
+ if (db->extensions)
+ {
+ g_hash_table_destroy (db->extensions);
+ db->extensions = NULL;
+ }
+
+ gimp_interpreter_db_clear_magics (db);
+}
+
+static void
+gimp_interpreter_db_load_interp_file (GimpInterpreterDB *db,
+ GFile *file)
+{
+ GInputStream *input;
+ GDataInputStream *data_input;
+ gchar *buffer;
+ gsize buffer_len;
+ GError *error = NULL;
+
+ if (db->verbose)
+ g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file));
+
+ input = G_INPUT_STREAM (g_file_read (file, NULL, &error));
+ if (! input)
+ {
+ g_message (_("Could not open '%s' for reading: %s"),
+ gimp_file_get_utf8_name (file),
+ error->message);
+ g_clear_error (&error);
+ return;
+ }
+
+ data_input = g_data_input_stream_new (input);
+ g_object_unref (input);
+
+ while ((buffer = g_data_input_stream_read_line (data_input, &buffer_len,
+ NULL, &error)))
+ {
+ /* Skip comments */
+ if (buffer[0] == '#')
+ {
+ g_free (buffer);
+ continue;
+ }
+
+ if (g_ascii_isalnum (buffer[0]) || (buffer[0] == '/'))
+ {
+ gimp_interpreter_db_add_program (db, file, buffer);
+ }
+ else if (! g_ascii_isspace (buffer[0]) && (buffer[0] != '\0'))
+ {
+ gimp_interpreter_db_add_binfmt_misc (db, file, buffer);
+ }
+
+ g_free (buffer);
+ }
+
+ if (error)
+ {
+ g_message (_("Error reading '%s': %s"),
+ gimp_file_get_utf8_name (file),
+ error->message);
+ g_clear_error (&error);
+ }
+
+ g_object_unref (data_input);
+}
+
+static void
+gimp_interpreter_db_add_program (GimpInterpreterDB *db,
+ GFile *file,
+ gchar *buffer)
+{
+ gchar *name;
+ gchar *program;
+ gchar *p;
+
+ p = strchr (buffer, '=');
+ if (! p)
+ return;
+
+ *p = '\0';
+
+ name = buffer;
+ program = p + 1;
+
+ if (! g_file_test (program, G_FILE_TEST_IS_EXECUTABLE))
+ {
+ gchar *prog;
+
+ prog = g_find_program_in_path (program);
+ if (! prog || ! g_file_test (prog, G_FILE_TEST_IS_EXECUTABLE))
+ {
+ g_message (_("Bad interpreter referenced in interpreter file %s: %s"),
+ gimp_file_get_utf8_name (file),
+ gimp_filename_to_utf8 (program));
+ if (prog)
+ g_free (prog);
+ return;
+ }
+ program = prog;
+ }
+ else
+ program = g_strdup (program);
+
+ if (! g_hash_table_lookup (db->programs, name))
+ g_hash_table_insert (db->programs, g_strdup (name), program);
+}
+
+static void
+gimp_interpreter_db_add_binfmt_misc (GimpInterpreterDB *db,
+ GFile *file,
+ gchar *buffer)
+{
+ gchar **tokens = NULL;
+ gchar *name, *type, *program;
+ gsize count;
+ gchar del[2];
+
+ count = strlen (buffer);
+
+ if ((count < 10) || (count > 255))
+ goto bail;
+
+ buffer = g_strndup (buffer, count + 9);
+
+ del[0] = *buffer;
+ del[1] = '\0';
+
+ memset (buffer + count, del[0], 8);
+
+ tokens = g_strsplit (buffer + 1, del, -1);
+
+ g_free (buffer);
+
+ name = tokens[0];
+ type = tokens[1];
+ program = tokens[5];
+
+ if ((name[0] == '\0') || (program[0] == '\0') ||
+ (type[0] == '\0') || (type[1] != '\0'))
+ goto bail;
+
+ switch (type[0])
+ {
+ case 'E':
+ if (! gimp_interpreter_db_add_extension (file, db, tokens))
+ goto bail;
+ break;
+
+ case 'M':
+ if (! gimp_interpreter_db_add_magic (db, tokens))
+ goto bail;
+ break;
+
+ default:
+ goto bail;
+ }
+
+ goto out;
+
+bail:
+ g_message (_("Bad binary format string in interpreter file %s"),
+ gimp_file_get_utf8_name (file));
+
+out:
+ g_strfreev (tokens);
+}
+
+static gboolean
+gimp_interpreter_db_add_extension (GFile *file,
+ GimpInterpreterDB *db,
+ gchar **tokens)
+{
+ const gchar *name = tokens[0];
+ const gchar *extension = tokens[3];
+ const gchar *program = tokens[5];
+
+ if (! g_hash_table_lookup (db->extension_names, name))
+ {
+ gchar *prog;
+
+ if (extension[0] == '\0' || extension[0] == '/')
+ return FALSE;
+
+ if (! g_file_test (program, G_FILE_TEST_IS_EXECUTABLE))
+ {
+ prog = g_find_program_in_path (program);
+ if (! prog || ! g_file_test (prog, G_FILE_TEST_IS_EXECUTABLE))
+ {
+ g_message (_("Bad interpreter referenced in interpreter file %s: %s"),
+ gimp_file_get_utf8_name (file),
+ gimp_filename_to_utf8 (program));
+ if (prog)
+ g_free (prog);
+ return FALSE;
+ }
+ }
+ else
+ prog = g_strdup (program);
+
+ g_hash_table_insert (db->extensions, g_strdup (extension), prog);
+ g_hash_table_insert (db->extension_names, g_strdup (name), prog);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+scanarg (const gchar *s)
+{
+ gchar c;
+
+ while ((c = *s++) != '\0')
+ {
+ if (c == '\\' && *s == 'x')
+ {
+ s++;
+
+ if (! g_ascii_isxdigit (*s++))
+ return FALSE;
+
+ if (! g_ascii_isxdigit (*s++))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static guint
+unquote (gchar *from)
+{
+ gchar *s = from;
+ gchar *p = from;
+ gchar c;
+
+ while ((c = *s++) != '\0')
+ {
+ if (c == '\\' && *s == 'x')
+ {
+ s++;
+ *p = g_ascii_xdigit_value (*s++) << 4;
+ *p++ |= g_ascii_xdigit_value (*s++);
+ continue;
+ }
+
+ *p++ = c;
+ }
+
+ return p - from;
+}
+
+static gboolean
+gimp_interpreter_db_add_magic (GimpInterpreterDB *db,
+ gchar **tokens)
+{
+ GimpInterpreterMagic *interp_magic;
+ gchar *name, *num, *magic, *mask, *program;
+ gulong offset;
+ guint size;
+
+ name = tokens[0];
+ num = tokens[2];
+ magic = tokens[3];
+ mask = tokens[4];
+ program = tokens[5];
+
+ if (! g_hash_table_lookup (db->magic_names, name))
+ {
+ if (num[0] != '\0')
+ {
+ offset = strtoul (num, &num, 10);
+
+ if (num[0] != '\0')
+ return FALSE;
+
+ if (offset > (BUFSIZE / 4))
+ return FALSE;
+ }
+ else
+ {
+ offset = 0;
+ }
+
+ if (! scanarg (magic))
+ return FALSE;
+
+ if (! scanarg (mask))
+ return FALSE;
+
+ size = unquote (magic);
+
+ if ((size + offset) > (BUFSIZE / 2))
+ return FALSE;
+
+ if (mask[0] == '\0')
+ mask = NULL;
+ else if (unquote (mask) != size)
+ return FALSE;
+
+ interp_magic = g_slice_new (GimpInterpreterMagic);
+
+ interp_magic->offset = offset;
+ interp_magic->magic = g_memdup (magic, size);
+ interp_magic->mask = g_memdup (mask, size);
+ interp_magic->size = size;
+ interp_magic->program = g_strdup (program);
+
+ db->magics = g_slist_append (db->magics, interp_magic);
+
+ g_hash_table_insert (db->magic_names, g_strdup (name), interp_magic);
+ }
+
+ return TRUE;
+}
+
+static void
+gimp_interpreter_db_clear_magics (GimpInterpreterDB *db)
+{
+ GimpInterpreterMagic *magic;
+ GSList *list, *last;
+
+ list = db->magics;
+ db->magics = NULL;
+
+ while (list)
+ {
+ magic = list->data;
+
+ g_free (magic->magic);
+ g_free (magic->mask);
+ g_free (magic->program);
+
+ g_slice_free (GimpInterpreterMagic, magic);
+
+ last = list;
+ list = list->next;
+
+ g_slist_free_1 (last);
+ }
+}
+
+#ifdef INTERP_DEBUG
+static void
+print_kv (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ g_print ("%s: %s\n", (gchar *) key, (gchar *) value);
+}
+
+static gchar *
+quote (gchar *s,
+ guint size)
+{
+ GString *d;
+ guint i;
+
+ if (s == NULL)
+ return "(null)";
+
+ d = g_string_sized_new (size * 4);
+
+ for (i = 0; i < size; i++)
+ g_string_append_printf (d, "\\x%02x", ((guint) s[i]) & 0xff);
+
+ return g_string_free (d, FALSE);
+}
+#endif
+
+static gboolean
+resolve_program (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GimpInterpreterDB *db = user_data;
+ gchar *program;
+
+ program = g_hash_table_lookup (db->programs, value);
+
+ if (program != NULL)
+ {
+ g_free (value);
+ value = g_strdup (program);
+ }
+
+ g_hash_table_insert (db->extensions, key, value);
+
+ return TRUE;
+}
+
+static void
+gimp_interpreter_db_resolve_programs (GimpInterpreterDB *db)
+{
+ GSList *list;
+ GHashTable *extensions;
+
+ for (list = db->magics; list; list = list->next)
+ {
+ GimpInterpreterMagic *magic = list->data;
+ const gchar *program;
+
+ program = g_hash_table_lookup (db->programs, magic->program);
+
+ if (program != NULL)
+ {
+ g_free (magic->program);
+ magic->program = g_strdup (program);
+ }
+ }
+
+ extensions = db->extensions;
+ db->extensions = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_free);
+
+ g_hash_table_foreach_steal (extensions, resolve_program, db);
+
+ g_hash_table_destroy (extensions);
+
+#ifdef INTERP_DEBUG
+ g_print ("Programs:\n");
+ g_hash_table_foreach (db->programs, print_kv, NULL);
+
+ g_print ("\nExtensions:\n");
+ g_hash_table_foreach (db->extensions, print_kv, NULL);
+
+ g_print ("\nMagics:\n");
+
+ list = db->magics;
+
+ while (list)
+ {
+ GimpInterpreterMagic *magic;
+
+ magic = list->data;
+ g_print ("program: %s, offset: %lu, magic: %s, mask: %s\n",
+ magic->program, magic->offset,
+ quote (magic->magic, magic->size),
+ quote (magic->mask, magic->size));
+
+ list = list->next;
+ }
+
+ g_print ("\n");
+#endif
+}
+
+static gchar *
+resolve_extension (GimpInterpreterDB *db,
+ const gchar *program_path)
+{
+ gchar *filename;
+ gchar *p;
+ const gchar *program;
+
+ filename = g_path_get_basename (program_path);
+
+ p = strrchr (filename, '.');
+ if (! p)
+ {
+ g_free (filename);
+ return NULL;
+ }
+
+ program = g_hash_table_lookup (db->extensions, p + 1);
+
+ g_free (filename);
+
+ return g_strdup (program);
+}
+
+static gchar *
+resolve_sh_bang (GimpInterpreterDB *db,
+ const gchar *program_path,
+ gchar *buffer,
+ gssize len,
+ gchar **interp_arg)
+{
+ gchar *cp;
+ gchar *name;
+ gchar *program;
+
+ cp = strchr (buffer, '\n');
+ if (! cp)
+ cp = buffer + len - 1;
+
+ *cp = '\0';
+
+ while (cp > buffer)
+ {
+ cp--;
+ if ((*cp == ' ') || (*cp == '\t') || (*cp == '\r'))
+ *cp = '\0';
+ else
+ break;
+ }
+
+ for (cp = buffer + 2; (*cp == ' ') || (*cp == '\t'); cp++);
+
+ if (*cp == '\0')
+ return NULL;
+
+ name = cp;
+
+ for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++)
+ /* nothing */ ;
+
+ while ((*cp == ' ') || (*cp == '\t'))
+ *cp++ = '\0';
+
+ if (*cp)
+ {
+ if (strcmp ("/usr/bin/env", name) == 0)
+ {
+ program = g_hash_table_lookup (db->programs, cp);
+
+ if (program)
+ {
+ /* Shift program name and arguments to the right, if and
+ * only if we recorded a specific interpreter for such
+ * script. Otherwise let `env` tool do its job.
+ */
+ name = cp;
+
+ for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++)
+ ;
+
+ while ((*cp == ' ') || (*cp == '\t'))
+ *cp++ = '\0';
+ }
+ }
+
+ if (*cp)
+ *interp_arg = g_strdup (cp);
+ }
+
+ program = g_hash_table_lookup (db->programs, name);
+ if (! program)
+ program = name;
+
+ return g_strdup (program);
+}
+
+static gchar *
+resolve_magic (GimpInterpreterDB *db,
+ const gchar *program_path,
+ gchar *buffer)
+{
+ GSList *list;
+ GimpInterpreterMagic *magic;
+ gchar *s;
+ guint i;
+
+ list = db->magics;
+
+ while (list)
+ {
+ magic = list->data;
+
+ s = buffer + magic->offset;
+
+ if (magic->mask)
+ {
+ for (i = 0; i < magic->size; i++)
+ if ((*s++ ^ magic->magic[i]) & magic->mask[i])
+ break;
+ }
+ else
+ {
+ for (i = 0; i < magic->size; i++)
+ if ((*s++ ^ magic->magic[i]))
+ break;
+ }
+
+ if (i == magic->size)
+ return g_strdup (magic->program);
+
+ list = list->next;
+ }
+
+ return NULL;
+}
+
+gchar *
+gimp_interpreter_db_resolve (GimpInterpreterDB *db,
+ const gchar *program_path,
+ gchar **interp_arg)
+{
+ GFile *file;
+ GInputStream *input;
+ gchar *program = NULL;
+
+ g_return_val_if_fail (GIMP_IS_INTERPRETER_DB (db), NULL);
+ g_return_val_if_fail (program_path != NULL, NULL);
+ g_return_val_if_fail (interp_arg != NULL, NULL);
+
+ *interp_arg = NULL;
+
+ file = g_file_new_for_path (program_path);
+ input = G_INPUT_STREAM (g_file_read (file, NULL, NULL));
+ g_object_unref (file);
+
+ if (input)
+ {
+ gsize bytes_read;
+ gchar buffer[BUFSIZE];
+
+ memset (buffer, 0, sizeof (buffer));
+ g_input_stream_read_all (input, buffer,
+ sizeof (buffer) - 1, /* leave one nul at the end */
+ &bytes_read, NULL, NULL);
+ g_object_unref (input);
+
+ if (bytes_read)
+ {
+ if (bytes_read > 3 && buffer[0] == '#' && buffer[1] == '!')
+ program = resolve_sh_bang (db, program_path, buffer, bytes_read, interp_arg);
+
+ if (! program)
+ program = resolve_magic (db, program_path, buffer);
+ }
+ }
+
+ if (! program)
+ program = resolve_extension (db, program_path);
+
+ return program;
+}
+
+static void
+collect_extensions (const gchar *ext,
+ const gchar *program G_GNUC_UNUSED,
+ GString *str)
+{
+ if (str->len)
+ g_string_append_c (str, G_SEARCHPATH_SEPARATOR);
+
+ g_string_append_c (str, '.');
+ g_string_append (str, ext);
+}
+
+/**
+ * gimp_interpreter_db_get_extensions:
+ * @db:
+ *
+ * Return value: a newly allocated string with all registered file
+ * extensions separated by %G_SEARCHPATH_SEPARATOR;
+ * or %NULL if no extensions are registered
+ **/
+gchar *
+gimp_interpreter_db_get_extensions (GimpInterpreterDB *db)
+{
+ GString *str;
+
+ g_return_val_if_fail (GIMP_IS_INTERPRETER_DB (db), NULL);
+
+ if (g_hash_table_size (db->extensions) == 0)
+ return NULL;
+
+ str = g_string_new (NULL);
+
+ g_hash_table_foreach (db->extensions, (GHFunc) collect_extensions, str);
+
+ return g_string_free (str, FALSE);
+}
diff --git a/app/plug-in/gimpinterpreterdb.h b/app/plug-in/gimpinterpreterdb.h
new file mode 100644
index 0000000..4c77acc
--- /dev/null
+++ b/app/plug-in/gimpinterpreterdb.h
@@ -0,0 +1,70 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpinterpreterdb.h
+ * (C) 2005 Manish Singh <yosh@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_INTERPRETER_DB_H__
+#define __GIMP_INTERPRETER_DB_H__
+
+
+#define GIMP_TYPE_INTERPRETER_DB (gimp_interpreter_db_get_type ())
+#define GIMP_INTERPRETER_DB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_INTERPRETER_DB, GimpInterpreterDB))
+#define GIMP_INTERPRETER_DB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_INTERPRETER_DB, GimpInterpreterDBClass))
+#define GIMP_IS_INTERPRETER_DB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_INTERPRETER_DB))
+#define GIMP_IS_INTERPRETER_DB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_INTERPRETER_DB))
+#define GIMP_INTERPRETER_DB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_INTERPRETER_DB, GimpInterpreterDBClass))
+
+
+typedef struct _GimpInterpreterDBClass GimpInterpreterDBClass;
+
+struct _GimpInterpreterDB
+{
+ GObject parent_instance;
+
+ gboolean verbose;
+
+ GHashTable *programs;
+
+ GSList *magics;
+ GHashTable *magic_names;
+
+ GHashTable *extensions;
+ GHashTable *extension_names;
+};
+
+struct _GimpInterpreterDBClass
+{
+ GObjectClass parent_class;
+};
+
+
+GType gimp_interpreter_db_get_type (void) G_GNUC_CONST;
+GimpInterpreterDB * gimp_interpreter_db_new (gboolean verbose);
+
+void gimp_interpreter_db_load (GimpInterpreterDB *db,
+ GList *path);
+
+void gimp_interpreter_db_clear (GimpInterpreterDB *db);
+
+gchar * gimp_interpreter_db_resolve (GimpInterpreterDB *db,
+ const gchar *program_path,
+ gchar **interp_arg);
+gchar * gimp_interpreter_db_get_extensions (GimpInterpreterDB *db);
+
+
+#endif /* __GIMP_INTERPRETER_DB_H__ */
diff --git a/app/plug-in/gimpplugin-cleanup.c b/app/plug-in/gimpplugin-cleanup.c
new file mode 100644
index 0000000..8c8d6d6
--- /dev/null
+++ b/app/plug-in/gimpplugin-cleanup.c
@@ -0,0 +1,578 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpplugin-cleanup.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "plug-in-types.h"
+
+#include "core/gimp.h"
+#include "core/gimpcontainer.h"
+#include "core/gimpdrawable.h"
+#include "core/gimpdrawable-shadow.h"
+#include "core/gimpimage.h"
+#include "core/gimpimage-undo.h"
+#include "core/gimpundostack.h"
+
+#include "gimpplugin.h"
+#include "gimpplugin-cleanup.h"
+#include "gimppluginmanager.h"
+#include "gimppluginprocedure.h"
+
+#include "gimp-log.h"
+
+
+typedef struct _GimpPlugInCleanupImage GimpPlugInCleanupImage;
+
+struct _GimpPlugInCleanupImage
+{
+ GimpImage *image;
+ gint image_ID;
+
+ gint undo_group_count;
+ gint layers_freeze_count;
+ gint channels_freeze_count;
+ gint vectors_freeze_count;
+};
+
+
+typedef struct _GimpPlugInCleanupItem GimpPlugInCleanupItem;
+
+struct _GimpPlugInCleanupItem
+{
+ GimpItem *item;
+ gint item_ID;
+
+ gboolean shadow_buffer;
+};
+
+
+/* local function prototypes */
+
+static GimpPlugInCleanupImage *
+ gimp_plug_in_cleanup_image_new (GimpPlugInProcFrame *proc_frame,
+ GimpImage *image);
+static void gimp_plug_in_cleanup_image_free (GimpPlugInProcFrame *proc_frame,
+ GimpPlugInCleanupImage *cleanup);
+static gboolean
+ gimp_plug_in_cleanup_image_is_clean (GimpPlugInCleanupImage *cleanup);
+static GimpPlugInCleanupImage *
+ gimp_plug_in_cleanup_image_get (GimpPlugInProcFrame *proc_frame,
+ GimpImage *image);
+static void gimp_plug_in_cleanup_image (GimpPlugInProcFrame *proc_frame,
+ GimpPlugInCleanupImage *cleanup);
+
+static GimpPlugInCleanupItem *
+ gimp_plug_in_cleanup_item_new (GimpPlugInProcFrame *proc_frame,
+ GimpItem *item);
+static void gimp_plug_in_cleanup_item_free (GimpPlugInProcFrame *proc_frame,
+ GimpPlugInCleanupItem *cleanup);
+static gboolean
+ gimp_plug_in_cleanup_item_is_clean (GimpPlugInCleanupItem *cleanup);
+static GimpPlugInCleanupItem *
+ gimp_plug_in_cleanup_item_get (GimpPlugInProcFrame *proc_frame,
+ GimpItem *item);
+static void gimp_plug_in_cleanup_item (GimpPlugInProcFrame *proc_frame,
+ GimpPlugInCleanupItem *cleanup);
+
+
+/* public functions */
+
+gboolean
+gimp_plug_in_cleanup_undo_group_start (GimpPlugIn *plug_in,
+ GimpImage *image)
+{
+ GimpPlugInProcFrame *proc_frame;
+ GimpPlugInCleanupImage *cleanup;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+ g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+ cleanup = gimp_plug_in_cleanup_image_get (proc_frame, image);
+
+ if (! cleanup)
+ cleanup = gimp_plug_in_cleanup_image_new (proc_frame, image);
+
+ cleanup->undo_group_count++;
+
+ return TRUE;
+}
+
+gboolean
+gimp_plug_in_cleanup_undo_group_end (GimpPlugIn *plug_in,
+ GimpImage *image)
+{
+ GimpPlugInProcFrame *proc_frame;
+ GimpPlugInCleanupImage *cleanup;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+ g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+ cleanup = gimp_plug_in_cleanup_image_get (proc_frame, image);
+
+ if (! cleanup)
+ return FALSE;
+
+ if (cleanup->undo_group_count > 0)
+ {
+ cleanup->undo_group_count--;
+
+ if (gimp_plug_in_cleanup_image_is_clean (cleanup))
+ gimp_plug_in_cleanup_image_free (proc_frame, cleanup);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
+gimp_plug_in_cleanup_layers_freeze (GimpPlugIn *plug_in,
+ GimpImage *image)
+{
+ GimpPlugInProcFrame *proc_frame;
+ GimpPlugInCleanupImage *cleanup;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+ g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+ cleanup = gimp_plug_in_cleanup_image_get (proc_frame, image);
+
+ if (! cleanup)
+ cleanup = gimp_plug_in_cleanup_image_new (proc_frame, image);
+
+ cleanup->layers_freeze_count++;
+
+ return TRUE;
+}
+
+gboolean
+gimp_plug_in_cleanup_layers_thaw (GimpPlugIn *plug_in,
+ GimpImage *image)
+{
+ GimpPlugInProcFrame *proc_frame;
+ GimpPlugInCleanupImage *cleanup;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+ g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+ cleanup = gimp_plug_in_cleanup_image_get (proc_frame, image);
+
+ if (! cleanup)
+ return FALSE;
+
+ if (cleanup->layers_freeze_count > 0)
+ {
+ cleanup->layers_freeze_count--;
+
+ if (gimp_plug_in_cleanup_image_is_clean (cleanup))
+ gimp_plug_in_cleanup_image_free (proc_frame, cleanup);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
+gimp_plug_in_cleanup_channels_freeze (GimpPlugIn *plug_in,
+ GimpImage *image)
+{
+ GimpPlugInProcFrame *proc_frame;
+ GimpPlugInCleanupImage *cleanup;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+ g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+ cleanup = gimp_plug_in_cleanup_image_get (proc_frame, image);
+
+ if (! cleanup)
+ cleanup = gimp_plug_in_cleanup_image_new (proc_frame, image);
+
+ cleanup->channels_freeze_count++;
+
+ return TRUE;
+}
+
+gboolean
+gimp_plug_in_cleanup_channels_thaw (GimpPlugIn *plug_in,
+ GimpImage *image)
+{
+ GimpPlugInProcFrame *proc_frame;
+ GimpPlugInCleanupImage *cleanup;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+ g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+ cleanup = gimp_plug_in_cleanup_image_get (proc_frame, image);
+
+ if (! cleanup)
+ return FALSE;
+
+ if (cleanup->channels_freeze_count > 0)
+ {
+ cleanup->channels_freeze_count--;
+
+ if (gimp_plug_in_cleanup_image_is_clean (cleanup))
+ gimp_plug_in_cleanup_image_free (proc_frame, cleanup);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
+gimp_plug_in_cleanup_vectors_freeze (GimpPlugIn *plug_in,
+ GimpImage *image)
+{
+ GimpPlugInProcFrame *proc_frame;
+ GimpPlugInCleanupImage *cleanup;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+ g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+ cleanup = gimp_plug_in_cleanup_image_get (proc_frame, image);
+
+ if (! cleanup)
+ cleanup = gimp_plug_in_cleanup_image_new (proc_frame, image);
+
+ cleanup->vectors_freeze_count++;
+
+ return TRUE;
+}
+
+gboolean
+gimp_plug_in_cleanup_vectors_thaw (GimpPlugIn *plug_in,
+ GimpImage *image)
+{
+ GimpPlugInProcFrame *proc_frame;
+ GimpPlugInCleanupImage *cleanup;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+ g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+ cleanup = gimp_plug_in_cleanup_image_get (proc_frame, image);
+
+ if (! cleanup)
+ return FALSE;
+
+ if (cleanup->vectors_freeze_count > 0)
+ {
+ cleanup->vectors_freeze_count--;
+
+ if (gimp_plug_in_cleanup_image_is_clean (cleanup))
+ gimp_plug_in_cleanup_image_free (proc_frame, cleanup);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
+gimp_plug_in_cleanup_add_shadow (GimpPlugIn *plug_in,
+ GimpDrawable *drawable)
+{
+ GimpPlugInProcFrame *proc_frame;
+ GimpPlugInCleanupItem *cleanup;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+ g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE);
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+ cleanup = gimp_plug_in_cleanup_item_get (proc_frame, GIMP_ITEM (drawable));
+
+ if (! cleanup)
+ cleanup = gimp_plug_in_cleanup_item_new (proc_frame, GIMP_ITEM (drawable));
+
+ cleanup->shadow_buffer = TRUE;
+
+ return TRUE;
+}
+
+gboolean
+gimp_plug_in_cleanup_remove_shadow (GimpPlugIn *plug_in,
+ GimpDrawable *drawable)
+{
+ GimpPlugInProcFrame *proc_frame;
+ GimpPlugInCleanupItem *cleanup;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+ g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE);
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+ cleanup = gimp_plug_in_cleanup_item_get (proc_frame, GIMP_ITEM (drawable));
+
+ if (! cleanup)
+ return FALSE;
+
+ if (cleanup->shadow_buffer)
+ {
+ cleanup->shadow_buffer = FALSE;
+
+ if (gimp_plug_in_cleanup_item_is_clean (cleanup))
+ gimp_plug_in_cleanup_item_free (proc_frame, cleanup);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void
+gimp_plug_in_cleanup (GimpPlugIn *plug_in,
+ GimpPlugInProcFrame *proc_frame)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+ g_return_if_fail (proc_frame != NULL);
+
+ while (proc_frame->image_cleanups)
+ {
+ GimpPlugInCleanupImage *cleanup = proc_frame->image_cleanups->data;
+
+ if (gimp_image_get_by_ID (plug_in->manager->gimp,
+ cleanup->image_ID) == cleanup->image)
+ {
+ gimp_plug_in_cleanup_image (proc_frame, cleanup);
+ }
+
+ gimp_plug_in_cleanup_image_free (proc_frame, cleanup);
+ }
+
+ while (proc_frame->item_cleanups)
+ {
+ GimpPlugInCleanupItem *cleanup = proc_frame->item_cleanups->data;
+
+ if (gimp_item_get_by_ID (plug_in->manager->gimp,
+ cleanup->item_ID) == cleanup->item)
+ {
+ gimp_plug_in_cleanup_item (proc_frame, cleanup);
+ }
+
+ gimp_plug_in_cleanup_item_free (proc_frame, cleanup);
+ }
+}
+
+
+/* private functions */
+
+static GimpPlugInCleanupImage *
+gimp_plug_in_cleanup_image_new (GimpPlugInProcFrame *proc_frame,
+ GimpImage *image)
+{
+ GimpPlugInCleanupImage *cleanup = g_slice_new0 (GimpPlugInCleanupImage);
+
+ cleanup->image = image;
+ cleanup->image_ID = gimp_image_get_ID (image);
+
+ proc_frame->image_cleanups = g_list_prepend (proc_frame->image_cleanups,
+ cleanup);
+
+ return cleanup;
+}
+
+static void
+gimp_plug_in_cleanup_image_free (GimpPlugInProcFrame *proc_frame,
+ GimpPlugInCleanupImage *cleanup)
+{
+ proc_frame->image_cleanups = g_list_remove (proc_frame->image_cleanups,
+ cleanup);
+
+ g_slice_free (GimpPlugInCleanupImage, cleanup);
+}
+
+static gboolean
+gimp_plug_in_cleanup_image_is_clean (GimpPlugInCleanupImage *cleanup)
+{
+ if (cleanup->undo_group_count > 0)
+ return FALSE;
+
+ if (cleanup->layers_freeze_count > 0)
+ return FALSE;
+
+ if (cleanup->channels_freeze_count > 0)
+ return FALSE;
+
+ if (cleanup->vectors_freeze_count > 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static GimpPlugInCleanupImage *
+gimp_plug_in_cleanup_image_get (GimpPlugInProcFrame *proc_frame,
+ GimpImage *image)
+{
+ GList *list;
+
+ for (list = proc_frame->image_cleanups; list; list = g_list_next (list))
+ {
+ GimpPlugInCleanupImage *cleanup = list->data;
+
+ if (cleanup->image == image)
+ return cleanup;
+ }
+
+ return NULL;
+}
+
+static void
+gimp_plug_in_cleanup_image (GimpPlugInProcFrame *proc_frame,
+ GimpPlugInCleanupImage *cleanup)
+{
+ GimpImage *image = cleanup->image;
+ GimpContainer *container;
+
+ if (cleanup->undo_group_count > 0)
+ {
+ g_message ("Plug-in '%s' left image undo in inconsistent state, "
+ "closing open undo groups.",
+ gimp_procedure_get_label (proc_frame->procedure));
+
+ while (cleanup->undo_group_count--)
+ if (! gimp_image_undo_group_end (image))
+ break;
+ }
+
+ container = gimp_image_get_layers (image);
+
+ if (cleanup->layers_freeze_count > 0)
+ {
+ g_message ("Plug-in '%s' left image's layers frozen, "
+ "thawing layers.",
+ gimp_procedure_get_label (proc_frame->procedure));
+
+ while (cleanup->layers_freeze_count-- > 0 &&
+ gimp_container_frozen (container))
+ {
+ gimp_container_thaw (container);
+ }
+ }
+
+ container = gimp_image_get_channels (image);
+
+ if (cleanup->channels_freeze_count > 0)
+ {
+ g_message ("Plug-in '%s' left image's channels frozen, "
+ "thawing channels.",
+ gimp_procedure_get_label (proc_frame->procedure));
+
+ while (cleanup->channels_freeze_count-- > 0 &&
+ gimp_container_frozen (container))
+ {
+ gimp_container_thaw (container);
+ }
+ }
+
+ container = gimp_image_get_vectors (image);
+
+ if (cleanup->vectors_freeze_count > 0)
+ {
+ g_message ("Plug-in '%s' left image's vectors frozen, "
+ "thawing vectors.",
+ gimp_procedure_get_label (proc_frame->procedure));
+
+ while (cleanup->vectors_freeze_count > 0 &&
+ gimp_container_frozen (container))
+ {
+ gimp_container_thaw (container);
+ }
+ }
+}
+
+static GimpPlugInCleanupItem *
+gimp_plug_in_cleanup_item_new (GimpPlugInProcFrame *proc_frame,
+ GimpItem *item)
+{
+ GimpPlugInCleanupItem *cleanup = g_slice_new0 (GimpPlugInCleanupItem);
+
+ cleanup->item = item;
+ cleanup->item_ID = gimp_item_get_ID (item);
+
+ proc_frame->item_cleanups = g_list_remove (proc_frame->item_cleanups,
+ cleanup);
+
+ return cleanup;
+}
+
+static void
+gimp_plug_in_cleanup_item_free (GimpPlugInProcFrame *proc_frame,
+ GimpPlugInCleanupItem *cleanup)
+{
+ proc_frame->item_cleanups = g_list_remove (proc_frame->item_cleanups,
+ cleanup);
+
+ g_slice_free (GimpPlugInCleanupItem, cleanup);
+}
+
+static gboolean
+gimp_plug_in_cleanup_item_is_clean (GimpPlugInCleanupItem *cleanup)
+{
+ if (cleanup->shadow_buffer)
+ return FALSE;
+
+ return TRUE;
+}
+
+static GimpPlugInCleanupItem *
+gimp_plug_in_cleanup_item_get (GimpPlugInProcFrame *proc_frame,
+ GimpItem *item)
+{
+ GList *list;
+
+ for (list = proc_frame->item_cleanups; list; list = g_list_next (list))
+ {
+ GimpPlugInCleanupItem *cleanup = list->data;
+
+ if (cleanup->item == item)
+ return cleanup;
+ }
+
+ return NULL;
+}
+
+static void
+gimp_plug_in_cleanup_item (GimpPlugInProcFrame *proc_frame,
+ GimpPlugInCleanupItem *cleanup)
+{
+ GimpItem *item = cleanup->item;
+
+ if (cleanup->shadow_buffer)
+ {
+ GIMP_LOG (SHADOW_TILES,
+ "Freeing shadow buffer of drawable '%s' on behalf of '%s'.",
+ gimp_object_get_name (item),
+ gimp_procedure_get_label (proc_frame->procedure));
+
+ gimp_drawable_free_shadow_buffer (GIMP_DRAWABLE (item));
+
+ cleanup->shadow_buffer = FALSE;
+ }
+}
diff --git a/app/plug-in/gimpplugin-cleanup.h b/app/plug-in/gimpplugin-cleanup.h
new file mode 100644
index 0000000..cb3b22d
--- /dev/null
+++ b/app/plug-in/gimpplugin-cleanup.h
@@ -0,0 +1,53 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpplugin-cleanup.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_CLEANUP_H__
+#define __GIMP_PLUG_IN_CLEANUP_H__
+
+
+gboolean gimp_plug_in_cleanup_undo_group_start (GimpPlugIn *plug_in,
+ GimpImage *image);
+gboolean gimp_plug_in_cleanup_undo_group_end (GimpPlugIn *plug_in,
+ GimpImage *image);
+
+gboolean gimp_plug_in_cleanup_layers_freeze (GimpPlugIn *plug_in,
+ GimpImage *image);
+gboolean gimp_plug_in_cleanup_layers_thaw (GimpPlugIn *plug_in,
+ GimpImage *image);
+
+gboolean gimp_plug_in_cleanup_channels_freeze (GimpPlugIn *plug_in,
+ GimpImage *image);
+gboolean gimp_plug_in_cleanup_channels_thaw (GimpPlugIn *plug_in,
+ GimpImage *image);
+
+gboolean gimp_plug_in_cleanup_vectors_freeze (GimpPlugIn *plug_in,
+ GimpImage *image);
+gboolean gimp_plug_in_cleanup_vectors_thaw (GimpPlugIn *plug_in,
+ GimpImage *image);
+
+gboolean gimp_plug_in_cleanup_add_shadow (GimpPlugIn *plug_in,
+ GimpDrawable *drawable);
+gboolean gimp_plug_in_cleanup_remove_shadow (GimpPlugIn *plug_in,
+ GimpDrawable *drawable);
+
+void gimp_plug_in_cleanup (GimpPlugIn *plug_in,
+ GimpPlugInProcFrame *proc_frame);
+
+
+#endif /* __GIMP_PLUG_IN_CLEANUP_H__ */
diff --git a/app/plug-in/gimpplugin-context.c b/app/plug-in/gimpplugin-context.c
new file mode 100644
index 0000000..6817695
--- /dev/null
+++ b/app/plug-in/gimpplugin-context.c
@@ -0,0 +1,81 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpplugin-context.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "plug-in-types.h"
+
+#include "core/gimp.h"
+
+#include "pdb/gimppdbcontext.h"
+
+#include "gimpplugin.h"
+#include "gimpplugin-context.h"
+#include "gimppluginmanager.h"
+
+
+gboolean
+gimp_plug_in_context_push (GimpPlugIn *plug_in)
+{
+ GimpPlugInProcFrame *proc_frame;
+ GimpContext *parent;
+ GimpContext *context;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+
+ if (proc_frame->context_stack)
+ parent = proc_frame->context_stack->data;
+ else
+ parent = proc_frame->main_context;
+
+ context = gimp_pdb_context_new (plug_in->manager->gimp, parent, FALSE);
+
+ proc_frame->context_stack = g_list_prepend (proc_frame->context_stack,
+ context);
+
+ return TRUE;
+}
+
+gboolean
+gimp_plug_in_context_pop (GimpPlugIn *plug_in)
+{
+ GimpPlugInProcFrame *proc_frame;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+
+ if (proc_frame->context_stack)
+ {
+ GimpContext *context = proc_frame->context_stack->data;
+
+ proc_frame->context_stack = g_list_remove (proc_frame->context_stack,
+ context);
+ g_object_unref (context);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/app/plug-in/gimpplugin-context.h b/app/plug-in/gimpplugin-context.h
new file mode 100644
index 0000000..8e46a20
--- /dev/null
+++ b/app/plug-in/gimpplugin-context.h
@@ -0,0 +1,28 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpplugin-context.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_CONTEXT_H__
+#define __GIMP_PLUG_IN_CONTEXT_H__
+
+
+gboolean gimp_plug_in_context_push (GimpPlugIn *plug_in);
+gboolean gimp_plug_in_context_pop (GimpPlugIn *plug_in);
+
+
+#endif /* __GIMP_PLUG_IN_CONTEXT_H__ */
diff --git a/app/plug-in/gimpplugin-message.c b/app/plug-in/gimpplugin-message.c
new file mode 100644
index 0000000..b27035e
--- /dev/null
+++ b/app/plug-in/gimpplugin-message.c
@@ -0,0 +1,1063 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpplugin-message.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpbase/gimpprotocol.h"
+#include "libgimpbase/gimpwire.h"
+
+#include "plug-in-types.h"
+
+#include "gegl/gimp-babl.h"
+#include "gegl/gimp-babl-compat.h"
+#include "gegl/gimp-gegl-tile-compat.h"
+
+#include "core/gimp.h"
+#include "core/gimpdrawable.h"
+#include "core/gimpdrawable-shadow.h"
+
+#include "pdb/gimp-pdb-compat.h"
+#include "pdb/gimppdb.h"
+#include "pdb/gimppdberror.h"
+
+#include "gimpplugin.h"
+#include "gimpplugin-cleanup.h"
+#include "gimpplugin-message.h"
+#include "gimppluginmanager.h"
+#include "gimpplugindef.h"
+#include "gimppluginshm.h"
+#include "gimptemporaryprocedure.h"
+#include "plug-in-params.h"
+
+#include "gimp-intl.h"
+
+
+/* local function prototypes */
+
+static void gimp_plug_in_handle_quit (GimpPlugIn *plug_in);
+static void gimp_plug_in_handle_tile_request (GimpPlugIn *plug_in,
+ GPTileReq *request);
+static void gimp_plug_in_handle_tile_put (GimpPlugIn *plug_in,
+ GPTileReq *request);
+static void gimp_plug_in_handle_tile_get (GimpPlugIn *plug_in,
+ GPTileReq *request);
+static void gimp_plug_in_handle_proc_run (GimpPlugIn *plug_in,
+ GPProcRun *proc_run);
+static void gimp_plug_in_handle_proc_return (GimpPlugIn *plug_in,
+ GPProcReturn *proc_return);
+static void gimp_plug_in_handle_temp_proc_return (GimpPlugIn *plug_in,
+ GPProcReturn *proc_return);
+static void gimp_plug_in_handle_proc_install (GimpPlugIn *plug_in,
+ GPProcInstall *proc_install);
+static void gimp_plug_in_handle_proc_uninstall (GimpPlugIn *plug_in,
+ GPProcUninstall *proc_uninstall);
+static void gimp_plug_in_handle_extension_ack (GimpPlugIn *plug_in);
+static void gimp_plug_in_handle_has_init (GimpPlugIn *plug_in);
+
+
+/* public functions */
+
+void
+gimp_plug_in_handle_message (GimpPlugIn *plug_in,
+ GimpWireMessage *msg)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+ g_return_if_fail (plug_in->open == TRUE);
+ g_return_if_fail (msg != NULL);
+
+ switch (msg->type)
+ {
+ case GP_QUIT:
+ gimp_plug_in_handle_quit (plug_in);
+ break;
+
+ case GP_CONFIG:
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "sent a CONFIG message. This should not happen.",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file));
+ gimp_plug_in_close (plug_in, TRUE);
+ break;
+
+ case GP_TILE_REQ:
+ gimp_plug_in_handle_tile_request (plug_in, msg->data);
+ break;
+
+ case GP_TILE_ACK:
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "sent a TILE_ACK message. This should not happen.",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file));
+ gimp_plug_in_close (plug_in, TRUE);
+ break;
+
+ case GP_TILE_DATA:
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "sent a TILE_DATA message. This should not happen.",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file));
+ gimp_plug_in_close (plug_in, TRUE);
+ break;
+
+ case GP_PROC_RUN:
+ gimp_plug_in_handle_proc_run (plug_in, msg->data);
+ break;
+
+ case GP_PROC_RETURN:
+ gimp_plug_in_handle_proc_return (plug_in, msg->data);
+ break;
+
+ case GP_TEMP_PROC_RUN:
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "sent a TEMP_PROC_RUN message. This should not happen.",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file));
+ gimp_plug_in_close (plug_in, TRUE);
+ break;
+
+ case GP_TEMP_PROC_RETURN:
+ gimp_plug_in_handle_temp_proc_return (plug_in, msg->data);
+ break;
+
+ case GP_PROC_INSTALL:
+ gimp_plug_in_handle_proc_install (plug_in, msg->data);
+ break;
+
+ case GP_PROC_UNINSTALL:
+ gimp_plug_in_handle_proc_uninstall (plug_in, msg->data);
+ break;
+
+ case GP_EXTENSION_ACK:
+ gimp_plug_in_handle_extension_ack (plug_in);
+ break;
+
+ case GP_HAS_INIT:
+ gimp_plug_in_handle_has_init (plug_in);
+ break;
+ }
+}
+
+
+/* private functions */
+
+static void
+gimp_plug_in_handle_quit (GimpPlugIn *plug_in)
+{
+ gimp_plug_in_close (plug_in, FALSE);
+}
+
+static void
+gimp_plug_in_handle_tile_request (GimpPlugIn *plug_in,
+ GPTileReq *request)
+{
+ g_return_if_fail (request != NULL);
+
+ if (request->drawable_ID == -1)
+ gimp_plug_in_handle_tile_put (plug_in, request);
+ else
+ gimp_plug_in_handle_tile_get (plug_in, request);
+}
+
+static void
+gimp_plug_in_handle_tile_put (GimpPlugIn *plug_in,
+ GPTileReq *request)
+{
+ GPTileData tile_data;
+ GPTileData *tile_info;
+ GimpWireMessage msg;
+ GimpDrawable *drawable;
+ GeglBuffer *buffer;
+ const Babl *format;
+ GeglRectangle tile_rect;
+
+ tile_data.drawable_ID = -1;
+ tile_data.tile_num = 0;
+ tile_data.shadow = 0;
+ tile_data.bpp = 0;
+ tile_data.width = 0;
+ tile_data.height = 0;
+ tile_data.use_shm = (plug_in->manager->shm != NULL);
+ tile_data.data = NULL;
+
+ if (! gp_tile_data_write (plug_in->my_write, &tile_data, plug_in))
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "%s: ERROR", G_STRFUNC);
+ gimp_plug_in_close (plug_in, TRUE);
+ return;
+ }
+
+ if (! gimp_wire_read_msg (plug_in->my_read, &msg, plug_in))
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "%s: ERROR", G_STRFUNC);
+ gimp_plug_in_close (plug_in, TRUE);
+ return;
+ }
+
+ if (msg.type != GP_TILE_DATA)
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "expected tile data and received: %d", msg.type);
+ gimp_plug_in_close (plug_in, TRUE);
+ return;
+ }
+
+ tile_info = msg.data;
+
+ drawable = (GimpDrawable *) gimp_item_get_by_ID (plug_in->manager->gimp,
+ tile_info->drawable_ID);
+
+ if (! GIMP_IS_DRAWABLE (drawable))
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "tried writing to invalid drawable %d (killing)",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ tile_info->drawable_ID);
+ gimp_plug_in_close (plug_in, TRUE);
+ return;
+ }
+ else if (gimp_item_is_removed (GIMP_ITEM (drawable)))
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "tried writing to drawable %d which was removed "
+ "from the image (killing)",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ tile_info->drawable_ID);
+ gimp_plug_in_close (plug_in, TRUE);
+ return;
+ }
+
+ if (tile_info->shadow)
+ {
+
+ /* don't check whether the drawable is a group or locked here,
+ * the plugin will get a proper error message when it tries to
+ * merge the shadow tiles, which is much better than just
+ * killing it.
+ */
+ buffer = gimp_drawable_get_shadow_buffer (drawable);
+
+ gimp_plug_in_cleanup_add_shadow (plug_in, drawable);
+ }
+ else
+ {
+ if (gimp_item_is_content_locked (GIMP_ITEM (drawable)))
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "tried writing to a locked drawable %d (killing)",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ tile_info->drawable_ID);
+ gimp_plug_in_close (plug_in, TRUE);
+ return;
+ }
+ else if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable)))
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "tried writing to a group layer %d (killing)",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ tile_info->drawable_ID);
+ gimp_plug_in_close (plug_in, TRUE);
+ return;
+ }
+
+ buffer = gimp_drawable_get_buffer (drawable);
+ }
+
+ if (! gimp_gegl_buffer_get_tile_rect (buffer,
+ GIMP_PLUG_IN_TILE_WIDTH,
+ GIMP_PLUG_IN_TILE_HEIGHT,
+ tile_info->tile_num,
+ &tile_rect))
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "requested invalid tile (killing)",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file));
+ gimp_plug_in_close (plug_in, TRUE);
+ return;
+ }
+
+ format = gegl_buffer_get_format (buffer);
+
+ if (! gimp_plug_in_precision_enabled (plug_in))
+ {
+ format = gimp_babl_compat_u8_format (format);
+ }
+
+ if (tile_data.use_shm)
+ {
+ gegl_buffer_set (buffer, &tile_rect, 0, format,
+ gimp_plug_in_shm_get_addr (plug_in->manager->shm),
+ GEGL_AUTO_ROWSTRIDE);
+ }
+ else
+ {
+ gegl_buffer_set (buffer, &tile_rect, 0, format,
+ tile_info->data,
+ GEGL_AUTO_ROWSTRIDE);
+ }
+
+ gimp_wire_destroy (&msg);
+
+ if (! gp_tile_ack_write (plug_in->my_write, plug_in))
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "%s: ERROR", G_STRFUNC);
+ gimp_plug_in_close (plug_in, TRUE);
+ return;
+ }
+}
+
+static void
+gimp_plug_in_handle_tile_get (GimpPlugIn *plug_in,
+ GPTileReq *request)
+{
+ GPTileData tile_data;
+ GimpWireMessage msg;
+ GimpDrawable *drawable;
+ GeglBuffer *buffer;
+ const Babl *format;
+ GeglRectangle tile_rect;
+ gint tile_size;
+
+ drawable = (GimpDrawable *) gimp_item_get_by_ID (plug_in->manager->gimp,
+ request->drawable_ID);
+
+ if (! GIMP_IS_DRAWABLE (drawable))
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "tried reading from invalid drawable %d (killing)",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ request->drawable_ID);
+ gimp_plug_in_close (plug_in, TRUE);
+ return;
+ }
+ else if (gimp_item_is_removed (GIMP_ITEM (drawable)))
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "tried reading from drawable %d which was removed "
+ "from the image (killing)",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ request->drawable_ID);
+ gimp_plug_in_close (plug_in, TRUE);
+ return;
+ }
+
+ if (request->shadow)
+ {
+ buffer = gimp_drawable_get_shadow_buffer (drawable);
+
+ gimp_plug_in_cleanup_add_shadow (plug_in, drawable);
+ }
+ else
+ {
+ buffer = gimp_drawable_get_buffer (drawable);
+ }
+
+ if (! gimp_gegl_buffer_get_tile_rect (buffer,
+ GIMP_PLUG_IN_TILE_WIDTH,
+ GIMP_PLUG_IN_TILE_HEIGHT,
+ request->tile_num,
+ &tile_rect))
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "requested invalid tile (killing)",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file));
+ gimp_plug_in_close (plug_in, TRUE);
+ return;
+ }
+
+ format = gegl_buffer_get_format (buffer);
+
+ if (! gimp_plug_in_precision_enabled (plug_in))
+ {
+ format = gimp_babl_compat_u8_format (format);
+ }
+
+ tile_size = (babl_format_get_bytes_per_pixel (format) *
+ tile_rect.width * tile_rect.height);
+
+ tile_data.drawable_ID = request->drawable_ID;
+ tile_data.tile_num = request->tile_num;
+ tile_data.shadow = request->shadow;
+ tile_data.bpp = babl_format_get_bytes_per_pixel (format);
+ tile_data.width = tile_rect.width;
+ tile_data.height = tile_rect.height;
+ tile_data.use_shm = (plug_in->manager->shm != NULL);
+
+ if (tile_data.use_shm)
+ {
+ gegl_buffer_get (buffer, &tile_rect, 1.0, format,
+ gimp_plug_in_shm_get_addr (plug_in->manager->shm),
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ }
+ else
+ {
+ tile_data.data = g_malloc (tile_size);
+
+ gegl_buffer_get (buffer, &tile_rect, 1.0, format,
+ tile_data.data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ }
+
+ if (! gp_tile_data_write (plug_in->my_write, &tile_data, plug_in))
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "%s: ERROR", G_STRFUNC);
+ gimp_plug_in_close (plug_in, TRUE);
+ return;
+ }
+
+ if (! gimp_wire_read_msg (plug_in->my_read, &msg, plug_in))
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "%s: ERROR", G_STRFUNC);
+ gimp_plug_in_close (plug_in, TRUE);
+ return;
+ }
+
+ if (msg.type != GP_TILE_ACK)
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "expected tile ack and received: %d", msg.type);
+ gimp_plug_in_close (plug_in, TRUE);
+ return;
+ }
+
+ gimp_wire_destroy (&msg);
+}
+
+static void
+gimp_plug_in_handle_proc_error (GimpPlugIn *plug_in,
+ GimpPlugInProcFrame *proc_frame,
+ const gchar *name,
+ const GError *error)
+{
+ switch (proc_frame->error_handler)
+ {
+ case GIMP_PDB_ERROR_HANDLER_INTERNAL:
+ if (error->domain == GIMP_PDB_ERROR)
+ {
+ gimp_message (plug_in->manager->gimp,
+ G_OBJECT (proc_frame->progress),
+ GIMP_MESSAGE_ERROR,
+ _("Calling error for procedure '%s':\n"
+ "%s"),
+ name, error->message);
+ }
+ else
+ {
+ gimp_message (plug_in->manager->gimp,
+ G_OBJECT (proc_frame->progress),
+ GIMP_MESSAGE_ERROR,
+ _("Execution error for procedure '%s':\n"
+ "%s"),
+ name, error->message);
+ }
+ break;
+
+ case GIMP_PDB_ERROR_HANDLER_PLUGIN:
+ /* the plug-in is responsible for handling this error */
+ break;
+ }
+}
+
+static void
+gimp_plug_in_handle_proc_run (GimpPlugIn *plug_in,
+ GPProcRun *proc_run)
+{
+ GimpPlugInProcFrame *proc_frame;
+ gchar *canonical;
+ const gchar *proc_name = NULL;
+ GimpProcedure *procedure;
+ GimpValueArray *args = NULL;
+ GimpValueArray *return_vals = NULL;
+ GError *error = NULL;
+
+ g_return_if_fail (proc_run != NULL);
+ g_return_if_fail (proc_run->name != NULL);
+
+ canonical = gimp_canonicalize_identifier (proc_run->name);
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+
+ procedure = gimp_pdb_lookup_procedure (plug_in->manager->gimp->pdb,
+ canonical);
+
+ if (! procedure)
+ {
+ proc_name = gimp_pdb_lookup_compat_proc_name (plug_in->manager->gimp->pdb,
+ canonical);
+
+ if (proc_name)
+ {
+ procedure = gimp_pdb_lookup_procedure (plug_in->manager->gimp->pdb,
+ proc_name);
+
+ if (plug_in->manager->gimp->pdb_compat_mode == GIMP_PDB_COMPAT_WARN)
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_WARNING,
+ "Plug-in \"%s\"\n(%s)\n"
+ "called deprecated procedure '%s'.\n"
+ "It should call '%s' instead!",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ canonical, proc_name);
+ }
+ }
+ }
+ else if (procedure->deprecated)
+ {
+ if (plug_in->manager->gimp->pdb_compat_mode == GIMP_PDB_COMPAT_WARN)
+ {
+ if (! strcmp (procedure->deprecated, "NONE"))
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_WARNING,
+ "Plug-in \"%s\"\n(%s)\n"
+ "called deprecated procedure '%s'.",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ canonical);
+ }
+ else
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_WARNING,
+ "WARNING: Plug-in \"%s\"\n(%s)\n"
+ "called deprecated procedure '%s'.\n"
+ "It should call '%s' instead!",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ canonical, procedure->deprecated);
+ }
+ }
+ }
+
+ if (! proc_name)
+ proc_name = canonical;
+
+ args = plug_in_params_to_args (procedure ? procedure->args : NULL,
+ procedure ? procedure->num_args : 0,
+ proc_run->params, proc_run->nparams,
+ FALSE, FALSE);
+
+ /* Execute the procedure even if gimp_pdb_lookup_procedure()
+ * returned NULL, gimp_pdb_execute_procedure_by_name_args() will
+ * return appropriate error return_vals.
+ */
+ gimp_plug_in_manager_plug_in_push (plug_in->manager, plug_in);
+ return_vals = gimp_pdb_execute_procedure_by_name_args (plug_in->manager->gimp->pdb,
+ proc_frame->context_stack ?
+ proc_frame->context_stack->data :
+ proc_frame->main_context,
+ proc_frame->progress,
+ &error,
+ proc_name,
+ args);
+ gimp_plug_in_manager_plug_in_pop (plug_in->manager);
+
+ gimp_value_array_unref (args);
+
+ if (error)
+ {
+ gimp_plug_in_handle_proc_error (plug_in, proc_frame,
+ canonical, error);
+ g_error_free (error);
+ }
+
+ g_free (canonical);
+
+ /* Don't bother to send the return value if executing the procedure
+ * closed the plug-in (e.g. if the procedure is gimp-quit)
+ */
+ if (plug_in->open)
+ {
+ GPProcReturn proc_return;
+
+ /* Return the name we got called with, *not* proc_name or canonical,
+ * since proc_name may have been remapped by gimp->procedural_compat_ht
+ * and canonical may be different too.
+ */
+ proc_return.name = proc_run->name;
+ proc_return.nparams = gimp_value_array_length (return_vals);
+ proc_return.params = plug_in_args_to_params (return_vals, FALSE);
+
+ if (! gp_proc_return_write (plug_in->my_write, &proc_return, plug_in))
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "%s: ERROR", G_STRFUNC);
+ gimp_plug_in_close (plug_in, TRUE);
+ }
+
+ g_free (proc_return.params);
+ }
+
+ gimp_value_array_unref (return_vals);
+}
+
+static void
+gimp_plug_in_handle_proc_return (GimpPlugIn *plug_in,
+ GPProcReturn *proc_return)
+{
+ GimpPlugInProcFrame *proc_frame = &plug_in->main_proc_frame;
+
+ g_return_if_fail (proc_return != NULL);
+
+ proc_frame->return_vals =
+ plug_in_params_to_args (proc_frame->procedure->values,
+ proc_frame->procedure->num_values,
+ proc_return->params,
+ proc_return->nparams,
+ TRUE, TRUE);
+
+ if (proc_frame->main_loop)
+ {
+ g_main_loop_quit (proc_frame->main_loop);
+ }
+ else
+ {
+ /* the plug-in is run asynchronously, so display its error
+ * messages here because nobody else will do it
+ */
+ gimp_plug_in_procedure_handle_return_values (GIMP_PLUG_IN_PROCEDURE (proc_frame->procedure),
+ plug_in->manager->gimp,
+ proc_frame->progress,
+ proc_frame->return_vals);
+ }
+
+ gimp_plug_in_close (plug_in, FALSE);
+}
+
+static void
+gimp_plug_in_handle_temp_proc_return (GimpPlugIn *plug_in,
+ GPProcReturn *proc_return)
+{
+ g_return_if_fail (proc_return != NULL);
+
+ if (plug_in->temp_proc_frames)
+ {
+ GimpPlugInProcFrame *proc_frame = plug_in->temp_proc_frames->data;
+
+ proc_frame->return_vals =
+ plug_in_params_to_args (proc_frame->procedure->values,
+ proc_frame->procedure->num_values,
+ proc_return->params,
+ proc_return->nparams,
+ TRUE, TRUE);
+
+ gimp_plug_in_main_loop_quit (plug_in);
+ gimp_plug_in_proc_frame_pop (plug_in);
+ }
+ else
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "sent a TEMP_PROC_RETURN message while not running "
+ "a temporary procedure. This should not happen.",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file));
+ gimp_plug_in_close (plug_in, TRUE);
+ }
+}
+
+static void
+gimp_plug_in_handle_proc_install (GimpPlugIn *plug_in,
+ GPProcInstall *proc_install)
+{
+ GimpPlugInProcedure *proc = NULL;
+ GimpProcedure *procedure = NULL;
+ gchar *canonical;
+ gboolean null_name = FALSE;
+ gboolean valid_utf8 = FALSE;
+ gint i;
+
+ g_return_if_fail (proc_install != NULL);
+ g_return_if_fail (proc_install->name != NULL);
+
+ canonical = gimp_canonicalize_identifier (proc_install->name);
+
+ /* Sanity check for array arguments */
+
+ for (i = 1; i < proc_install->nparams; i++)
+ {
+ if ((proc_install->params[i].type == GIMP_PDB_INT32ARRAY ||
+ proc_install->params[i].type == GIMP_PDB_INT8ARRAY ||
+ proc_install->params[i].type == GIMP_PDB_FLOATARRAY ||
+ proc_install->params[i].type == GIMP_PDB_STRINGARRAY ||
+ proc_install->params[i].type == GIMP_PDB_COLORARRAY)
+ &&
+ proc_install->params[i - 1].type != GIMP_PDB_INT32)
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "attempted to install procedure \"%s\" "
+ "which fails to comply with the array parameter "
+ "passing standard. Argument %d is noncompliant.",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ canonical, i);
+ g_free (canonical);
+ return;
+ }
+ }
+
+ /* Sanity check strings for UTF-8 validity */
+
+#define VALIDATE(str) (g_utf8_validate ((str), -1, NULL))
+#define VALIDATE_OR_NULL(str) ((str) == NULL || g_utf8_validate ((str), -1, NULL))
+
+ if (VALIDATE_OR_NULL (proc_install->menu_path) &&
+ VALIDATE (canonical) &&
+ VALIDATE_OR_NULL (proc_install->blurb) &&
+ VALIDATE_OR_NULL (proc_install->help) &&
+ VALIDATE_OR_NULL (proc_install->author) &&
+ VALIDATE_OR_NULL (proc_install->copyright) &&
+ VALIDATE_OR_NULL (proc_install->date))
+ {
+ null_name = FALSE;
+ valid_utf8 = TRUE;
+
+ for (i = 0; i < proc_install->nparams && valid_utf8 && !null_name; i++)
+ {
+ if (! proc_install->params[i].name)
+ {
+ null_name = TRUE;
+ }
+ else if (! (VALIDATE (proc_install->params[i].name) &&
+ VALIDATE_OR_NULL (proc_install->params[i].description)))
+ {
+ valid_utf8 = FALSE;
+ }
+ }
+
+ for (i = 0; i < proc_install->nreturn_vals && valid_utf8 && !null_name; i++)
+ {
+ if (! proc_install->return_vals[i].name)
+ {
+ null_name = TRUE;
+ }
+ else if (! (VALIDATE (proc_install->return_vals[i].name) &&
+ VALIDATE_OR_NULL (proc_install->return_vals[i].description)))
+ {
+ valid_utf8 = FALSE;
+ }
+ }
+ }
+
+#undef VALIDATE
+#undef VALIDATE_OR_NULL
+
+ if (null_name)
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "attempted to install procedure \"%s\" with a "
+ "NULL parameter name.",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ canonical);
+ g_free (canonical);
+ return;
+ }
+
+ if (! valid_utf8)
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "attempted to install procedure \"%s\" with "
+ "invalid UTF-8 strings.",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ canonical);
+ g_free (canonical);
+ return;
+ }
+
+ if (proc_install->menu_path && strlen (proc_install->menu_path) &&
+ proc_install->menu_path[0] == '<')
+ {
+ g_printerr ("Plug-in \"%s\"\n(%s) "
+ "is installing procedure \"%s\" with a full "
+ "menu path \"%s\" as menu label, this deprecated and will "
+ "be an error in GIMP 3.0\n",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ canonical,
+ proc_install->menu_path);
+ }
+
+ /* Create the procedure object */
+
+ switch (proc_install->type)
+ {
+ case GIMP_PLUGIN:
+ case GIMP_EXTENSION:
+ procedure = gimp_plug_in_procedure_new (proc_install->type,
+ plug_in->file);
+ break;
+
+ case GIMP_TEMPORARY:
+ procedure = gimp_temporary_procedure_new (plug_in);
+ break;
+ }
+
+ proc = GIMP_PLUG_IN_PROCEDURE (procedure);
+
+ proc->mtime = time (NULL);
+ proc->installed_during_init = (plug_in->call_mode == GIMP_PLUG_IN_CALL_INIT);
+
+ gimp_object_take_name (GIMP_OBJECT (procedure), canonical);
+ gimp_procedure_set_strings (procedure,
+ proc_install->name,
+ proc_install->blurb,
+ proc_install->help,
+ proc_install->author,
+ proc_install->copyright,
+ proc_install->date,
+ NULL);
+
+ gimp_plug_in_procedure_set_image_types (proc, proc_install->image_types);
+
+ for (i = 0; i < proc_install->nparams; i++)
+ {
+ GParamSpec *pspec;
+ gboolean name_valid;
+
+ pspec = gimp_pdb_compat_param_spec (plug_in->manager->gimp,
+ proc_install->params[i].type,
+ proc_install->params[i].name,
+ proc_install->params[i].description,
+ &name_valid);
+
+ gimp_procedure_add_argument (procedure, pspec);
+
+ if (pspec && ! name_valid)
+ {
+ switch (plug_in->manager->gimp->pdb_compat_mode)
+ {
+ case GIMP_PDB_COMPAT_ON:
+ break;
+
+ case GIMP_PDB_COMPAT_WARN:
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_WARNING,
+ "Plug-in \"%s\"\n(%s)\n"
+ "attempted to install procedure \"%s\" "
+ "with invalid parameter name \"%s\".\n"
+ "This is deprecated.\n"
+ "The parameter name was changed to \"%s\".",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ gimp_object_get_name (proc),
+ proc_install->params[i].name,
+ pspec->name);
+ break;
+
+ case GIMP_PDB_COMPAT_OFF:
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n"
+ "attempted to install procedure \"%s\" "
+ "with invalid parameter name \"%s\".\n"
+ "This is not allowed.",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ gimp_object_get_name (proc),
+ proc_install->params[i].name);
+
+ g_object_unref (proc);
+
+ return;
+ }
+ }
+ }
+
+ for (i = 0; i < proc_install->nreturn_vals; i++)
+ {
+ GParamSpec *pspec;
+ gboolean name_valid;
+
+ pspec = gimp_pdb_compat_param_spec (plug_in->manager->gimp,
+ proc_install->return_vals[i].type,
+ proc_install->return_vals[i].name,
+ proc_install->return_vals[i].description,
+ &name_valid);
+
+ gimp_procedure_add_return_value (procedure, pspec);
+
+ if (pspec && ! name_valid)
+ {
+ switch (plug_in->manager->gimp->pdb_compat_mode)
+ {
+ case GIMP_PDB_COMPAT_ON:
+ break;
+
+ case GIMP_PDB_COMPAT_WARN:
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_WARNING,
+ "Plug-in \"%s\"\n(%s)\n"
+ "attempted to install procedure \"%s\" "
+ "with invalid return-value name \"%s\".\n"
+ "This is deprecated.\n"
+ "The return-value name was changed to \"%s\".",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ gimp_object_get_name (proc),
+ proc_install->return_vals[i].name,
+ pspec->name);
+ break;
+
+ case GIMP_PDB_COMPAT_OFF:
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n"
+ "attempted to install procedure \"%s\" "
+ "with invalid return-value name \"%s\".\n"
+ "This is not allowed.",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ gimp_object_get_name (proc),
+ proc_install->return_vals[i].name);
+
+ g_object_unref (proc);
+
+ return;
+ }
+ }
+ }
+
+ /* Sanity check menu path */
+
+ if (proc_install->menu_path && strlen (proc_install->menu_path))
+ {
+ if (proc_install->menu_path[0] == '<')
+ {
+ GError *error = NULL;
+
+ if (! gimp_plug_in_procedure_add_menu_path (proc,
+ proc_install->menu_path,
+ &error))
+ {
+ gimp_message_literal (plug_in->manager->gimp,
+ NULL, GIMP_MESSAGE_WARNING,
+ error->message);
+ g_clear_error (&error);
+ }
+ }
+ else
+ {
+ proc->menu_label = g_strdup (proc_install->menu_path);
+ }
+ }
+
+ /* Install the procedure */
+
+ switch (proc_install->type)
+ {
+ case GIMP_PLUGIN:
+ case GIMP_EXTENSION:
+ gimp_plug_in_def_add_procedure (plug_in->plug_in_def, proc);
+ break;
+
+ case GIMP_TEMPORARY:
+ gimp_plug_in_add_temp_proc (plug_in, GIMP_TEMPORARY_PROCEDURE (proc));
+ break;
+ }
+
+ g_object_unref (proc);
+}
+
+static void
+gimp_plug_in_handle_proc_uninstall (GimpPlugIn *plug_in,
+ GPProcUninstall *proc_uninstall)
+{
+ GimpPlugInProcedure *proc;
+ gchar *canonical;
+
+ g_return_if_fail (proc_uninstall != NULL);
+ g_return_if_fail (proc_uninstall->name != NULL);
+
+ canonical = gimp_canonicalize_identifier (proc_uninstall->name);
+
+ proc = gimp_plug_in_procedure_find (plug_in->temp_procedures, canonical);
+
+ if (proc)
+ gimp_plug_in_remove_temp_proc (plug_in, GIMP_TEMPORARY_PROCEDURE (proc));
+
+ g_free (canonical);
+}
+
+static void
+gimp_plug_in_handle_extension_ack (GimpPlugIn *plug_in)
+{
+ if (plug_in->ext_main_loop)
+ {
+ g_main_loop_quit (plug_in->ext_main_loop);
+ }
+ else
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "sent an EXTENSION_ACK message while not being started "
+ "as an extension. This should not happen.",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file));
+ gimp_plug_in_close (plug_in, TRUE);
+ }
+}
+
+static void
+gimp_plug_in_handle_has_init (GimpPlugIn *plug_in)
+{
+ if (plug_in->call_mode == GIMP_PLUG_IN_CALL_QUERY)
+ {
+ gimp_plug_in_def_set_has_init (plug_in->plug_in_def, TRUE);
+ }
+ else
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "sent an HAS_INIT message while not in query(). "
+ "This should not happen.",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file));
+ gimp_plug_in_close (plug_in, TRUE);
+ }
+}
diff --git a/app/plug-in/gimpplugin-message.h b/app/plug-in/gimpplugin-message.h
new file mode 100644
index 0000000..1dd301f
--- /dev/null
+++ b/app/plug-in/gimpplugin-message.h
@@ -0,0 +1,28 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpplugin-message.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_MESSAGE_H__
+#define __GIMP_PLUG_IN_MESSAGE_H__
+
+
+void gimp_plug_in_handle_message (GimpPlugIn *plug_in,
+ GimpWireMessage *msg);
+
+
+#endif /* __GIMP_PLUG_IN_MESSAGE_H__ */
diff --git a/app/plug-in/gimpplugin-progress.c b/app/plug-in/gimpplugin-progress.c
new file mode 100644
index 0000000..ddf2ef5
--- /dev/null
+++ b/app/plug-in/gimpplugin-progress.c
@@ -0,0 +1,366 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpplugin-progress.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "plug-in-types.h"
+
+#include "core/gimp.h"
+#include "core/gimpparamspecs.h"
+#include "core/gimppdbprogress.h"
+#include "core/gimpprogress.h"
+
+#include "pdb/gimppdb.h"
+#include "pdb/gimppdberror.h"
+
+#include "gimpplugin.h"
+#include "gimpplugin-progress.h"
+#include "gimppluginmanager.h"
+#include "gimptemporaryprocedure.h"
+
+#include "gimp-intl.h"
+
+
+/* local function prototypes */
+
+static void gimp_plug_in_progress_cancel_callback (GimpProgress *progress,
+ GimpPlugIn *plug_in);
+
+
+/* public functions */
+
+gint
+gimp_plug_in_progress_attach (GimpProgress *progress)
+{
+ gint attach_count;
+
+ g_return_val_if_fail (GIMP_IS_PROGRESS (progress), 0);
+
+ attach_count =
+ GPOINTER_TO_INT (g_object_get_data (G_OBJECT (progress),
+ "plug-in-progress-attach-count"));
+
+ attach_count++;
+
+ g_object_set_data (G_OBJECT (progress), "plug-in-progress-attach-count",
+ GINT_TO_POINTER (attach_count));
+
+ return attach_count;
+}
+
+gint
+gimp_plug_in_progress_detach (GimpProgress *progress)
+{
+ gint attach_count;
+
+ g_return_val_if_fail (GIMP_IS_PROGRESS (progress), 0);
+
+ attach_count =
+ GPOINTER_TO_INT (g_object_get_data (G_OBJECT (progress),
+ "plug-in-progress-attach-count"));
+
+ attach_count--;
+
+ g_object_set_data (G_OBJECT (progress), "plug-in-progress-attach-count",
+ GINT_TO_POINTER (attach_count));
+
+ return attach_count;
+}
+
+void
+gimp_plug_in_progress_start (GimpPlugIn *plug_in,
+ const gchar *message,
+ GimpObject *display)
+{
+ GimpPlugInProcFrame *proc_frame;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+ g_return_if_fail (display == NULL || GIMP_IS_OBJECT (display));
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+
+ if (! proc_frame->progress)
+ {
+ proc_frame->progress = gimp_new_progress (plug_in->manager->gimp,
+ display);
+
+ if (proc_frame->progress)
+ {
+ proc_frame->progress_created = TRUE;
+
+ g_object_ref (proc_frame->progress);
+
+ gimp_plug_in_progress_attach (proc_frame->progress);
+ }
+ }
+
+ if (proc_frame->progress)
+ {
+ if (! proc_frame->progress_cancel_id)
+ {
+ g_object_add_weak_pointer (G_OBJECT (proc_frame->progress),
+ (gpointer) &proc_frame->progress);
+
+ proc_frame->progress_cancel_id =
+ g_signal_connect (proc_frame->progress, "cancel",
+ G_CALLBACK (gimp_plug_in_progress_cancel_callback),
+ plug_in);
+ }
+
+ if (gimp_progress_is_active (proc_frame->progress))
+ {
+ if (message)
+ gimp_progress_set_text_literal (proc_frame->progress, message);
+
+ if (gimp_progress_get_value (proc_frame->progress) > 0.0)
+ gimp_progress_set_value (proc_frame->progress, 0.0);
+ }
+ else
+ {
+ gimp_progress_start (proc_frame->progress, TRUE,
+ "%s", message ? message : "");
+ }
+ }
+}
+
+void
+gimp_plug_in_progress_end (GimpPlugIn *plug_in,
+ GimpPlugInProcFrame *proc_frame)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+ g_return_if_fail (proc_frame != NULL);
+
+ if (proc_frame->progress)
+ {
+ if (proc_frame->progress_cancel_id)
+ {
+ g_signal_handler_disconnect (proc_frame->progress,
+ proc_frame->progress_cancel_id);
+ proc_frame->progress_cancel_id = 0;
+
+ g_object_remove_weak_pointer (G_OBJECT (proc_frame->progress),
+ (gpointer) &proc_frame->progress);
+ }
+
+ if (gimp_plug_in_progress_detach (proc_frame->progress) < 1 &&
+ gimp_progress_is_active (proc_frame->progress))
+ {
+ gimp_progress_end (proc_frame->progress);
+ }
+
+ if (proc_frame->progress_created)
+ {
+ gimp_free_progress (plug_in->manager->gimp, proc_frame->progress);
+ g_clear_object (&proc_frame->progress);
+ }
+ }
+}
+
+void
+gimp_plug_in_progress_set_text (GimpPlugIn *plug_in,
+ const gchar *message)
+{
+ GimpPlugInProcFrame *proc_frame;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+
+ if (proc_frame->progress)
+ gimp_progress_set_text_literal (proc_frame->progress, message);
+}
+
+void
+gimp_plug_in_progress_set_value (GimpPlugIn *plug_in,
+ gdouble percentage)
+{
+ GimpPlugInProcFrame *proc_frame;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+
+ if (! proc_frame->progress ||
+ ! gimp_progress_is_active (proc_frame->progress) ||
+ ! proc_frame->progress_cancel_id)
+ {
+ gimp_plug_in_progress_start (plug_in, NULL, NULL);
+ }
+
+ if (proc_frame->progress && gimp_progress_is_active (proc_frame->progress))
+ gimp_progress_set_value (proc_frame->progress, percentage);
+}
+
+void
+gimp_plug_in_progress_pulse (GimpPlugIn *plug_in)
+{
+ GimpPlugInProcFrame *proc_frame;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+
+ if (! proc_frame->progress ||
+ ! gimp_progress_is_active (proc_frame->progress) ||
+ ! proc_frame->progress_cancel_id)
+ {
+ gimp_plug_in_progress_start (plug_in, NULL, NULL);
+ }
+
+ if (proc_frame->progress && gimp_progress_is_active (proc_frame->progress))
+ gimp_progress_pulse (proc_frame->progress);
+}
+
+guint32
+gimp_plug_in_progress_get_window_id (GimpPlugIn *plug_in)
+{
+ GimpPlugInProcFrame *proc_frame;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), 0);
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+
+ if (proc_frame->progress)
+ return gimp_progress_get_window_id (proc_frame->progress);
+
+ return 0;
+}
+
+gboolean
+gimp_plug_in_progress_install (GimpPlugIn *plug_in,
+ const gchar *progress_callback)
+{
+ GimpPlugInProcFrame *proc_frame;
+ GimpProcedure *procedure;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+ g_return_val_if_fail (progress_callback != NULL, FALSE);
+
+ procedure = gimp_pdb_lookup_procedure (plug_in->manager->gimp->pdb,
+ progress_callback);
+
+ if (! GIMP_IS_TEMPORARY_PROCEDURE (procedure) ||
+ GIMP_TEMPORARY_PROCEDURE (procedure)->plug_in != plug_in ||
+ procedure->num_args != 3 ||
+ ! GIMP_IS_PARAM_SPEC_INT32 (procedure->args[0]) ||
+ ! G_IS_PARAM_SPEC_STRING (procedure->args[1]) ||
+ ! G_IS_PARAM_SPEC_DOUBLE (procedure->args[2]))
+ {
+ return FALSE;
+ }
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+
+ if (proc_frame->progress)
+ {
+ gimp_plug_in_progress_end (plug_in, proc_frame);
+
+ g_clear_object (&proc_frame->progress);
+ }
+
+ proc_frame->progress = g_object_new (GIMP_TYPE_PDB_PROGRESS,
+ "pdb", plug_in->manager->gimp->pdb,
+ "context", proc_frame->main_context,
+ "callback-name", progress_callback,
+ NULL);
+
+ gimp_plug_in_progress_attach (proc_frame->progress);
+
+ return TRUE;
+}
+
+gboolean
+gimp_plug_in_progress_uninstall (GimpPlugIn *plug_in,
+ const gchar *progress_callback)
+{
+ GimpPlugInProcFrame *proc_frame;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+ g_return_val_if_fail (progress_callback != NULL, FALSE);
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+
+ if (GIMP_IS_PDB_PROGRESS (proc_frame->progress))
+ {
+ gimp_plug_in_progress_end (plug_in, proc_frame);
+
+ g_clear_object (&proc_frame->progress);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
+gimp_plug_in_progress_cancel (GimpPlugIn *plug_in,
+ const gchar *progress_callback)
+{
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+ g_return_val_if_fail (progress_callback != NULL, FALSE);
+
+ return FALSE;
+}
+
+
+/* private functions */
+
+static GimpValueArray *
+get_cancel_return_values (GimpProcedure *procedure)
+{
+ GimpValueArray *return_vals;
+ GError *error;
+
+ error = g_error_new_literal (GIMP_PDB_ERROR, GIMP_PDB_ERROR_CANCELLED,
+ _("Cancelled"));
+ return_vals = gimp_procedure_get_return_values (procedure, FALSE, error);
+ g_error_free (error);
+
+ return return_vals;
+}
+
+static void
+gimp_plug_in_progress_cancel_callback (GimpProgress *progress,
+ GimpPlugIn *plug_in)
+{
+ GimpPlugInProcFrame *proc_frame = &plug_in->main_proc_frame;
+ GList *list;
+
+ if (proc_frame->main_loop)
+ {
+ proc_frame->return_vals =
+ get_cancel_return_values (proc_frame->procedure);
+ }
+
+ for (list = plug_in->temp_proc_frames; list; list = g_list_next (list))
+ {
+ proc_frame = list->data;
+
+ if (proc_frame->main_loop)
+ {
+ proc_frame->return_vals =
+ get_cancel_return_values (proc_frame->procedure);
+ }
+ }
+
+ gimp_plug_in_close (plug_in, TRUE);
+}
diff --git a/app/plug-in/gimpplugin-progress.h b/app/plug-in/gimpplugin-progress.h
new file mode 100644
index 0000000..56e5285
--- /dev/null
+++ b/app/plug-in/gimpplugin-progress.h
@@ -0,0 +1,47 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpplugin-progress.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_PROGRESS_H__
+#define __GIMP_PLUG_IN_PROGRESS_H__
+
+
+gint gimp_plug_in_progress_attach (GimpProgress *progress);
+gint gimp_plug_in_progress_detach (GimpProgress *progress);
+
+void gimp_plug_in_progress_start (GimpPlugIn *plug_in,
+ const gchar *message,
+ GimpObject *display);
+void gimp_plug_in_progress_end (GimpPlugIn *plug_in,
+ GimpPlugInProcFrame *proc_frame);
+void gimp_plug_in_progress_set_text (GimpPlugIn *plug_in,
+ const gchar *message);
+void gimp_plug_in_progress_set_value (GimpPlugIn *plug_in,
+ gdouble percentage);
+void gimp_plug_in_progress_pulse (GimpPlugIn *plug_in);
+guint32 gimp_plug_in_progress_get_window_id (GimpPlugIn *plug_in);
+
+gboolean gimp_plug_in_progress_install (GimpPlugIn *plug_in,
+ const gchar *progress_callback);
+gboolean gimp_plug_in_progress_uninstall (GimpPlugIn *plug_in,
+ const gchar *progress_callback);
+gboolean gimp_plug_in_progress_cancel (GimpPlugIn *plug_in,
+ const gchar *progress_callback);
+
+
+#endif /* __GIMP_PLUG_IN_PROGRESS_H__ */
diff --git a/app/plug-in/gimpplugin.c b/app/plug-in/gimpplugin.c
new file mode 100644
index 0000000..fc18486
--- /dev/null
+++ b/app/plug-in/gimpplugin.c
@@ -0,0 +1,1060 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpplugin.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#ifndef _WIN32
+#define _GNU_SOURCE
+#endif
+
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
+
+#define STRICT
+#include <windows.h>
+#include <process.h>
+
+#ifdef G_OS_WIN32
+#include <fcntl.h>
+#include <io.h>
+
+#ifndef pipe
+#define pipe(fds) _pipe(fds, 4096, _O_BINARY)
+#endif
+
+#endif
+
+#ifdef G_WITH_CYGWIN
+#define O_TEXT 0x0100 /* text file */
+#define _O_TEXT 0x0100 /* text file */
+#define O_BINARY 0x0200 /* binary file */
+#define _O_BINARY 0x0200 /* binary file */
+#endif
+
+#endif /* G_OS_WIN32 || G_WITH_CYGWIN */
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpbase/gimpprotocol.h"
+#include "libgimpbase/gimpwire.h"
+
+#include "plug-in-types.h"
+
+#include "core/gimp.h"
+#include "core/gimp-spawn.h"
+#include "core/gimpprogress.h"
+
+#include "pdb/gimppdbcontext.h"
+
+#include "gimpenvirontable.h"
+#include "gimpinterpreterdb.h"
+#include "gimpplugin.h"
+#include "gimpplugin-message.h"
+#include "gimpplugin-progress.h"
+#include "gimpplugindebug.h"
+#include "gimpplugindef.h"
+#include "gimppluginmanager.h"
+#include "gimppluginmanager-help-domain.h"
+#include "gimppluginmanager-locale-domain.h"
+#include "gimptemporaryprocedure.h"
+#include "plug-in-params.h"
+
+#include "gimp-intl.h"
+
+
+static void gimp_plug_in_finalize (GObject *object);
+
+static gboolean gimp_plug_in_recv_message (GIOChannel *channel,
+ GIOCondition cond,
+ gpointer data);
+static gboolean gimp_plug_in_write (GIOChannel *channel,
+ const guint8 *buf,
+ gulong count,
+ gpointer data);
+static gboolean gimp_plug_in_flush (GIOChannel *channel,
+ gpointer data);
+
+#if defined G_OS_WIN32 && defined WIN32_32BIT_DLL_FOLDER
+static void gimp_plug_in_set_dll_directory (const gchar *path);
+#endif
+
+
+
+G_DEFINE_TYPE (GimpPlugIn, gimp_plug_in, GIMP_TYPE_OBJECT)
+
+#define parent_class gimp_plug_in_parent_class
+
+
+static void
+gimp_plug_in_class_init (GimpPlugInClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gimp_plug_in_finalize;
+
+ /* initialize the gimp protocol library and set the read and
+ * write handlers.
+ */
+ gp_init ();
+ gimp_wire_set_writer (gimp_plug_in_write);
+ gimp_wire_set_flusher (gimp_plug_in_flush);
+}
+
+static void
+gimp_plug_in_init (GimpPlugIn *plug_in)
+{
+ plug_in->manager = NULL;
+ plug_in->file = NULL;
+
+ plug_in->call_mode = GIMP_PLUG_IN_CALL_NONE;
+ plug_in->open = FALSE;
+ plug_in->hup = FALSE;
+ plug_in->pid = 0;
+
+ plug_in->my_read = NULL;
+ plug_in->my_write = NULL;
+ plug_in->his_read = NULL;
+ plug_in->his_write = NULL;
+
+ plug_in->input_id = 0;
+ plug_in->write_buffer_index = 0;
+
+ plug_in->temp_procedures = NULL;
+
+ plug_in->ext_main_loop = NULL;
+
+ plug_in->temp_proc_frames = NULL;
+
+ plug_in->plug_in_def = NULL;
+}
+
+static void
+gimp_plug_in_finalize (GObject *object)
+{
+ GimpPlugIn *plug_in = GIMP_PLUG_IN (object);
+
+ g_clear_object (&plug_in->file);
+
+ gimp_plug_in_proc_frame_dispose (&plug_in->main_proc_frame, plug_in);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+gimp_plug_in_recv_message (GIOChannel *channel,
+ GIOCondition cond,
+ gpointer data)
+{
+ GimpPlugIn *plug_in = data;
+ gboolean got_message = FALSE;
+
+#ifdef G_OS_WIN32
+ /* Workaround for GLib bug #137968: sometimes we are called for no
+ * reason...
+ */
+ if (cond == 0)
+ return TRUE;
+#endif
+
+ if (plug_in->my_read == NULL)
+ return TRUE;
+
+ g_object_ref (plug_in);
+
+ if (cond & (G_IO_IN | G_IO_PRI))
+ {
+ GimpWireMessage msg;
+
+ memset (&msg, 0, sizeof (GimpWireMessage));
+
+ if (! gimp_wire_read_msg (plug_in->my_read, &msg, plug_in))
+ {
+ gimp_plug_in_close (plug_in, TRUE);
+ }
+ else
+ {
+ gimp_plug_in_handle_message (plug_in, &msg);
+ gimp_wire_destroy (&msg);
+ got_message = TRUE;
+ }
+ }
+
+ if (cond & (G_IO_ERR | G_IO_HUP))
+ {
+ if (cond & G_IO_HUP)
+ plug_in->hup = TRUE;
+
+ if (plug_in->open)
+ gimp_plug_in_close (plug_in, TRUE);
+ }
+
+ if (! got_message)
+ {
+ GimpPlugInProcFrame *frame = gimp_plug_in_get_proc_frame (plug_in);
+ GimpProgress *progress = frame ? frame->progress : NULL;
+
+ gimp_message (plug_in->manager->gimp, G_OBJECT (progress),
+ GIMP_MESSAGE_ERROR,
+ _("Plug-in crashed: \"%s\"\n(%s)\n\n"
+ "The dying plug-in may have messed up GIMP's internal "
+ "state. You may want to save your images and restart "
+ "GIMP to be on the safe side."),
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file));
+ }
+
+ g_object_unref (plug_in);
+
+ return TRUE;
+}
+
+static gboolean
+gimp_plug_in_write (GIOChannel *channel,
+ const guint8 *buf,
+ gulong count,
+ gpointer data)
+{
+ GimpPlugIn *plug_in = data;
+ gulong bytes;
+
+ while (count > 0)
+ {
+ if ((plug_in->write_buffer_index + count) >= WRITE_BUFFER_SIZE)
+ {
+ bytes = WRITE_BUFFER_SIZE - plug_in->write_buffer_index;
+ memcpy (&plug_in->write_buffer[plug_in->write_buffer_index],
+ buf, bytes);
+ plug_in->write_buffer_index += bytes;
+ if (! gimp_wire_flush (channel, plug_in))
+ return FALSE;
+ }
+ else
+ {
+ bytes = count;
+ memcpy (&plug_in->write_buffer[plug_in->write_buffer_index],
+ buf, bytes);
+ plug_in->write_buffer_index += bytes;
+ }
+
+ buf += bytes;
+ count -= bytes;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gimp_plug_in_flush (GIOChannel *channel,
+ gpointer data)
+{
+ GimpPlugIn *plug_in = data;
+
+ if (plug_in->write_buffer_index > 0)
+ {
+ GIOStatus status;
+ GError *error = NULL;
+ gint count;
+ gsize bytes;
+
+ count = 0;
+ while (count != plug_in->write_buffer_index)
+ {
+ do
+ {
+ bytes = 0;
+ status = g_io_channel_write_chars (channel,
+ &plug_in->write_buffer[count],
+ (plug_in->write_buffer_index - count),
+ &bytes,
+ &error);
+ }
+ while (status == G_IO_STATUS_AGAIN);
+
+ if (status != G_IO_STATUS_NORMAL)
+ {
+ if (error)
+ {
+ g_warning ("%s: plug_in_flush(): error: %s",
+ gimp_filename_to_utf8 (g_get_prgname ()),
+ error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ g_warning ("%s: plug_in_flush(): error",
+ gimp_filename_to_utf8 (g_get_prgname ()));
+ }
+
+ return FALSE;
+ }
+
+ count += bytes;
+ }
+
+ plug_in->write_buffer_index = 0;
+ }
+
+ return TRUE;
+}
+
+#if defined G_OS_WIN32 && defined WIN32_32BIT_DLL_FOLDER
+static void
+gimp_plug_in_set_dll_directory (const gchar *path)
+{
+ const gchar *install_dir;
+ gchar *bin_dir;
+ LPWSTR w_bin_dir;
+ DWORD BinaryType;
+ int n;
+
+ w_bin_dir = NULL;
+ install_dir = gimp_installation_directory ();
+ if (path &&
+ GetBinaryTypeA (path, &BinaryType) &&
+ BinaryType == SCS_32BIT_BINARY)
+ bin_dir = g_build_filename (install_dir, WIN32_32BIT_DLL_FOLDER, NULL);
+ else
+ bin_dir = g_build_filename (install_dir, "bin", NULL);
+
+ n = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS,
+ bin_dir, -1, NULL, 0);
+ if (n == 0)
+ goto out;
+
+ w_bin_dir = g_malloc_n (n + 1, sizeof (wchar_t));
+ n = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS,
+ bin_dir, -1,
+ w_bin_dir, (n + 1) * sizeof (wchar_t));
+ if (n == 0)
+ goto out;
+
+ SetDllDirectoryW (w_bin_dir);
+
+out:
+ if (w_bin_dir)
+ g_free ((void*) w_bin_dir);
+ g_free (bin_dir);
+}
+#endif
+
+
+/* public functions */
+
+GimpPlugIn *
+gimp_plug_in_new (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpPlugInProcedure *procedure,
+ GFile *file)
+{
+ GimpPlugIn *plug_in;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), NULL);
+ g_return_val_if_fail (GIMP_IS_PDB_CONTEXT (context), NULL);
+ g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
+ g_return_val_if_fail (procedure == NULL ||
+ GIMP_IS_PLUG_IN_PROCEDURE (procedure), NULL);
+ g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL);
+ g_return_val_if_fail ((procedure != NULL || file != NULL) &&
+ ! (procedure != NULL && file != NULL), NULL);
+
+ plug_in = g_object_new (GIMP_TYPE_PLUG_IN, NULL);
+
+ if (! file)
+ file = gimp_plug_in_procedure_get_file (procedure);
+
+ gimp_object_take_name (GIMP_OBJECT (plug_in),
+ g_path_get_basename (gimp_file_get_utf8_name (file)));
+
+ plug_in->manager = manager;
+ plug_in->file = g_object_ref (file);
+
+ gimp_plug_in_proc_frame_init (&plug_in->main_proc_frame,
+ context, progress, procedure);
+
+ return plug_in;
+}
+
+gboolean
+gimp_plug_in_open (GimpPlugIn *plug_in,
+ GimpPlugInCallMode call_mode,
+ gboolean synchronous)
+{
+ gchar *progname;
+ gint my_read[2];
+ gint my_write[2];
+ gchar **envp;
+ const gchar *args[9];
+ gchar **argv;
+ gint argc;
+ gchar *interp, *interp_arg;
+ gchar *his_read_fd, *his_write_fd;
+ const gchar *mode;
+ gchar *stm;
+ GError *error = NULL;
+ gboolean debug;
+ guint debug_flag;
+ guint spawn_flags;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+ g_return_val_if_fail (plug_in->call_mode == GIMP_PLUG_IN_CALL_NONE, FALSE);
+
+ /* Open two pipes. (Bidirectional communication).
+ */
+ if ((pipe (my_read) == -1) || (pipe (my_write) == -1))
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Unable to run plug-in \"%s\"\n(%s)\n\npipe() failed: %s",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ g_strerror (errno));
+ return FALSE;
+ }
+
+#if defined(G_WITH_CYGWIN)
+ /* Set to binary mode */
+ setmode (my_read[0], _O_BINARY);
+ setmode (my_write[0], _O_BINARY);
+ setmode (my_read[1], _O_BINARY);
+ setmode (my_write[1], _O_BINARY);
+#endif
+
+ /* Prevent the plug-in from inheriting our end of the pipes */
+ gimp_spawn_set_cloexec (my_read[0]);
+ gimp_spawn_set_cloexec (my_write[1]);
+
+#ifdef G_OS_WIN32
+ plug_in->my_read = g_io_channel_win32_new_fd (my_read[0]);
+ plug_in->my_write = g_io_channel_win32_new_fd (my_write[1]);
+ plug_in->his_read = g_io_channel_win32_new_fd (my_write[0]);
+ plug_in->his_write = g_io_channel_win32_new_fd (my_read[1]);
+#else
+ plug_in->my_read = g_io_channel_unix_new (my_read[0]);
+ plug_in->my_write = g_io_channel_unix_new (my_write[1]);
+ plug_in->his_read = g_io_channel_unix_new (my_write[0]);
+ plug_in->his_write = g_io_channel_unix_new (my_read[1]);
+#endif
+
+ g_io_channel_set_encoding (plug_in->my_read, NULL, NULL);
+ g_io_channel_set_encoding (plug_in->my_write, NULL, NULL);
+ g_io_channel_set_encoding (plug_in->his_read, NULL, NULL);
+ g_io_channel_set_encoding (plug_in->his_write, NULL, NULL);
+
+ g_io_channel_set_buffered (plug_in->my_read, FALSE);
+ g_io_channel_set_buffered (plug_in->my_write, FALSE);
+ g_io_channel_set_buffered (plug_in->his_read, FALSE);
+ g_io_channel_set_buffered (plug_in->his_write, FALSE);
+
+ g_io_channel_set_close_on_unref (plug_in->my_read, TRUE);
+ g_io_channel_set_close_on_unref (plug_in->my_write, TRUE);
+ g_io_channel_set_close_on_unref (plug_in->his_read, TRUE);
+ g_io_channel_set_close_on_unref (plug_in->his_write, TRUE);
+
+ /* Remember the file descriptors for the pipes.
+ */
+ his_read_fd = g_strdup_printf ("%d",
+ g_io_channel_unix_get_fd (plug_in->his_read));
+ his_write_fd = g_strdup_printf ("%d",
+ g_io_channel_unix_get_fd (plug_in->his_write));
+
+ switch (call_mode)
+ {
+ case GIMP_PLUG_IN_CALL_QUERY:
+ mode = "-query";
+ debug_flag = GIMP_DEBUG_WRAP_QUERY;
+ break;
+
+ case GIMP_PLUG_IN_CALL_INIT:
+ mode = "-init";
+ debug_flag = GIMP_DEBUG_WRAP_INIT;
+ break;
+
+ case GIMP_PLUG_IN_CALL_RUN:
+ mode = "-run";
+ debug_flag = GIMP_DEBUG_WRAP_RUN;
+ break;
+
+ default:
+ gimp_assert_not_reached ();
+ }
+
+ stm = g_strdup_printf ("%d", plug_in->manager->gimp->stack_trace_mode);
+
+ progname = g_file_get_path (plug_in->file);
+
+ interp = gimp_interpreter_db_resolve (plug_in->manager->interpreter_db,
+ progname, &interp_arg);
+
+ argc = 0;
+
+ if (interp)
+ args[argc++] = interp;
+
+ if (interp_arg)
+ args[argc++] = interp_arg;
+
+ args[argc++] = progname;
+ args[argc++] = "-gimp";
+ args[argc++] = his_read_fd;
+ args[argc++] = his_write_fd;
+ args[argc++] = mode;
+ args[argc++] = stm;
+ args[argc++] = NULL;
+
+ argv = (gchar **) args;
+ envp = gimp_environ_table_get_envp (plug_in->manager->environ_table);
+ spawn_flags = (G_SPAWN_LEAVE_DESCRIPTORS_OPEN |
+ G_SPAWN_DO_NOT_REAP_CHILD |
+ G_SPAWN_CHILD_INHERITS_STDIN);
+
+ debug = FALSE;
+
+ if (plug_in->manager->debug)
+ {
+ gchar **debug_argv = gimp_plug_in_debug_argv (plug_in->manager->debug,
+ progname,
+ debug_flag, args);
+
+ if (debug_argv)
+ {
+ debug = TRUE;
+ argv = debug_argv;
+ spawn_flags |= G_SPAWN_SEARCH_PATH;
+ }
+ }
+
+ /* Fork another process. We'll remember the process id so that we
+ * can later use it to kill the filter if necessary.
+ */
+#if defined G_OS_WIN32 && defined WIN32_32BIT_DLL_FOLDER
+ gimp_plug_in_set_dll_directory (argv[0]);
+#endif
+ if (! gimp_spawn_async (argv, envp, spawn_flags, &plug_in->pid, &error))
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Unable to run plug-in \"%s\"\n(%s)\n\n%s",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ error->message);
+ g_clear_error (&error);
+ goto cleanup;
+ }
+
+ g_clear_pointer (&plug_in->his_read, g_io_channel_unref);
+ g_clear_pointer (&plug_in->his_write, g_io_channel_unref);
+
+ if (! synchronous)
+ {
+ GSource *source;
+
+ source = g_io_create_watch (plug_in->my_read,
+ G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP);
+
+ g_source_set_callback (source,
+ (GSourceFunc) gimp_plug_in_recv_message, plug_in,
+ NULL);
+
+ g_source_set_can_recurse (source, TRUE);
+
+ plug_in->input_id = g_source_attach (source, NULL);
+ g_source_unref (source);
+ }
+
+ plug_in->open = TRUE;
+ plug_in->call_mode = call_mode;
+
+ gimp_plug_in_manager_add_open_plug_in (plug_in->manager, plug_in);
+
+ cleanup:
+
+#if defined G_OS_WIN32 && defined WIN32_32BIT_DLL_FOLDER
+ gimp_plug_in_set_dll_directory (NULL);
+#endif
+
+ if (debug)
+ g_free (argv);
+
+ g_free (his_read_fd);
+ g_free (his_write_fd);
+ g_free (stm);
+ g_free (interp);
+ g_free (interp_arg);
+ g_free (progname);
+
+ return plug_in->open;
+}
+
+void
+gimp_plug_in_close (GimpPlugIn *plug_in,
+ gboolean kill_it)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+ g_return_if_fail (plug_in->open);
+
+ plug_in->open = FALSE;
+
+ if (plug_in->pid)
+ {
+#ifndef G_OS_WIN32
+ gint status;
+#endif
+
+ /* Ask the filter to exit gracefully,
+ but not if it is closed because of a broken pipe. */
+ if (kill_it && ! plug_in->hup)
+ {
+ gp_quit_write (plug_in->my_write, plug_in);
+
+ /* give the plug-in some time (10 ms) */
+ g_usleep (10000);
+ }
+
+ /* If necessary, kill the filter. */
+
+#ifndef G_OS_WIN32
+
+ if (kill_it)
+ {
+ if (plug_in->manager->gimp->be_verbose)
+ g_print ("Terminating plug-in: '%s'\n",
+ gimp_file_get_utf8_name (plug_in->file));
+
+ /* If the plug-in opened a process group, kill the group instead
+ * of only the plug-in, so we kill the plug-in's children too
+ */
+ if (getpgid (0) != getpgid (plug_in->pid))
+ status = kill (- plug_in->pid, SIGKILL);
+ else
+ status = kill (plug_in->pid, SIGKILL);
+ }
+
+ /* Wait for the process to exit. This will happen
+ * immediately if it was just killed.
+ */
+ waitpid (plug_in->pid, &status, 0);
+
+#else /* G_OS_WIN32 */
+
+ if (kill_it)
+ {
+ /* Trying to avoid TerminateProcess (does mostly work).
+ * Otherwise some of our needed DLLs may get into an
+ * unstable state (see Win32 API docs).
+ */
+ DWORD dwExitCode = STILL_ACTIVE;
+ DWORD dwTries = 10;
+
+ while (dwExitCode == STILL_ACTIVE &&
+ GetExitCodeProcess ((HANDLE) plug_in->pid, &dwExitCode) &&
+ (dwTries > 0))
+ {
+ Sleep (10);
+ dwTries--;
+ }
+
+ if (dwExitCode == STILL_ACTIVE)
+ {
+ if (plug_in->manager->gimp->be_verbose)
+ g_print ("Terminating plug-in: '%s'\n",
+ gimp_file_get_utf8_name (plug_in->file));
+
+ TerminateProcess ((HANDLE) plug_in->pid, 0);
+ }
+ }
+
+#endif /* G_OS_WIN32 */
+
+ g_spawn_close_pid (plug_in->pid);
+ plug_in->pid = 0;
+ }
+
+ /* Remove the input handler. */
+ if (plug_in->input_id)
+ {
+ g_source_remove (plug_in->input_id);
+ plug_in->input_id = 0;
+ }
+
+ /* Close the pipes. */
+ g_clear_pointer (&plug_in->my_read, g_io_channel_unref);
+ g_clear_pointer (&plug_in->my_write, g_io_channel_unref);
+ g_clear_pointer (&plug_in->his_read, g_io_channel_unref);
+ g_clear_pointer (&plug_in->his_write, g_io_channel_unref);
+
+ gimp_wire_clear_error ();
+
+ while (plug_in->temp_proc_frames)
+ {
+ GimpPlugInProcFrame *proc_frame = plug_in->temp_proc_frames->data;
+
+#ifdef GIMP_UNSTABLE
+ g_printerr ("plug-in '%s' aborted before sending its "
+ "temporary procedure return values\n",
+ gimp_object_get_name (plug_in));
+#endif
+
+ if (proc_frame->main_loop &&
+ g_main_loop_is_running (proc_frame->main_loop))
+ {
+ g_main_loop_quit (proc_frame->main_loop);
+ }
+
+ /* pop the frame here, because normally this only happens in
+ * gimp_plug_in_handle_temp_proc_return(), which can't
+ * be called after plug_in_close()
+ */
+ gimp_plug_in_proc_frame_pop (plug_in);
+ }
+
+ if (plug_in->main_proc_frame.main_loop &&
+ g_main_loop_is_running (plug_in->main_proc_frame.main_loop))
+ {
+#ifdef GIMP_UNSTABLE
+ g_printerr ("plug-in '%s' aborted before sending its "
+ "procedure return values\n",
+ gimp_object_get_name (plug_in));
+#endif
+
+ g_main_loop_quit (plug_in->main_proc_frame.main_loop);
+ }
+
+ if (plug_in->ext_main_loop &&
+ g_main_loop_is_running (plug_in->ext_main_loop))
+ {
+#ifdef GIMP_UNSTABLE
+ g_printerr ("extension '%s' aborted before sending its "
+ "extension_ack message\n",
+ gimp_object_get_name (plug_in));
+#endif
+
+ g_main_loop_quit (plug_in->ext_main_loop);
+ }
+
+ /* Unregister any temporary procedures. */
+ while (plug_in->temp_procedures)
+ gimp_plug_in_remove_temp_proc (plug_in, plug_in->temp_procedures->data);
+
+ gimp_plug_in_manager_remove_open_plug_in (plug_in->manager, plug_in);
+}
+
+GimpPlugInProcFrame *
+gimp_plug_in_get_proc_frame (GimpPlugIn *plug_in)
+{
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), NULL);
+
+ if (plug_in->temp_proc_frames)
+ return plug_in->temp_proc_frames->data;
+ else
+ return &plug_in->main_proc_frame;
+}
+
+GimpPlugInProcFrame *
+gimp_plug_in_proc_frame_push (GimpPlugIn *plug_in,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpTemporaryProcedure *procedure)
+{
+ GimpPlugInProcFrame *proc_frame;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), NULL);
+ g_return_val_if_fail (GIMP_IS_PDB_CONTEXT (context), NULL);
+ g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
+ g_return_val_if_fail (GIMP_IS_TEMPORARY_PROCEDURE (procedure), NULL);
+
+ proc_frame = gimp_plug_in_proc_frame_new (context, progress,
+ GIMP_PLUG_IN_PROCEDURE (procedure));
+
+ plug_in->temp_proc_frames = g_list_prepend (plug_in->temp_proc_frames,
+ proc_frame);
+
+ return proc_frame;
+}
+
+void
+gimp_plug_in_proc_frame_pop (GimpPlugIn *plug_in)
+{
+ GimpPlugInProcFrame *proc_frame;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+ g_return_if_fail (plug_in->temp_proc_frames != NULL);
+
+ proc_frame = (GimpPlugInProcFrame *) plug_in->temp_proc_frames->data;
+
+ gimp_plug_in_proc_frame_unref (proc_frame, plug_in);
+
+ plug_in->temp_proc_frames = g_list_remove (plug_in->temp_proc_frames,
+ proc_frame);
+}
+
+void
+gimp_plug_in_main_loop (GimpPlugIn *plug_in)
+{
+ GimpPlugInProcFrame *proc_frame;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+ g_return_if_fail (plug_in->temp_proc_frames != NULL);
+
+ proc_frame = (GimpPlugInProcFrame *) plug_in->temp_proc_frames->data;
+
+ g_return_if_fail (proc_frame->main_loop == NULL);
+
+ proc_frame->main_loop = g_main_loop_new (NULL, FALSE);
+
+ gimp_threads_leave (plug_in->manager->gimp);
+ g_main_loop_run (proc_frame->main_loop);
+ gimp_threads_enter (plug_in->manager->gimp);
+
+ g_clear_pointer (&proc_frame->main_loop, g_main_loop_unref);
+}
+
+void
+gimp_plug_in_main_loop_quit (GimpPlugIn *plug_in)
+{
+ GimpPlugInProcFrame *proc_frame;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+ g_return_if_fail (plug_in->temp_proc_frames != NULL);
+
+ proc_frame = (GimpPlugInProcFrame *) plug_in->temp_proc_frames->data;
+
+ g_return_if_fail (proc_frame->main_loop != NULL);
+
+ g_main_loop_quit (proc_frame->main_loop);
+}
+
+const gchar *
+gimp_plug_in_get_undo_desc (GimpPlugIn *plug_in)
+{
+ GimpPlugInProcFrame *proc_frame;
+ const gchar *undo_desc = NULL;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), NULL);
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+
+ if (proc_frame && proc_frame->procedure)
+ undo_desc = gimp_procedure_get_label (proc_frame->procedure);
+
+ return undo_desc ? undo_desc : gimp_object_get_name (plug_in);
+}
+
+/* called from the PDB (gimp_plugin_menu_register) */
+gboolean
+gimp_plug_in_menu_register (GimpPlugIn *plug_in,
+ const gchar *proc_name,
+ const gchar *menu_path)
+{
+ GimpPlugInProcedure *proc = NULL;
+ GError *error = NULL;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+ g_return_val_if_fail (proc_name != NULL, FALSE);
+ g_return_val_if_fail (menu_path != NULL, FALSE);
+
+ if (plug_in->plug_in_def)
+ proc = gimp_plug_in_procedure_find (plug_in->plug_in_def->procedures,
+ proc_name);
+
+ if (! proc)
+ proc = gimp_plug_in_procedure_find (plug_in->temp_procedures, proc_name);
+
+ if (! proc)
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n"
+ "attempted to register the menu item \"%s\" "
+ "for the procedure \"%s\".\n"
+ "It has however not installed that procedure. This "
+ "is not allowed.",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ menu_path, proc_name);
+
+ return FALSE;
+ }
+
+ switch (GIMP_PROCEDURE (proc)->proc_type)
+ {
+ case GIMP_INTERNAL:
+ return FALSE;
+
+ case GIMP_PLUGIN:
+ case GIMP_EXTENSION:
+ if (plug_in->call_mode != GIMP_PLUG_IN_CALL_QUERY &&
+ plug_in->call_mode != GIMP_PLUG_IN_CALL_INIT)
+ return FALSE;
+
+ case GIMP_TEMPORARY:
+ break;
+ }
+
+ if (! proc->menu_label)
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n"
+ "attempted to register the menu item \"%s\" "
+ "for procedure \"%s\".\n"
+ "The menu label given in gimp_install_procedure() "
+ "already contained a path. To make this work, "
+ "pass just the menu's label to "
+ "gimp_install_procedure().",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ menu_path, proc_name);
+
+ return FALSE;
+ }
+
+ if (! strlen (proc->menu_label))
+ {
+ gimp_message (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "Plug-in \"%s\"\n(%s)\n"
+ "attempted to register the procedure \"%s\" "
+ "in the menu \"%s\", but the procedure has no label. "
+ "This is not allowed.",
+ gimp_object_get_name (plug_in),
+ gimp_file_get_utf8_name (plug_in->file),
+ proc_name, menu_path);
+
+ return FALSE;
+ }
+
+ if (! gimp_plug_in_procedure_add_menu_path (proc, menu_path, &error))
+ {
+ gimp_message_literal (plug_in->manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ error->message);
+ g_clear_error (&error);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void
+gimp_plug_in_add_temp_proc (GimpPlugIn *plug_in,
+ GimpTemporaryProcedure *proc)
+{
+ GimpPlugInProcedure *overridden;
+ const gchar *locale_domain;
+ const gchar *help_domain;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+ g_return_if_fail (GIMP_IS_TEMPORARY_PROCEDURE (proc));
+
+ overridden = gimp_plug_in_procedure_find (plug_in->temp_procedures,
+ gimp_object_get_name (proc));
+
+ if (overridden)
+ gimp_plug_in_remove_temp_proc (plug_in,
+ GIMP_TEMPORARY_PROCEDURE (overridden));
+
+ locale_domain = gimp_plug_in_manager_get_locale_domain (plug_in->manager,
+ plug_in->file,
+ NULL);
+ help_domain = gimp_plug_in_manager_get_help_domain (plug_in->manager,
+ plug_in->file,
+ NULL);
+
+ gimp_plug_in_procedure_set_locale_domain (GIMP_PLUG_IN_PROCEDURE (proc),
+ locale_domain);
+ gimp_plug_in_procedure_set_help_domain (GIMP_PLUG_IN_PROCEDURE (proc),
+ help_domain);
+
+ plug_in->temp_procedures = g_slist_prepend (plug_in->temp_procedures,
+ g_object_ref (proc));
+ gimp_plug_in_manager_add_temp_proc (plug_in->manager, proc);
+}
+
+void
+gimp_plug_in_remove_temp_proc (GimpPlugIn *plug_in,
+ GimpTemporaryProcedure *proc)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+ g_return_if_fail (GIMP_IS_TEMPORARY_PROCEDURE (proc));
+
+ plug_in->temp_procedures = g_slist_remove (plug_in->temp_procedures, proc);
+
+ gimp_plug_in_manager_remove_temp_proc (plug_in->manager, proc);
+ g_object_unref (proc);
+}
+
+void
+gimp_plug_in_set_error_handler (GimpPlugIn *plug_in,
+ GimpPDBErrorHandler handler)
+{
+ GimpPlugInProcFrame *proc_frame;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+
+ if (proc_frame)
+ proc_frame->error_handler = handler;
+}
+
+GimpPDBErrorHandler
+gimp_plug_in_get_error_handler (GimpPlugIn *plug_in)
+{
+ GimpPlugInProcFrame *proc_frame;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in),
+ GIMP_PDB_ERROR_HANDLER_INTERNAL);
+
+ proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+
+ if (proc_frame)
+ return proc_frame->error_handler;
+
+ return GIMP_PDB_ERROR_HANDLER_INTERNAL;
+}
+
+void
+gimp_plug_in_enable_precision (GimpPlugIn *plug_in)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+
+ plug_in->precision = TRUE;
+}
+
+gboolean
+gimp_plug_in_precision_enabled (GimpPlugIn *plug_in)
+{
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
+
+ return plug_in->precision;
+}
diff --git a/app/plug-in/gimpplugin.h b/app/plug-in/gimpplugin.h
new file mode 100644
index 0000000..15326a0
--- /dev/null
+++ b/app/plug-in/gimpplugin.h
@@ -0,0 +1,127 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpplugin.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_H__
+#define __GIMP_PLUG_IN_H__
+
+
+#include "core/gimpobject.h"
+#include "gimppluginprocframe.h"
+
+
+#define WRITE_BUFFER_SIZE 512
+
+
+#define GIMP_TYPE_PLUG_IN (gimp_plug_in_get_type ())
+#define GIMP_PLUG_IN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PLUG_IN, GimpPlugIn))
+#define GIMP_PLUG_IN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PLUG_IN, GimpPlugInClass))
+#define GIMP_IS_PLUG_IN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PLUG_IN))
+#define GIMP_IS_PLUG_IN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PLUG_IN))
+
+
+typedef struct _GimpPlugInClass GimpPlugInClass;
+
+struct _GimpPlugIn
+{
+ GimpObject parent_instance;
+
+ GimpPlugInManager *manager;
+ GFile *file; /* Plug-in's full path name */
+
+ GimpPlugInCallMode call_mode; /* QUERY, INIT or RUN */
+ guint open : 1; /* Is the plug-in open? */
+ guint hup : 1; /* Did we receive a G_IO_HUP */
+ guint precision : 1; /* True drawable precision enabled */
+ GPid pid; /* Plug-in's process id */
+
+ GIOChannel *my_read; /* App's read and write channels */
+ GIOChannel *my_write;
+ GIOChannel *his_read; /* Plug-in's read and write channels */
+ GIOChannel *his_write;
+
+ guint input_id; /* Id of input proc */
+
+ gchar write_buffer[WRITE_BUFFER_SIZE]; /* Buffer for writing */
+ gint write_buffer_index; /* Buffer index */
+
+ GSList *temp_procedures; /* Temporary procedures */
+
+ GMainLoop *ext_main_loop; /* for waiting for extension_ack */
+
+ GimpPlugInProcFrame main_proc_frame;
+
+ GList *temp_proc_frames;
+
+ GimpPlugInDef *plug_in_def; /* Valid during query() and init() */
+};
+
+struct _GimpPlugInClass
+{
+ GimpObjectClass parent_class;
+};
+
+
+GType gimp_plug_in_get_type (void) G_GNUC_CONST;
+
+GimpPlugIn * gimp_plug_in_new (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpPlugInProcedure *procedure,
+ GFile *file);
+
+gboolean gimp_plug_in_open (GimpPlugIn *plug_in,
+ GimpPlugInCallMode call_mode,
+ gboolean synchronous);
+void gimp_plug_in_close (GimpPlugIn *plug_in,
+ gboolean kill_it);
+
+GimpPlugInProcFrame *
+ gimp_plug_in_get_proc_frame (GimpPlugIn *plug_in);
+
+GimpPlugInProcFrame *
+ gimp_plug_in_proc_frame_push (GimpPlugIn *plug_in,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpTemporaryProcedure *procedure);
+void gimp_plug_in_proc_frame_pop (GimpPlugIn *plug_in);
+
+void gimp_plug_in_main_loop (GimpPlugIn *plug_in);
+void gimp_plug_in_main_loop_quit (GimpPlugIn *plug_in);
+
+const gchar * gimp_plug_in_get_undo_desc (GimpPlugIn *plug_in);
+
+gboolean gimp_plug_in_menu_register (GimpPlugIn *plug_in,
+ const gchar *proc_name,
+ const gchar *menu_path);
+
+void gimp_plug_in_add_temp_proc (GimpPlugIn *plug_in,
+ GimpTemporaryProcedure *procedure);
+void gimp_plug_in_remove_temp_proc (GimpPlugIn *plug_in,
+ GimpTemporaryProcedure *procedure);
+
+void gimp_plug_in_set_error_handler (GimpPlugIn *plug_in,
+ GimpPDBErrorHandler handler);
+GimpPDBErrorHandler
+ gimp_plug_in_get_error_handler (GimpPlugIn *plug_in);
+
+void gimp_plug_in_enable_precision (GimpPlugIn *plug_in);
+gboolean gimp_plug_in_precision_enabled (GimpPlugIn *plug_in);
+
+
+#endif /* __GIMP_PLUG_IN_H__ */
diff --git a/app/plug-in/gimpplugindebug.c b/app/plug-in/gimpplugindebug.c
new file mode 100644
index 0000000..81c1060
--- /dev/null
+++ b/app/plug-in/gimpplugindebug.c
@@ -0,0 +1,142 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpplugindebug.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib-object.h>
+
+#include "plug-in-types.h"
+
+#include "gimpplugindebug.h"
+
+
+struct _GimpPlugInDebug
+{
+ gchar *name;
+ guint flags;
+ gchar **args;
+};
+
+
+static const GDebugKey gimp_debug_wrap_keys[] =
+{
+ { "query", GIMP_DEBUG_WRAP_QUERY },
+ { "init", GIMP_DEBUG_WRAP_INIT },
+ { "run", GIMP_DEBUG_WRAP_RUN },
+ { "on", GIMP_DEBUG_WRAP_DEFAULT }
+};
+
+
+GimpPlugInDebug *
+gimp_plug_in_debug_new (void)
+{
+ GimpPlugInDebug *debug;
+ const gchar *wrap, *wrapper;
+ gchar *debug_string;
+ gchar **args;
+ GError *error = NULL;
+
+ wrap = g_getenv ("GIMP_PLUGIN_DEBUG_WRAP");
+ wrapper = g_getenv ("GIMP_PLUGIN_DEBUG_WRAPPER");
+
+ if (!(wrap && wrapper))
+ return NULL;
+
+ if (!g_shell_parse_argv (wrapper, NULL, &args, &error))
+ {
+ g_warning ("Unable to parse debug wrapper: \"%s\"\n%s",
+ wrapper, error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ debug = g_slice_new (GimpPlugInDebug);
+
+ debug->args = args;
+
+ debug_string = strchr (wrap, ',');
+
+ if (debug_string)
+ {
+ debug->name = g_strndup (wrap, debug_string - wrap);
+ debug->flags = g_parse_debug_string (debug_string + 1,
+ gimp_debug_wrap_keys,
+ G_N_ELEMENTS (gimp_debug_wrap_keys));
+ }
+ else
+ {
+ debug->name = g_strdup (wrap);
+ debug->flags = GIMP_DEBUG_WRAP_DEFAULT;
+ }
+
+ return debug;
+}
+
+void
+gimp_plug_in_debug_free (GimpPlugInDebug *debug)
+{
+ g_return_if_fail (debug != NULL);
+
+ if (debug->name)
+ g_free (debug->name);
+
+ if (debug->args)
+ g_strfreev (debug->args);
+
+ g_slice_free (GimpPlugInDebug, debug);
+}
+
+gchar **
+gimp_plug_in_debug_argv (GimpPlugInDebug *debug,
+ const gchar *name,
+ GimpDebugWrapFlag flag,
+ const gchar **args)
+{
+ GPtrArray *argv;
+ gchar **arg;
+ gchar *basename;
+
+ g_return_val_if_fail (debug != NULL, NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (args != NULL, NULL);
+
+ basename = g_path_get_basename (name);
+
+ if (!(debug->flags & flag) || (strcmp (debug->name, basename) != 0))
+ {
+ g_free (basename);
+ return NULL;
+ }
+
+ g_free (basename);
+
+ argv = g_ptr_array_sized_new (8);
+
+ for (arg = debug->args; *arg != NULL; arg++)
+ g_ptr_array_add (argv, *arg);
+
+ for (arg = (gchar **) args; *arg != NULL; arg++)
+ g_ptr_array_add (argv, *arg);
+
+ g_ptr_array_add (argv, NULL);
+
+ return (gchar **) g_ptr_array_free (argv, FALSE);
+}
diff --git a/app/plug-in/gimpplugindebug.h b/app/plug-in/gimpplugindebug.h
new file mode 100644
index 0000000..ec5bc33
--- /dev/null
+++ b/app/plug-in/gimpplugindebug.h
@@ -0,0 +1,43 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpplugindebug.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_DEBUG_H__
+#define __GIMP_PLUG_IN_DEBUG_H__
+
+
+typedef enum
+{
+ GIMP_DEBUG_WRAP_QUERY = 1 << 0,
+ GIMP_DEBUG_WRAP_INIT = 1 << 1,
+ GIMP_DEBUG_WRAP_RUN = 1 << 2,
+
+ GIMP_DEBUG_WRAP_DEFAULT = GIMP_DEBUG_WRAP_RUN
+} GimpDebugWrapFlag;
+
+
+GimpPlugInDebug * gimp_plug_in_debug_new (void);
+void gimp_plug_in_debug_free (GimpPlugInDebug *debug);
+
+gchar ** gimp_plug_in_debug_argv (GimpPlugInDebug *debug,
+ const gchar *name,
+ GimpDebugWrapFlag flag,
+ const gchar **args);
+
+
+#endif /* __GIMP_PLUG_IN_DEBUG_H__ */
diff --git a/app/plug-in/gimpplugindef.c b/app/plug-in/gimpplugindef.c
new file mode 100644
index 0000000..83f6378
--- /dev/null
+++ b/app/plug-in/gimpplugindef.c
@@ -0,0 +1,235 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpplugindef.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "plug-in-types.h"
+
+#include "core/gimp-memsize.h"
+
+#include "gimpplugindef.h"
+#include "gimppluginprocedure.h"
+
+
+static void gimp_plug_in_def_finalize (GObject *object);
+
+static gint64 gimp_plug_in_def_get_memsize (GimpObject *object,
+ gint64 *gui_size);
+
+
+G_DEFINE_TYPE (GimpPlugInDef, gimp_plug_in_def, GIMP_TYPE_OBJECT)
+
+#define parent_class gimp_plug_in_def_parent_class
+
+
+static void
+gimp_plug_in_def_class_init (GimpPlugInDefClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
+
+ object_class->finalize = gimp_plug_in_def_finalize;
+
+ gimp_object_class->get_memsize = gimp_plug_in_def_get_memsize;
+}
+
+static void
+gimp_plug_in_def_init (GimpPlugInDef *def)
+{
+}
+
+static void
+gimp_plug_in_def_finalize (GObject *object)
+{
+ GimpPlugInDef *plug_in_def = GIMP_PLUG_IN_DEF (object);
+
+ g_object_unref (plug_in_def->file);
+ g_free (plug_in_def->locale_domain_name);
+ g_free (plug_in_def->locale_domain_path);
+ g_free (plug_in_def->help_domain_name);
+ g_free (plug_in_def->help_domain_uri);
+
+ g_slist_free_full (plug_in_def->procedures, (GDestroyNotify) g_object_unref);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gint64
+gimp_plug_in_def_get_memsize (GimpObject *object,
+ gint64 *gui_size)
+{
+ GimpPlugInDef *plug_in_def = GIMP_PLUG_IN_DEF (object);
+ gint64 memsize = 0;
+
+ memsize += gimp_g_object_get_memsize (G_OBJECT (plug_in_def->file));
+ memsize += gimp_string_get_memsize (plug_in_def->locale_domain_name);
+ memsize += gimp_string_get_memsize (plug_in_def->locale_domain_path);
+ memsize += gimp_string_get_memsize (plug_in_def->help_domain_name);
+ memsize += gimp_string_get_memsize (plug_in_def->help_domain_uri);
+
+ memsize += gimp_g_slist_get_memsize (plug_in_def->procedures, 0);
+
+ return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
+ gui_size);
+}
+
+
+/* public functions */
+
+GimpPlugInDef *
+gimp_plug_in_def_new (GFile *file)
+{
+ GimpPlugInDef *plug_in_def;
+
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+ plug_in_def = g_object_new (GIMP_TYPE_PLUG_IN_DEF, NULL);
+
+ plug_in_def->file = g_object_ref (file);
+
+ return plug_in_def;
+}
+
+void
+gimp_plug_in_def_add_procedure (GimpPlugInDef *plug_in_def,
+ GimpPlugInProcedure *proc)
+{
+ GimpPlugInProcedure *overridden;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_DEF (plug_in_def));
+ g_return_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc));
+
+ overridden = gimp_plug_in_procedure_find (plug_in_def->procedures,
+ gimp_object_get_name (proc));
+
+ if (overridden)
+ gimp_plug_in_def_remove_procedure (plug_in_def, overridden);
+
+ proc->mtime = plug_in_def->mtime;
+
+ gimp_plug_in_procedure_set_locale_domain (proc,
+ plug_in_def->locale_domain_name);
+ gimp_plug_in_procedure_set_help_domain (proc,
+ plug_in_def->help_domain_name);
+
+ plug_in_def->procedures = g_slist_append (plug_in_def->procedures,
+ g_object_ref (proc));
+}
+
+void
+gimp_plug_in_def_remove_procedure (GimpPlugInDef *plug_in_def,
+ GimpPlugInProcedure *proc)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_DEF (plug_in_def));
+ g_return_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc));
+
+ plug_in_def->procedures = g_slist_remove (plug_in_def->procedures, proc);
+ g_object_unref (proc);
+}
+
+void
+gimp_plug_in_def_set_locale_domain (GimpPlugInDef *plug_in_def,
+ const gchar *domain_name,
+ const gchar *domain_path)
+{
+ GSList *list;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_DEF (plug_in_def));
+
+ if (plug_in_def->locale_domain_name)
+ g_free (plug_in_def->locale_domain_name);
+ plug_in_def->locale_domain_name = g_strdup (domain_name);
+
+ if (plug_in_def->locale_domain_path)
+ g_free (plug_in_def->locale_domain_path);
+ plug_in_def->locale_domain_path = g_strdup (domain_path);
+
+ for (list = plug_in_def->procedures; list; list = g_slist_next (list))
+ {
+ GimpPlugInProcedure *procedure = list->data;
+
+ gimp_plug_in_procedure_set_locale_domain (procedure,
+ plug_in_def->locale_domain_name);
+ }
+}
+
+void
+gimp_plug_in_def_set_help_domain (GimpPlugInDef *plug_in_def,
+ const gchar *domain_name,
+ const gchar *domain_uri)
+{
+ GSList *list;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_DEF (plug_in_def));
+
+ if (plug_in_def->help_domain_name)
+ g_free (plug_in_def->help_domain_name);
+ plug_in_def->help_domain_name = g_strdup (domain_name);
+
+ if (plug_in_def->help_domain_uri)
+ g_free (plug_in_def->help_domain_uri);
+ plug_in_def->help_domain_uri = g_strdup (domain_uri);
+
+ for (list = plug_in_def->procedures; list; list = g_slist_next (list))
+ {
+ GimpPlugInProcedure *procedure = list->data;
+
+ gimp_plug_in_procedure_set_help_domain (procedure,
+ plug_in_def->help_domain_name);
+ }
+}
+
+void
+gimp_plug_in_def_set_mtime (GimpPlugInDef *plug_in_def,
+ gint64 mtime)
+{
+ GSList *list;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_DEF (plug_in_def));
+
+ plug_in_def->mtime = mtime;
+
+ for (list = plug_in_def->procedures; list; list = g_slist_next (list))
+ {
+ GimpPlugInProcedure *proc = list->data;
+
+ proc->mtime = plug_in_def->mtime;
+ }
+}
+
+void
+gimp_plug_in_def_set_needs_query (GimpPlugInDef *plug_in_def,
+ gboolean needs_query)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_DEF (plug_in_def));
+
+ plug_in_def->needs_query = needs_query ? TRUE : FALSE;
+}
+
+void
+gimp_plug_in_def_set_has_init (GimpPlugInDef *plug_in_def,
+ gboolean has_init)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_DEF (plug_in_def));
+
+ plug_in_def->has_init = has_init ? TRUE : FALSE;
+}
diff --git a/app/plug-in/gimpplugindef.h b/app/plug-in/gimpplugindef.h
new file mode 100644
index 0000000..d819e1a
--- /dev/null
+++ b/app/plug-in/gimpplugindef.h
@@ -0,0 +1,82 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpplugindef.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_DEF_H__
+#define __GIMP_PLUG_IN_DEF_H__
+
+
+#include "core/gimpobject.h"
+
+
+#define GIMP_TYPE_PLUG_IN_DEF (gimp_plug_in_def_get_type ())
+#define GIMP_PLUG_IN_DEF(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PLUG_IN_DEF, GimpPlugInDef))
+#define GIMP_PLUG_IN_DEF_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PLUG_IN_DEF, GimpPlugInDefClass))
+#define GIMP_IS_PLUG_IN_DEF(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PLUG_IN_DEF))
+#define GIMP_IS_PLUG_IN_DEF_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PLUG_IN_DEF))
+
+
+typedef struct _GimpPlugInDefClass GimpPlugInDefClass;
+
+struct _GimpPlugInDef
+{
+ GimpObject parent_instance;
+
+ GFile *file;
+ GSList *procedures;
+ gchar *locale_domain_name;
+ gchar *locale_domain_path;
+ gchar *help_domain_name;
+ gchar *help_domain_uri;
+ gint64 mtime;
+ gboolean needs_query; /* Does the plug-in need to be queried ? */
+ gboolean has_init; /* Does the plug-in need to be initialized ? */
+};
+
+struct _GimpPlugInDefClass
+{
+ GimpObjectClass parent_class;
+};
+
+
+GType gimp_plug_in_def_get_type (void) G_GNUC_CONST;
+
+GimpPlugInDef * gimp_plug_in_def_new (GFile *file);
+
+void gimp_plug_in_def_add_procedure (GimpPlugInDef *plug_in_def,
+ GimpPlugInProcedure *proc);
+void gimp_plug_in_def_remove_procedure (GimpPlugInDef *plug_in_def,
+ GimpPlugInProcedure *proc);
+
+void gimp_plug_in_def_set_locale_domain (GimpPlugInDef *plug_in_def,
+ const gchar *domain_name,
+ const gchar *domain_path);
+
+void gimp_plug_in_def_set_help_domain (GimpPlugInDef *plug_in_def,
+ const gchar *domain_name,
+ const gchar *domain_uri);
+
+void gimp_plug_in_def_set_mtime (GimpPlugInDef *plug_in_def,
+ gint64 mtime);
+void gimp_plug_in_def_set_needs_query (GimpPlugInDef *plug_in_def,
+ gboolean needs_query);
+void gimp_plug_in_def_set_has_init (GimpPlugInDef *plug_in_def,
+ gboolean has_init);
+
+
+#endif /* __GIMP_PLUG_IN_DEF_H__ */
diff --git a/app/plug-in/gimppluginerror.c b/app/plug-in/gimppluginerror.c
new file mode 100644
index 0000000..e73e1e5
--- /dev/null
+++ b/app/plug-in/gimppluginerror.c
@@ -0,0 +1,36 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+
+#include "gimppluginerror.h"
+
+
+/**
+ * gimp_plug_in_error_quark:
+ *
+ * This function is never called directly. Use GIMP_PLUG_IN_ERROR() instead.
+ *
+ * Return value: the #GQuark that defines the GimpPlugIn error domain.
+ **/
+GQuark
+gimp_plug_in_error_quark (void)
+{
+ return g_quark_from_static_string ("gimp-plug-in-error-quark");
+}
diff --git a/app/plug-in/gimppluginerror.h b/app/plug-in/gimppluginerror.h
new file mode 100644
index 0000000..7cf7eb2
--- /dev/null
+++ b/app/plug-in/gimppluginerror.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_ERROR_H__
+#define __GIMP_PLUG_IN_ERROR_H__
+
+
+typedef enum
+{
+ GIMP_PLUG_IN_FAILED, /* generic error condition */
+ GIMP_PLUG_IN_EXECUTION_FAILED,
+ GIMP_PLUG_IN_NOT_FOUND
+} GimpPlugInErrorCode;
+
+
+#define GIMP_PLUG_IN_ERROR (gimp_plug_in_error_quark ())
+
+GQuark gimp_plug_in_error_quark (void) G_GNUC_CONST;
+
+
+#endif /* __GIMP_PLUG_IN_ERROR_H__ */
diff --git a/app/plug-in/gimppluginmanager-call.c b/app/plug-in/gimppluginmanager-call.c
new file mode 100644
index 0000000..47791e6
--- /dev/null
+++ b/app/plug-in/gimppluginmanager-call.c
@@ -0,0 +1,375 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginmanager-call.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#ifdef G_OS_WIN32
+#include <windows.h>
+#endif
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpbase/gimpprotocol.h"
+#include "libgimpbase/gimpwire.h"
+
+#include "plug-in-types.h"
+
+#include "config/gimpguiconfig.h"
+
+#include "core/gimp.h"
+#include "core/gimpprogress.h"
+
+#include "pdb/gimppdbcontext.h"
+
+#include "gimpplugin.h"
+#include "gimpplugin-message.h"
+#include "gimpplugindef.h"
+#include "gimppluginerror.h"
+#include "gimppluginmanager.h"
+#define __YES_I_NEED_GIMP_PLUG_IN_MANAGER_CALL__
+#include "gimppluginmanager-call.h"
+#include "gimppluginshm.h"
+#include "gimptemporaryprocedure.h"
+#include "plug-in-params.h"
+
+#include "gimp-intl.h"
+
+
+static void
+gimp_allow_set_foreground_window (GimpPlugIn *plug_in)
+{
+#ifdef G_OS_WIN32
+ AllowSetForegroundWindow (GetProcessId (plug_in->pid));
+#endif
+}
+
+/* public functions */
+
+void
+gimp_plug_in_manager_call_query (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpPlugInDef *plug_in_def)
+{
+ GimpPlugIn *plug_in;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+ g_return_if_fail (GIMP_IS_PDB_CONTEXT (context));
+ g_return_if_fail (GIMP_IS_PLUG_IN_DEF (plug_in_def));
+
+ plug_in = gimp_plug_in_new (manager, context, NULL,
+ NULL, plug_in_def->file);
+
+ if (plug_in)
+ {
+ plug_in->plug_in_def = plug_in_def;
+
+ if (gimp_plug_in_open (plug_in, GIMP_PLUG_IN_CALL_QUERY, TRUE))
+ {
+ while (plug_in->open)
+ {
+ GimpWireMessage msg;
+
+ if (! gimp_wire_read_msg (plug_in->my_read, &msg, plug_in))
+ {
+ gimp_plug_in_close (plug_in, TRUE);
+ }
+ else
+ {
+ gimp_plug_in_handle_message (plug_in, &msg);
+ gimp_wire_destroy (&msg);
+ }
+ }
+ }
+
+ g_object_unref (plug_in);
+ }
+}
+
+void
+gimp_plug_in_manager_call_init (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpPlugInDef *plug_in_def)
+{
+ GimpPlugIn *plug_in;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+ g_return_if_fail (GIMP_IS_PDB_CONTEXT (context));
+ g_return_if_fail (GIMP_IS_PLUG_IN_DEF (plug_in_def));
+
+ plug_in = gimp_plug_in_new (manager, context, NULL,
+ NULL, plug_in_def->file);
+
+ if (plug_in)
+ {
+ plug_in->plug_in_def = plug_in_def;
+
+ if (gimp_plug_in_open (plug_in, GIMP_PLUG_IN_CALL_INIT, TRUE))
+ {
+ while (plug_in->open)
+ {
+ GimpWireMessage msg;
+
+ if (! gimp_wire_read_msg (plug_in->my_read, &msg, plug_in))
+ {
+ gimp_plug_in_close (plug_in, TRUE);
+ }
+ else
+ {
+ gimp_plug_in_handle_message (plug_in, &msg);
+ gimp_wire_destroy (&msg);
+ }
+ }
+ }
+
+ g_object_unref (plug_in);
+ }
+}
+
+GimpValueArray *
+gimp_plug_in_manager_call_run (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpPlugInProcedure *procedure,
+ GimpValueArray *args,
+ gboolean synchronous,
+ GimpObject *display)
+{
+ GimpValueArray *return_vals = NULL;
+ GimpPlugIn *plug_in;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), NULL);
+ g_return_val_if_fail (GIMP_IS_PDB_CONTEXT (context), NULL);
+ g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (procedure), NULL);
+ g_return_val_if_fail (args != NULL, NULL);
+ g_return_val_if_fail (display == NULL || GIMP_IS_OBJECT (display), NULL);
+
+ plug_in = gimp_plug_in_new (manager, context, progress, procedure, NULL);
+
+ if (plug_in)
+ {
+ GimpCoreConfig *core_config = manager->gimp->config;
+ GimpGeglConfig *gegl_config = GIMP_GEGL_CONFIG (core_config);
+ GimpDisplayConfig *display_config = GIMP_DISPLAY_CONFIG (core_config);
+ GimpGuiConfig *gui_config = GIMP_GUI_CONFIG (core_config);
+ GPConfig config;
+ GPProcRun proc_run;
+ gint display_ID;
+ GObject *screen;
+ gint monitor;
+ GFile *icon_theme_dir;
+
+ if (! gimp_plug_in_open (plug_in, GIMP_PLUG_IN_CALL_RUN, FALSE))
+ {
+ const gchar *name = gimp_object_get_name (plug_in);
+ GError *error = g_error_new (GIMP_PLUG_IN_ERROR,
+ GIMP_PLUG_IN_EXECUTION_FAILED,
+ _("Failed to run plug-in \"%s\""),
+ name);
+
+ g_object_unref (plug_in);
+
+ return_vals = gimp_procedure_get_return_values (GIMP_PROCEDURE (procedure),
+ FALSE, error);
+ g_error_free (error);
+
+ return return_vals;
+ }
+
+ display_ID = display ? gimp_get_display_ID (manager->gimp, display) : -1;
+
+ icon_theme_dir = gimp_get_icon_theme_dir (manager->gimp);
+
+ config.version = GIMP_PROTOCOL_VERSION;
+ config.tile_width = GIMP_PLUG_IN_TILE_WIDTH;
+ config.tile_height = GIMP_PLUG_IN_TILE_HEIGHT;
+ config.shm_ID = (manager->shm ?
+ gimp_plug_in_shm_get_ID (manager->shm) : -1);
+ config.check_size = display_config->transparency_size;
+ config.check_type = display_config->transparency_type;
+ config.show_help_button = (gui_config->use_help &&
+ gui_config->show_help_button);
+ config.use_cpu_accel = manager->gimp->use_cpu_accel;
+ config.use_opencl = gegl_config->use_opencl;
+ config.export_profile = core_config->export_color_profile;
+ config.export_exif = core_config->export_metadata_exif;
+ config.export_xmp = core_config->export_metadata_xmp;
+ config.export_iptc = core_config->export_metadata_iptc;
+ config.show_tooltips = gui_config->show_tooltips;
+ config.min_colors = 144;
+ config.gdisp_ID = display_ID;
+ config.app_name = (gchar *) g_get_application_name ();
+ config.wm_class = (gchar *) gimp_get_program_class (manager->gimp);
+ config.display_name = gimp_get_display_name (manager->gimp,
+ display_ID,
+ &screen, &monitor);
+ config.monitor_number = monitor;
+ config.timestamp = gimp_get_user_time (manager->gimp);
+ config.icon_theme_dir = icon_theme_dir ?
+ g_file_get_path (icon_theme_dir) :
+ NULL;
+ config.tile_cache_size = gegl_config->tile_cache_size;
+ config.swap_path = gegl_config->swap_path;
+ config.num_processors = gegl_config->num_processors;
+ config.swap_compression = gegl_config->swap_compression;
+
+ proc_run.name = GIMP_PROCEDURE (procedure)->original_name;
+ proc_run.nparams = gimp_value_array_length (args);
+ proc_run.params = plug_in_args_to_params (args, FALSE);
+
+ if (! gp_config_write (plug_in->my_write, &config, plug_in) ||
+ ! gp_proc_run_write (plug_in->my_write, &proc_run, plug_in) ||
+ ! gimp_wire_flush (plug_in->my_write, plug_in))
+ {
+ const gchar *name = gimp_object_get_name (plug_in);
+ GError *error = g_error_new (GIMP_PLUG_IN_ERROR,
+ GIMP_PLUG_IN_EXECUTION_FAILED,
+ _("Failed to run plug-in \"%s\""),
+ name);
+
+ g_free (config.display_name);
+ g_free (config.icon_theme_dir);
+ g_free (proc_run.params);
+
+ g_object_unref (plug_in);
+
+ return_vals = gimp_procedure_get_return_values (GIMP_PROCEDURE (procedure),
+ FALSE, error);
+ g_error_free (error);
+
+ return return_vals;
+ }
+
+ g_free (config.display_name);
+ g_free (config.icon_theme_dir);
+ g_free (proc_run.params);
+
+ /* If this is an extension,
+ * wait for an installation-confirmation message
+ */
+ if (GIMP_PROCEDURE (procedure)->proc_type == GIMP_EXTENSION)
+ {
+ plug_in->ext_main_loop = g_main_loop_new (NULL, FALSE);
+
+ gimp_threads_leave (manager->gimp);
+ g_main_loop_run (plug_in->ext_main_loop);
+ gimp_threads_enter (manager->gimp);
+
+ /* main_loop is quit in gimp_plug_in_handle_extension_ack() */
+
+ g_clear_pointer (&plug_in->ext_main_loop, g_main_loop_unref);
+ }
+
+ /* If this plug-in is requested to run synchronously,
+ * wait for its return values
+ */
+ if (synchronous)
+ {
+ GimpPlugInProcFrame *proc_frame = &plug_in->main_proc_frame;
+
+ proc_frame->main_loop = g_main_loop_new (NULL, FALSE);
+
+ gimp_threads_leave (manager->gimp);
+ g_main_loop_run (proc_frame->main_loop);
+ gimp_threads_enter (manager->gimp);
+
+ /* main_loop is quit in gimp_plug_in_handle_proc_return() */
+
+ g_clear_pointer (&proc_frame->main_loop, g_main_loop_unref);
+
+ return_vals = gimp_plug_in_proc_frame_get_return_values (proc_frame);
+ }
+
+ g_object_unref (plug_in);
+ }
+
+ return return_vals;
+}
+
+GimpValueArray *
+gimp_plug_in_manager_call_run_temp (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpTemporaryProcedure *procedure,
+ GimpValueArray *args)
+{
+ GimpValueArray *return_vals = NULL;
+ GimpPlugIn *plug_in;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), NULL);
+ g_return_val_if_fail (GIMP_IS_PDB_CONTEXT (context), NULL);
+ g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
+ g_return_val_if_fail (GIMP_IS_TEMPORARY_PROCEDURE (procedure), NULL);
+ g_return_val_if_fail (args != NULL, NULL);
+
+ plug_in = procedure->plug_in;
+
+ if (plug_in)
+ {
+ GimpPlugInProcFrame *proc_frame;
+ GPProcRun proc_run;
+
+ proc_frame = gimp_plug_in_proc_frame_push (plug_in, context, progress,
+ procedure);
+
+ proc_run.name = GIMP_PROCEDURE (procedure)->original_name;
+ proc_run.nparams = gimp_value_array_length (args);
+ proc_run.params = plug_in_args_to_params (args, FALSE);
+
+ if (! gp_temp_proc_run_write (plug_in->my_write, &proc_run, plug_in) ||
+ ! gimp_wire_flush (plug_in->my_write, plug_in))
+ {
+ const gchar *name = gimp_object_get_name (plug_in);
+ GError *error = g_error_new (GIMP_PLUG_IN_ERROR,
+ GIMP_PLUG_IN_EXECUTION_FAILED,
+ _("Failed to run plug-in \"%s\""),
+ name);
+
+ g_free (proc_run.params);
+ gimp_plug_in_proc_frame_pop (plug_in);
+
+ return_vals = gimp_procedure_get_return_values (GIMP_PROCEDURE (procedure),
+ FALSE, error);
+ g_error_free (error);
+
+ return return_vals;
+ }
+ gimp_allow_set_foreground_window (plug_in);
+
+ g_free (proc_run.params);
+
+ g_object_ref (plug_in);
+ gimp_plug_in_proc_frame_ref (proc_frame);
+
+ gimp_plug_in_main_loop (plug_in);
+
+ /* main_loop is quit and proc_frame is popped in
+ * gimp_plug_in_handle_temp_proc_return()
+ */
+
+ return_vals = gimp_plug_in_proc_frame_get_return_values (proc_frame);
+
+ gimp_plug_in_proc_frame_unref (proc_frame, plug_in);
+ g_object_unref (plug_in);
+ }
+
+ return return_vals;
+}
diff --git a/app/plug-in/gimppluginmanager-call.h b/app/plug-in/gimppluginmanager-call.h
new file mode 100644
index 0000000..c456ab3
--- /dev/null
+++ b/app/plug-in/gimppluginmanager-call.h
@@ -0,0 +1,59 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginmanager-call.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_MANAGER_CALL_H__
+#define __GIMP_PLUG_IN_MANAGER_CALL_H__
+
+#ifndef __YES_I_NEED_GIMP_PLUG_IN_MANAGER_CALL__
+#error Do not use gimp_plug_in_manager_call_run*(), use gimp_procedure_execute*() instead.
+#endif
+
+
+/* Call the plug-in's query() function
+ */
+void gimp_plug_in_manager_call_query (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpPlugInDef *plug_in_def);
+
+/* Call the plug-in's init() function
+ */
+void gimp_plug_in_manager_call_init (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpPlugInDef *plug_in_def);
+
+/* Run a plug-in as if it were a procedure database procedure
+ */
+GimpValueArray * gimp_plug_in_manager_call_run (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpPlugInProcedure *procedure,
+ GimpValueArray *args,
+ gboolean synchronous,
+ GimpObject *display);
+
+/* Run a temp plug-in proc as if it were a procedure database procedure
+ */
+GimpValueArray * gimp_plug_in_manager_call_run_temp (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpTemporaryProcedure *procedure,
+ GimpValueArray *args);
+
+
+#endif /* __GIMP_PLUG_IN_MANAGER_CALL_H__ */
diff --git a/app/plug-in/gimppluginmanager-data.c b/app/plug-in/gimppluginmanager-data.c
new file mode 100644
index 0000000..08047b1
--- /dev/null
+++ b/app/plug-in/gimppluginmanager-data.c
@@ -0,0 +1,133 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginmanager-data.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gio/gio.h>
+
+#include "plug-in-types.h"
+
+#include "gimppluginmanager.h"
+#include "gimppluginmanager-data.h"
+
+
+typedef struct _GimpPlugInData GimpPlugInData;
+
+struct _GimpPlugInData
+{
+ gchar *identifier;
+ gint32 bytes;
+ guint8 *data;
+};
+
+
+/* public functions */
+
+void
+gimp_plug_in_manager_data_free (GimpPlugInManager *manager)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+
+ if (manager->data_list)
+ {
+ GList *list;
+
+ for (list = manager->data_list;
+ list;
+ list = g_list_next (list))
+ {
+ GimpPlugInData *data = list->data;
+
+ g_free (data->identifier);
+ g_free (data->data);
+ g_slice_free (GimpPlugInData, data);
+ }
+
+ g_list_free (manager->data_list);
+ manager->data_list = NULL;
+ }
+}
+
+void
+gimp_plug_in_manager_set_data (GimpPlugInManager *manager,
+ const gchar *identifier,
+ gint32 bytes,
+ const guint8 *data)
+{
+ GimpPlugInData *plug_in_data;
+ GList *list;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+ g_return_if_fail (identifier != NULL);
+ g_return_if_fail (bytes > 0);
+ g_return_if_fail (data != NULL);
+
+ for (list = manager->data_list; list; list = g_list_next (list))
+ {
+ plug_in_data = list->data;
+
+ if (! strcmp (plug_in_data->identifier, identifier))
+ break;
+ }
+
+ /* If there isn't already data with the specified identifier, create one */
+ if (list == NULL)
+ {
+ plug_in_data = g_slice_new0 (GimpPlugInData);
+ plug_in_data->identifier = g_strdup (identifier);
+
+ manager->data_list = g_list_prepend (manager->data_list, plug_in_data);
+ }
+ else
+ {
+ g_free (plug_in_data->data);
+ }
+
+ plug_in_data->bytes = bytes;
+ plug_in_data->data = g_memdup (data, bytes);
+}
+
+const guint8 *
+gimp_plug_in_manager_get_data (GimpPlugInManager *manager,
+ const gchar *identifier,
+ gint32 *bytes)
+{
+ GList *list;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), NULL);
+ g_return_val_if_fail (identifier != NULL, NULL);
+ g_return_val_if_fail (bytes != NULL, NULL);
+
+ *bytes = 0;
+
+ for (list = manager->data_list; list; list = g_list_next (list))
+ {
+ GimpPlugInData *plug_in_data = list->data;
+
+ if (! strcmp (plug_in_data->identifier, identifier))
+ {
+ *bytes = plug_in_data->bytes;
+ return plug_in_data->data;
+ }
+ }
+
+ return NULL;
+}
diff --git a/app/plug-in/gimppluginmanager-data.h b/app/plug-in/gimppluginmanager-data.h
new file mode 100644
index 0000000..1a88f32
--- /dev/null
+++ b/app/plug-in/gimppluginmanager-data.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginmanager-data.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_MANAGER_DATA_H__
+#define __GIMP_PLUG_IN_MANAGER_DATA_H__
+
+
+void gimp_plug_in_manager_data_free (GimpPlugInManager *manager);
+
+void gimp_plug_in_manager_set_data (GimpPlugInManager *manager,
+ const gchar *identifier,
+ gint32 bytes,
+ const guint8 *data);
+const guint8 * gimp_plug_in_manager_get_data (GimpPlugInManager *manager,
+ const gchar *identifier,
+ gint32 *bytes);
+
+
+#endif /* __GIMP_PLUG_IN_MANAGER_DATA_H__ */
diff --git a/app/plug-in/gimppluginmanager-file-procedure.c b/app/plug-in/gimppluginmanager-file-procedure.c
new file mode 100644
index 0000000..ec11ef6
--- /dev/null
+++ b/app/plug-in/gimppluginmanager-file-procedure.c
@@ -0,0 +1,716 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis
+ * Copyright (C) 1997 Josh MacDonald
+ *
+ * gimppluginmanager-file-procedure.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "plug-in-types.h"
+
+#include "core/gimp-utils.h"
+
+#include "gimppluginmanager-file-procedure.h"
+#include "gimppluginprocedure.h"
+
+#include "gimp-log.h"
+
+#include "gimp-intl.h"
+
+
+typedef enum
+{
+ /* positive values indicate the length of a matching magic */
+
+ FILE_MATCH_NONE = 0,
+ FILE_MATCH_SIZE = -1
+} FileMatchType;
+
+
+/* local function prototypes */
+
+static GimpPlugInProcedure * file_proc_find_by_prefix (GSList *procs,
+ GFile *file,
+ gboolean skip_magic);
+static GimpPlugInProcedure * file_proc_find_by_extension (GSList *procs,
+ GFile *file,
+ gboolean skip_magic);
+static GimpPlugInProcedure * file_proc_find_by_name (GSList *procs,
+ GFile *file,
+ gboolean skip_magic);
+
+static void file_convert_string (const gchar *instr,
+ gchar *outmem,
+ gint maxmem,
+ gint *nmem);
+static FileMatchType file_check_single_magic (const gchar *offset,
+ const gchar *type,
+ const gchar *value,
+ const guchar *file_head,
+ gint headsize,
+ GFile *file,
+ GInputStream *input);
+static FileMatchType file_check_magic_list (GSList *magics_list,
+ const guchar *head,
+ gint headsize,
+ GFile *file,
+ GInputStream *input);
+
+
+/* public functions */
+
+GimpPlugInProcedure *
+file_procedure_find (GSList *procs,
+ GFile *file,
+ GError **error)
+{
+ GimpPlugInProcedure *file_proc;
+ GimpPlugInProcedure *size_matched_proc = NULL;
+ gint size_match_count = 0;
+
+ g_return_val_if_fail (procs != NULL, NULL);
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ /* First, check magicless prefixes/suffixes */
+ file_proc = file_proc_find_by_name (procs, file, TRUE);
+
+ if (file_proc)
+ return file_proc;
+
+ /* Then look for magics, but not on remote files */
+ if (g_file_is_native (file))
+ {
+ GSList *list;
+ GInputStream *input = NULL;
+ gboolean opened = FALSE;
+ gsize head_size = 0;
+ guchar head[256];
+ FileMatchType best_match_val = FILE_MATCH_NONE;
+ GimpPlugInProcedure *best_file_proc = NULL;
+
+ for (list = procs; list; list = g_slist_next (list))
+ {
+ file_proc = list->data;
+
+ if (file_proc->magics_list)
+ {
+ if (G_UNLIKELY (! opened))
+ {
+ input = G_INPUT_STREAM (g_file_read (file, NULL, error));
+
+ if (input)
+ {
+ g_input_stream_read_all (input,
+ head, sizeof (head),
+ &head_size, NULL, error);
+
+ if (head_size < 4)
+ {
+ g_object_unref (input);
+ input = NULL;
+ }
+ else
+ {
+ GDataInputStream *data_input;
+
+ data_input = g_data_input_stream_new (input);
+ g_object_unref (input);
+ input = G_INPUT_STREAM (data_input);
+ }
+ }
+
+ opened = TRUE;
+ }
+
+ if (head_size >= 4)
+ {
+ FileMatchType match_val;
+
+ match_val = file_check_magic_list (file_proc->magics_list,
+ head, head_size,
+ file, input);
+
+ if (match_val == FILE_MATCH_SIZE)
+ {
+ /* Use it only if no other magic matches */
+ size_match_count++;
+ size_matched_proc = file_proc;
+ }
+ else if (match_val != FILE_MATCH_NONE)
+ {
+ GIMP_LOG (MAGIC_MATCH,
+ "magic match %d on %s\n",
+ match_val,
+ gimp_object_get_name (file_proc));
+
+ if (match_val > best_match_val)
+ {
+ best_match_val = match_val;
+ best_file_proc = file_proc;
+ }
+ }
+ }
+ }
+ }
+
+ if (input)
+ g_object_unref (input);
+
+ if (best_file_proc)
+ {
+ GIMP_LOG (MAGIC_MATCH,
+ "best magic match on %s\n",
+ gimp_object_get_name (best_file_proc));
+
+ return best_file_proc;
+ }
+ }
+
+ if (size_match_count == 1)
+ return size_matched_proc;
+
+ /* As a last resort, try matching by name, not skipping magic procs */
+ file_proc = file_proc_find_by_name (procs, file, FALSE);
+
+ if (file_proc)
+ {
+ /* we found a procedure, clear error that might have been set */
+ g_clear_error (error);
+ }
+ else
+ {
+ /* set an error message unless one was already set */
+ if (error && *error == NULL)
+ g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unknown file type"));
+ }
+
+ return file_proc;
+}
+
+GimpPlugInProcedure *
+file_procedure_find_by_prefix (GSList *procs,
+ GFile *file)
+{
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+ return file_proc_find_by_prefix (procs, file, FALSE);
+}
+
+GimpPlugInProcedure *
+file_procedure_find_by_extension (GSList *procs,
+ GFile *file)
+{
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+ return file_proc_find_by_extension (procs, file, FALSE);
+}
+
+GimpPlugInProcedure *
+file_procedure_find_by_mime_type (GSList *procs,
+ const gchar *mime_type)
+{
+ GSList *list;
+
+ g_return_val_if_fail (mime_type != NULL, NULL);
+
+ for (list = procs; list; list = g_slist_next (list))
+ {
+ GimpPlugInProcedure *proc = list->data;
+ GSList *mime;
+
+ for (mime = proc->mime_types_list; mime; mime = g_slist_next (mime))
+ {
+ if (! strcmp (mime_type, mime->data))
+ return proc;
+ }
+ }
+
+ return NULL;
+}
+
+
+/* private functions */
+
+static GimpPlugInProcedure *
+file_proc_find_by_prefix (GSList *procs,
+ GFile *file,
+ gboolean skip_magic)
+{
+ gchar *uri = g_file_get_uri (file);
+ GSList *p;
+
+ for (p = procs; p; p = g_slist_next (p))
+ {
+ GimpPlugInProcedure *proc = p->data;
+ GSList *prefixes;
+
+ if (skip_magic && proc->magics_list)
+ continue;
+
+ for (prefixes = proc->prefixes_list;
+ prefixes;
+ prefixes = g_slist_next (prefixes))
+ {
+ if (g_str_has_prefix (uri, prefixes->data))
+ {
+ g_free (uri);
+ return proc;
+ }
+ }
+ }
+
+ g_free (uri);
+
+ return NULL;
+}
+
+static GimpPlugInProcedure *
+file_proc_find_by_extension (GSList *procs,
+ GFile *file,
+ gboolean skip_magic)
+{
+ gchar *ext = gimp_file_get_extension (file);
+
+ if (ext)
+ {
+ GSList *p;
+
+ for (p = procs; p; p = g_slist_next (p))
+ {
+ GimpPlugInProcedure *proc = p->data;
+
+ if (skip_magic && proc->magics_list)
+ continue;
+
+ if (g_slist_find_custom (proc->extensions_list,
+ ext + 1,
+ (GCompareFunc) g_ascii_strcasecmp))
+ {
+ g_free (ext);
+
+ return proc;
+ }
+ }
+
+ g_free (ext);
+ }
+
+ return NULL;
+}
+
+static GimpPlugInProcedure *
+file_proc_find_by_name (GSList *procs,
+ GFile *file,
+ gboolean skip_magic)
+{
+ GimpPlugInProcedure *proc;
+
+ proc = file_proc_find_by_prefix (procs, file, skip_magic);
+
+ if (! proc)
+ proc = file_proc_find_by_extension (procs, file, skip_magic);
+
+ return proc;
+}
+
+static void
+file_convert_string (const gchar *instr,
+ gchar *outmem,
+ gint maxmem,
+ gint *nmem)
+{
+ /* Convert a string in C-notation to array of char */
+ const guchar *uin = (const guchar *) instr;
+ guchar *uout = (guchar *) outmem;
+ guchar tmp[5], *tmpptr;
+ guint k;
+
+ while ((*uin != '\0') && ((((gchar *) uout) - outmem) < maxmem))
+ {
+ if (*uin != '\\') /* Not an escaped character ? */
+ {
+ *(uout++) = *(uin++);
+ continue;
+ }
+
+ if (*(++uin) == '\0')
+ {
+ *(uout++) = '\\';
+ break;
+ }
+
+ switch (*uin)
+ {
+ case '0': case '1': case '2': case '3': /* octal */
+ for (tmpptr = tmp; (tmpptr - tmp) <= 3;)
+ {
+ *(tmpptr++) = *(uin++);
+ if ( (*uin == '\0') || (!g_ascii_isdigit (*uin))
+ || (*uin == '8') || (*uin == '9'))
+ break;
+ }
+
+ *tmpptr = '\0';
+ sscanf ((gchar *) tmp, "%o", &k);
+ *(uout++) = k;
+ break;
+
+ case 'a': *(uout++) = '\a'; uin++; break;
+ case 'b': *(uout++) = '\b'; uin++; break;
+ case 't': *(uout++) = '\t'; uin++; break;
+ case 'n': *(uout++) = '\n'; uin++; break;
+ case 'v': *(uout++) = '\v'; uin++; break;
+ case 'f': *(uout++) = '\f'; uin++; break;
+ case 'r': *(uout++) = '\r'; uin++; break;
+
+ default : *(uout++) = *(uin++); break;
+ }
+ }
+
+ *nmem = ((gchar *) uout) - outmem;
+}
+
+static FileMatchType
+file_check_single_magic (const gchar *offset,
+ const gchar *type,
+ const gchar *value,
+ const guchar *file_head,
+ gint headsize,
+ GFile *file,
+ GInputStream *input)
+
+{
+ FileMatchType found = FILE_MATCH_NONE;
+ glong offs;
+ gulong num_testval;
+ gulong num_operator_val;
+ gint numbytes, k;
+ const gchar *num_operator_ptr;
+ gchar num_operator;
+
+ /* Check offset */
+ if (sscanf (offset, "%ld", &offs) != 1)
+ return FILE_MATCH_NONE;
+
+ /* Check type of test */
+ num_operator_ptr = NULL;
+ num_operator = '\0';
+
+ if (g_str_has_prefix (type, "byte"))
+ {
+ numbytes = 1;
+ num_operator_ptr = type + strlen ("byte");
+ }
+ else if (g_str_has_prefix (type, "short"))
+ {
+ numbytes = 2;
+ num_operator_ptr = type + strlen ("short");
+ }
+ else if (g_str_has_prefix (type, "long"))
+ {
+ numbytes = 4;
+ num_operator_ptr = type + strlen ("long");
+ }
+ else if (g_str_has_prefix (type, "size"))
+ {
+ numbytes = 5;
+ }
+ else if (strcmp (type, "string") == 0)
+ {
+ numbytes = 0;
+ }
+ else
+ {
+ return FILE_MATCH_NONE;
+ }
+
+ /* Check numerical operator value if present */
+ if (num_operator_ptr && (*num_operator_ptr == '&'))
+ {
+ if (g_ascii_isdigit (num_operator_ptr[1]))
+ {
+ if (num_operator_ptr[1] != '0') /* decimal */
+ sscanf (num_operator_ptr+1, "%lu", &num_operator_val);
+ else if (num_operator_ptr[2] == 'x') /* hexadecimal */
+ sscanf (num_operator_ptr+3, "%lx", &num_operator_val);
+ else /* octal */
+ sscanf (num_operator_ptr+2, "%lo", &num_operator_val);
+
+ num_operator = *num_operator_ptr;
+ }
+ }
+
+ if (numbytes > 0)
+ {
+ /* Numerical test */
+
+ gchar num_test = '=';
+ gulong fileval = 0;
+
+ /* Check test value */
+ if ((value[0] == '>') || (value[0] == '<'))
+ {
+ num_test = value[0];
+ value++;
+ }
+
+ errno = 0;
+ num_testval = strtol (value, NULL, 0);
+
+ if (errno != 0)
+ return FILE_MATCH_NONE;
+
+ if (numbytes == 5)
+ {
+ /* Check for file size */
+
+ GFileInfo *info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_SIZE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+ if (! info)
+ return FILE_MATCH_NONE;
+
+ fileval = g_file_info_get_size (info);
+ g_object_unref (info);
+ }
+ else if (offs >= 0 &&
+ (offs + numbytes <= headsize))
+ {
+ /* We have it in memory */
+
+ for (k = 0; k < numbytes; k++)
+ fileval = (fileval << 8) | (glong) file_head[offs + k];
+ }
+ else
+ {
+ /* Read it from file */
+
+ if (! g_seekable_seek (G_SEEKABLE (input), offs,
+ (offs >= 0) ? G_SEEK_SET : G_SEEK_END,
+ NULL, NULL))
+ return FILE_MATCH_NONE;
+
+ for (k = 0; k < numbytes; k++)
+ {
+ guchar byte;
+ GError *error = NULL;
+
+ byte = g_data_input_stream_read_byte (G_DATA_INPUT_STREAM (input),
+ NULL, &error);
+ if (error)
+ {
+ g_clear_error (&error);
+ return FILE_MATCH_NONE;
+ }
+
+ fileval = (fileval << 8) | byte;
+ }
+ }
+
+ if (num_operator == '&')
+ fileval &= num_operator_val;
+
+ if (num_test == '<')
+ {
+ if (fileval < num_testval)
+ found = numbytes;
+ }
+ else if (num_test == '>')
+ {
+ if (fileval > num_testval)
+ found = numbytes;
+ }
+ else
+ {
+ if (fileval == num_testval)
+ found = numbytes;
+ }
+
+ if (found && (numbytes == 5))
+ found = FILE_MATCH_SIZE;
+ }
+ else if (numbytes == 0)
+ {
+ /* String test */
+
+ gchar mem_testval[256];
+
+ file_convert_string (value,
+ mem_testval, sizeof (mem_testval),
+ &numbytes);
+
+ if (numbytes <= 0)
+ return FILE_MATCH_NONE;
+
+ if (offs >= 0 &&
+ (offs + numbytes <= headsize))
+ {
+ /* We have it in memory */
+
+ if (memcmp (mem_testval, file_head + offs, numbytes) == 0)
+ found = numbytes;
+ }
+ else
+ {
+ /* Read it from file */
+
+ if (! g_seekable_seek (G_SEEKABLE (input), offs,
+ (offs >= 0) ? G_SEEK_SET : G_SEEK_END,
+ NULL, NULL))
+ return FILE_MATCH_NONE;
+
+ for (k = 0; k < numbytes; k++)
+ {
+ guchar byte;
+ GError *error = NULL;
+
+ byte = g_data_input_stream_read_byte (G_DATA_INPUT_STREAM (input),
+ NULL, &error);
+ if (error)
+ {
+ g_clear_error (&error);
+
+ return FILE_MATCH_NONE;
+ }
+
+ if (byte != mem_testval[k])
+ return FILE_MATCH_NONE;
+ }
+
+ found = numbytes;
+ }
+ }
+
+ return found;
+}
+
+static FileMatchType
+file_check_magic_list (GSList *magics_list,
+ const guchar *head,
+ gint headsize,
+ GFile *file,
+ GInputStream *input)
+
+{
+ gboolean and = FALSE;
+ gboolean found = FALSE;
+ FileMatchType best_match_val = FILE_MATCH_NONE;
+ FileMatchType match_val = FILE_MATCH_NONE;
+
+ for (; magics_list; magics_list = magics_list->next)
+ {
+ const gchar *offset;
+ const gchar *type;
+ const gchar *value;
+ FileMatchType single_match_val = FILE_MATCH_NONE;
+
+ if ((offset = magics_list->data) == NULL) return FILE_MATCH_NONE;
+ if ((magics_list = magics_list->next) == NULL) return FILE_MATCH_NONE;
+ if ((type = magics_list->data) == NULL) return FILE_MATCH_NONE;
+ if ((magics_list = magics_list->next) == NULL) return FILE_MATCH_NONE;
+ if ((value = magics_list->data) == NULL) return FILE_MATCH_NONE;
+
+ single_match_val = file_check_single_magic (offset, type, value,
+ head, headsize,
+ file, input);
+
+ if (and)
+ found = found && (single_match_val != FILE_MATCH_NONE);
+ else
+ found = (single_match_val != FILE_MATCH_NONE);
+
+ if (found)
+ {
+ if (match_val == FILE_MATCH_NONE)
+ {
+ /* if we have no match yet, this is it in any case */
+
+ match_val = single_match_val;
+ }
+ else if (single_match_val != FILE_MATCH_NONE)
+ {
+ /* else if we have a match on this one, combine it with the
+ * existing return value
+ */
+
+ if (single_match_val == FILE_MATCH_SIZE)
+ {
+ /* if we already have a magic match, simply increase
+ * that by one to indicate "better match", not perfect
+ * but better than losing the additional size match
+ * entirely
+ */
+ if (match_val != FILE_MATCH_SIZE)
+ match_val += 1;
+ }
+ else
+ {
+ /* if we already have a magic match, simply add to its
+ * length; otherwise if we already have a size match,
+ * combine it with this match, see comment above
+ */
+ if (match_val != FILE_MATCH_SIZE)
+ match_val += single_match_val;
+ else
+ match_val = single_match_val + 1;
+ }
+ }
+ }
+ else
+ {
+ match_val = FILE_MATCH_NONE;
+ }
+
+ and = (strchr (offset, '&') != NULL);
+
+ if (! and)
+ {
+ /* when done with this 'and' list, update best_match_val */
+
+ if (best_match_val == FILE_MATCH_NONE)
+ {
+ /* if we have no best match yet, this is it */
+
+ best_match_val = match_val;
+ }
+ else if (match_val != FILE_MATCH_NONE)
+ {
+ /* otherwise if this was a match, update the best match, note
+ * that by using MAX we will not overwrite a magic match
+ * with a size match
+ */
+
+ best_match_val = MAX (best_match_val, match_val);
+ }
+
+ match_val = FILE_MATCH_NONE;
+ }
+ }
+
+ return best_match_val;
+}
diff --git a/app/plug-in/gimppluginmanager-file-procedure.h b/app/plug-in/gimppluginmanager-file-procedure.h
new file mode 100644
index 0000000..3508551
--- /dev/null
+++ b/app/plug-in/gimppluginmanager-file-procedure.h
@@ -0,0 +1,37 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginmanager-file-procedure.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_MANAGER_FILE_PROCEDURE_H__
+#define __GIMP_PLUG_IN_MANAGER_FILE_PROCEDURE_H__
+
+
+
+GimpPlugInProcedure *file_procedure_find (GSList *procs,
+ GFile *file,
+ GError **error);
+GimpPlugInProcedure *file_procedure_find_by_prefix (GSList *procs,
+ GFile *file);
+GimpPlugInProcedure *file_procedure_find_by_extension (GSList *procs,
+ GFile *file);
+
+GimpPlugInProcedure *file_procedure_find_by_mime_type (GSList *procs,
+ const gchar *mime_type);
+
+
+#endif /* __GIMP_PLUG_IN_MANAGER_FILE_PROCEDURE_H__ */
diff --git a/app/plug-in/gimppluginmanager-file.c b/app/plug-in/gimppluginmanager-file.c
new file mode 100644
index 0000000..4da5e55
--- /dev/null
+++ b/app/plug-in/gimppluginmanager-file.c
@@ -0,0 +1,451 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginmanager-file.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "plug-in-types.h"
+
+#include "core/gimp.h"
+#include "core/gimpparamspecs.h"
+
+#include "gimpplugin.h"
+#include "gimpplugindef.h"
+#include "gimppluginmanager.h"
+#include "gimppluginmanager-file.h"
+#include "gimppluginmanager-file-procedure.h"
+#include "gimppluginprocedure.h"
+
+
+static gboolean file_procedure_in_group (GimpPlugInProcedure *file_proc,
+ GimpFileProcedureGroup group);
+
+
+/* public functions */
+
+gboolean
+gimp_plug_in_manager_register_load_handler (GimpPlugInManager *manager,
+ const gchar *name,
+ const gchar *extensions,
+ const gchar *prefixes,
+ const gchar *magics)
+{
+ GimpPlugInProcedure *file_proc;
+ GimpProcedure *procedure;
+ GSList *list;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ if (manager->current_plug_in && manager->current_plug_in->plug_in_def)
+ list = manager->current_plug_in->plug_in_def->procedures;
+ else
+ list = manager->plug_in_procedures;
+
+ file_proc = gimp_plug_in_procedure_find (list, name);
+
+ if (! file_proc)
+ {
+ gimp_message (manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "attempt to register nonexistent load handler \"%s\"",
+ name);
+ return FALSE;
+ }
+
+ procedure = GIMP_PROCEDURE (file_proc);
+
+ if ((procedure->num_args < 3) ||
+ (procedure->num_values < 1) ||
+ ! GIMP_IS_PARAM_SPEC_INT32 (procedure->args[0]) ||
+ ! G_IS_PARAM_SPEC_STRING (procedure->args[1]) ||
+ ! G_IS_PARAM_SPEC_STRING (procedure->args[2]) ||
+ ! GIMP_IS_PARAM_SPEC_IMAGE_ID (procedure->values[0]))
+ {
+ gimp_message (manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "load handler \"%s\" does not take the standard "
+ "load handler args", name);
+ return FALSE;
+ }
+
+ gimp_plug_in_procedure_set_file_proc (file_proc,
+ extensions, prefixes, magics);
+
+ if (! g_slist_find (manager->load_procs, file_proc))
+ manager->load_procs = g_slist_prepend (manager->load_procs, file_proc);
+
+ return TRUE;
+}
+
+gboolean
+gimp_plug_in_manager_register_save_handler (GimpPlugInManager *manager,
+ const gchar *name,
+ const gchar *extensions,
+ const gchar *prefixes)
+{
+ GimpPlugInProcedure *file_proc;
+ GimpProcedure *procedure;
+ GSList *list;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ if (manager->current_plug_in && manager->current_plug_in->plug_in_def)
+ list = manager->current_plug_in->plug_in_def->procedures;
+ else
+ list = manager->plug_in_procedures;
+
+ file_proc = gimp_plug_in_procedure_find (list, name);
+
+ if (! file_proc)
+ {
+ gimp_message (manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "attempt to register nonexistent save handler \"%s\"",
+ name);
+ return FALSE;
+ }
+
+ procedure = GIMP_PROCEDURE (file_proc);
+
+ if ((procedure->num_args < 5) ||
+ ! GIMP_IS_PARAM_SPEC_INT32 (procedure->args[0]) ||
+ ! GIMP_IS_PARAM_SPEC_IMAGE_ID (procedure->args[1]) ||
+ ! GIMP_IS_PARAM_SPEC_DRAWABLE_ID (procedure->args[2]) ||
+ ! G_IS_PARAM_SPEC_STRING (procedure->args[3]) ||
+ ! G_IS_PARAM_SPEC_STRING (procedure->args[4]))
+ {
+ gimp_message (manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ "save handler \"%s\" does not take the standard "
+ "save handler args", name);
+ return FALSE;
+ }
+
+ gimp_plug_in_procedure_set_file_proc (file_proc,
+ extensions, prefixes, NULL);
+
+ if (file_procedure_in_group (file_proc, GIMP_FILE_PROCEDURE_GROUP_SAVE))
+ {
+ if (! g_slist_find (manager->save_procs, file_proc))
+ manager->save_procs = g_slist_prepend (manager->save_procs, file_proc);
+ }
+
+ if (file_procedure_in_group (file_proc, GIMP_FILE_PROCEDURE_GROUP_EXPORT))
+ {
+ if (! g_slist_find (manager->export_procs, file_proc))
+ manager->export_procs = g_slist_prepend (manager->export_procs, file_proc);
+ }
+
+ return TRUE;
+}
+
+gboolean
+gimp_plug_in_manager_register_priority (GimpPlugInManager *manager,
+ const gchar *name,
+ gint priority)
+{
+ GimpPlugInProcedure *file_proc;
+ GSList *list;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ if (manager->current_plug_in && manager->current_plug_in->plug_in_def)
+ list = manager->current_plug_in->plug_in_def->procedures;
+ else
+ list = manager->plug_in_procedures;
+
+ file_proc = gimp_plug_in_procedure_find (list, name);
+
+ if (! file_proc)
+ return FALSE;
+
+ gimp_plug_in_procedure_set_priority (file_proc, priority);
+
+ return TRUE;
+}
+
+gboolean
+gimp_plug_in_manager_register_mime_types (GimpPlugInManager *manager,
+ const gchar *name,
+ const gchar *mime_types)
+{
+ GimpPlugInProcedure *file_proc;
+ GSList *list;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+ g_return_val_if_fail (mime_types != NULL, FALSE);
+
+ if (manager->current_plug_in && manager->current_plug_in->plug_in_def)
+ list = manager->current_plug_in->plug_in_def->procedures;
+ else
+ list = manager->plug_in_procedures;
+
+ file_proc = gimp_plug_in_procedure_find (list, name);
+
+ if (! file_proc)
+ return FALSE;
+
+ gimp_plug_in_procedure_set_mime_types (file_proc, mime_types);
+
+ return TRUE;
+}
+
+gboolean
+gimp_plug_in_manager_register_handles_uri (GimpPlugInManager *manager,
+ const gchar *name)
+{
+ GimpPlugInProcedure *file_proc;
+ GSList *list;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ if (manager->current_plug_in && manager->current_plug_in->plug_in_def)
+ list = manager->current_plug_in->plug_in_def->procedures;
+ else
+ list = manager->plug_in_procedures;
+
+ file_proc = gimp_plug_in_procedure_find (list, name);
+
+ if (! file_proc)
+ return FALSE;
+
+ gimp_plug_in_procedure_set_handles_uri (file_proc);
+
+ return TRUE;
+}
+
+gboolean
+gimp_plug_in_manager_register_handles_raw (GimpPlugInManager *manager,
+ const gchar *name)
+{
+ GimpPlugInProcedure *file_proc;
+ GSList *list;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ if (manager->current_plug_in && manager->current_plug_in->plug_in_def)
+ list = manager->current_plug_in->plug_in_def->procedures;
+ else
+ list = manager->plug_in_procedures;
+
+ file_proc = gimp_plug_in_procedure_find (list, name);
+
+ if (! file_proc)
+ return FALSE;
+
+ gimp_plug_in_procedure_set_handles_raw (file_proc);
+
+ return TRUE;
+}
+
+gboolean
+gimp_plug_in_manager_register_thumb_loader (GimpPlugInManager *manager,
+ const gchar *load_proc,
+ const gchar *thumb_proc)
+{
+ GimpPlugInProcedure *file_proc;
+ GSList *list;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), FALSE);
+ g_return_val_if_fail (load_proc, FALSE);
+ g_return_val_if_fail (thumb_proc, FALSE);
+
+ if (manager->current_plug_in && manager->current_plug_in->plug_in_def)
+ list = manager->current_plug_in->plug_in_def->procedures;
+ else
+ list = manager->plug_in_procedures;
+
+ file_proc = gimp_plug_in_procedure_find (list, load_proc);
+
+ if (! file_proc)
+ return FALSE;
+
+ gimp_plug_in_procedure_set_thumb_loader (file_proc, thumb_proc);
+
+ return TRUE;
+}
+
+GSList *
+gimp_plug_in_manager_get_file_procedures (GimpPlugInManager *manager,
+ GimpFileProcedureGroup group)
+{
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), NULL);
+
+ switch (group)
+ {
+ case GIMP_FILE_PROCEDURE_GROUP_NONE:
+ return NULL;
+
+ case GIMP_FILE_PROCEDURE_GROUP_OPEN:
+ return manager->display_load_procs;
+
+ case GIMP_FILE_PROCEDURE_GROUP_SAVE:
+ return manager->display_save_procs;
+
+ case GIMP_FILE_PROCEDURE_GROUP_EXPORT:
+ return manager->display_export_procs;
+
+ default:
+ g_return_val_if_reached (NULL);
+ }
+}
+
+GimpPlugInProcedure *
+gimp_plug_in_manager_file_procedure_find (GimpPlugInManager *manager,
+ GimpFileProcedureGroup group,
+ GFile *file,
+ GError **error)
+{
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), NULL);
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ switch (group)
+ {
+ case GIMP_FILE_PROCEDURE_GROUP_OPEN:
+ return file_procedure_find (manager->load_procs, file, error);
+
+ case GIMP_FILE_PROCEDURE_GROUP_SAVE:
+ return file_procedure_find (manager->save_procs, file, error);
+
+ case GIMP_FILE_PROCEDURE_GROUP_EXPORT:
+ return file_procedure_find (manager->export_procs, file, error);
+
+ default:
+ g_return_val_if_reached (NULL);
+ }
+}
+
+GimpPlugInProcedure *
+gimp_plug_in_manager_file_procedure_find_by_prefix (GimpPlugInManager *manager,
+ GimpFileProcedureGroup group,
+ GFile *file)
+{
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), NULL);
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+ switch (group)
+ {
+ case GIMP_FILE_PROCEDURE_GROUP_OPEN:
+ return file_procedure_find_by_prefix (manager->load_procs, file);
+
+ case GIMP_FILE_PROCEDURE_GROUP_SAVE:
+ return file_procedure_find_by_prefix (manager->save_procs, file);
+
+ case GIMP_FILE_PROCEDURE_GROUP_EXPORT:
+ return file_procedure_find_by_prefix (manager->export_procs, file);
+
+ default:
+ g_return_val_if_reached (NULL);
+ }
+}
+
+GimpPlugInProcedure *
+gimp_plug_in_manager_file_procedure_find_by_extension (GimpPlugInManager *manager,
+ GimpFileProcedureGroup group,
+ GFile *file)
+{
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), NULL);
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+ switch (group)
+ {
+ case GIMP_FILE_PROCEDURE_GROUP_OPEN:
+ return file_procedure_find_by_extension (manager->load_procs, file);
+
+ case GIMP_FILE_PROCEDURE_GROUP_SAVE:
+ return file_procedure_find_by_extension (manager->save_procs, file);
+
+ case GIMP_FILE_PROCEDURE_GROUP_EXPORT:
+ return file_procedure_find_by_extension (manager->export_procs, file);
+
+ default:
+ g_return_val_if_reached (NULL);
+ }
+}
+
+GimpPlugInProcedure *
+gimp_plug_in_manager_file_procedure_find_by_mime_type (GimpPlugInManager *manager,
+ GimpFileProcedureGroup group,
+ const gchar *mime_type)
+{
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), NULL);
+ g_return_val_if_fail (mime_type != NULL, NULL);
+
+ switch (group)
+ {
+ case GIMP_FILE_PROCEDURE_GROUP_OPEN:
+ return file_procedure_find_by_mime_type (manager->load_procs, mime_type);
+
+ case GIMP_FILE_PROCEDURE_GROUP_SAVE:
+ return file_procedure_find_by_mime_type (manager->save_procs, mime_type);
+
+ case GIMP_FILE_PROCEDURE_GROUP_EXPORT:
+ return file_procedure_find_by_mime_type (manager->export_procs, mime_type);
+
+ default:
+ g_return_val_if_reached (NULL);
+ }
+}
+
+
+/* private functions */
+
+static gboolean
+file_procedure_in_group (GimpPlugInProcedure *file_proc,
+ GimpFileProcedureGroup group)
+{
+ const gchar *name = gimp_object_get_name (file_proc);
+ gboolean is_xcf_save = FALSE;
+ gboolean is_filter = FALSE;
+
+ is_xcf_save = (strcmp (name, "gimp-xcf-save") == 0);
+
+ is_filter = (strcmp (name, "file-gz-save") == 0 ||
+ strcmp (name, "file-bz2-save") == 0 ||
+ strcmp (name, "file-xz-save") == 0);
+
+ switch (group)
+ {
+ case GIMP_FILE_PROCEDURE_GROUP_NONE:
+ return FALSE;
+
+ case GIMP_FILE_PROCEDURE_GROUP_SAVE:
+ /* Only .xcf shall pass */
+ return is_xcf_save || is_filter;
+
+ case GIMP_FILE_PROCEDURE_GROUP_EXPORT:
+ /* Anything but .xcf shall pass */
+ return ! is_xcf_save;
+
+ case GIMP_FILE_PROCEDURE_GROUP_OPEN:
+ /* No filter applied for Open */
+ return TRUE;
+
+ default:
+ case GIMP_FILE_PROCEDURE_GROUP_ANY:
+ return TRUE;
+ }
+}
diff --git a/app/plug-in/gimppluginmanager-file.h b/app/plug-in/gimppluginmanager-file.h
new file mode 100644
index 0000000..8408218
--- /dev/null
+++ b/app/plug-in/gimppluginmanager-file.h
@@ -0,0 +1,75 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginmanager-file.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_MANAGER_FILE_H__
+#define __GIMP_PLUG_IN_MANAGER_FILE_H__
+
+
+gboolean gimp_plug_in_manager_register_load_handler (GimpPlugInManager *manager,
+ const gchar *name,
+ const gchar *extensions,
+ const gchar *prefixes,
+ const gchar *magics);
+gboolean gimp_plug_in_manager_register_save_handler (GimpPlugInManager *manager,
+ const gchar *name,
+ const gchar *extensions,
+ const gchar *prefixes);
+
+gboolean gimp_plug_in_manager_register_priority (GimpPlugInManager *manager,
+ const gchar *name,
+ gint priority);
+
+
+gboolean gimp_plug_in_manager_register_mime_types (GimpPlugInManager *manager,
+ const gchar *name,
+ const gchar *mime_types);
+
+gboolean gimp_plug_in_manager_register_handles_uri (GimpPlugInManager *manager,
+ const gchar *name);
+
+gboolean gimp_plug_in_manager_register_handles_raw (GimpPlugInManager *manager,
+ const gchar *name);
+
+gboolean gimp_plug_in_manager_register_thumb_loader (GimpPlugInManager *manager,
+ const gchar *load_proc,
+ const gchar *thumb_proc);
+
+GSList * gimp_plug_in_manager_get_file_procedures (GimpPlugInManager *manager,
+ GimpFileProcedureGroup group);
+
+GimpPlugInProcedure *
+gimp_plug_in_manager_file_procedure_find (GimpPlugInManager *manager,
+ GimpFileProcedureGroup group,
+ GFile *file,
+ GError **error);
+GimpPlugInProcedure *
+gimp_plug_in_manager_file_procedure_find_by_prefix (GimpPlugInManager *manager,
+ GimpFileProcedureGroup group,
+ GFile *file);
+GimpPlugInProcedure *
+gimp_plug_in_manager_file_procedure_find_by_extension (GimpPlugInManager *manager,
+ GimpFileProcedureGroup group,
+ GFile *file);
+GimpPlugInProcedure *
+gimp_plug_in_manager_file_procedure_find_by_mime_type (GimpPlugInManager *manager,
+ GimpFileProcedureGroup group,
+ const gchar *mime_type);
+
+
+#endif /* __GIMP_PLUG_IN_MANAGER_FILE_H__ */
diff --git a/app/plug-in/gimppluginmanager-help-domain.c b/app/plug-in/gimppluginmanager-help-domain.c
new file mode 100644
index 0000000..96ea41b
--- /dev/null
+++ b/app/plug-in/gimppluginmanager-help-domain.c
@@ -0,0 +1,160 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginmanager-help-domain.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gio/gio.h>
+
+#include "plug-in-types.h"
+
+#include "gimppluginmanager.h"
+#include "gimppluginmanager-help-domain.h"
+
+
+typedef struct _GimpPlugInHelpDomain GimpPlugInHelpDomain;
+
+struct _GimpPlugInHelpDomain
+{
+ GFile *file;
+ gchar *domain_name;
+ gchar *domain_uri;
+};
+
+
+void
+gimp_plug_in_manager_help_domain_exit (GimpPlugInManager *manager)
+{
+ GSList *list;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+
+ for (list = manager->help_domains; list; list = list->next)
+ {
+ GimpPlugInHelpDomain *domain = list->data;
+
+ g_object_unref (domain->file);
+ g_free (domain->domain_name);
+ g_free (domain->domain_uri);
+ g_slice_free (GimpPlugInHelpDomain, domain);
+ }
+
+ g_slist_free (manager->help_domains);
+ manager->help_domains = NULL;
+}
+
+void
+gimp_plug_in_manager_add_help_domain (GimpPlugInManager *manager,
+ GFile *file,
+ const gchar *domain_name,
+ const gchar *domain_uri)
+{
+ GimpPlugInHelpDomain *domain;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+ g_return_if_fail (G_IS_FILE (file));
+ g_return_if_fail (domain_name != NULL);
+
+ domain = g_slice_new (GimpPlugInHelpDomain);
+
+ domain->file = g_object_ref (file);
+ domain->domain_name = g_strdup (domain_name);
+ domain->domain_uri = g_strdup (domain_uri);
+
+ manager->help_domains = g_slist_prepend (manager->help_domains, domain);
+
+#ifdef VERBOSE
+ g_print ("added help domain \"%s\" for base uri \"%s\"\n",
+ domain->domain_name ? domain->domain_name : "(null)",
+ domain->domain_uri ? domain->domain_uri : "(null)");
+#endif
+}
+
+const gchar *
+gimp_plug_in_manager_get_help_domain (GimpPlugInManager *manager,
+ GFile *file,
+ const gchar **domain_uri)
+{
+ GSList *list;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), NULL);
+ g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL);
+
+ if (domain_uri)
+ *domain_uri = NULL;
+
+ /* A NULL prog_name is GIMP itself, return the default domain */
+ if (! file)
+ return NULL;
+
+ for (list = manager->help_domains; list; list = list->next)
+ {
+ GimpPlugInHelpDomain *domain = list->data;
+
+ if (domain && domain->file &&
+ g_file_equal (domain->file, file))
+ {
+ if (domain_uri && domain->domain_uri)
+ *domain_uri = domain->domain_uri;
+
+ return domain->domain_name;
+ }
+ }
+
+ return NULL;
+}
+
+gint
+gimp_plug_in_manager_get_help_domains (GimpPlugInManager *manager,
+ gchar ***help_domains,
+ gchar ***help_uris)
+{
+ gint n_domains;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), 0);
+ g_return_val_if_fail (help_domains != NULL, 0);
+ g_return_val_if_fail (help_uris != NULL, 0);
+
+ n_domains = g_slist_length (manager->help_domains);
+
+ if (n_domains > 0)
+ {
+ GSList *list;
+ gint i;
+
+ *help_domains = g_new0 (gchar *, n_domains + 1);
+ *help_uris = g_new0 (gchar *, n_domains + 1);
+
+ for (list = manager->help_domains, i = 0; list; list = list->next, i++)
+ {
+ GimpPlugInHelpDomain *domain = list->data;
+
+ (*help_domains)[i] = g_strdup (domain->domain_name);
+ (*help_uris)[i] = g_strdup (domain->domain_uri);
+ }
+ }
+ else
+ {
+ *help_domains = NULL;
+ *help_uris = NULL;
+ }
+
+ return n_domains;
+}
diff --git a/app/plug-in/gimppluginmanager-help-domain.h b/app/plug-in/gimppluginmanager-help-domain.h
new file mode 100644
index 0000000..e4f8972
--- /dev/null
+++ b/app/plug-in/gimppluginmanager-help-domain.h
@@ -0,0 +1,43 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginmanager-help-domain.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_MANAGER_HELP_DOMAIN_H__
+#define __GIMP_PLUG_IN_MANAGER_HELP_DOMAIN_H__
+
+
+void gimp_plug_in_manager_help_domain_exit (GimpPlugInManager *manager);
+
+/* Add a help domain */
+void gimp_plug_in_manager_add_help_domain (GimpPlugInManager *manager,
+ GFile *file,
+ const gchar *domain_name,
+ const gchar *domain_uri);
+
+/* Retrieve a plug-ins help domain */
+const gchar * gimp_plug_in_manager_get_help_domain (GimpPlugInManager *manager,
+ GFile *file,
+ const gchar **help_uri);
+
+/* Retrieve all help domains */
+gint gimp_plug_in_manager_get_help_domains (GimpPlugInManager *manager,
+ gchar ***help_domains,
+ gchar ***help_uris);
+
+
+#endif /* __GIMP_PLUG_IN_MANAGER_HELP_DOMAIN_H__ */
diff --git a/app/plug-in/gimppluginmanager-locale-domain.c b/app/plug-in/gimppluginmanager-locale-domain.c
new file mode 100644
index 0000000..ae0433b
--- /dev/null
+++ b/app/plug-in/gimppluginmanager-locale-domain.c
@@ -0,0 +1,180 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginmanager-locale-domain.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gio/gio.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "plug-in-types.h"
+
+#include "gimppluginmanager.h"
+#include "gimppluginmanager-locale-domain.h"
+
+
+#define STD_PLUG_INS_LOCALE_DOMAIN GETTEXT_PACKAGE "-std-plug-ins"
+
+
+typedef struct _GimpPlugInLocaleDomain GimpPlugInLocaleDomain;
+
+struct _GimpPlugInLocaleDomain
+{
+ GFile *file;
+ gchar *domain_name;
+ gchar *domain_path;
+};
+
+
+void
+gimp_plug_in_manager_locale_domain_exit (GimpPlugInManager *manager)
+{
+ GSList *list;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+
+ for (list = manager->locale_domains; list; list = list->next)
+ {
+ GimpPlugInLocaleDomain *domain = list->data;
+
+ g_object_unref (domain->file);
+ g_free (domain->domain_name);
+ g_free (domain->domain_path);
+ g_slice_free (GimpPlugInLocaleDomain, domain);
+ }
+
+ g_slist_free (manager->locale_domains);
+ manager->locale_domains = NULL;
+}
+
+void
+gimp_plug_in_manager_add_locale_domain (GimpPlugInManager *manager,
+ GFile *file,
+ const gchar *domain_name,
+ const gchar *domain_path)
+{
+ GimpPlugInLocaleDomain *domain;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+ g_return_if_fail (G_IS_FILE (file));
+ g_return_if_fail (domain_name != NULL);
+
+ domain = g_slice_new (GimpPlugInLocaleDomain);
+
+ domain->file = g_object_ref (file);
+ domain->domain_name = g_strdup (domain_name);
+ domain->domain_path = g_strdup (domain_path);
+
+ manager->locale_domains = g_slist_prepend (manager->locale_domains, domain);
+
+#ifdef VERBOSE
+ g_print ("added locale domain \"%s\" for path \"%s\"\n",
+ domain->domain_name ? domain->domain_name : "(null)",
+ domain->domain_path ?
+ gimp_filename_to_utf8 (domain->domain_path) : "(null)");
+#endif
+}
+
+const gchar *
+gimp_plug_in_manager_get_locale_domain (GimpPlugInManager *manager,
+ GFile *file,
+ const gchar **domain_path)
+{
+ GSList *list;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), NULL);
+ g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL);
+
+ if (domain_path)
+ *domain_path = gimp_locale_directory ();
+
+ /* A NULL prog_name is GIMP itself, return the default domain */
+ if (! file)
+ return NULL;
+
+ for (list = manager->locale_domains; list; list = list->next)
+ {
+ GimpPlugInLocaleDomain *domain = list->data;
+
+ if (domain && domain->file &&
+ g_file_equal (domain->file, file))
+ {
+ if (domain_path && domain->domain_path)
+ *domain_path = domain->domain_path;
+
+ return domain->domain_name;
+ }
+ }
+
+ return STD_PLUG_INS_LOCALE_DOMAIN;
+}
+
+gint
+gimp_plug_in_manager_get_locale_domains (GimpPlugInManager *manager,
+ gchar ***locale_domains,
+ gchar ***locale_paths)
+{
+ GSList *list;
+ GSList *unique = NULL;
+ gint n_domains;
+ gint i;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), 0);
+ g_return_val_if_fail (locale_domains != NULL, 0);
+ g_return_val_if_fail (locale_paths != NULL, 0);
+
+ for (list = manager->locale_domains; list; list = list->next)
+ {
+ GimpPlugInLocaleDomain *domain = list->data;
+ GSList *tmp;
+
+ for (tmp = unique; tmp; tmp = tmp->next)
+ if (! strcmp (domain->domain_name, (const gchar *) tmp->data))
+ break;
+
+ if (! tmp)
+ unique = g_slist_prepend (unique, domain);
+ }
+
+ unique = g_slist_reverse (unique);
+
+ n_domains = g_slist_length (unique) + 1;
+
+ *locale_domains = g_new0 (gchar *, n_domains + 1);
+ *locale_paths = g_new0 (gchar *, n_domains + 1);
+
+ (*locale_domains)[0] = g_strdup (STD_PLUG_INS_LOCALE_DOMAIN);
+ (*locale_paths)[0] = g_strdup (gimp_locale_directory ());
+
+ for (list = unique, i = 1; list; list = list->next, i++)
+ {
+ GimpPlugInLocaleDomain *domain = list->data;
+
+ (*locale_domains)[i] = g_strdup (domain->domain_name);
+ (*locale_paths)[i] = (domain->domain_path ?
+ g_strdup (domain->domain_path) :
+ g_strdup (gimp_locale_directory ()));
+ }
+
+ g_slist_free (unique);
+
+ return n_domains;
+}
diff --git a/app/plug-in/gimppluginmanager-locale-domain.h b/app/plug-in/gimppluginmanager-locale-domain.h
new file mode 100644
index 0000000..2061ebe
--- /dev/null
+++ b/app/plug-in/gimppluginmanager-locale-domain.h
@@ -0,0 +1,43 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginmanager-locale-domain.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_MANAGER_LOCALE_DOMAIN_H__
+#define __GIMP_PLUG_IN_MANAGER_LOCALE_DOMAIN_H__
+
+
+void gimp_plug_in_manager_locale_domain_exit (GimpPlugInManager *manager);
+
+/* Add a locale domain */
+void gimp_plug_in_manager_add_locale_domain (GimpPlugInManager *manager,
+ GFile *file,
+ const gchar *domain_name,
+ const gchar *domain_path);
+
+/* Retrieve a plug-ins locale domain */
+const gchar * gimp_plug_in_manager_get_locale_domain (GimpPlugInManager *manager,
+ GFile *file,
+ const gchar **locale_path);
+
+/* Retrieve all locale domains */
+gint gimp_plug_in_manager_get_locale_domains (GimpPlugInManager *manager,
+ gchar ***locale_domains,
+ gchar ***locale_paths);
+
+
+#endif /* __GIMP_PLUG_IN_MANAGER_LOCALE_DOMAIN_H__ */
diff --git a/app/plug-in/gimppluginmanager-menu-branch.c b/app/plug-in/gimppluginmanager-menu-branch.c
new file mode 100644
index 0000000..c15d374
--- /dev/null
+++ b/app/plug-in/gimppluginmanager-menu-branch.c
@@ -0,0 +1,92 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginmanager-menu-branch.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "plug-in-types.h"
+
+#include "gimppluginmanager.h"
+#include "gimppluginmanager-menu-branch.h"
+#include "plug-in-menu-path.h"
+
+
+/* public functions */
+
+void
+gimp_plug_in_manager_menu_branch_exit (GimpPlugInManager *manager)
+{
+ GSList *list;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+
+ for (list = manager->menu_branches; list; list = list->next)
+ {
+ GimpPlugInMenuBranch *branch = list->data;
+
+ g_object_unref (branch->file);
+ g_free (branch->menu_path);
+ g_free (branch->menu_label);
+ g_slice_free (GimpPlugInMenuBranch, branch);
+ }
+
+ g_slist_free (manager->menu_branches);
+ manager->menu_branches = NULL;
+}
+
+void
+gimp_plug_in_manager_add_menu_branch (GimpPlugInManager *manager,
+ GFile *file,
+ const gchar *menu_path,
+ const gchar *menu_label)
+{
+ GimpPlugInMenuBranch *branch;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+ g_return_if_fail (G_IS_FILE (file));
+ g_return_if_fail (menu_path != NULL);
+ g_return_if_fail (menu_label != NULL);
+
+ branch = g_slice_new (GimpPlugInMenuBranch);
+
+ branch->file = g_object_ref (file);
+ branch->menu_path = plug_in_menu_path_map (menu_path, menu_label);
+ branch->menu_label = g_strdup (menu_label);
+
+ manager->menu_branches = g_slist_append (manager->menu_branches, branch);
+
+ g_signal_emit_by_name (manager, "menu-branch-added",
+ branch->file,
+ branch->menu_path,
+ branch->menu_label);
+
+#ifdef VERBOSE
+ g_print ("added menu branch \"%s\" at path \"%s\"\n",
+ branch->menu_label, branch->menu_path);
+#endif
+}
+
+GSList *
+gimp_plug_in_manager_get_menu_branches (GimpPlugInManager *manager)
+{
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), NULL);
+
+ return manager->menu_branches;
+}
diff --git a/app/plug-in/gimppluginmanager-menu-branch.h b/app/plug-in/gimppluginmanager-menu-branch.h
new file mode 100644
index 0000000..e092d01
--- /dev/null
+++ b/app/plug-in/gimppluginmanager-menu-branch.h
@@ -0,0 +1,42 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginmanager-menu-branch.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_MANAGER_MENU_BRANCH_H__
+#define __GIMP_PLUG_IN_MANAGER_MENU_BRANCH_H__
+
+
+struct _GimpPlugInMenuBranch
+{
+ GFile *file;
+ gchar *menu_path;
+ gchar *menu_label;
+};
+
+
+void gimp_plug_in_manager_menu_branch_exit (GimpPlugInManager *manager);
+
+/* Add a menu branch */
+void gimp_plug_in_manager_add_menu_branch (GimpPlugInManager *manager,
+ GFile *file,
+ const gchar *menu_path,
+ const gchar *menu_label);
+GSList * gimp_plug_in_manager_get_menu_branches (GimpPlugInManager *manager);
+
+
+#endif /* __GIMP_PLUG_IN_MANAGER_MENU_BRANCH_H__ */
diff --git a/app/plug-in/gimppluginmanager-query.c b/app/plug-in/gimppluginmanager-query.c
new file mode 100644
index 0000000..861a89a
--- /dev/null
+++ b/app/plug-in/gimppluginmanager-query.c
@@ -0,0 +1,162 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-2003 Spencer Kimball and Peter Mattis
+ *
+ * plug-ins-query.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "plug-in-types.h"
+
+#include "gimppluginmanager.h"
+#include "gimppluginmanager-query.h"
+#include "gimppluginprocedure.h"
+
+
+static gboolean
+match_string (GRegex *regex,
+ gchar *string)
+{
+ return g_regex_match (regex, string, 0, NULL);
+}
+
+gint
+gimp_plug_in_manager_query (GimpPlugInManager *manager,
+ const gchar *search_str,
+ gchar ***menu_strs,
+ gchar ***accel_strs,
+ gchar ***prog_strs,
+ gchar ***types_strs,
+ gchar ***realname_strs,
+ gint32 **time_ints)
+{
+ gint32 num_plugins = 0;
+ GSList *list;
+ GSList *matched = NULL;
+ gint i = 0;
+ GRegex *sregex = NULL;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager), 0);
+ g_return_val_if_fail (menu_strs != NULL, 0);
+ g_return_val_if_fail (accel_strs != NULL, 0);
+ g_return_val_if_fail (prog_strs != NULL, 0);
+ g_return_val_if_fail (types_strs != NULL, 0);
+ g_return_val_if_fail (realname_strs != NULL, 0);
+ g_return_val_if_fail (time_ints != NULL, 0);
+
+ *menu_strs = NULL;
+ *accel_strs = NULL;
+ *prog_strs = NULL;
+ *types_strs = NULL;
+ *realname_strs = NULL;
+ *time_ints = NULL;
+
+ if (search_str && ! strlen (search_str))
+ search_str = NULL;
+
+ if (search_str)
+ {
+ sregex = g_regex_new (search_str, G_REGEX_CASELESS | G_REGEX_OPTIMIZE, 0,
+ NULL);
+ if (! sregex)
+ return 0;
+ }
+
+ /* count number of plugin entries, then allocate arrays of correct size
+ * where we can store the strings.
+ */
+
+ for (list = manager->plug_in_procedures; list; list = g_slist_next (list))
+ {
+ GimpPlugInProcedure *proc = list->data;
+
+ if (proc->file && proc->menu_paths)
+ {
+ gchar *name;
+
+ if (proc->menu_label)
+ {
+ name = proc->menu_label;
+ }
+ else
+ {
+ name = strrchr (proc->menu_paths->data, '/');
+
+ if (name)
+ name = name + 1;
+ else
+ name = proc->menu_paths->data;
+ }
+
+ name = gimp_strip_uline (name);
+
+ if (! search_str || match_string (sregex, name))
+ {
+ num_plugins++;
+ matched = g_slist_prepend (matched, proc);
+ }
+
+ g_free (name);
+ }
+ }
+
+ *menu_strs = g_new (gchar *, num_plugins);
+ *accel_strs = g_new (gchar *, num_plugins);
+ *prog_strs = g_new (gchar *, num_plugins);
+ *types_strs = g_new (gchar *, num_plugins);
+ *realname_strs = g_new (gchar *, num_plugins);
+ *time_ints = g_new (gint, num_plugins);
+
+ matched = g_slist_reverse (matched);
+
+ for (list = matched; list; list = g_slist_next (list))
+ {
+ GimpPlugInProcedure *proc = list->data;
+ gchar *name;
+
+ if (proc->menu_label)
+ name = g_strdup_printf ("%s/%s",
+ (gchar *) proc->menu_paths->data,
+ proc->menu_label);
+ else
+ name = g_strdup (proc->menu_paths->data);
+
+ (*menu_strs)[i] = gimp_strip_uline (name);
+ (*accel_strs)[i] = NULL;
+ (*prog_strs)[i] = g_file_get_path (proc->file);
+ (*types_strs)[i] = g_strdup (proc->image_types);
+ (*realname_strs)[i] = g_strdup (gimp_object_get_name (proc));
+ (*time_ints)[i] = proc->mtime;
+
+ g_free (name);
+
+ i++;
+ }
+
+ g_slist_free (matched);
+
+ if (sregex)
+ g_regex_unref (sregex);
+
+ return num_plugins;
+}
diff --git a/app/plug-in/gimppluginmanager-query.h b/app/plug-in/gimppluginmanager-query.h
new file mode 100644
index 0000000..94b78c8
--- /dev/null
+++ b/app/plug-in/gimppluginmanager-query.h
@@ -0,0 +1,34 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginmanager-query.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_MANAGER_QUERY_H__
+#define __GIMP_PLUG_IN_MANAGER_QUERY_H__
+
+
+gint gimp_plug_in_manager_query (GimpPlugInManager *manager,
+ const gchar *search_str,
+ gchar ***menu_strs,
+ gchar ***accel_strs,
+ gchar ***prog_strs,
+ gchar ***types_strs,
+ gchar ***realname_strs,
+ gint32 **time_ints);
+
+
+#endif /* __GIMP_PLUG_IN_MANAGER_QUERY_H__ */
diff --git a/app/plug-in/gimppluginmanager-restore.c b/app/plug-in/gimppluginmanager-restore.c
new file mode 100644
index 0000000..a1f37ec
--- /dev/null
+++ b/app/plug-in/gimppluginmanager-restore.c
@@ -0,0 +1,1065 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-2002 Spencer Kimball, Peter Mattis, and others
+ *
+ * gimppluginmanager-restore.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "plug-in-types.h"
+
+#include "config/gimpcoreconfig.h"
+
+#include "core/gimp.h"
+#include "core/gimp-utils.h"
+
+#include "pdb/gimppdb.h"
+#include "pdb/gimppdbcontext.h"
+
+#include "gimpinterpreterdb.h"
+#include "gimpplugindef.h"
+#include "gimppluginmanager.h"
+#define __YES_I_NEED_GIMP_PLUG_IN_MANAGER_CALL__
+#include "gimppluginmanager-call.h"
+#include "gimppluginmanager-help-domain.h"
+#include "gimppluginmanager-locale-domain.h"
+#include "gimppluginmanager-restore.h"
+#include "gimppluginprocedure.h"
+#include "plug-in-rc.h"
+
+#include "gimp-intl.h"
+
+
+static void gimp_plug_in_manager_search (GimpPlugInManager *manager,
+ GimpInitStatusFunc status_callback);
+static void gimp_plug_in_manager_search_directory (GimpPlugInManager *manager,
+ GFile *directory);
+static GFile * gimp_plug_in_manager_get_pluginrc (GimpPlugInManager *manager);
+static void gimp_plug_in_manager_read_pluginrc (GimpPlugInManager *manager,
+ GFile *file,
+ GimpInitStatusFunc status_callback);
+static void gimp_plug_in_manager_query_new (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpInitStatusFunc status_callback);
+static void gimp_plug_in_manager_init_plug_ins (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpInitStatusFunc status_callback);
+static void gimp_plug_in_manager_run_extensions (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpInitStatusFunc status_callback);
+static void gimp_plug_in_manager_bind_text_domains (GimpPlugInManager *manager);
+static void gimp_plug_in_manager_add_from_file (GimpPlugInManager *manager,
+ GFile *file,
+ guint64 mtime);
+static void gimp_plug_in_manager_add_from_rc (GimpPlugInManager *manager,
+ GimpPlugInDef *plug_in_def);
+static void gimp_plug_in_manager_add_to_db (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpPlugInProcedure *proc);
+static void gimp_plug_in_manager_sort_file_procs (GimpPlugInManager *manager);
+static gint gimp_plug_in_manager_file_proc_compare (gconstpointer a,
+ gconstpointer b,
+ gpointer data);
+
+
+
+void
+gimp_plug_in_manager_restore (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpInitStatusFunc status_callback)
+{
+ Gimp *gimp;
+ GFile *pluginrc;
+ GSList *list;
+ GError *error = NULL;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+ g_return_if_fail (GIMP_IS_CONTEXT (context));
+ g_return_if_fail (status_callback != NULL);
+
+ gimp = manager->gimp;
+
+ /* need a GimpPDBContext for calling gimp_plug_in_manager_run_foo() */
+ context = gimp_pdb_context_new (gimp, context, TRUE);
+
+ /* search for binaries in the plug-in directory path */
+ gimp_plug_in_manager_search (manager, status_callback);
+
+ /* read the pluginrc file for cached data */
+ pluginrc = gimp_plug_in_manager_get_pluginrc (manager);
+
+ gimp_plug_in_manager_read_pluginrc (manager, pluginrc, status_callback);
+
+ /* query any plug-ins that changed since we last wrote out pluginrc */
+ gimp_plug_in_manager_query_new (manager, context, status_callback);
+
+ /* initialize the plug-ins */
+ gimp_plug_in_manager_init_plug_ins (manager, context, status_callback);
+
+ /* add the procedures to manager->plug_in_procedures */
+ for (list = manager->plug_in_defs; list; list = list->next)
+ {
+ GimpPlugInDef *plug_in_def = list->data;
+ GSList *list2;
+
+ for (list2 = plug_in_def->procedures; list2; list2 = list2->next)
+ {
+ gimp_plug_in_manager_add_procedure (manager, list2->data);
+ }
+ }
+
+ /* write the pluginrc file if necessary */
+ if (manager->write_pluginrc)
+ {
+ if (gimp->be_verbose)
+ g_print ("Writing '%s'\n", gimp_file_get_utf8_name (pluginrc));
+
+ if (! plug_in_rc_write (manager->plug_in_defs, pluginrc, &error))
+ {
+ gimp_message_literal (gimp,
+ NULL, GIMP_MESSAGE_ERROR, error->message);
+ g_clear_error (&error);
+ }
+
+ manager->write_pluginrc = FALSE;
+ }
+
+ g_object_unref (pluginrc);
+
+ /* create locale and help domain lists */
+ for (list = manager->plug_in_defs; list; list = list->next)
+ {
+ GimpPlugInDef *plug_in_def = list->data;
+
+ if (plug_in_def->locale_domain_name)
+ gimp_plug_in_manager_add_locale_domain (manager,
+ plug_in_def->file,
+ plug_in_def->locale_domain_name,
+ plug_in_def->locale_domain_path);
+ else
+ /* set the default plug-in locale domain */
+ gimp_plug_in_def_set_locale_domain (plug_in_def,
+ gimp_plug_in_manager_get_locale_domain (manager,
+ plug_in_def->file,
+ NULL),
+ NULL);
+
+ if (plug_in_def->help_domain_name)
+ gimp_plug_in_manager_add_help_domain (manager,
+ plug_in_def->file,
+ plug_in_def->help_domain_name,
+ plug_in_def->help_domain_uri);
+ }
+
+ /* we're done with the plug-in-defs */
+ g_slist_free_full (manager->plug_in_defs, (GDestroyNotify) g_object_unref);
+ manager->plug_in_defs = NULL;
+
+ /* bind plug-in text domains */
+ gimp_plug_in_manager_bind_text_domains (manager);
+
+ /* add the plug-in procs to the procedure database */
+ for (list = manager->plug_in_procedures; list; list = list->next)
+ {
+ gimp_plug_in_manager_add_to_db (manager, context, list->data);
+ }
+
+ /* sort the load, save and export procedures, make the raw handler list */
+ gimp_plug_in_manager_sort_file_procs (manager);
+
+ gimp_plug_in_manager_run_extensions (manager, context, status_callback);
+
+ g_object_unref (context);
+}
+
+
+/* search for binaries in the plug-in directory path */
+static void
+gimp_plug_in_manager_search (GimpPlugInManager *manager,
+ GimpInitStatusFunc status_callback)
+{
+ const gchar *path_str;
+ GList *path;
+ GList *list;
+
+#ifdef G_OS_WIN32
+ const gchar *pathext = g_getenv ("PATHEXT");
+
+ /* On Windows, we need to add the known file extensions in PATHEXT. */
+ if (pathext)
+ {
+ gchar *exts;
+
+ exts = gimp_interpreter_db_get_extensions (manager->interpreter_db);
+
+ if (exts)
+ {
+ gchar *value;
+
+ value = g_strconcat (pathext, G_SEARCHPATH_SEPARATOR_S, exts, NULL);
+
+ g_setenv ("PATHEXT", value, TRUE);
+
+ g_free (value);
+ g_free (exts);
+ }
+ }
+#endif /* G_OS_WIN32 */
+
+ status_callback (_("Searching plug-ins"), "", 0.0);
+
+ /* Give automatic tests a chance to use plug-ins from the build
+ * dir
+ */
+ path_str = g_getenv ("GIMP_TESTING_PLUGINDIRS");
+ if (! path_str)
+ path_str = manager->gimp->config->plug_in_path;
+
+ path = gimp_config_path_expand_to_files (path_str, NULL);
+
+ for (list = path; list; list = g_list_next (list))
+ {
+ gimp_plug_in_manager_search_directory (manager, list->data);
+ }
+
+ g_list_free_full (path, (GDestroyNotify) g_object_unref);
+}
+
+static void
+gimp_plug_in_manager_search_directory (GimpPlugInManager *manager,
+ GFile *directory)
+{
+ GFileEnumerator *enumerator;
+
+ enumerator = g_file_enumerate_children (directory,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
+ G_FILE_ATTRIBUTE_TIME_MODIFIED,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+ if (enumerator)
+ {
+ GFileInfo *info;
+
+ while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)))
+ {
+ GFile *child;
+
+ if (g_file_info_get_is_hidden (info))
+ {
+ g_object_unref (info);
+ continue;
+ }
+
+ child = g_file_enumerator_get_child (enumerator, info);
+
+ if (gimp_file_is_executable (child))
+ {
+ guint64 mtime;
+
+ mtime = g_file_info_get_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED);
+
+ gimp_plug_in_manager_add_from_file (manager, child, mtime);
+ }
+ else if (g_file_query_file_type (child,
+ G_FILE_QUERY_INFO_NONE,
+ NULL) == G_FILE_TYPE_DIRECTORY)
+ {
+ /* Search in subdirectory the first executable file with
+ * the same name as the directory (except extension).
+ * We don't search recursively, but only at a single
+ * level and assume that there can be only 1 plugin
+ * inside a directory.
+ */
+ GFileEnumerator *enumerator2;
+
+ enumerator2 = g_file_enumerate_children (child,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
+ G_FILE_ATTRIBUTE_TIME_MODIFIED,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+ if (enumerator2)
+ {
+ GFileInfo *info2;
+
+ while ((info2 = g_file_enumerator_next_file (enumerator2, NULL, NULL)))
+ {
+ GFile *child2;
+ gchar *file_name;
+ char *ext;
+
+ if (g_file_info_get_is_hidden (info2))
+ {
+ g_object_unref (info2);
+ continue;
+ }
+
+ child2 = g_file_enumerator_get_child (enumerator2, info2);
+ file_name = g_strdup (g_file_info_get_name (info2));
+
+ ext = strrchr (file_name, '.');
+ if (ext)
+ *ext = '\0';
+
+ if (g_strcmp0 (file_name, g_file_info_get_name (info)) == 0 &&
+ gimp_file_is_executable (child2))
+ {
+ guint64 mtime;
+
+ mtime = g_file_info_get_attribute_uint64 (info2,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED);
+
+ gimp_plug_in_manager_add_from_file (manager, child2, mtime);
+
+ g_free (file_name);
+ g_object_unref (child2);
+ g_object_unref (info2);
+ break;
+ }
+
+ g_free (file_name);
+ g_object_unref (child2);
+ g_object_unref (info2);
+ }
+
+ g_object_unref (enumerator2);
+ }
+ }
+
+ g_object_unref (child);
+ g_object_unref (info);
+ }
+
+ g_object_unref (enumerator);
+ }
+}
+
+static GFile *
+gimp_plug_in_manager_get_pluginrc (GimpPlugInManager *manager)
+{
+ Gimp *gimp = manager->gimp;
+ GFile *pluginrc;
+
+ if (gimp->config->plug_in_rc_path)
+ {
+ gchar *path = gimp_config_path_expand (gimp->config->plug_in_rc_path,
+ TRUE, NULL);
+
+ if (g_path_is_absolute (path))
+ pluginrc = g_file_new_for_path (path);
+ else
+ pluginrc = gimp_directory_file (path, NULL);
+
+ g_free (path);
+ }
+ else
+ {
+ pluginrc = gimp_directory_file ("pluginrc", NULL);
+ }
+
+ return pluginrc;
+}
+
+/* read the pluginrc file for cached data */
+static void
+gimp_plug_in_manager_read_pluginrc (GimpPlugInManager *manager,
+ GFile *pluginrc,
+ GimpInitStatusFunc status_callback)
+{
+ GSList *rc_defs;
+ GError *error = NULL;
+
+ status_callback (_("Resource configuration"),
+ gimp_file_get_utf8_name (pluginrc), 0.0);
+
+ if (manager->gimp->be_verbose)
+ g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (pluginrc));
+
+ rc_defs = plug_in_rc_parse (manager->gimp, pluginrc, &error);
+
+ if (rc_defs)
+ {
+ GSList *list;
+
+ for (list = rc_defs; list; list = g_slist_next (list))
+ gimp_plug_in_manager_add_from_rc (manager, list->data); /* consumes list->data */
+
+ g_slist_free (rc_defs);
+ }
+ else if (error)
+ {
+ if (error->code != GIMP_CONFIG_ERROR_OPEN_ENOENT)
+ gimp_message_literal (manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ error->message);
+
+ g_clear_error (&error);
+ }
+}
+
+/* query any plug-ins that changed since we last wrote out pluginrc */
+static void
+gimp_plug_in_manager_query_new (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpInitStatusFunc status_callback)
+{
+ GSList *list;
+ gint n_plugins;
+
+ status_callback (_("Querying new Plug-ins"), "", 0.0);
+
+ for (list = manager->plug_in_defs, n_plugins = 0; list; list = list->next)
+ {
+ GimpPlugInDef *plug_in_def = list->data;
+
+ if (plug_in_def->needs_query)
+ n_plugins++;
+ }
+
+ if (n_plugins)
+ {
+ gint nth;
+
+ manager->write_pluginrc = TRUE;
+
+ for (list = manager->plug_in_defs, nth = 0; list; list = list->next)
+ {
+ GimpPlugInDef *plug_in_def = list->data;
+
+ if (plug_in_def->needs_query)
+ {
+ gchar *basename;
+
+ basename =
+ g_path_get_basename (gimp_file_get_utf8_name (plug_in_def->file));
+ status_callback (NULL, basename,
+ (gdouble) nth++ / (gdouble) n_plugins);
+ g_free (basename);
+
+ if (manager->gimp->be_verbose)
+ g_print ("Querying plug-in: '%s'\n",
+ gimp_file_get_utf8_name (plug_in_def->file));
+
+ gimp_plug_in_manager_call_query (manager, context, plug_in_def);
+ }
+ }
+ }
+
+ status_callback (NULL, "", 1.0);
+}
+
+/* initialize the plug-ins */
+static void
+gimp_plug_in_manager_init_plug_ins (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpInitStatusFunc status_callback)
+{
+ GSList *list;
+ gint n_plugins;
+
+ status_callback (_("Initializing Plug-ins"), "", 0.0);
+
+ for (list = manager->plug_in_defs, n_plugins = 0; list; list = list->next)
+ {
+ GimpPlugInDef *plug_in_def = list->data;
+
+ if (plug_in_def->has_init)
+ n_plugins++;
+ }
+
+ if (n_plugins)
+ {
+ gint nth;
+
+ for (list = manager->plug_in_defs, nth = 0; list; list = list->next)
+ {
+ GimpPlugInDef *plug_in_def = list->data;
+
+ if (plug_in_def->has_init)
+ {
+ gchar *basename;
+
+ basename =
+ g_path_get_basename (gimp_file_get_utf8_name (plug_in_def->file));
+ status_callback (NULL, basename,
+ (gdouble) nth++ / (gdouble) n_plugins);
+ g_free (basename);
+
+ if (manager->gimp->be_verbose)
+ g_print ("Initializing plug-in: '%s'\n",
+ gimp_file_get_utf8_name (plug_in_def->file));
+
+ gimp_plug_in_manager_call_init (manager, context, plug_in_def);
+ }
+ }
+ }
+
+ status_callback (NULL, "", 1.0);
+}
+
+/* run automatically started extensions */
+static void
+gimp_plug_in_manager_run_extensions (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpInitStatusFunc status_callback)
+{
+ Gimp *gimp = manager->gimp;
+ GSList *list;
+ GList *extensions = NULL;
+ gint n_extensions;
+
+ /* build list of automatically started extensions */
+ for (list = manager->plug_in_procedures; list; list = list->next)
+ {
+ GimpPlugInProcedure *proc = list->data;
+
+ if (proc->file &&
+ GIMP_PROCEDURE (proc)->proc_type == GIMP_EXTENSION &&
+ GIMP_PROCEDURE (proc)->num_args == 0)
+ {
+ extensions = g_list_prepend (extensions, proc);
+ }
+ }
+
+ extensions = g_list_reverse (extensions);
+ n_extensions = g_list_length (extensions);
+
+ /* run the available extensions */
+ if (extensions)
+ {
+ GList *list;
+ gint nth;
+
+ status_callback (_("Starting Extensions"), "", 0.0);
+
+ for (list = extensions, nth = 0; list; list = g_list_next (list), nth++)
+ {
+ GimpPlugInProcedure *proc = list->data;
+ GimpValueArray *args;
+ GError *error = NULL;
+
+ if (gimp->be_verbose)
+ g_print ("Starting extension: '%s'\n", gimp_object_get_name (proc));
+
+ status_callback (NULL, gimp_object_get_name (proc),
+ (gdouble) nth / (gdouble) n_extensions);
+
+ args = gimp_value_array_new (0);
+
+ gimp_procedure_execute_async (GIMP_PROCEDURE (proc),
+ gimp, context, NULL,
+ args, NULL, &error);
+
+ gimp_value_array_unref (args);
+
+ if (error)
+ {
+ gimp_message_literal (gimp, NULL, GIMP_MESSAGE_ERROR,
+ error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ g_list_free (extensions);
+
+ status_callback (NULL, "", 1.0);
+ }
+}
+
+static void
+gimp_plug_in_manager_bind_text_domains (GimpPlugInManager *manager)
+{
+ gchar **locale_domains;
+ gchar **locale_paths;
+ gint n_domains;
+ gint i;
+
+ n_domains = gimp_plug_in_manager_get_locale_domains (manager,
+ &locale_domains,
+ &locale_paths);
+
+ for (i = 0; i < n_domains; i++)
+ {
+ bindtextdomain (locale_domains[i], locale_paths[i]);
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (locale_domains[i], "UTF-8");
+#endif
+ }
+
+ g_strfreev (locale_domains);
+ g_strfreev (locale_paths);
+}
+
+/**
+ * gimp_plug_in_manager_ignore_plugin_basename:
+ * @basename: Basename to test with
+ *
+ * Checks the environment variable
+ * GIMP_TESTING_PLUGINDIRS_BASENAME_IGNORES for file basenames.
+ *
+ * Returns: %TRUE if @basename was in GIMP_TESTING_PLUGINDIRS_BASENAME_IGNORES
+ **/
+static gboolean
+gimp_plug_in_manager_ignore_plugin_basename (const gchar *plugin_basename)
+{
+ const gchar *ignore_basenames_string;
+ GList *ignore_basenames;
+ GList *iter;
+ gboolean ignore = FALSE;
+
+ ignore_basenames_string = g_getenv ("GIMP_TESTING_PLUGINDIRS_BASENAME_IGNORES");
+ ignore_basenames = gimp_path_parse (ignore_basenames_string,
+ 256 /*max_paths*/,
+ FALSE /*check*/,
+ NULL /*check_failed*/);
+
+ for (iter = ignore_basenames; iter; iter = g_list_next (iter))
+ {
+ const gchar *ignore_basename = iter->data;
+
+ if (g_ascii_strcasecmp (ignore_basename, plugin_basename) == 0)
+ {
+ ignore = TRUE;
+ break;
+ }
+ }
+
+ gimp_path_free (ignore_basenames);
+
+ return ignore;
+}
+
+static void
+gimp_plug_in_manager_add_from_file (GimpPlugInManager *manager,
+ GFile *file,
+ guint64 mtime)
+{
+ GimpPlugInDef *plug_in_def;
+ GSList *list;
+ gchar *filename;
+ gchar *basename;
+
+ filename = g_file_get_path (file);
+ basename = g_path_get_basename (filename);
+ g_free (filename);
+
+ /* When we scan build dirs for plug-ins, there will be some
+ * executable files that are not plug-ins that we want to ignore,
+ * for example plug-ins/common/mkgen.pl if
+ * GIMP_TESTING_PLUGINDIRS=plug-ins/common
+ */
+ if (gimp_plug_in_manager_ignore_plugin_basename (basename))
+ {
+ g_free (basename);
+ return;
+ }
+
+ for (list = manager->plug_in_defs; list; list = list->next)
+ {
+ gchar *path;
+ gchar *plug_in_name;
+
+ plug_in_def = list->data;
+
+ path = g_file_get_path (plug_in_def->file);
+ plug_in_name = g_path_get_basename (path);
+ g_free (path);
+
+ if (g_ascii_strcasecmp (basename, plug_in_name) == 0)
+ {
+ g_printerr ("Skipping duplicate plug-in: '%s'\n",
+ gimp_file_get_utf8_name (file));
+
+ g_free (plug_in_name);
+ g_free (basename);
+
+ return;
+ }
+
+ g_free (plug_in_name);
+ }
+
+ g_free (basename);
+
+ plug_in_def = gimp_plug_in_def_new (file);
+
+ gimp_plug_in_def_set_mtime (plug_in_def, mtime);
+ gimp_plug_in_def_set_needs_query (plug_in_def, TRUE);
+
+ manager->plug_in_defs = g_slist_prepend (manager->plug_in_defs, plug_in_def);
+}
+
+static void
+gimp_plug_in_manager_add_from_rc (GimpPlugInManager *manager,
+ GimpPlugInDef *plug_in_def)
+{
+ GSList *list;
+ gchar *path1;
+ gchar *basename1;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+ g_return_if_fail (plug_in_def != NULL);
+ g_return_if_fail (plug_in_def->file != NULL);
+
+ path1 = g_file_get_path (plug_in_def->file);
+
+ if (! g_path_is_absolute (path1))
+ {
+ g_warning ("plug_ins_def_add_from_rc: filename not absolute (skipping)");
+ g_object_unref (plug_in_def);
+ g_free (path1);
+ return;
+ }
+
+ basename1 = g_path_get_basename (path1);
+
+ /* If this is a file load or save plugin, make sure we have
+ * something for one of the extensions, prefixes, or magic number.
+ * Other bits of code rely on detecting file plugins by the
+ * presence of one of these things, but the raw plug-in needs to be
+ * able to register no extensions, prefixes or magics.
+ */
+ for (list = plug_in_def->procedures; list; list = list->next)
+ {
+ GimpPlugInProcedure *proc = list->data;
+
+ if (! proc->extensions &&
+ ! proc->prefixes &&
+ ! proc->magics &&
+ proc->menu_paths &&
+ (g_str_has_prefix (proc->menu_paths->data, "<Load>") ||
+ g_str_has_prefix (proc->menu_paths->data, "<Save>")))
+ {
+ proc->extensions = g_strdup ("");
+ }
+ }
+
+ /* Check if the entry mentioned in pluginrc matches an executable
+ * found in the plug_in_path.
+ */
+ for (list = manager->plug_in_defs; list; list = list->next)
+ {
+ GimpPlugInDef *ondisk_plug_in_def = list->data;
+ gchar *path2;
+ gchar *basename2;
+
+ path2 = g_file_get_path (ondisk_plug_in_def->file);
+
+ basename2 = g_path_get_basename (path2);
+
+ g_free (path2);
+
+ if (! strcmp (basename1, basename2))
+ {
+ if (g_file_equal (plug_in_def->file,
+ ondisk_plug_in_def->file) &&
+ (plug_in_def->mtime == ondisk_plug_in_def->mtime))
+ {
+ /* Use pluginrc entry, deleting on-disk entry */
+ list->data = plug_in_def;
+ g_object_unref (ondisk_plug_in_def);
+ }
+ else
+ {
+ /* Use on-disk entry, deleting pluginrc entry */
+ g_object_unref (plug_in_def);
+ }
+
+ g_free (basename2);
+ g_free (basename1);
+ g_free (path1);
+
+ return;
+ }
+
+ g_free (basename2);
+ }
+
+ g_free (basename1);
+ g_free (path1);
+
+ manager->write_pluginrc = TRUE;
+
+ if (manager->gimp->be_verbose)
+ {
+ g_printerr ("pluginrc lists '%s', but it wasn't found\n",
+ gimp_file_get_utf8_name (plug_in_def->file));
+ }
+
+ g_object_unref (plug_in_def);
+}
+
+
+static void
+gimp_plug_in_manager_add_to_db (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpPlugInProcedure *proc)
+{
+ gimp_pdb_register_procedure (manager->gimp->pdb, GIMP_PROCEDURE (proc));
+
+ if (proc->file_proc)
+ {
+ GimpValueArray *return_vals;
+ GError *error = NULL;
+
+ if (proc->image_types)
+ {
+ return_vals =
+ gimp_pdb_execute_procedure_by_name (manager->gimp->pdb,
+ context, NULL, &error,
+ "gimp-register-save-handler",
+ G_TYPE_STRING, gimp_object_get_name (proc),
+ G_TYPE_STRING, proc->extensions,
+ G_TYPE_STRING, proc->prefixes,
+ G_TYPE_NONE);
+ }
+ else
+ {
+ return_vals =
+ gimp_pdb_execute_procedure_by_name (manager->gimp->pdb,
+ context, NULL, &error,
+ "gimp-register-magic-load-handler",
+ G_TYPE_STRING, gimp_object_get_name (proc),
+ G_TYPE_STRING, proc->extensions,
+ G_TYPE_STRING, proc->prefixes,
+ G_TYPE_STRING, proc->magics,
+ G_TYPE_NONE);
+ }
+
+ gimp_value_array_unref (return_vals);
+
+ if (error)
+ {
+ gimp_message_literal (manager->gimp, NULL, GIMP_MESSAGE_ERROR,
+ error->message);
+ g_error_free (error);
+ }
+ }
+}
+
+static void
+gimp_plug_in_manager_sort_file_procs (GimpPlugInManager *manager)
+{
+ GimpCoreConfig *config = manager->gimp->config;
+ GFile *config_plug_in = NULL;
+ GFile *raw_plug_in = NULL;
+ GSList *list;
+
+ manager->load_procs =
+ g_slist_sort_with_data (manager->load_procs,
+ gimp_plug_in_manager_file_proc_compare,
+ GINT_TO_POINTER (FALSE));
+ manager->save_procs =
+ g_slist_sort_with_data (manager->save_procs,
+ gimp_plug_in_manager_file_proc_compare,
+ GINT_TO_POINTER (FALSE));
+ manager->export_procs =
+ g_slist_sort_with_data (manager->export_procs,
+ gimp_plug_in_manager_file_proc_compare,
+ GINT_TO_POINTER (FALSE));
+
+ g_clear_pointer (&manager->display_load_procs, g_slist_free);
+ g_clear_pointer (&manager->display_save_procs, g_slist_free);
+ g_clear_pointer (&manager->display_export_procs, g_slist_free);
+
+ manager->display_load_procs = g_slist_copy (manager->load_procs);
+ manager->display_save_procs = g_slist_copy (manager->save_procs);
+ manager->display_export_procs = g_slist_copy (manager->export_procs);
+
+ manager->display_load_procs =
+ g_slist_sort_with_data (manager->display_load_procs,
+ gimp_plug_in_manager_file_proc_compare,
+ GINT_TO_POINTER (TRUE));
+ manager->display_save_procs =
+ g_slist_sort_with_data (manager->display_save_procs,
+ gimp_plug_in_manager_file_proc_compare,
+ GINT_TO_POINTER (TRUE));
+ manager->display_export_procs =
+ g_slist_sort_with_data (manager->display_export_procs,
+ gimp_plug_in_manager_file_proc_compare,
+ GINT_TO_POINTER (TRUE));
+
+ g_clear_pointer (&manager->raw_load_procs, g_slist_free);
+ g_clear_pointer (&manager->display_raw_load_procs, g_slist_free);
+
+ if (config->import_raw_plug_in)
+ {
+ /* remember the configured raw loader, unless it's the placeholder */
+ if (! strstr (config->import_raw_plug_in, "file-raw-placeholder"))
+ config_plug_in =
+ gimp_file_new_for_config_path (config->import_raw_plug_in,
+ NULL);
+ }
+
+ /* make the list of raw loaders, and remember the one configured in
+ * config if found
+ */
+ for (list = manager->load_procs; list; list = g_slist_next (list))
+ {
+ GimpPlugInProcedure *file_proc = list->data;
+
+ if (file_proc->handles_raw)
+ {
+ GFile *file;
+
+ manager->raw_load_procs = g_slist_prepend (manager->raw_load_procs,
+ file_proc);
+
+ file = gimp_plug_in_procedure_get_file (file_proc);
+
+ if (! raw_plug_in &&
+ config_plug_in &&
+ g_file_equal (config_plug_in, file))
+ {
+ raw_plug_in = file;
+ }
+ }
+ }
+
+ manager->raw_load_procs = g_slist_reverse (manager->raw_load_procs);
+ manager->display_raw_load_procs = g_slist_copy (manager->raw_load_procs);
+ manager->display_raw_load_procs =
+ g_slist_sort_with_data (manager->display_raw_load_procs,
+ gimp_plug_in_manager_file_proc_compare,
+ GINT_TO_POINTER (TRUE));
+
+ if (config_plug_in)
+ g_object_unref (config_plug_in);
+
+ /* if no raw loader was configured, or the configured raw loader
+ * wasn't found, default to the first loader that is not the
+ * placeholder, if any
+ */
+ if (! raw_plug_in && manager->raw_load_procs)
+ {
+ gchar *path;
+
+ for (list = manager->raw_load_procs; list; list = g_slist_next (list))
+ {
+ GimpPlugInProcedure *file_proc = list->data;
+
+ raw_plug_in = gimp_plug_in_procedure_get_file (file_proc);
+
+ path = gimp_file_get_config_path (raw_plug_in, NULL);
+
+ if (! strstr (path, "file-raw-placeholder"))
+ break;
+
+ g_free (path);
+ path = NULL;
+
+ raw_plug_in = NULL;
+ }
+
+ if (! raw_plug_in)
+ {
+ raw_plug_in =
+ gimp_plug_in_procedure_get_file (manager->raw_load_procs->data);
+
+ path = gimp_file_get_config_path (raw_plug_in, NULL);
+ }
+
+ g_object_set (config,
+ "import-raw-plug-in", path,
+ NULL);
+
+ g_free (path);
+ }
+
+ /* finally, remove all raw loaders except the configured one from
+ * the list of load_procs
+ */
+ list = manager->load_procs;
+ while (list)
+ {
+ GimpPlugInProcedure *file_proc = list->data;
+
+ list = g_slist_next (list);
+
+ if (file_proc->handles_raw &&
+ ! g_file_equal (gimp_plug_in_procedure_get_file (file_proc),
+ raw_plug_in))
+ {
+ manager->load_procs =
+ g_slist_remove (manager->load_procs, file_proc);
+ manager->display_load_procs =
+ g_slist_remove (manager->display_load_procs, file_proc);
+ }
+ }
+}
+
+static gint
+gimp_plug_in_manager_file_proc_compare (gconstpointer a,
+ gconstpointer b,
+ gpointer data)
+{
+ GimpPlugInProcedure *proc_a = GIMP_PLUG_IN_PROCEDURE (a);
+ GimpPlugInProcedure *proc_b = GIMP_PLUG_IN_PROCEDURE (b);
+ gboolean display = GPOINTER_TO_INT (data);
+ const gchar *label_a;
+ const gchar *label_b;
+
+ if (g_str_has_prefix (gimp_file_get_utf8_name (proc_a->file),
+ "gimp-xcf"))
+ {
+ if (! g_str_has_prefix (gimp_file_get_utf8_name (proc_b->file),
+ "gimp-xcf"))
+ {
+ return -1;
+ }
+ }
+ else if (g_str_has_prefix (gimp_file_get_utf8_name (proc_b->file),
+ "gimp-xcf"))
+ {
+ return 1;
+ }
+
+ if (! display && proc_a->priority != proc_b->priority)
+ return proc_a->priority - proc_b->priority;
+
+ label_a = gimp_procedure_get_label (GIMP_PROCEDURE (proc_a));
+ label_b = gimp_procedure_get_label (GIMP_PROCEDURE (proc_b));
+
+ if (label_a)
+ {
+ if (label_b)
+ {
+ gint comp = g_utf8_collate (label_a, label_b);
+
+ if (comp)
+ return comp;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ else if (label_b)
+ {
+ return 1;
+ }
+
+ return strcmp (gimp_object_get_name (proc_a), gimp_object_get_name (proc_b));
+}
diff --git a/app/plug-in/gimppluginmanager-restore.h b/app/plug-in/gimppluginmanager-restore.h
new file mode 100644
index 0000000..0389dcf
--- /dev/null
+++ b/app/plug-in/gimppluginmanager-restore.h
@@ -0,0 +1,29 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginmanager-restore.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_MANAGER_RESTORE_H__
+#define __GIMP_PLUG_IN_MANAGER_RESTORE_H__
+
+
+void gimp_plug_in_manager_restore (GimpPlugInManager *manager,
+ GimpContext *context,
+ GimpInitStatusFunc status_callback);
+
+
+#endif /* __GIMP_PLUG_IN_MANAGER_RESTORE_H__ */
diff --git a/app/plug-in/gimppluginmanager.c b/app/plug-in/gimppluginmanager.c
new file mode 100644
index 0000000..d8952a2
--- /dev/null
+++ b/app/plug-in/gimppluginmanager.c
@@ -0,0 +1,426 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-2002 Spencer Kimball, Peter Mattis, and others
+ *
+ * gimppluginmanager.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "plug-in-types.h"
+
+#include "config/gimpcoreconfig.h"
+
+#include "core/gimp.h"
+#include "core/gimp-filter-history.h"
+#include "core/gimp-memsize.h"
+#include "core/gimpmarshal.h"
+
+#include "pdb/gimppdb.h"
+
+#include "gimpenvirontable.h"
+#include "gimpinterpreterdb.h"
+#include "gimpplugin.h"
+#include "gimpplugindebug.h"
+#include "gimpplugindef.h"
+#include "gimppluginmanager.h"
+#include "gimppluginmanager-data.h"
+#include "gimppluginmanager-help-domain.h"
+#include "gimppluginmanager-locale-domain.h"
+#include "gimppluginmanager-menu-branch.h"
+#include "gimppluginshm.h"
+#include "gimptemporaryprocedure.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PLUG_IN_OPENED,
+ PLUG_IN_CLOSED,
+ MENU_BRANCH_ADDED,
+ LAST_SIGNAL
+};
+
+
+static void gimp_plug_in_manager_finalize (GObject *object);
+
+static gint64 gimp_plug_in_manager_get_memsize (GimpObject *object,
+ gint64 *gui_size);
+
+
+G_DEFINE_TYPE (GimpPlugInManager, gimp_plug_in_manager, GIMP_TYPE_OBJECT)
+
+#define parent_class gimp_plug_in_manager_parent_class
+
+static guint manager_signals[LAST_SIGNAL] = { 0, };
+
+
+static void
+gimp_plug_in_manager_class_init (GimpPlugInManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
+
+ manager_signals[PLUG_IN_OPENED] =
+ g_signal_new ("plug-in-opened",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GimpPlugInManagerClass,
+ plug_in_opened),
+ NULL, NULL,
+ gimp_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ GIMP_TYPE_PLUG_IN);
+
+ manager_signals[PLUG_IN_CLOSED] =
+ g_signal_new ("plug-in-closed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GimpPlugInManagerClass,
+ plug_in_closed),
+ NULL, NULL,
+ gimp_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ GIMP_TYPE_PLUG_IN);
+
+ manager_signals[MENU_BRANCH_ADDED] =
+ g_signal_new ("menu-branch-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GimpPlugInManagerClass,
+ menu_branch_added),
+ NULL, NULL,
+ gimp_marshal_VOID__OBJECT_STRING_STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_FILE,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+
+ object_class->finalize = gimp_plug_in_manager_finalize;
+
+ gimp_object_class->get_memsize = gimp_plug_in_manager_get_memsize;
+}
+
+static void
+gimp_plug_in_manager_init (GimpPlugInManager *manager)
+{
+}
+
+static void
+gimp_plug_in_manager_finalize (GObject *object)
+{
+ GimpPlugInManager *manager = GIMP_PLUG_IN_MANAGER (object);
+
+ g_clear_pointer (&manager->load_procs, g_slist_free);
+ g_clear_pointer (&manager->save_procs, g_slist_free);
+ g_clear_pointer (&manager->export_procs, g_slist_free);
+ g_clear_pointer (&manager->raw_load_procs, g_slist_free);
+
+ g_clear_pointer (&manager->display_load_procs, g_slist_free);
+ g_clear_pointer (&manager->display_save_procs, g_slist_free);
+ g_clear_pointer (&manager->display_export_procs, g_slist_free);
+ g_clear_pointer (&manager->display_raw_load_procs, g_slist_free);
+
+ if (manager->plug_in_procedures)
+ {
+ g_slist_free_full (manager->plug_in_procedures,
+ (GDestroyNotify) g_object_unref);
+ manager->plug_in_procedures = NULL;
+ }
+
+ if (manager->plug_in_defs)
+ {
+ g_slist_free_full (manager->plug_in_defs,
+ (GDestroyNotify) g_object_unref);
+ manager->plug_in_defs = NULL;
+ }
+
+ g_clear_object (&manager->environ_table);
+ g_clear_object (&manager->interpreter_db);
+
+ g_clear_pointer (&manager->debug, gimp_plug_in_debug_free);
+
+ gimp_plug_in_manager_menu_branch_exit (manager);
+ gimp_plug_in_manager_locale_domain_exit (manager);
+ gimp_plug_in_manager_help_domain_exit (manager);
+ gimp_plug_in_manager_data_free (manager);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gint64
+gimp_plug_in_manager_get_memsize (GimpObject *object,
+ gint64 *gui_size)
+{
+ GimpPlugInManager *manager = GIMP_PLUG_IN_MANAGER (object);
+ gint64 memsize = 0;
+
+ memsize += gimp_g_slist_get_memsize_foreach (manager->plug_in_defs,
+ (GimpMemsizeFunc)
+ gimp_object_get_memsize,
+ gui_size);
+
+ memsize += gimp_g_slist_get_memsize (manager->plug_in_procedures, 0);
+ memsize += gimp_g_slist_get_memsize (manager->load_procs, 0);
+ memsize += gimp_g_slist_get_memsize (manager->save_procs, 0);
+ memsize += gimp_g_slist_get_memsize (manager->export_procs, 0);
+ memsize += gimp_g_slist_get_memsize (manager->raw_load_procs, 0);
+ memsize += gimp_g_slist_get_memsize (manager->display_load_procs, 0);
+ memsize += gimp_g_slist_get_memsize (manager->display_save_procs, 0);
+ memsize += gimp_g_slist_get_memsize (manager->display_export_procs, 0);
+ memsize += gimp_g_slist_get_memsize (manager->display_raw_load_procs, 0);
+
+ memsize += gimp_g_slist_get_memsize (manager->menu_branches, 0 /* FIXME */);
+ memsize += gimp_g_slist_get_memsize (manager->locale_domains, 0 /* FIXME */);
+ memsize += gimp_g_slist_get_memsize (manager->help_domains, 0 /* FIXME */);
+
+ memsize += gimp_g_slist_get_memsize_foreach (manager->open_plug_ins,
+ (GimpMemsizeFunc)
+ gimp_object_get_memsize,
+ gui_size);
+ memsize += gimp_g_slist_get_memsize (manager->plug_in_stack, 0);
+
+ memsize += 0; /* FIXME manager->shm */
+ memsize += /* FIXME */ gimp_g_object_get_memsize (G_OBJECT (manager->interpreter_db));
+ memsize += /* FIXME */ gimp_g_object_get_memsize (G_OBJECT (manager->environ_table));
+ memsize += 0; /* FIXME manager->plug_in_debug */
+ memsize += gimp_g_list_get_memsize (manager->data_list, 0 /* FIXME */);
+
+ return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
+ gui_size);
+}
+
+GimpPlugInManager *
+gimp_plug_in_manager_new (Gimp *gimp)
+{
+ GimpPlugInManager *manager;
+
+ manager = g_object_new (GIMP_TYPE_PLUG_IN_MANAGER, NULL);
+
+ manager->gimp = gimp;
+ manager->interpreter_db = gimp_interpreter_db_new (gimp->be_verbose);
+ manager->environ_table = gimp_environ_table_new (gimp->be_verbose);
+
+ return manager;
+}
+
+void
+gimp_plug_in_manager_initialize (GimpPlugInManager *manager,
+ GimpInitStatusFunc status_callback)
+{
+ GimpCoreConfig *config;
+ GList *path;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+ g_return_if_fail (status_callback != NULL);
+
+ config = manager->gimp->config;
+
+ status_callback (NULL, _("Plug-in Interpreters"), 0.8);
+
+ path = gimp_config_path_expand_to_files (config->interpreter_path, NULL);
+ gimp_interpreter_db_load (manager->interpreter_db, path);
+ g_list_free_full (path, (GDestroyNotify) g_object_unref);
+
+ status_callback (NULL, _("Plug-in Environment"), 0.9);
+
+ path = gimp_config_path_expand_to_files (config->environ_path, NULL);
+ gimp_environ_table_load (manager->environ_table, path);
+ g_list_free_full (path, (GDestroyNotify) g_object_unref);
+
+ /* allocate a piece of shared memory for use in transporting tiles
+ * to plug-ins. if we can't allocate a piece of shared memory then
+ * we'll fall back on sending the data over the pipe.
+ */
+ if (manager->gimp->use_shm)
+ manager->shm = gimp_plug_in_shm_new ();
+
+ manager->debug = gimp_plug_in_debug_new ();
+}
+
+void
+gimp_plug_in_manager_exit (GimpPlugInManager *manager)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+
+ while (manager->open_plug_ins)
+ gimp_plug_in_close (manager->open_plug_ins->data, TRUE);
+
+ /* need to detach from shared memory, we can't rely on exit()
+ * cleaning up behind us (see bug #609026)
+ */
+ if (manager->shm)
+ {
+ gimp_plug_in_shm_free (manager->shm);
+ manager->shm = NULL;
+ }
+}
+
+void
+gimp_plug_in_manager_add_procedure (GimpPlugInManager *manager,
+ GimpPlugInProcedure *procedure)
+{
+ GSList *list;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+ g_return_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (procedure));
+
+ for (list = manager->plug_in_procedures; list; list = list->next)
+ {
+ GimpPlugInProcedure *tmp_proc = list->data;
+
+ if (strcmp (gimp_object_get_name (procedure),
+ gimp_object_get_name (tmp_proc)) == 0)
+ {
+ GSList *list2;
+
+ list->data = g_object_ref (procedure);
+
+ g_printerr ("Removing duplicate PDB procedure '%s' "
+ "registered by '%s'\n",
+ gimp_object_get_name (tmp_proc),
+ gimp_file_get_utf8_name (tmp_proc->file));
+
+ /* search the plugin list to see if any plugins had references to
+ * the tmp_proc.
+ */
+ for (list2 = manager->plug_in_defs; list2; list2 = list2->next)
+ {
+ GimpPlugInDef *plug_in_def = list2->data;
+
+ if (g_slist_find (plug_in_def->procedures, tmp_proc))
+ gimp_plug_in_def_remove_procedure (plug_in_def, tmp_proc);
+ }
+
+ /* also remove it from the lists of load, save and export procs */
+ manager->load_procs = g_slist_remove (manager->load_procs, tmp_proc);
+ manager->save_procs = g_slist_remove (manager->save_procs, tmp_proc);
+ manager->export_procs = g_slist_remove (manager->export_procs, tmp_proc);
+ manager->raw_load_procs = g_slist_remove (manager->raw_load_procs, tmp_proc);
+ manager->display_load_procs = g_slist_remove (manager->display_load_procs, tmp_proc);
+ manager->display_save_procs = g_slist_remove (manager->display_save_procs, tmp_proc);
+ manager->display_export_procs = g_slist_remove (manager->display_export_procs, tmp_proc);
+ manager->display_raw_load_procs = g_slist_remove (manager->display_raw_load_procs, tmp_proc);
+
+ /* and from the history */
+ gimp_filter_history_remove (manager->gimp, GIMP_PROCEDURE (tmp_proc));
+
+ g_object_unref (tmp_proc);
+
+ return;
+ }
+ }
+
+ manager->plug_in_procedures = g_slist_prepend (manager->plug_in_procedures,
+ g_object_ref (procedure));
+}
+
+void
+gimp_plug_in_manager_add_temp_proc (GimpPlugInManager *manager,
+ GimpTemporaryProcedure *procedure)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+ g_return_if_fail (GIMP_IS_TEMPORARY_PROCEDURE (procedure));
+
+ gimp_pdb_register_procedure (manager->gimp->pdb, GIMP_PROCEDURE (procedure));
+
+ manager->plug_in_procedures = g_slist_prepend (manager->plug_in_procedures,
+ g_object_ref (procedure));
+}
+
+void
+gimp_plug_in_manager_remove_temp_proc (GimpPlugInManager *manager,
+ GimpTemporaryProcedure *procedure)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+ g_return_if_fail (GIMP_IS_TEMPORARY_PROCEDURE (procedure));
+
+ manager->plug_in_procedures = g_slist_remove (manager->plug_in_procedures,
+ procedure);
+
+ gimp_filter_history_remove (manager->gimp,
+ GIMP_PROCEDURE (procedure));
+
+ gimp_pdb_unregister_procedure (manager->gimp->pdb,
+ GIMP_PROCEDURE (procedure));
+
+ g_object_unref (procedure);
+}
+
+void
+gimp_plug_in_manager_add_open_plug_in (GimpPlugInManager *manager,
+ GimpPlugIn *plug_in)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+
+ manager->open_plug_ins = g_slist_prepend (manager->open_plug_ins,
+ g_object_ref (plug_in));
+
+ g_signal_emit (manager, manager_signals[PLUG_IN_OPENED], 0,
+ plug_in);
+}
+
+void
+gimp_plug_in_manager_remove_open_plug_in (GimpPlugInManager *manager,
+ GimpPlugIn *plug_in)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+
+ manager->open_plug_ins = g_slist_remove (manager->open_plug_ins, plug_in);
+
+ g_signal_emit (manager, manager_signals[PLUG_IN_CLOSED], 0,
+ plug_in);
+
+ g_object_unref (plug_in);
+}
+
+void
+gimp_plug_in_manager_plug_in_push (GimpPlugInManager *manager,
+ GimpPlugIn *plug_in)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+
+ manager->current_plug_in = plug_in;
+
+ manager->plug_in_stack = g_slist_prepend (manager->plug_in_stack,
+ manager->current_plug_in);
+}
+
+void
+gimp_plug_in_manager_plug_in_pop (GimpPlugInManager *manager)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_MANAGER (manager));
+
+ if (manager->current_plug_in)
+ manager->plug_in_stack = g_slist_remove (manager->plug_in_stack,
+ manager->plug_in_stack->data);
+
+ if (manager->plug_in_stack)
+ manager->current_plug_in = manager->plug_in_stack->data;
+ else
+ manager->current_plug_in = NULL;
+}
diff --git a/app/plug-in/gimppluginmanager.h b/app/plug-in/gimppluginmanager.h
new file mode 100644
index 0000000..11f80b1
--- /dev/null
+++ b/app/plug-in/gimppluginmanager.h
@@ -0,0 +1,118 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginmanager.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_MANAGER_H__
+#define __GIMP_PLUG_IN_MANAGER_H__
+
+
+#include "core/gimpobject.h"
+
+
+#define GIMP_TYPE_PLUG_IN_MANAGER (gimp_plug_in_manager_get_type ())
+#define GIMP_PLUG_IN_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PLUG_IN_MANAGER, GimpPlugInManager))
+#define GIMP_PLUG_IN_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PLUG_IN_MANAGER, GimpPlugInManagerClass))
+#define GIMP_IS_PLUG_IN_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PLUG_IN_MANAGER))
+#define GIMP_IS_PLUG_IN_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PLUG_IN_MANAGER))
+
+
+typedef struct _GimpPlugInManagerClass GimpPlugInManagerClass;
+
+struct _GimpPlugInManager
+{
+ GimpObject parent_instance;
+
+ Gimp *gimp;
+
+ GSList *plug_in_defs;
+ gboolean write_pluginrc;
+
+ GSList *plug_in_procedures;
+
+ GSList *load_procs;
+ GSList *save_procs;
+ GSList *export_procs;
+ GSList *raw_load_procs;
+
+ GSList *display_load_procs;
+ GSList *display_save_procs;
+ GSList *display_export_procs;
+ GSList *display_raw_load_procs;
+
+ GSList *menu_branches;
+ GSList *locale_domains;
+ GSList *help_domains;
+
+ GimpPlugIn *current_plug_in;
+ GSList *open_plug_ins;
+ GSList *plug_in_stack;
+
+ GimpPlugInShm *shm;
+ GimpInterpreterDB *interpreter_db;
+ GimpEnvironTable *environ_table;
+ GimpPlugInDebug *debug;
+ GList *data_list;
+};
+
+struct _GimpPlugInManagerClass
+{
+ GimpObjectClass parent_class;
+
+ void (* plug_in_opened) (GimpPlugInManager *manager,
+ GimpPlugIn *plug_in);
+ void (* plug_in_closed) (GimpPlugInManager *manager,
+ GimpPlugIn *plug_in);
+
+ void (* menu_branch_added) (GimpPlugInManager *manager,
+ GFile *file,
+ const gchar *menu_path,
+ const gchar *menu_label);
+};
+
+
+GType gimp_plug_in_manager_get_type (void) G_GNUC_CONST;
+
+GimpPlugInManager * gimp_plug_in_manager_new (Gimp *gimp);
+
+void gimp_plug_in_manager_initialize (GimpPlugInManager *manager,
+ GimpInitStatusFunc status_callback);
+void gimp_plug_in_manager_exit (GimpPlugInManager *manager);
+
+/* Register a plug-in. This function is public for file load-save
+ * handlers, which are organized around the plug-in data structure.
+ * This could all be done a little better, but oh well. -josh
+ */
+void gimp_plug_in_manager_add_procedure (GimpPlugInManager *manager,
+ GimpPlugInProcedure *procedure);
+
+void gimp_plug_in_manager_add_temp_proc (GimpPlugInManager *manager,
+ GimpTemporaryProcedure *procedure);
+void gimp_plug_in_manager_remove_temp_proc (GimpPlugInManager *manager,
+ GimpTemporaryProcedure *procedure);
+
+void gimp_plug_in_manager_add_open_plug_in (GimpPlugInManager *manager,
+ GimpPlugIn *plug_in);
+void gimp_plug_in_manager_remove_open_plug_in (GimpPlugInManager *manager,
+ GimpPlugIn *plug_in);
+
+void gimp_plug_in_manager_plug_in_push (GimpPlugInManager *manager,
+ GimpPlugIn *plug_in);
+void gimp_plug_in_manager_plug_in_pop (GimpPlugInManager *manager);
+
+
+#endif /* __GIMP_PLUG_IN_MANAGER_H__ */
diff --git a/app/plug-in/gimppluginprocedure.c b/app/plug-in/gimppluginprocedure.c
new file mode 100644
index 0000000..db7d240
--- /dev/null
+++ b/app/plug-in/gimppluginprocedure.c
@@ -0,0 +1,1293 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginprocedure.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "plug-in-types.h"
+
+#include "gegl/gimp-babl-compat.h"
+
+#include "core/gimp.h"
+#include "core/gimp-memsize.h"
+#include "core/gimpdrawable.h"
+#include "core/gimpmarshal.h"
+#include "core/gimpparamspecs.h"
+
+#include "file/file-utils.h"
+
+#define __YES_I_NEED_GIMP_PLUG_IN_MANAGER_CALL__
+#include "gimppluginmanager-call.h"
+
+#include "gimppluginerror.h"
+#include "gimppluginprocedure.h"
+#include "plug-in-menu-path.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ MENU_PATH_ADDED,
+ LAST_SIGNAL
+};
+
+
+static void gimp_plug_in_procedure_finalize (GObject *object);
+
+static gint64 gimp_plug_in_procedure_get_memsize (GimpObject *object,
+ gint64 *gui_size);
+
+static gchar * gimp_plug_in_procedure_get_description (GimpViewable *viewable,
+ gchar **tooltip);
+
+static const gchar * gimp_plug_in_procedure_get_label (GimpProcedure *procedure);
+static const gchar * gimp_plug_in_procedure_get_menu_label
+ (GimpProcedure *procedure);
+static const gchar * gimp_plug_in_procedure_get_blurb (GimpProcedure *procedure);
+static const gchar * gimp_plug_in_procedure_get_help_id(GimpProcedure *procedure);
+static gboolean gimp_plug_in_procedure_get_sensitive (GimpProcedure *procedure,
+ GimpObject *object,
+ const gchar **tooltip);
+static GimpValueArray * gimp_plug_in_procedure_execute (GimpProcedure *procedure,
+ Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpValueArray *args,
+ GError **error);
+static void gimp_plug_in_procedure_execute_async (GimpProcedure *procedure,
+ Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpValueArray *args,
+ GimpObject *display);
+
+static GFile * gimp_plug_in_procedure_real_get_file (GimpPlugInProcedure *procedure);
+
+static gboolean gimp_plug_in_procedure_validate_args (GimpPlugInProcedure *proc,
+ Gimp *gimp,
+ GimpValueArray *args,
+ GError **error);
+
+
+G_DEFINE_TYPE (GimpPlugInProcedure, gimp_plug_in_procedure,
+ GIMP_TYPE_PROCEDURE)
+
+#define parent_class gimp_plug_in_procedure_parent_class
+
+static guint gimp_plug_in_procedure_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gimp_plug_in_procedure_class_init (GimpPlugInProcedureClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
+ GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass);
+ GimpProcedureClass *proc_class = GIMP_PROCEDURE_CLASS (klass);
+
+ gimp_plug_in_procedure_signals[MENU_PATH_ADDED] =
+ g_signal_new ("menu-path-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpPlugInProcedureClass, menu_path_added),
+ NULL, NULL,
+ gimp_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+
+ object_class->finalize = gimp_plug_in_procedure_finalize;
+
+ gimp_object_class->get_memsize = gimp_plug_in_procedure_get_memsize;
+
+ viewable_class->default_icon_name = "system-run";
+ viewable_class->get_description = gimp_plug_in_procedure_get_description;
+
+ proc_class->get_label = gimp_plug_in_procedure_get_label;
+ proc_class->get_menu_label = gimp_plug_in_procedure_get_menu_label;
+ proc_class->get_blurb = gimp_plug_in_procedure_get_blurb;
+ proc_class->get_help_id = gimp_plug_in_procedure_get_help_id;
+ proc_class->get_sensitive = gimp_plug_in_procedure_get_sensitive;
+ proc_class->execute = gimp_plug_in_procedure_execute;
+ proc_class->execute_async = gimp_plug_in_procedure_execute_async;
+
+ klass->get_file = gimp_plug_in_procedure_real_get_file;
+ klass->menu_path_added = NULL;
+}
+
+static void
+gimp_plug_in_procedure_init (GimpPlugInProcedure *proc)
+{
+ GIMP_PROCEDURE (proc)->proc_type = GIMP_PLUGIN;
+
+ proc->icon_data_length = -1;
+}
+
+static void
+gimp_plug_in_procedure_finalize (GObject *object)
+{
+ GimpPlugInProcedure *proc = GIMP_PLUG_IN_PROCEDURE (object);
+
+ g_object_unref (proc->file);
+ g_free (proc->menu_label);
+
+ g_list_free_full (proc->menu_paths, (GDestroyNotify) g_free);
+
+ g_free (proc->label);
+ g_free (proc->help_id);
+
+ g_free (proc->icon_data);
+ g_free (proc->image_types);
+ g_free (proc->image_types_tooltip);
+
+ g_free (proc->extensions);
+ g_free (proc->prefixes);
+ g_free (proc->magics);
+ g_free (proc->mime_types);
+
+ g_slist_free_full (proc->extensions_list, (GDestroyNotify) g_free);
+ g_slist_free_full (proc->prefixes_list, (GDestroyNotify) g_free);
+ g_slist_free_full (proc->magics_list, (GDestroyNotify) g_free);
+ g_slist_free_full (proc->mime_types_list, (GDestroyNotify) g_free);
+
+ g_free (proc->thumb_loader);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gint64
+gimp_plug_in_procedure_get_memsize (GimpObject *object,
+ gint64 *gui_size)
+{
+ GimpPlugInProcedure *proc = GIMP_PLUG_IN_PROCEDURE (object);
+ gint64 memsize = 0;
+ GList *list;
+ GSList *slist;
+
+ memsize += gimp_g_object_get_memsize (G_OBJECT (proc->file));
+ memsize += gimp_string_get_memsize (proc->menu_label);
+
+ for (list = proc->menu_paths; list; list = g_list_next (list))
+ memsize += sizeof (GList) + gimp_string_get_memsize (list->data);
+
+ switch (proc->icon_type)
+ {
+ case GIMP_ICON_TYPE_ICON_NAME:
+ case GIMP_ICON_TYPE_IMAGE_FILE:
+ memsize += gimp_string_get_memsize ((const gchar *) proc->icon_data);
+ break;
+
+ case GIMP_ICON_TYPE_INLINE_PIXBUF:
+ memsize += proc->icon_data_length;
+ break;
+ }
+
+ memsize += gimp_string_get_memsize (proc->extensions);
+ memsize += gimp_string_get_memsize (proc->prefixes);
+ memsize += gimp_string_get_memsize (proc->magics);
+ memsize += gimp_string_get_memsize (proc->mime_types);
+ memsize += gimp_string_get_memsize (proc->thumb_loader);
+
+ for (slist = proc->extensions_list; slist; slist = g_slist_next (slist))
+ memsize += sizeof (GSList) + gimp_string_get_memsize (slist->data);
+
+ for (slist = proc->prefixes_list; slist; slist = g_slist_next (slist))
+ memsize += sizeof (GSList) + gimp_string_get_memsize (slist->data);
+
+ for (slist = proc->magics_list; slist; slist = g_slist_next (slist))
+ memsize += sizeof (GSList) + gimp_string_get_memsize (slist->data);
+
+ for (slist = proc->mime_types_list; slist; slist = g_slist_next (slist))
+ memsize += sizeof (GSList) + gimp_string_get_memsize (slist->data);
+
+ return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
+ gui_size);
+}
+
+static gchar *
+gimp_plug_in_procedure_get_description (GimpViewable *viewable,
+ gchar **tooltip)
+{
+ GimpProcedure *procedure = GIMP_PROCEDURE (viewable);
+
+ if (tooltip)
+ *tooltip = g_strdup (gimp_procedure_get_blurb (procedure));
+
+ return g_strdup (gimp_procedure_get_label (procedure));
+}
+
+static const gchar *
+gimp_plug_in_procedure_get_label (GimpProcedure *procedure)
+{
+ GimpPlugInProcedure *proc = GIMP_PLUG_IN_PROCEDURE (procedure);
+ const gchar *path;
+ gchar *stripped;
+ gchar *ellipsis;
+ gchar *label;
+
+ if (proc->label)
+ return proc->label;
+
+ if (proc->menu_label)
+ path = dgettext (gimp_plug_in_procedure_get_locale_domain (proc),
+ proc->menu_label);
+ else if (proc->menu_paths)
+ path = dgettext (gimp_plug_in_procedure_get_locale_domain (proc),
+ proc->menu_paths->data);
+ else
+ return NULL;
+
+ stripped = gimp_strip_uline (path);
+
+ if (proc->menu_label)
+ label = g_strdup (stripped);
+ else
+ label = g_path_get_basename (stripped);
+
+ g_free (stripped);
+
+ ellipsis = strstr (label, "...");
+
+ if (! ellipsis)
+ ellipsis = strstr (label, "\342\200\246" /* U+2026 HORIZONTAL ELLIPSIS */);
+
+ if (ellipsis && ellipsis == (label + strlen (label) - 3))
+ *ellipsis = '\0';
+
+ proc->label = label;
+
+ return proc->label;
+}
+
+static const gchar *
+gimp_plug_in_procedure_get_menu_label (GimpProcedure *procedure)
+{
+ GimpPlugInProcedure *proc = GIMP_PLUG_IN_PROCEDURE (procedure);
+
+ if (proc->menu_label)
+ {
+ return dgettext (gimp_plug_in_procedure_get_locale_domain (proc),
+ proc->menu_label);
+ }
+ else if (proc->menu_paths)
+ {
+ const gchar *translated;
+
+ translated = dgettext (gimp_plug_in_procedure_get_locale_domain (proc),
+ proc->menu_paths->data);
+
+ translated = strrchr (translated, '/');
+
+ if (translated)
+ return translated + 1;
+ }
+
+ return GIMP_PROCEDURE_CLASS (parent_class)->get_menu_label (procedure);
+}
+
+static const gchar *
+gimp_plug_in_procedure_get_blurb (GimpProcedure *procedure)
+{
+ GimpPlugInProcedure *proc = GIMP_PLUG_IN_PROCEDURE (procedure);
+
+ /* do not to pass the empty string to gettext() */
+ if (procedure->blurb && strlen (procedure->blurb))
+ return dgettext (gimp_plug_in_procedure_get_locale_domain (proc),
+ procedure->blurb);
+
+ return NULL;
+}
+
+static const gchar *
+gimp_plug_in_procedure_get_help_id (GimpProcedure *procedure)
+{
+ GimpPlugInProcedure *proc = GIMP_PLUG_IN_PROCEDURE (procedure);
+ const gchar *domain;
+
+ if (proc->help_id)
+ return proc->help_id;
+
+ domain = gimp_plug_in_procedure_get_help_domain (proc);
+
+ if (domain)
+ proc->help_id = g_strconcat (domain, "?", gimp_object_get_name (proc), NULL);
+ else
+ proc->help_id = g_strdup (gimp_object_get_name (proc));
+
+ return proc->help_id;
+}
+
+static gboolean
+gimp_plug_in_procedure_get_sensitive (GimpProcedure *procedure,
+ GimpObject *object,
+ const gchar **tooltip)
+{
+ GimpPlugInProcedure *proc = GIMP_PLUG_IN_PROCEDURE (procedure);
+ GimpDrawable *drawable;
+ GimpImageType image_type = -1;
+ gboolean sensitive = FALSE;
+
+ g_return_val_if_fail (object == NULL || GIMP_IS_DRAWABLE (object), FALSE);
+
+ drawable = GIMP_DRAWABLE (object);
+
+ if (drawable)
+ {
+ const Babl *format = gimp_drawable_get_format (drawable);
+
+ image_type = gimp_babl_format_get_image_type (format);
+ }
+
+ switch (image_type)
+ {
+ case GIMP_RGB_IMAGE:
+ sensitive = proc->image_types_val & GIMP_PLUG_IN_RGB_IMAGE;
+ break;
+ case GIMP_RGBA_IMAGE:
+ sensitive = proc->image_types_val & GIMP_PLUG_IN_RGBA_IMAGE;
+ break;
+ case GIMP_GRAY_IMAGE:
+ sensitive = proc->image_types_val & GIMP_PLUG_IN_GRAY_IMAGE;
+ break;
+ case GIMP_GRAYA_IMAGE:
+ sensitive = proc->image_types_val & GIMP_PLUG_IN_GRAYA_IMAGE;
+ break;
+ case GIMP_INDEXED_IMAGE:
+ sensitive = proc->image_types_val & GIMP_PLUG_IN_INDEXED_IMAGE;
+ break;
+ case GIMP_INDEXEDA_IMAGE:
+ sensitive = proc->image_types_val & GIMP_PLUG_IN_INDEXEDA_IMAGE;
+ break;
+ default:
+ break;
+ }
+
+ if (! sensitive)
+ *tooltip = proc->image_types_tooltip;
+
+ return sensitive ? TRUE : FALSE;
+}
+
+static GimpValueArray *
+gimp_plug_in_procedure_execute (GimpProcedure *procedure,
+ Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpValueArray *args,
+ GError **error)
+{
+ GimpPlugInProcedure *plug_in_procedure = GIMP_PLUG_IN_PROCEDURE (procedure);
+ GError *pdb_error = NULL;
+
+ if (! gimp_plug_in_procedure_validate_args (plug_in_procedure, gimp,
+ args, &pdb_error))
+ {
+ GimpValueArray *return_vals;
+
+ return_vals = gimp_procedure_get_return_values (procedure, FALSE,
+ pdb_error);
+ g_propagate_error (error, pdb_error);
+
+ return return_vals;
+ }
+
+ if (procedure->proc_type == GIMP_INTERNAL)
+ return GIMP_PROCEDURE_CLASS (parent_class)->execute (procedure, gimp,
+ context, progress,
+ args, error);
+
+ return gimp_plug_in_manager_call_run (gimp->plug_in_manager,
+ context, progress,
+ GIMP_PLUG_IN_PROCEDURE (procedure),
+ args, TRUE, NULL);
+}
+
+static void
+gimp_plug_in_procedure_execute_async (GimpProcedure *procedure,
+ Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpValueArray *args,
+ GimpObject *display)
+{
+ GimpPlugInProcedure *plug_in_procedure = GIMP_PLUG_IN_PROCEDURE (procedure);
+ GError *error = NULL;
+
+ if (gimp_plug_in_procedure_validate_args (plug_in_procedure, gimp,
+ args, &error))
+ {
+ GimpValueArray *return_vals;
+
+ return_vals = gimp_plug_in_manager_call_run (gimp->plug_in_manager,
+ context, progress,
+ plug_in_procedure,
+ args, FALSE, display);
+
+ if (return_vals)
+ {
+ gimp_plug_in_procedure_handle_return_values (plug_in_procedure,
+ gimp, progress,
+ return_vals);
+ gimp_value_array_unref (return_vals);
+ }
+ }
+ else
+ {
+ gimp_message_literal (gimp, G_OBJECT (progress), GIMP_MESSAGE_ERROR,
+ error->message);
+ g_error_free (error);
+ }
+}
+
+static GFile *
+gimp_plug_in_procedure_real_get_file (GimpPlugInProcedure *procedure)
+{
+ return procedure->file;
+}
+
+static gboolean
+gimp_plug_in_procedure_validate_args (GimpPlugInProcedure *proc,
+ Gimp *gimp,
+ GimpValueArray *args,
+ GError **error)
+{
+ if (proc->file_proc && proc->handles_uri)
+ {
+ /* for file procedures that handle URIs, make sure that the
+ * passed string actually is an URI, not just a file path
+ * (bug 758685)
+ */
+ GimpProcedure *procedure = GIMP_PROCEDURE (proc);
+ GValue *uri_value = NULL;
+
+ if ((procedure->num_args >= 3) &&
+ (procedure->num_values >= 1) &&
+ GIMP_IS_PARAM_SPEC_INT32 (procedure->args[0]) &&
+ G_IS_PARAM_SPEC_STRING (procedure->args[1]) &&
+ G_IS_PARAM_SPEC_STRING (procedure->args[2]) &&
+ GIMP_IS_PARAM_SPEC_IMAGE_ID (procedure->values[0]))
+ {
+ uri_value = gimp_value_array_index (args, 1);
+ }
+ else if ((procedure->num_args >= 5) &&
+ GIMP_IS_PARAM_SPEC_INT32 (procedure->args[0]) &&
+ GIMP_IS_PARAM_SPEC_IMAGE_ID (procedure->args[1]) &&
+ GIMP_IS_PARAM_SPEC_DRAWABLE_ID (procedure->args[2]) &&
+ G_IS_PARAM_SPEC_STRING (procedure->args[3]) &&
+ G_IS_PARAM_SPEC_STRING (procedure->args[4]))
+ {
+ uri_value = gimp_value_array_index (args, 3);
+ }
+
+ if (uri_value)
+ {
+ GFile *file;
+
+ file = file_utils_filename_to_file (gimp,
+ g_value_get_string (uri_value),
+ error);
+
+ if (! file)
+ return FALSE;
+
+ g_value_take_string (uri_value, g_file_get_uri (file));
+ g_object_unref (file);
+ }
+ }
+
+ return TRUE;
+}
+
+
+/* public functions */
+
+GimpProcedure *
+gimp_plug_in_procedure_new (GimpPDBProcType proc_type,
+ GFile *file)
+{
+ GimpPlugInProcedure *proc;
+
+ g_return_val_if_fail (proc_type == GIMP_PLUGIN ||
+ proc_type == GIMP_EXTENSION, NULL);
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+ proc = g_object_new (GIMP_TYPE_PLUG_IN_PROCEDURE, NULL);
+
+ proc->file = g_object_ref (file);
+
+ GIMP_PROCEDURE (proc)->proc_type = proc_type;
+
+ return GIMP_PROCEDURE (proc);
+}
+
+GimpPlugInProcedure *
+gimp_plug_in_procedure_find (GSList *list,
+ const gchar *proc_name)
+{
+ GSList *l;
+
+ for (l = list; l; l = g_slist_next (l))
+ {
+ GimpObject *object = l->data;
+
+ if (! strcmp (proc_name, gimp_object_get_name (object)))
+ return GIMP_PLUG_IN_PROCEDURE (object);
+ }
+
+ return NULL;
+}
+
+GFile *
+gimp_plug_in_procedure_get_file (GimpPlugInProcedure *proc)
+{
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc), NULL);
+
+ return GIMP_PLUG_IN_PROCEDURE_GET_CLASS (proc)->get_file (proc);
+}
+
+void
+gimp_plug_in_procedure_set_locale_domain (GimpPlugInProcedure *proc,
+ const gchar *locale_domain)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc));
+
+ proc->locale_domain = locale_domain ? g_quark_from_string (locale_domain) : 0;
+}
+
+const gchar *
+gimp_plug_in_procedure_get_locale_domain (GimpPlugInProcedure *proc)
+{
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc), NULL);
+
+ return g_quark_to_string (proc->locale_domain);
+}
+
+void
+gimp_plug_in_procedure_set_help_domain (GimpPlugInProcedure *proc,
+ const gchar *help_domain)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc));
+
+ proc->help_domain = help_domain ? g_quark_from_string (help_domain) : 0;
+}
+
+const gchar *
+gimp_plug_in_procedure_get_help_domain (GimpPlugInProcedure *proc)
+{
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc), NULL);
+
+ return g_quark_to_string (proc->help_domain);
+}
+
+gboolean
+gimp_plug_in_procedure_add_menu_path (GimpPlugInProcedure *proc,
+ const gchar *menu_path,
+ GError **error)
+{
+ GimpProcedure *procedure;
+ gchar *basename = NULL;
+ const gchar *required = NULL;
+ gchar *p;
+ gchar *mapped_path;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc), FALSE);
+ g_return_val_if_fail (menu_path != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ procedure = GIMP_PROCEDURE (proc);
+
+ p = strchr (menu_path, '>');
+ if (p == NULL || (*(++p) && *p != '/'))
+ {
+ basename = g_path_get_basename (gimp_file_get_utf8_name (proc->file));
+
+ g_set_error (error, GIMP_PLUG_IN_ERROR, GIMP_PLUG_IN_FAILED,
+ "Plug-in \"%s\"\n(%s)\n"
+ "attempted to install procedure \"%s\"\n"
+ "in the invalid menu location \"%s\".\n"
+ "The menu path must look like either \"<Prefix>\" "
+ "or \"<Prefix>/path/to/item\".",
+ basename, gimp_file_get_utf8_name (proc->file),
+ gimp_object_get_name (proc),
+ menu_path);
+ goto failure;
+ }
+
+ if (g_str_has_prefix (menu_path, "<Toolbox>") ||
+ g_str_has_prefix (menu_path, "<Image>"))
+ {
+ if ((procedure->num_args < 1) ||
+ ! GIMP_IS_PARAM_SPEC_INT32 (procedure->args[0]))
+ {
+ required = "INT32";
+ goto failure;
+ }
+ }
+ else if (g_str_has_prefix (menu_path, "<Layers>"))
+ {
+ if ((procedure->num_args < 3) ||
+ ! GIMP_IS_PARAM_SPEC_INT32 (procedure->args[0]) ||
+ ! GIMP_IS_PARAM_SPEC_IMAGE_ID (procedure->args[1]) ||
+ ! (G_TYPE_FROM_INSTANCE (procedure->args[2])
+ == GIMP_TYPE_PARAM_LAYER_ID ||
+ G_TYPE_FROM_INSTANCE (procedure->args[2])
+ == GIMP_TYPE_PARAM_DRAWABLE_ID))
+ {
+ required = "INT32, IMAGE, (LAYER | DRAWABLE)";
+ goto failure;
+ }
+ }
+ else if (g_str_has_prefix (menu_path, "<Channels>"))
+ {
+ if ((procedure->num_args < 3) ||
+ ! GIMP_IS_PARAM_SPEC_INT32 (procedure->args[0]) ||
+ ! GIMP_IS_PARAM_SPEC_IMAGE_ID (procedure->args[1]) ||
+ ! (G_TYPE_FROM_INSTANCE (procedure->args[2])
+ == GIMP_TYPE_PARAM_CHANNEL_ID ||
+ G_TYPE_FROM_INSTANCE (procedure->args[2])
+ == GIMP_TYPE_PARAM_DRAWABLE_ID))
+ {
+ required = "INT32, IMAGE, (CHANNEL | DRAWABLE)";
+ goto failure;
+ }
+ }
+ else if (g_str_has_prefix (menu_path, "<Vectors>"))
+ {
+ if ((procedure->num_args < 3) ||
+ ! GIMP_IS_PARAM_SPEC_INT32 (procedure->args[0]) ||
+ ! GIMP_IS_PARAM_SPEC_IMAGE_ID (procedure->args[1]) ||
+ ! GIMP_IS_PARAM_SPEC_VECTORS_ID (procedure->args[2]))
+ {
+ required = "INT32, IMAGE, VECTORS";
+ goto failure;
+ }
+ }
+ else if (g_str_has_prefix (menu_path, "<Colormap>"))
+ {
+ if ((procedure->num_args < 2) ||
+ ! GIMP_IS_PARAM_SPEC_INT32 (procedure->args[0]) ||
+ ! GIMP_IS_PARAM_SPEC_IMAGE_ID (procedure->args[1]))
+ {
+ required = "INT32, IMAGE";
+ goto failure;
+ }
+ }
+ else if (g_str_has_prefix (menu_path, "<Load>"))
+ {
+ if ((procedure->num_args < 3) ||
+ ! GIMP_IS_PARAM_SPEC_INT32 (procedure->args[0]) ||
+ ! G_IS_PARAM_SPEC_STRING (procedure->args[1]) ||
+ ! G_IS_PARAM_SPEC_STRING (procedure->args[2]))
+ {
+ required = "INT32, STRING, STRING";
+ goto failure;
+ }
+
+ if ((procedure->num_values < 1) ||
+ ! GIMP_IS_PARAM_SPEC_IMAGE_ID (procedure->values[0]))
+ {
+ required = "IMAGE";
+ goto failure;
+ }
+ }
+ else if (g_str_has_prefix (menu_path, "<Save>"))
+ {
+ if ((procedure->num_args < 5) ||
+ ! GIMP_IS_PARAM_SPEC_INT32 (procedure->args[0]) ||
+ ! GIMP_IS_PARAM_SPEC_IMAGE_ID (procedure->args[1]) ||
+ ! GIMP_IS_PARAM_SPEC_DRAWABLE_ID (procedure->args[2]) ||
+ ! G_IS_PARAM_SPEC_STRING (procedure->args[3]) ||
+ ! G_IS_PARAM_SPEC_STRING (procedure->args[4]))
+ {
+ required = "INT32, IMAGE, DRAWABLE, STRING, STRING";
+ goto failure;
+ }
+ }
+ else if (g_str_has_prefix (menu_path, "<Brushes>") ||
+ g_str_has_prefix (menu_path, "<Dynamics>") ||
+ g_str_has_prefix (menu_path, "<MyPaintBrushes>") ||
+ g_str_has_prefix (menu_path, "<Gradients>") ||
+ g_str_has_prefix (menu_path, "<Palettes>") ||
+ g_str_has_prefix (menu_path, "<Patterns>") ||
+ g_str_has_prefix (menu_path, "<ToolPresets>") ||
+ g_str_has_prefix (menu_path, "<Fonts>") ||
+ g_str_has_prefix (menu_path, "<Buffers>"))
+ {
+ if ((procedure->num_args < 1) ||
+ ! GIMP_IS_PARAM_SPEC_INT32 (procedure->args[0]))
+ {
+ required = "INT32";
+ goto failure;
+ }
+ }
+ else
+ {
+ basename = g_path_get_basename (gimp_file_get_utf8_name (proc->file));
+
+ g_set_error (error, GIMP_PLUG_IN_ERROR, GIMP_PLUG_IN_FAILED,
+ "Plug-in \"%s\"\n(%s)\n"
+ "attempted to install procedure \"%s\" "
+ "in the invalid menu location \"%s\".\n"
+ "Use either \"<Image>\", "
+ "\"<Layers>\", \"<Channels>\", \"<Vectors>\", "
+ "\"<Colormap>\", \"<Brushes>\", \"<Dynamics>\", "
+ "\"<MyPaintBrushes>\", \"<Gradients>\", \"<Palettes>\", "
+ "\"<Patterns>\", \"<ToolPresets>\", \"<Fonts>\" "
+ "or \"<Buffers>\".",
+ basename, gimp_file_get_utf8_name (proc->file),
+ gimp_object_get_name (proc),
+ menu_path);
+ goto failure;
+ }
+
+ g_free (basename);
+
+ mapped_path = plug_in_menu_path_map (menu_path, NULL);
+
+ proc->menu_paths = g_list_append (proc->menu_paths, mapped_path);
+
+ g_signal_emit (proc, gimp_plug_in_procedure_signals[MENU_PATH_ADDED], 0,
+ mapped_path);
+
+ return TRUE;
+
+ failure:
+ if (required)
+ {
+ gchar *prefix = g_strdup (menu_path);
+
+ p = strchr (prefix, '>') + 1;
+ *p = '\0';
+
+ basename = g_path_get_basename (gimp_file_get_utf8_name (proc->file));
+
+ g_set_error (error, GIMP_PLUG_IN_ERROR, GIMP_PLUG_IN_FAILED,
+ "Plug-in \"%s\"\n(%s)\n\n"
+ "attempted to install %s procedure \"%s\" "
+ "which does not take the standard %s plug-in's "
+ "arguments: (%s).",
+ basename, gimp_file_get_utf8_name (proc->file),
+ prefix, gimp_object_get_name (proc), prefix,
+ required);
+
+ g_free (prefix);
+ }
+
+ g_free (basename);
+
+ return FALSE;
+}
+
+void
+gimp_plug_in_procedure_set_icon (GimpPlugInProcedure *proc,
+ GimpIconType icon_type,
+ const guint8 *icon_data,
+ gint icon_data_length)
+{
+ guint8 *data_copy = NULL;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc));
+
+ switch (icon_type)
+ {
+ case GIMP_ICON_TYPE_ICON_NAME:
+ data_copy = (guint8 *) g_strdup ((gchar *) icon_data);
+ break;
+
+ case GIMP_ICON_TYPE_INLINE_PIXBUF:
+ data_copy = g_memdup (icon_data, icon_data_length);
+ break;
+
+ case GIMP_ICON_TYPE_IMAGE_FILE:
+ data_copy = (guint8 *) g_strdup ((gchar *) icon_data);
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+
+ gimp_plug_in_procedure_take_icon (proc, icon_type,
+ data_copy, icon_data_length);
+}
+
+void
+gimp_plug_in_procedure_take_icon (GimpPlugInProcedure *proc,
+ GimpIconType icon_type,
+ guint8 *icon_data,
+ gint icon_data_length)
+{
+ const gchar *icon_name = NULL;
+ GdkPixbuf *icon_pixbuf = NULL;
+ GError *error = NULL;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc));
+
+ if (proc->icon_data)
+ {
+ g_free (proc->icon_data);
+ proc->icon_data_length = -1;
+ proc->icon_data = NULL;
+ }
+
+ proc->icon_type = icon_type;
+
+ switch (proc->icon_type)
+ {
+ case GIMP_ICON_TYPE_ICON_NAME:
+ proc->icon_data_length = -1;
+ proc->icon_data = icon_data;
+
+ icon_name = (const gchar *) proc->icon_data;
+ break;
+
+ case GIMP_ICON_TYPE_INLINE_PIXBUF:
+ proc->icon_data_length = icon_data_length;
+ proc->icon_data = icon_data;
+
+ icon_pixbuf = gdk_pixbuf_new_from_inline (proc->icon_data_length,
+ proc->icon_data, TRUE, &error);
+ break;
+
+ case GIMP_ICON_TYPE_IMAGE_FILE:
+ proc->icon_data_length = -1;
+ proc->icon_data = icon_data;
+
+ icon_pixbuf = gdk_pixbuf_new_from_file ((gchar *) proc->icon_data,
+ &error);
+ break;
+ }
+
+ if (! icon_pixbuf && error)
+ {
+ g_printerr ("gimp_plug_in_procedure_take_icon: %s\n", error->message);
+ g_clear_error (&error);
+ }
+
+ gimp_viewable_set_icon_name (GIMP_VIEWABLE (proc), icon_name);
+ g_object_set (proc, "icon-pixbuf", icon_pixbuf, NULL);
+
+ if (icon_pixbuf)
+ g_object_unref (icon_pixbuf);
+}
+
+static GimpPlugInImageType
+image_types_parse (const gchar *name,
+ const gchar *image_types)
+{
+ const gchar *type_spec = image_types;
+ GimpPlugInImageType types = 0;
+
+ /* If the plug_in registers with image_type == NULL or "", return 0
+ * By doing so it won't be touched by plug_in_set_menu_sensitivity()
+ */
+ if (! image_types)
+ return types;
+
+ while (*image_types)
+ {
+ while (*image_types &&
+ ((*image_types == ' ') ||
+ (*image_types == '\t') ||
+ (*image_types == ',')))
+ image_types++;
+
+ if (*image_types)
+ {
+ if (g_str_has_prefix (image_types, "RGBA"))
+ {
+ types |= GIMP_PLUG_IN_RGBA_IMAGE;
+ image_types += strlen ("RGBA");
+ }
+ else if (g_str_has_prefix (image_types, "RGB*"))
+ {
+ types |= GIMP_PLUG_IN_RGB_IMAGE | GIMP_PLUG_IN_RGBA_IMAGE;
+ image_types += strlen ("RGB*");
+ }
+ else if (g_str_has_prefix (image_types, "RGB"))
+ {
+ types |= GIMP_PLUG_IN_RGB_IMAGE;
+ image_types += strlen ("RGB");
+ }
+ else if (g_str_has_prefix (image_types, "GRAYA"))
+ {
+ types |= GIMP_PLUG_IN_GRAYA_IMAGE;
+ image_types += strlen ("GRAYA");
+ }
+ else if (g_str_has_prefix (image_types, "GRAY*"))
+ {
+ types |= GIMP_PLUG_IN_GRAY_IMAGE | GIMP_PLUG_IN_GRAYA_IMAGE;
+ image_types += strlen ("GRAY*");
+ }
+ else if (g_str_has_prefix (image_types, "GRAY"))
+ {
+ types |= GIMP_PLUG_IN_GRAY_IMAGE;
+ image_types += strlen ("GRAY");
+ }
+ else if (g_str_has_prefix (image_types, "INDEXEDA"))
+ {
+ types |= GIMP_PLUG_IN_INDEXEDA_IMAGE;
+ image_types += strlen ("INDEXEDA");
+ }
+ else if (g_str_has_prefix (image_types, "INDEXED*"))
+ {
+ types |= GIMP_PLUG_IN_INDEXED_IMAGE | GIMP_PLUG_IN_INDEXEDA_IMAGE;
+ image_types += strlen ("INDEXED*");
+ }
+ else if (g_str_has_prefix (image_types, "INDEXED"))
+ {
+ types |= GIMP_PLUG_IN_INDEXED_IMAGE;
+ image_types += strlen ("INDEXED");
+ }
+ else if (g_str_has_prefix (image_types, "*"))
+ {
+ types |= (GIMP_PLUG_IN_RGB_IMAGE | GIMP_PLUG_IN_RGBA_IMAGE |
+ GIMP_PLUG_IN_GRAY_IMAGE | GIMP_PLUG_IN_GRAYA_IMAGE |
+ GIMP_PLUG_IN_INDEXED_IMAGE | GIMP_PLUG_IN_INDEXEDA_IMAGE);
+ image_types += strlen ("*");
+ }
+ else
+ {
+ g_printerr ("%s: image-type contains unrecognizable parts:"
+ "'%s'\n", name, type_spec);
+
+ /* skip to next token */
+ while (*image_types &&
+ *image_types != ' ' &&
+ *image_types != '\t' &&
+ *image_types != ',')
+ {
+ image_types++;
+ }
+ }
+ }
+ }
+
+ return types;
+}
+
+void
+gimp_plug_in_procedure_set_image_types (GimpPlugInProcedure *proc,
+ const gchar *image_types)
+{
+ GList *types = NULL;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc));
+
+ if (proc->image_types)
+ g_free (proc->image_types);
+
+ proc->image_types = g_strdup (image_types);
+ proc->image_types_val = image_types_parse (gimp_object_get_name (proc),
+ proc->image_types);
+
+ g_clear_pointer (&proc->image_types_tooltip, g_free);
+
+ if (proc->image_types_val &
+ (GIMP_PLUG_IN_RGB_IMAGE | GIMP_PLUG_IN_RGBA_IMAGE))
+ {
+ if ((proc->image_types_val & GIMP_PLUG_IN_RGB_IMAGE) &&
+ (proc->image_types_val & GIMP_PLUG_IN_RGBA_IMAGE))
+ {
+ types = g_list_prepend (types, _("RGB"));
+ }
+ else if (proc->image_types_val & GIMP_PLUG_IN_RGB_IMAGE)
+ {
+ types = g_list_prepend (types, _("RGB without alpha"));
+ }
+ else
+ {
+ types = g_list_prepend (types, _("RGB with alpha"));
+ }
+ }
+
+ if (proc->image_types_val &
+ (GIMP_PLUG_IN_GRAY_IMAGE | GIMP_PLUG_IN_GRAYA_IMAGE))
+ {
+ if ((proc->image_types_val & GIMP_PLUG_IN_GRAY_IMAGE) &&
+ (proc->image_types_val & GIMP_PLUG_IN_GRAYA_IMAGE))
+ {
+ types = g_list_prepend (types, _("Grayscale"));
+ }
+ else if (proc->image_types_val & GIMP_PLUG_IN_GRAY_IMAGE)
+ {
+ types = g_list_prepend (types, _("Grayscale without alpha"));
+ }
+ else
+ {
+ types = g_list_prepend (types, _("Grayscale with alpha"));
+ }
+ }
+
+ if (proc->image_types_val &
+ (GIMP_PLUG_IN_INDEXED_IMAGE | GIMP_PLUG_IN_INDEXEDA_IMAGE))
+ {
+ if ((proc->image_types_val & GIMP_PLUG_IN_INDEXED_IMAGE) &&
+ (proc->image_types_val & GIMP_PLUG_IN_INDEXEDA_IMAGE))
+ {
+ types = g_list_prepend (types, _("Indexed"));
+ }
+ else if (proc->image_types_val & GIMP_PLUG_IN_INDEXED_IMAGE)
+ {
+ types = g_list_prepend (types, _("Indexed without alpha"));
+ }
+ else
+ {
+ types = g_list_prepend (types, _("Indexed with alpha"));
+ }
+ }
+
+ if (types)
+ {
+ GString *string;
+ GList *list;
+
+ types = g_list_reverse (types);
+
+ string = g_string_new (gimp_procedure_get_blurb (GIMP_PROCEDURE (proc)));
+
+ g_string_append (string, "\n\n");
+ g_string_append (string, _("This plug-in only works on the "
+ "following layer types:"));
+ g_string_append (string, "\n");
+
+ for (list = types; list; list = g_list_next (list))
+ {
+ g_string_append (string, list->data);
+
+ if (list->next)
+ g_string_append (string, ", ");
+ else
+ g_string_append (string, ".");
+ }
+
+ g_list_free (types);
+
+ proc->image_types_tooltip = g_string_free (string, FALSE);
+ }
+}
+
+static GSList *
+extensions_parse (gchar *extensions)
+{
+ GSList *list = NULL;
+
+ /* extensions can be NULL. Avoid calling strtok if it is. */
+ if (extensions)
+ {
+ gchar *extension;
+ gchar *next_token;
+
+ /* work on a copy */
+ extensions = g_strdup (extensions);
+
+ next_token = extensions;
+ extension = strtok (next_token, " \t,");
+
+ while (extension)
+ {
+ list = g_slist_prepend (list, g_strdup (extension));
+ extension = strtok (NULL, " \t,");
+ }
+
+ g_free (extensions);
+ }
+
+ return g_slist_reverse (list);
+}
+
+void
+gimp_plug_in_procedure_set_file_proc (GimpPlugInProcedure *proc,
+ const gchar *extensions,
+ const gchar *prefixes,
+ const gchar *magics)
+{
+ GSList *list;
+
+ g_return_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc));
+
+ proc->file_proc = TRUE;
+
+ /* extensions */
+
+ if (proc->extensions != extensions)
+ {
+ if (proc->extensions)
+ g_free (proc->extensions);
+
+ proc->extensions = g_strdup (extensions);
+ }
+
+ if (proc->extensions_list)
+ g_slist_free_full (proc->extensions_list, (GDestroyNotify) g_free);
+
+ proc->extensions_list = extensions_parse (proc->extensions);
+
+ /* prefixes */
+
+ if (proc->prefixes != prefixes)
+ {
+ if (proc->prefixes)
+ g_free (proc->prefixes);
+
+ proc->prefixes = g_strdup (prefixes);
+ }
+
+ if (proc->prefixes_list)
+ g_slist_free_full (proc->prefixes_list, (GDestroyNotify) g_free);
+
+ proc->prefixes_list = extensions_parse (proc->prefixes);
+
+ /* don't allow "file:" to be registered as prefix */
+ for (list = proc->prefixes_list; list; list = g_slist_next (list))
+ {
+ const gchar *prefix = list->data;
+
+ if (prefix && strcmp (prefix, "file:") == 0)
+ {
+ g_free (list->data);
+ proc->prefixes_list = g_slist_delete_link (proc->prefixes_list, list);
+ break;
+ }
+ }
+
+ /* magics */
+
+ if (proc->magics != magics)
+ {
+ if (proc->magics)
+ g_free (proc->magics);
+
+ proc->magics = g_strdup (magics);
+ }
+
+ if (proc->magics_list)
+ g_slist_free_full (proc->magics_list, (GDestroyNotify) g_free);
+
+ proc->magics_list = extensions_parse (proc->magics);
+}
+
+void
+gimp_plug_in_procedure_set_priority (GimpPlugInProcedure *proc,
+ gint priority)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc));
+
+ proc->priority = priority;
+}
+
+void
+gimp_plug_in_procedure_set_mime_types (GimpPlugInProcedure *proc,
+ const gchar *mime_types)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc));
+
+ if (proc->mime_types != mime_types)
+ {
+ if (proc->mime_types)
+ g_free (proc->mime_types);
+
+ proc->mime_types = g_strdup (mime_types);
+ }
+
+ if (proc->mime_types_list)
+ g_slist_free_full (proc->mime_types_list, (GDestroyNotify) g_free);
+
+ proc->mime_types_list = extensions_parse (proc->mime_types);
+}
+
+void
+gimp_plug_in_procedure_set_handles_uri (GimpPlugInProcedure *proc)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc));
+
+ proc->handles_uri = TRUE;
+}
+
+void
+gimp_plug_in_procedure_set_handles_raw (GimpPlugInProcedure *proc)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc));
+
+ proc->handles_raw = TRUE;
+}
+
+void
+gimp_plug_in_procedure_set_thumb_loader (GimpPlugInProcedure *proc,
+ const gchar *thumb_loader)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc));
+
+ if (proc->thumb_loader)
+ g_free (proc->thumb_loader);
+
+ proc->thumb_loader = g_strdup (thumb_loader);
+}
+
+void
+gimp_plug_in_procedure_handle_return_values (GimpPlugInProcedure *proc,
+ Gimp *gimp,
+ GimpProgress *progress,
+ GimpValueArray *return_vals)
+{
+ g_return_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (proc));
+ g_return_if_fail (return_vals != NULL);
+
+ if (gimp_value_array_length (return_vals) == 0 ||
+ G_VALUE_TYPE (gimp_value_array_index (return_vals, 0)) !=
+ GIMP_TYPE_PDB_STATUS_TYPE)
+ {
+ return;
+ }
+
+ switch (g_value_get_enum (gimp_value_array_index (return_vals, 0)))
+ {
+ case GIMP_PDB_SUCCESS:
+ break;
+
+ case GIMP_PDB_CALLING_ERROR:
+ if (gimp_value_array_length (return_vals) > 1 &&
+ G_VALUE_HOLDS_STRING (gimp_value_array_index (return_vals, 1)))
+ {
+ gimp_message (gimp, G_OBJECT (progress), GIMP_MESSAGE_ERROR,
+ _("Calling error for '%s':\n"
+ "%s"),
+ gimp_procedure_get_label (GIMP_PROCEDURE (proc)),
+ g_value_get_string (gimp_value_array_index (return_vals, 1)));
+ }
+ break;
+
+ case GIMP_PDB_EXECUTION_ERROR:
+ if (gimp_value_array_length (return_vals) > 1 &&
+ G_VALUE_HOLDS_STRING (gimp_value_array_index (return_vals, 1)))
+ {
+ gimp_message (gimp, G_OBJECT (progress), GIMP_MESSAGE_ERROR,
+ _("Execution error for '%s':\n"
+ "%s"),
+ gimp_procedure_get_label (GIMP_PROCEDURE (proc)),
+ g_value_get_string (gimp_value_array_index (return_vals, 1)));
+ }
+ break;
+ }
+}
diff --git a/app/plug-in/gimppluginprocedure.h b/app/plug-in/gimppluginprocedure.h
new file mode 100644
index 0000000..e462040
--- /dev/null
+++ b/app/plug-in/gimppluginprocedure.h
@@ -0,0 +1,140 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginprocedure.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_PROCEDURE_H__
+#define __GIMP_PLUG_IN_PROCEDURE_H__
+
+
+#include "pdb/gimpprocedure.h"
+
+
+#define GIMP_TYPE_PLUG_IN_PROCEDURE (gimp_plug_in_procedure_get_type ())
+#define GIMP_PLUG_IN_PROCEDURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_PLUG_IN_PROCEDURE, GimpPlugInProcedure))
+#define GIMP_PLUG_IN_PROCEDURE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_PLUG_IN_PROCEDURE, GimpPlugInProcedureClass))
+#define GIMP_IS_PLUG_IN_PROCEDURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_PLUG_IN_PROCEDURE))
+#define GIMP_IS_PLUG_IN_PROCEDURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_PLUG_IN_PROCEDURE))
+#define GIMP_PLUG_IN_PROCEDURE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_PLUG_IN_PROCEDURE, GimpPlugInProcedureClass))
+
+
+typedef struct _GimpPlugInProcedureClass GimpPlugInProcedureClass;
+
+struct _GimpPlugInProcedure
+{
+ GimpProcedure parent_instance;
+
+ /* common members */
+ GFile *file;
+ GQuark locale_domain;
+ GQuark help_domain;
+ gchar *menu_label;
+ GList *menu_paths;
+ gchar *label;
+ gchar *help_id;
+ GimpIconType icon_type;
+ gint icon_data_length;
+ guint8 *icon_data;
+ gchar *image_types;
+ GimpPlugInImageType image_types_val;
+ gchar *image_types_tooltip;
+ gint64 mtime;
+ gboolean installed_during_init;
+
+ /* file proc specific members */
+ gboolean file_proc;
+ gchar *extensions;
+ gchar *prefixes;
+ gchar *magics;
+ gint priority;
+ gchar *mime_types;
+ gboolean handles_uri;
+ gboolean handles_raw;
+ GSList *extensions_list;
+ GSList *prefixes_list;
+ GSList *magics_list;
+ GSList *mime_types_list;
+ gchar *thumb_loader;
+};
+
+struct _GimpPlugInProcedureClass
+{
+ GimpProcedureClass parent_class;
+
+ /* virtual functions */
+ GFile * (* get_file) (GimpPlugInProcedure *procedure);
+
+ /* signals */
+ void (* menu_path_added) (GimpPlugInProcedure *procedure,
+ const gchar *menu_path);
+};
+
+
+GType gimp_plug_in_procedure_get_type (void) G_GNUC_CONST;
+
+GimpProcedure * gimp_plug_in_procedure_new (GimpPDBProcType proc_type,
+ GFile *file);
+
+GimpPlugInProcedure * gimp_plug_in_procedure_find (GSList *list,
+ const gchar *proc_name);
+
+GFile * gimp_plug_in_procedure_get_file (GimpPlugInProcedure *proc);
+
+void gimp_plug_in_procedure_set_locale_domain (GimpPlugInProcedure *proc,
+ const gchar *locale_domain);
+const gchar * gimp_plug_in_procedure_get_locale_domain (GimpPlugInProcedure *proc);
+
+void gimp_plug_in_procedure_set_help_domain (GimpPlugInProcedure *proc,
+ const gchar *help_domain);
+const gchar * gimp_plug_in_procedure_get_help_domain (GimpPlugInProcedure *proc);
+
+gboolean gimp_plug_in_procedure_add_menu_path (GimpPlugInProcedure *proc,
+ const gchar *menu_path,
+ GError **error);
+
+void gimp_plug_in_procedure_set_icon (GimpPlugInProcedure *proc,
+ GimpIconType type,
+ const guint8 *data,
+ gint data_length);
+void gimp_plug_in_procedure_take_icon (GimpPlugInProcedure *proc,
+ GimpIconType type,
+ guint8 *data,
+ gint data_length);
+
+void gimp_plug_in_procedure_set_image_types (GimpPlugInProcedure *proc,
+ const gchar *image_types);
+void gimp_plug_in_procedure_set_file_proc (GimpPlugInProcedure *proc,
+ const gchar *extensions,
+ const gchar *prefixes,
+ const gchar *magics);
+void gimp_plug_in_procedure_set_priority (GimpPlugInProcedure *proc,
+ gint priority);
+void gimp_plug_in_procedure_set_mime_types (GimpPlugInProcedure *proc,
+ const gchar *mime_ypes);
+void gimp_plug_in_procedure_set_handles_uri (GimpPlugInProcedure *proc);
+void gimp_plug_in_procedure_set_handles_raw (GimpPlugInProcedure *proc);
+void gimp_plug_in_procedure_set_thumb_loader (GimpPlugInProcedure *proc,
+ const gchar *thumbnailer);
+
+void gimp_plug_in_procedure_handle_return_values (GimpPlugInProcedure *proc,
+ Gimp *gimp,
+ GimpProgress *progress,
+
+ GimpValueArray *return_vals);
+
+
+#endif /* __GIMP_PLUG_IN_PROCEDURE_H__ */
diff --git a/app/plug-in/gimppluginprocframe.c b/app/plug-in/gimppluginprocframe.c
new file mode 100644
index 0000000..10445ef
--- /dev/null
+++ b/app/plug-in/gimppluginprocframe.c
@@ -0,0 +1,200 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginprocframe.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "plug-in-types.h"
+
+#include "core/gimpprogress.h"
+
+#include "pdb/gimppdbcontext.h"
+#include "pdb/gimppdberror.h"
+
+#include "gimpplugin.h"
+#include "gimpplugin-cleanup.h"
+#include "gimpplugin-progress.h"
+#include "gimppluginprocedure.h"
+
+#include "gimp-intl.h"
+
+
+/* public functions */
+
+GimpPlugInProcFrame *
+gimp_plug_in_proc_frame_new (GimpContext *context,
+ GimpProgress *progress,
+ GimpPlugInProcedure *procedure)
+{
+ GimpPlugInProcFrame *proc_frame;
+
+ g_return_val_if_fail (GIMP_IS_PDB_CONTEXT (context), NULL);
+ g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL);
+ g_return_val_if_fail (GIMP_IS_PLUG_IN_PROCEDURE (procedure), NULL);
+
+ proc_frame = g_slice_new0 (GimpPlugInProcFrame);
+
+ proc_frame->ref_count = 1;
+
+ gimp_plug_in_proc_frame_init (proc_frame, context, progress, procedure);
+
+ return proc_frame;
+}
+
+void
+gimp_plug_in_proc_frame_init (GimpPlugInProcFrame *proc_frame,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpPlugInProcedure *procedure)
+{
+ g_return_if_fail (proc_frame != NULL);
+ g_return_if_fail (GIMP_IS_PDB_CONTEXT (context));
+ g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));
+ g_return_if_fail (procedure == NULL ||
+ GIMP_IS_PLUG_IN_PROCEDURE (procedure));
+
+ proc_frame->main_context = g_object_ref (context);
+ proc_frame->context_stack = NULL;
+ proc_frame->procedure = procedure ? g_object_ref (GIMP_PROCEDURE (procedure)) : NULL;
+ proc_frame->main_loop = NULL;
+ proc_frame->return_vals = NULL;
+ proc_frame->progress = progress ? g_object_ref (progress) : NULL;
+ proc_frame->progress_created = FALSE;
+ proc_frame->progress_cancel_id = 0;
+ proc_frame->error_handler = GIMP_PDB_ERROR_HANDLER_INTERNAL;
+
+ if (progress)
+ gimp_plug_in_progress_attach (progress);
+}
+
+void
+gimp_plug_in_proc_frame_dispose (GimpPlugInProcFrame *proc_frame,
+ GimpPlugIn *plug_in)
+{
+ g_return_if_fail (proc_frame != NULL);
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+
+ if (proc_frame->progress)
+ {
+ gimp_plug_in_progress_end (plug_in, proc_frame);
+
+ g_clear_object (&proc_frame->progress);
+ }
+
+ if (proc_frame->context_stack)
+ {
+ g_list_free_full (proc_frame->context_stack,
+ (GDestroyNotify) g_object_unref);
+ proc_frame->context_stack = NULL;
+ }
+
+ g_clear_object (&proc_frame->main_context);
+ g_clear_pointer (&proc_frame->return_vals, gimp_value_array_unref);
+ g_clear_pointer (&proc_frame->main_loop, g_main_loop_unref);
+
+ if (proc_frame->image_cleanups || proc_frame->item_cleanups)
+ gimp_plug_in_cleanup (plug_in, proc_frame);
+
+ g_clear_object (&proc_frame->procedure);
+}
+
+GimpPlugInProcFrame *
+gimp_plug_in_proc_frame_ref (GimpPlugInProcFrame *proc_frame)
+{
+ g_return_val_if_fail (proc_frame != NULL, NULL);
+
+ proc_frame->ref_count++;
+
+ return proc_frame;
+}
+
+void
+gimp_plug_in_proc_frame_unref (GimpPlugInProcFrame *proc_frame,
+ GimpPlugIn *plug_in)
+{
+ g_return_if_fail (proc_frame != NULL);
+ g_return_if_fail (GIMP_IS_PLUG_IN (plug_in));
+
+ proc_frame->ref_count--;
+
+ if (proc_frame->ref_count < 1)
+ {
+ gimp_plug_in_proc_frame_dispose (proc_frame, plug_in);
+ g_slice_free (GimpPlugInProcFrame, proc_frame);
+ }
+}
+
+GimpValueArray *
+gimp_plug_in_proc_frame_get_return_values (GimpPlugInProcFrame *proc_frame)
+{
+ GimpValueArray *return_vals;
+
+ g_return_val_if_fail (proc_frame != NULL, NULL);
+
+ if (proc_frame->return_vals)
+ {
+ if (gimp_value_array_length (proc_frame->return_vals) >=
+ proc_frame->procedure->num_values + 1)
+ {
+ return_vals = proc_frame->return_vals;
+ }
+ else
+ {
+ /* Allocate new return values of the correct size. */
+ return_vals = gimp_procedure_get_return_values (proc_frame->procedure,
+ TRUE, NULL);
+
+ /* Copy all of the arguments we can. */
+ memcpy (gimp_value_array_index (return_vals, 0),
+ gimp_value_array_index (proc_frame->return_vals, 0),
+ sizeof (GValue) *
+ gimp_value_array_length (proc_frame->return_vals));
+
+ /* Free the old arguments. */
+ memset (gimp_value_array_index (proc_frame->return_vals, 0), 0,
+ sizeof (GValue) *
+ gimp_value_array_length (proc_frame->return_vals));
+ gimp_value_array_unref (proc_frame->return_vals);
+ }
+
+ /* We have consumed any saved values, so clear them. */
+ proc_frame->return_vals = NULL;
+ }
+ else
+ {
+ GimpProcedure *procedure = proc_frame->procedure;
+ GError *error;
+
+ error = g_error_new (GIMP_PDB_ERROR, GIMP_PDB_ERROR_INVALID_RETURN_VALUE,
+ _("Procedure '%s' returned no return values"),
+ gimp_object_get_name (procedure));
+
+ return_vals = gimp_procedure_get_return_values (procedure, FALSE,
+ error);
+ g_error_free (error);
+ }
+
+ return return_vals;
+}
diff --git a/app/plug-in/gimppluginprocframe.h b/app/plug-in/gimppluginprocframe.h
new file mode 100644
index 0000000..1f21b8e
--- /dev/null
+++ b/app/plug-in/gimppluginprocframe.h
@@ -0,0 +1,67 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginprocframe.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_PROC_FRAME_H__
+#define __GIMP_PLUG_IN_PROC_FRAME_H__
+
+
+struct _GimpPlugInProcFrame
+{
+ gint ref_count;
+
+ GimpContext *main_context;
+ GList *context_stack;
+
+ GimpProcedure *procedure;
+ GMainLoop *main_loop;
+
+ GimpValueArray *return_vals;
+
+ GimpProgress *progress;
+ gboolean progress_created;
+ gulong progress_cancel_id;
+
+ GimpPDBErrorHandler error_handler;
+
+ /* lists of things to clean up on dispose */
+ GList *image_cleanups;
+ GList *item_cleanups;
+};
+
+
+GimpPlugInProcFrame * gimp_plug_in_proc_frame_new (GimpContext *context,
+ GimpProgress *progress,
+ GimpPlugInProcedure *procedure);
+void gimp_plug_in_proc_frame_init (GimpPlugInProcFrame *proc_frame,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpPlugInProcedure *procedure);
+
+void gimp_plug_in_proc_frame_dispose (GimpPlugInProcFrame *proc_frame,
+ GimpPlugIn *plug_in);
+
+GimpPlugInProcFrame * gimp_plug_in_proc_frame_ref (GimpPlugInProcFrame *proc_frame);
+void gimp_plug_in_proc_frame_unref (GimpPlugInProcFrame *proc_frame,
+ GimpPlugIn *plug_in);
+
+GimpValueArray * gimp_plug_in_proc_frame_get_return_values
+ (GimpPlugInProcFrame *proc_frame);
+
+
+#endif /* __GIMP_PLUG_IN_PROC_FRAME_H__ */
diff --git a/app/plug-in/gimppluginshm.c b/app/plug-in/gimppluginshm.c
new file mode 100644
index 0000000..8f92905
--- /dev/null
+++ b/app/plug-in/gimppluginshm.c
@@ -0,0 +1,301 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginhsm.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#include <errno.h>
+
+#if defined(USE_SYSV_SHM)
+
+#ifdef HAVE_IPC_H
+#include <sys/ipc.h>
+#endif
+
+#ifdef HAVE_SHM_H
+#include <sys/shm.h>
+#endif
+
+#elif defined(USE_POSIX_SHM)
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#endif /* USE_POSIX_SHM */
+
+#include <gio/gio.h>
+#include <gegl.h>
+
+#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
+
+#define STRICT
+#include <windows.h>
+#include <process.h>
+
+#ifdef G_OS_WIN32
+#include <fcntl.h>
+#include <io.h>
+#endif
+
+#define USE_WIN32_SHM 1
+
+#endif /* G_OS_WIN32 || G_WITH_CYGWIN */
+
+#include "plug-in-types.h"
+
+#include "core/gimp-utils.h"
+
+#include "gimppluginshm.h"
+
+#include "gimp-log.h"
+
+
+#define TILE_MAP_SIZE (GIMP_PLUG_IN_TILE_WIDTH * GIMP_PLUG_IN_TILE_HEIGHT * 32)
+
+#define ERRMSG_SHM_DISABLE "Disabling shared memory tile transport"
+
+
+struct _GimpPlugInShm
+{
+ gint shm_ID;
+ guchar *shm_addr;
+
+#if defined(USE_WIN32_SHM)
+ HANDLE shm_handle;
+#endif
+};
+
+
+GimpPlugInShm *
+gimp_plug_in_shm_new (void)
+{
+ /* allocate a piece of shared memory for use in transporting tiles
+ * to plug-ins. if we can't allocate a piece of shared memory then
+ * we'll fall back on sending the data over the pipe.
+ */
+
+ GimpPlugInShm *shm = g_slice_new0 (GimpPlugInShm);
+
+ shm->shm_ID = -1;
+
+#if defined(USE_SYSV_SHM)
+
+ /* Use SysV shared memory mechanisms for transferring tile data. */
+ {
+ shm->shm_ID = shmget (IPC_PRIVATE, TILE_MAP_SIZE, IPC_CREAT | 0600);
+
+ if (shm->shm_ID != -1)
+ {
+ shm->shm_addr = (guchar *) shmat (shm->shm_ID, NULL, 0);
+
+ if (shm->shm_addr == (guchar *) -1)
+ {
+ g_printerr ("shmat() failed: %s\n" ERRMSG_SHM_DISABLE,
+ g_strerror (errno));
+ shmctl (shm->shm_ID, IPC_RMID, NULL);
+ shm->shm_ID = -1;
+ }
+
+#ifdef IPC_RMID_DEFERRED_RELEASE
+ if (shm->shm_addr != (guchar *) -1)
+ shmctl (shm->shm_ID, IPC_RMID, NULL);
+#endif
+ }
+ else
+ {
+ g_printerr ("shmget() failed: %s\n" ERRMSG_SHM_DISABLE,
+ g_strerror (errno));
+ }
+ }
+
+#elif defined(USE_WIN32_SHM)
+
+ /* Use Win32 shared memory mechanisms for transferring tile data. */
+ {
+ gint pid;
+ gchar fileMapName[MAX_PATH];
+
+ /* Our shared memory id will be our process ID */
+ pid = GetCurrentProcessId ();
+
+ /* From the id, derive the file map name */
+ g_snprintf (fileMapName, sizeof (fileMapName), "GIMP%d.SHM", pid);
+
+ /* Create the file mapping into paging space */
+ shm->shm_handle = CreateFileMapping (INVALID_HANDLE_VALUE, NULL,
+ PAGE_READWRITE, 0,
+ TILE_MAP_SIZE,
+ fileMapName);
+
+ if (shm->shm_handle)
+ {
+ /* Map the shared memory into our address space for use */
+ shm->shm_addr = (guchar *) MapViewOfFile (shm->shm_handle,
+ FILE_MAP_ALL_ACCESS,
+ 0, 0, TILE_MAP_SIZE);
+
+ /* Verify that we mapped our view */
+ if (shm->shm_addr)
+ {
+ shm->shm_ID = pid;
+ }
+ else
+ {
+ g_printerr ("MapViewOfFile error: %d... " ERRMSG_SHM_DISABLE,
+ GetLastError ());
+ }
+ }
+ else
+ {
+ g_printerr ("CreateFileMapping error: %d... " ERRMSG_SHM_DISABLE,
+ GetLastError ());
+ }
+ }
+
+#elif defined(USE_POSIX_SHM)
+
+ /* Use POSIX shared memory mechanisms for transferring tile data. */
+ {
+ gint pid;
+ gchar shm_handle[32];
+ gint shm_fd;
+
+ /* Our shared memory id will be our process ID */
+ pid = gimp_get_pid ();
+
+ /* From the id, derive the file map name */
+ g_snprintf (shm_handle, sizeof (shm_handle), "/gimp-shm-%d", pid);
+
+ /* Create the file mapping into paging space */
+ shm_fd = shm_open (shm_handle, O_RDWR | O_CREAT, 0600);
+
+ if (shm_fd != -1)
+ {
+ if (ftruncate (shm_fd, TILE_MAP_SIZE) != -1)
+ {
+ /* Map the shared memory into our address space for use */
+ shm->shm_addr = (guchar *) mmap (NULL, TILE_MAP_SIZE,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ shm_fd, 0);
+
+ /* Verify that we mapped our view */
+ if (shm->shm_addr != MAP_FAILED)
+ {
+ shm->shm_ID = pid;
+ }
+ else
+ {
+ g_printerr ("mmap() failed: %s\n" ERRMSG_SHM_DISABLE,
+ g_strerror (errno));
+
+ shm_unlink (shm_handle);
+ }
+ }
+ else
+ {
+ g_printerr ("ftruncate() failed: %s\n" ERRMSG_SHM_DISABLE,
+ g_strerror (errno));
+
+ shm_unlink (shm_handle);
+ }
+
+ close (shm_fd);
+ }
+ else
+ {
+ g_printerr ("shm_open() failed: %s\n" ERRMSG_SHM_DISABLE,
+ g_strerror (errno));
+ }
+ }
+
+#endif
+
+ if (shm->shm_ID == -1)
+ {
+ g_slice_free (GimpPlugInShm, shm);
+ shm = NULL;
+ }
+ else
+ {
+ GIMP_LOG (SHM, "attached shared memory segment ID = %d", shm->shm_ID);
+ }
+
+ return shm;
+}
+
+void
+gimp_plug_in_shm_free (GimpPlugInShm *shm)
+{
+ g_return_if_fail (shm != NULL);
+
+ if (shm->shm_ID != -1)
+ {
+
+#if defined (USE_SYSV_SHM)
+
+ shmdt (shm->shm_addr);
+
+#ifndef IPC_RMID_DEFERRED_RELEASE
+ shmctl (shm->shm_ID, IPC_RMID, NULL);
+#endif
+
+#elif defined(USE_WIN32_SHM)
+
+ if (shm->shm_handle)
+ CloseHandle (shm->shm_handle);
+
+#elif defined(USE_POSIX_SHM)
+
+ gchar shm_handle[32];
+
+ munmap (shm->shm_addr, TILE_MAP_SIZE);
+
+ g_snprintf (shm_handle, sizeof (shm_handle), "/gimp-shm-%d",
+ shm->shm_ID);
+
+ shm_unlink (shm_handle);
+
+#endif
+
+ GIMP_LOG (SHM, "detached shared memory segment ID = %d", shm->shm_ID);
+ }
+
+ g_slice_free (GimpPlugInShm, shm);
+}
+
+gint
+gimp_plug_in_shm_get_ID (GimpPlugInShm *shm)
+{
+ g_return_val_if_fail (shm != NULL, -1);
+
+ return shm->shm_ID;
+}
+
+guchar *
+gimp_plug_in_shm_get_addr (GimpPlugInShm *shm)
+{
+ g_return_val_if_fail (shm != NULL, NULL);
+
+ return shm->shm_addr;
+}
diff --git a/app/plug-in/gimppluginshm.h b/app/plug-in/gimppluginshm.h
new file mode 100644
index 0000000..ee89918
--- /dev/null
+++ b/app/plug-in/gimppluginshm.h
@@ -0,0 +1,31 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimppluginshm.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_PLUG_IN_SHM_H__
+#define __GIMP_PLUG_IN_SHM_H__
+
+
+GimpPlugInShm * gimp_plug_in_shm_new (void);
+void gimp_plug_in_shm_free (GimpPlugInShm *shm);
+
+gint gimp_plug_in_shm_get_ID (GimpPlugInShm *shm);
+guchar * gimp_plug_in_shm_get_addr (GimpPlugInShm *shm);
+
+
+#endif /* __GIMP_PLUG_IN_SHM_H__ */
diff --git a/app/plug-in/gimptemporaryprocedure.c b/app/plug-in/gimptemporaryprocedure.c
new file mode 100644
index 0000000..5b03d05
--- /dev/null
+++ b/app/plug-in/gimptemporaryprocedure.c
@@ -0,0 +1,156 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimptemporaryprocedure.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "plug-in-types.h"
+
+#include "core/gimp.h"
+
+#include "gimpplugin.h"
+#define __YES_I_NEED_GIMP_PLUG_IN_MANAGER_CALL__
+#include "gimppluginmanager-call.h"
+#include "gimptemporaryprocedure.h"
+
+#include "gimp-intl.h"
+
+
+static void gimp_temporary_procedure_finalize (GObject *object);
+
+static GimpValueArray * gimp_temporary_procedure_execute (GimpProcedure *procedure,
+ Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpValueArray *args,
+ GError **error);
+static void gimp_temporary_procedure_execute_async (GimpProcedure *procedure,
+ Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpValueArray *args,
+ GimpObject *display);
+
+static GFile * gimp_temporary_procedure_get_file (GimpPlugInProcedure *procedure);
+
+
+G_DEFINE_TYPE (GimpTemporaryProcedure, gimp_temporary_procedure,
+ GIMP_TYPE_PLUG_IN_PROCEDURE)
+
+#define parent_class gimp_temporary_procedure_parent_class
+
+
+static void
+gimp_temporary_procedure_class_init (GimpTemporaryProcedureClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpProcedureClass *proc_class = GIMP_PROCEDURE_CLASS (klass);
+ GimpPlugInProcedureClass *plug_class = GIMP_PLUG_IN_PROCEDURE_CLASS (klass);
+
+ object_class->finalize = gimp_temporary_procedure_finalize;
+
+ proc_class->execute = gimp_temporary_procedure_execute;
+ proc_class->execute_async = gimp_temporary_procedure_execute_async;
+
+ plug_class->get_file = gimp_temporary_procedure_get_file;
+}
+
+static void
+gimp_temporary_procedure_init (GimpTemporaryProcedure *proc)
+{
+ GIMP_PROCEDURE (proc)->proc_type = GIMP_TEMPORARY;
+}
+
+static void
+gimp_temporary_procedure_finalize (GObject *object)
+{
+ /* GimpTemporaryProcedure *proc = GIMP_TEMPORARY_PROCEDURE (object); */
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static GimpValueArray *
+gimp_temporary_procedure_execute (GimpProcedure *procedure,
+ Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpValueArray *args,
+ GError **error)
+{
+ return gimp_plug_in_manager_call_run_temp (gimp->plug_in_manager,
+ context, progress,
+ GIMP_TEMPORARY_PROCEDURE (procedure),
+ args);
+}
+
+static void
+gimp_temporary_procedure_execute_async (GimpProcedure *procedure,
+ Gimp *gimp,
+ GimpContext *context,
+ GimpProgress *progress,
+ GimpValueArray *args,
+ GimpObject *display)
+{
+ GimpTemporaryProcedure *temp_procedure = GIMP_TEMPORARY_PROCEDURE (procedure);
+ GimpValueArray *return_vals;
+
+ return_vals = gimp_plug_in_manager_call_run_temp (gimp->plug_in_manager,
+ context, progress,
+ temp_procedure,
+ args);
+
+ if (return_vals)
+ {
+ GimpPlugInProcedure *proc = GIMP_PLUG_IN_PROCEDURE (procedure);
+
+ gimp_plug_in_procedure_handle_return_values (proc,
+ gimp, progress,
+ return_vals);
+ gimp_value_array_unref (return_vals);
+ }
+}
+
+static GFile *
+gimp_temporary_procedure_get_file (GimpPlugInProcedure *procedure)
+{
+ return GIMP_TEMPORARY_PROCEDURE (procedure)->plug_in->file;
+}
+
+
+/* public functions */
+
+GimpProcedure *
+gimp_temporary_procedure_new (GimpPlugIn *plug_in)
+{
+ GimpTemporaryProcedure *proc;
+
+ g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), NULL);
+
+ proc = g_object_new (GIMP_TYPE_TEMPORARY_PROCEDURE, NULL);
+
+ proc->plug_in = plug_in;
+
+ GIMP_PLUG_IN_PROCEDURE (proc)->file = g_file_new_for_path ("none");
+
+ return GIMP_PROCEDURE (proc);
+}
diff --git a/app/plug-in/gimptemporaryprocedure.h b/app/plug-in/gimptemporaryprocedure.h
new file mode 100644
index 0000000..3df53c8
--- /dev/null
+++ b/app/plug-in/gimptemporaryprocedure.h
@@ -0,0 +1,55 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimptemporaryprocedure.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_TEMPORARY_PROCEDURE_H__
+#define __GIMP_TEMPORARY_PROCEDURE_H__
+
+
+#include "gimppluginprocedure.h"
+
+
+#define GIMP_TYPE_TEMPORARY_PROCEDURE (gimp_temporary_procedure_get_type ())
+#define GIMP_TEMPORARY_PROCEDURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TEMPORARY_PROCEDURE, GimpTemporaryProcedure))
+#define GIMP_TEMPORARY_PROCEDURE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TEMPORARY_PROCEDURE, GimpTemporaryProcedureClass))
+#define GIMP_IS_TEMPORARY_PROCEDURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TEMPORARY_PROCEDURE))
+#define GIMP_IS_TEMPORARY_PROCEDURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TEMPORARY_PROCEDURE))
+#define GIMP_TEMPORARY_PROCEDURE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TEMPORARY_PROCEDURE, GimpTemporaryProcedureClass))
+
+
+typedef struct _GimpTemporaryProcedureClass GimpTemporaryProcedureClass;
+
+struct _GimpTemporaryProcedure
+{
+ GimpPlugInProcedure parent_instance;
+
+ GimpPlugIn *plug_in;
+};
+
+struct _GimpTemporaryProcedureClass
+{
+ GimpPlugInProcedureClass parent_class;
+};
+
+
+GType gimp_temporary_procedure_get_type (void) G_GNUC_CONST;
+
+GimpProcedure * gimp_temporary_procedure_new (GimpPlugIn *plug_in);
+
+
+#endif /* __GIMP_TEMPORARY_PROCEDURE_H__ */
diff --git a/app/plug-in/plug-in-enums.c b/app/plug-in/plug-in-enums.c
new file mode 100644
index 0000000..4f062bb
--- /dev/null
+++ b/app/plug-in/plug-in-enums.c
@@ -0,0 +1,118 @@
+
+/* Generated data (by gimp-mkenums) */
+
+#include "config.h"
+#include <gio/gio.h>
+#include "libgimpbase/gimpbase.h"
+#include "plug-in-enums.h"
+#include "gimp-intl.h"
+
+/* enumerations from "plug-in-enums.h" */
+GType
+gimp_plug_in_image_type_get_type (void)
+{
+ static const GFlagsValue values[] =
+ {
+ { GIMP_PLUG_IN_RGB_IMAGE, "GIMP_PLUG_IN_RGB_IMAGE", "rgb-image" },
+ { GIMP_PLUG_IN_GRAY_IMAGE, "GIMP_PLUG_IN_GRAY_IMAGE", "gray-image" },
+ { GIMP_PLUG_IN_INDEXED_IMAGE, "GIMP_PLUG_IN_INDEXED_IMAGE", "indexed-image" },
+ { GIMP_PLUG_IN_RGBA_IMAGE, "GIMP_PLUG_IN_RGBA_IMAGE", "rgba-image" },
+ { GIMP_PLUG_IN_GRAYA_IMAGE, "GIMP_PLUG_IN_GRAYA_IMAGE", "graya-image" },
+ { GIMP_PLUG_IN_INDEXEDA_IMAGE, "GIMP_PLUG_IN_INDEXEDA_IMAGE", "indexeda-image" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpFlagsDesc descs[] =
+ {
+ { GIMP_PLUG_IN_RGB_IMAGE, "GIMP_PLUG_IN_RGB_IMAGE", NULL },
+ { GIMP_PLUG_IN_GRAY_IMAGE, "GIMP_PLUG_IN_GRAY_IMAGE", NULL },
+ { GIMP_PLUG_IN_INDEXED_IMAGE, "GIMP_PLUG_IN_INDEXED_IMAGE", NULL },
+ { GIMP_PLUG_IN_RGBA_IMAGE, "GIMP_PLUG_IN_RGBA_IMAGE", NULL },
+ { GIMP_PLUG_IN_GRAYA_IMAGE, "GIMP_PLUG_IN_GRAYA_IMAGE", NULL },
+ { GIMP_PLUG_IN_INDEXEDA_IMAGE, "GIMP_PLUG_IN_INDEXEDA_IMAGE", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_flags_register_static ("GimpPlugInImageType", values);
+ gimp_type_set_translation_context (type, "plug-in-image-type");
+ gimp_flags_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_plug_in_call_mode_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_PLUG_IN_CALL_NONE, "GIMP_PLUG_IN_CALL_NONE", "none" },
+ { GIMP_PLUG_IN_CALL_RUN, "GIMP_PLUG_IN_CALL_RUN", "run" },
+ { GIMP_PLUG_IN_CALL_QUERY, "GIMP_PLUG_IN_CALL_QUERY", "query" },
+ { GIMP_PLUG_IN_CALL_INIT, "GIMP_PLUG_IN_CALL_INIT", "init" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_PLUG_IN_CALL_NONE, "GIMP_PLUG_IN_CALL_NONE", NULL },
+ { GIMP_PLUG_IN_CALL_RUN, "GIMP_PLUG_IN_CALL_RUN", NULL },
+ { GIMP_PLUG_IN_CALL_QUERY, "GIMP_PLUG_IN_CALL_QUERY", NULL },
+ { GIMP_PLUG_IN_CALL_INIT, "GIMP_PLUG_IN_CALL_INIT", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpPlugInCallMode", values);
+ gimp_type_set_translation_context (type, "plug-in-call-mode");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
+gimp_file_procedure_group_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_FILE_PROCEDURE_GROUP_NONE, "GIMP_FILE_PROCEDURE_GROUP_NONE", "none" },
+ { GIMP_FILE_PROCEDURE_GROUP_ANY, "GIMP_FILE_PROCEDURE_GROUP_ANY", "any" },
+ { GIMP_FILE_PROCEDURE_GROUP_OPEN, "GIMP_FILE_PROCEDURE_GROUP_OPEN", "open" },
+ { GIMP_FILE_PROCEDURE_GROUP_SAVE, "GIMP_FILE_PROCEDURE_GROUP_SAVE", "save" },
+ { GIMP_FILE_PROCEDURE_GROUP_EXPORT, "GIMP_FILE_PROCEDURE_GROUP_EXPORT", "export" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_FILE_PROCEDURE_GROUP_NONE, "GIMP_FILE_PROCEDURE_GROUP_NONE", NULL },
+ { GIMP_FILE_PROCEDURE_GROUP_ANY, "GIMP_FILE_PROCEDURE_GROUP_ANY", NULL },
+ { GIMP_FILE_PROCEDURE_GROUP_OPEN, "GIMP_FILE_PROCEDURE_GROUP_OPEN", NULL },
+ { GIMP_FILE_PROCEDURE_GROUP_SAVE, "GIMP_FILE_PROCEDURE_GROUP_SAVE", NULL },
+ { GIMP_FILE_PROCEDURE_GROUP_EXPORT, "GIMP_FILE_PROCEDURE_GROUP_EXPORT", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpFileProcedureGroup", values);
+ gimp_type_set_translation_context (type, "file-procedure-group");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+
+/* Generated data ends here */
+
diff --git a/app/plug-in/plug-in-enums.h b/app/plug-in/plug-in-enums.h
new file mode 100644
index 0000000..c5b6775
--- /dev/null
+++ b/app/plug-in/plug-in-enums.h
@@ -0,0 +1,64 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PLUG_IN_ENUMS_H__
+#define __PLUG_IN_ENUMS_H__
+
+
+#define GIMP_TYPE_PLUG_IN_IMAGE_TYPE (gimp_plug_in_image_type_get_type ())
+
+GType gimp_plug_in_image_type_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_PLUG_IN_RGB_IMAGE = 1 << 0,
+ GIMP_PLUG_IN_GRAY_IMAGE = 1 << 1,
+ GIMP_PLUG_IN_INDEXED_IMAGE = 1 << 2,
+ GIMP_PLUG_IN_RGBA_IMAGE = 1 << 3,
+ GIMP_PLUG_IN_GRAYA_IMAGE = 1 << 4,
+ GIMP_PLUG_IN_INDEXEDA_IMAGE = 1 << 5
+} GimpPlugInImageType;
+
+
+#define GIMP_TYPE_PLUG_CALL_MODE (gimp_plug_in_call_mode_get_type ())
+
+GType gimp_plug_in_call_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_PLUG_IN_CALL_NONE,
+ GIMP_PLUG_IN_CALL_RUN,
+ GIMP_PLUG_IN_CALL_QUERY,
+ GIMP_PLUG_IN_CALL_INIT
+} GimpPlugInCallMode;
+
+
+#define GIMP_TYPE_FILE_PROCEDURE_GROUP (gimp_file_procedure_group_get_type ())
+
+GType gimp_file_procedure_group_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_FILE_PROCEDURE_GROUP_NONE,
+ GIMP_FILE_PROCEDURE_GROUP_ANY,
+ GIMP_FILE_PROCEDURE_GROUP_OPEN,
+ GIMP_FILE_PROCEDURE_GROUP_SAVE,
+ GIMP_FILE_PROCEDURE_GROUP_EXPORT
+} GimpFileProcedureGroup;
+
+
+#endif /* __PLUG_IN_ENUMS_H__ */
diff --git a/app/plug-in/plug-in-menu-path.c b/app/plug-in/plug-in-menu-path.c
new file mode 100644
index 0000000..fa6fa16
--- /dev/null
+++ b/app/plug-in/plug-in-menu-path.c
@@ -0,0 +1,141 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * plug-in-menu-path.c
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gio/gio.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "plug-in-types.h"
+
+#include "plug-in-menu-path.h"
+
+
+typedef struct _MenuPathMapping MenuPathMapping;
+
+struct _MenuPathMapping
+{
+ const gchar *orig_path;
+ const gchar *label;
+ const gchar *mapped_path;
+};
+
+
+static const MenuPathMapping menu_path_mappings[] =
+{
+ { "<Toolbox>/Xtns/Languages", NULL, "<Image>/Filters/Languages" },
+ { "<Toolbox>/Xtns/Extensions", NULL, "<Image>/Filters/Extensions" },
+
+ { "<Toolbox>/Xtns/Buttons", NULL, "<Image>/File/Create/Buttons" },
+ { "<Toolbox>/Xtns/Logos", NULL, "<Image>/File/Create/Logos" },
+ { "<Toolbox>/Xtns/Misc", NULL, "<Image>/File/Create/Misc" },
+ { "<Toolbox>/Xtns/Patterns", NULL, "<Image>/File/Create/Patterns" },
+ { "<Toolbox>/Xtns/Web Page Themes", NULL, "<Image>/File/Create/Web Page Themes" },
+
+ { "<Toolbox>/Xtns", "Buttons", "<Image>/File/Create" },
+ { "<Toolbox>/Xtns", "Logos", "<Image>/File/Create" },
+ { "<Toolbox>/Xtns", "Misc", "<Image>/File/Create" },
+ { "<Toolbox>/Xtns", "Patterns", "<Image>/File/Create" },
+ { "<Toolbox>/Xtns", "Web Page Themes", "<Image>/File/Create" },
+
+ { "<Toolbox>/Xtns", NULL, "<Image>/Filters/Extensions" },
+ { "<Toolbox>/Help", NULL, "<Image>/Help" },
+
+ { "<Toolbox>/File/Acquire", NULL, "<Image>/File/Create/Acquire" },
+ { "<Toolbox>", NULL, "<Image>" },
+ { "<Image>/File/Acquire", NULL, "<Image>/File/Create/Acquire" },
+ { "<Image>/File/New", NULL, "<Image>/File/Create" },
+ { "<Image>/Image/Mode/Color Profile", NULL, "<Image>/Image/Color Management" },
+ { NULL, NULL, NULL }
+};
+
+
+gchar *
+plug_in_menu_path_map (const gchar *menu_path,
+ const gchar *menu_label)
+{
+ const MenuPathMapping *mapping;
+ gchar *stripped_label = NULL;
+
+ g_return_val_if_fail (menu_path != NULL, NULL);
+
+ if (menu_label)
+ stripped_label = gimp_strip_uline (menu_label);
+
+ for (mapping = menu_path_mappings; mapping->orig_path; mapping++)
+ {
+ if (g_str_has_prefix (menu_path, mapping->orig_path))
+ {
+ gint orig_len = strlen (mapping->orig_path);
+ gchar *mapped_path;
+
+ /* if the mapping has a label, only map if the passed label
+ * is identical and the paths' lengths match exactly.
+ */
+ if (mapping->label &&
+ (! stripped_label ||
+ strlen (menu_path) != orig_len ||
+ strcmp (mapping->label, stripped_label)))
+ {
+ continue;
+ }
+
+ if (strlen (menu_path) > orig_len)
+ mapped_path = g_strconcat (mapping->mapped_path,
+ menu_path + orig_len,
+ NULL);
+ else
+ mapped_path = g_strdup (mapping->mapped_path);
+
+#if GIMP_UNSTABLE
+ {
+ gchar *orig;
+ gchar *mapped;
+
+ if (menu_label)
+ {
+ orig = g_strdup_printf ("%s/%s", menu_path, stripped_label);
+ mapped = g_strdup_printf ("%s/%s", mapped_path, stripped_label);
+ }
+ else
+ {
+ orig = g_strdup (menu_path);
+ mapped = g_strdup (mapped_path);
+ }
+
+ g_printerr (" mapped '%s' to '%s'\n", orig, mapped);
+
+ g_free (orig);
+ g_free (mapped);
+ }
+#endif
+
+ g_free (stripped_label);
+
+ return mapped_path;
+ }
+ }
+
+ g_free (stripped_label);
+
+ return g_strdup (menu_path);
+}
diff --git a/app/plug-in/plug-in-menu-path.h b/app/plug-in/plug-in-menu-path.h
new file mode 100644
index 0000000..b222c06
--- /dev/null
+++ b/app/plug-in/plug-in-menu-path.h
@@ -0,0 +1,28 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * plug-in-menu-path.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PLUG_IN_MENU_PATH_H__
+#define __PLUG_IN_MENU_PATH_H__
+
+
+gchar * plug_in_menu_path_map (const gchar *menu_path,
+ const gchar *menu_label);
+
+
+#endif /* __PLUG_IN_MENU_PATH_H__ */
diff --git a/app/plug-in/plug-in-params.c b/app/plug-in/plug-in-params.c
new file mode 100644
index 0000000..3f781b2
--- /dev/null
+++ b/app/plug-in/plug-in-params.c
@@ -0,0 +1,442 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpbase/gimpprotocol.h"
+#include "libgimpcolor/gimpcolor.h"
+
+#include "plug-in-types.h"
+
+#include "core/gimpparamspecs.h"
+
+#include "pdb/gimp-pdb-compat.h"
+
+#include "plug-in-params.h"
+
+
+GimpValueArray *
+plug_in_params_to_args (GParamSpec **pspecs,
+ gint n_pspecs,
+ GPParam *params,
+ gint n_params,
+ gboolean return_values,
+ gboolean full_copy)
+{
+ GimpValueArray *args;
+ gint i;
+
+ g_return_val_if_fail ((pspecs != NULL && n_pspecs > 0) ||
+ (pspecs == NULL && n_pspecs == 0), NULL);
+ g_return_val_if_fail ((params != NULL && n_params > 0) ||
+ (params == NULL && n_params == 0), NULL);
+
+ args = gimp_value_array_new (n_params);
+
+ for (i = 0; i < n_params; i++)
+ {
+ GValue value = G_VALUE_INIT;
+ GType type;
+ gint count;
+
+ /* first get the fallback compat GType that matches the pdb type */
+ type = gimp_pdb_compat_arg_type_to_gtype (params[i].type);
+
+ /* then try to try to be more specific by looking at the param
+ * spec (return values have one additional value (the status),
+ * skip that, it's not in the array of param specs)
+ */
+ if (i > 0 || ! return_values)
+ {
+ gint pspec_index = i;
+
+ if (return_values)
+ pspec_index--;
+
+ /* are there param specs left? */
+ if (pspec_index < n_pspecs)
+ {
+ GType pspec_gtype;
+ GimpPDBArgType pspec_arg_type;
+
+ pspec_gtype = G_PARAM_SPEC_VALUE_TYPE (pspecs[pspec_index]);
+ pspec_arg_type = gimp_pdb_compat_arg_type_from_gtype (pspec_gtype);
+
+ /* if the param spec's GType, mapped to a pdb type, matches
+ * the passed pdb type, use the param spec's GType
+ */
+ if (pspec_arg_type == params[i].type)
+ type = pspec_gtype;
+ }
+ }
+
+ g_value_init (&value, type);
+
+ switch (gimp_pdb_compat_arg_type_from_gtype (type))
+ {
+ case GIMP_PDB_INT32:
+ if (G_VALUE_HOLDS_INT (&value))
+ g_value_set_int (&value, params[i].data.d_int32);
+ else if (G_VALUE_HOLDS_UINT (&value))
+ g_value_set_uint (&value, params[i].data.d_int32);
+ else if (G_VALUE_HOLDS_ENUM (&value))
+ g_value_set_enum (&value, params[i].data.d_int32);
+ else if (G_VALUE_HOLDS_BOOLEAN (&value))
+ g_value_set_boolean (&value, params[i].data.d_int32 ? TRUE : FALSE);
+ else
+ {
+ g_printerr ("%s: unhandled GIMP_PDB_INT32 type: %s\n",
+ G_STRFUNC, g_type_name (G_VALUE_TYPE (&value)));
+ g_return_val_if_reached (args);
+ }
+ break;
+
+ case GIMP_PDB_INT16:
+ g_value_set_int (&value, params[i].data.d_int16);
+ break;
+
+ case GIMP_PDB_INT8:
+ g_value_set_uint (&value, params[i].data.d_int8);
+ break;
+
+ case GIMP_PDB_FLOAT:
+ g_value_set_double (&value, params[i].data.d_float);
+ break;
+
+ case GIMP_PDB_STRING:
+ if (full_copy)
+ g_value_set_string (&value, params[i].data.d_string);
+ else
+ g_value_set_static_string (&value, params[i].data.d_string);
+ break;
+
+ case GIMP_PDB_INT32ARRAY:
+ count = g_value_get_int (gimp_value_array_index (args, i - 1));
+ if (full_copy)
+ gimp_value_set_int32array (&value,
+ params[i].data.d_int32array,
+ count);
+ else
+ gimp_value_set_static_int32array (&value,
+ params[i].data.d_int32array,
+ count);
+ break;
+
+ case GIMP_PDB_INT16ARRAY:
+ count = g_value_get_int (gimp_value_array_index (args, i - 1));
+ if (full_copy)
+ gimp_value_set_int16array (&value,
+ params[i].data.d_int16array,
+ count);
+ else
+ gimp_value_set_static_int16array (&value,
+ params[i].data.d_int16array,
+ count);
+ break;
+
+ case GIMP_PDB_INT8ARRAY:
+ count = g_value_get_int (gimp_value_array_index (args, i - 1));
+ if (full_copy)
+ gimp_value_set_int8array (&value,
+ params[i].data.d_int8array,
+ count);
+ else
+ gimp_value_set_static_int8array (&value,
+ params[i].data.d_int8array,
+ count);
+ break;
+
+ case GIMP_PDB_FLOATARRAY:
+ count = g_value_get_int (gimp_value_array_index (args, i - 1));
+ if (full_copy)
+ gimp_value_set_floatarray (&value,
+ params[i].data.d_floatarray,
+ count);
+ else
+ gimp_value_set_static_floatarray (&value,
+ params[i].data.d_floatarray,
+ count);
+ break;
+
+ case GIMP_PDB_STRINGARRAY:
+ count = g_value_get_int (gimp_value_array_index (args, i - 1));
+ if (full_copy)
+ gimp_value_set_stringarray (&value,
+ (const gchar **) params[i].data.d_stringarray,
+ count);
+ else
+ gimp_value_set_static_stringarray (&value,
+ (const gchar **) params[i].data.d_stringarray,
+ count);
+ break;
+
+ case GIMP_PDB_COLOR:
+ gimp_value_set_rgb (&value, &params[i].data.d_color);
+ break;
+
+ case GIMP_PDB_ITEM:
+ g_value_set_int (&value, params[i].data.d_item);
+ break;
+
+ case GIMP_PDB_DISPLAY:
+ g_value_set_int (&value, params[i].data.d_display);
+ break;
+
+ case GIMP_PDB_IMAGE:
+ g_value_set_int (&value, params[i].data.d_image);
+ break;
+
+ case GIMP_PDB_LAYER:
+ g_value_set_int (&value, params[i].data.d_layer);
+ break;
+
+ case GIMP_PDB_CHANNEL:
+ g_value_set_int (&value, params[i].data.d_channel);
+ break;
+
+ case GIMP_PDB_DRAWABLE:
+ g_value_set_int (&value, params[i].data.d_drawable);
+ break;
+
+ case GIMP_PDB_SELECTION:
+ g_value_set_int (&value, params[i].data.d_selection);
+ break;
+
+ case GIMP_PDB_COLORARRAY:
+ count = g_value_get_int (gimp_value_array_index (args, i - 1));
+ if (full_copy)
+ gimp_value_set_colorarray (&value,
+ params[i].data.d_colorarray,
+ count);
+ else
+ gimp_value_set_static_colorarray (&value,
+ params[i].data.d_colorarray,
+ count);
+ break;
+
+ case GIMP_PDB_VECTORS:
+ g_value_set_int (&value, params[i].data.d_vectors);
+ break;
+
+ case GIMP_PDB_PARASITE:
+ if (full_copy)
+ g_value_set_boxed (&value, &params[i].data.d_parasite);
+ else
+ g_value_set_static_boxed (&value, &params[i].data.d_parasite);
+ break;
+
+ case GIMP_PDB_STATUS:
+ g_value_set_enum (&value, params[i].data.d_status);
+ break;
+
+ case GIMP_PDB_END:
+ break;
+ }
+
+ gimp_value_array_append (args, &value);
+ g_value_unset (&value);
+ }
+
+ return args;
+}
+
+GPParam *
+plug_in_args_to_params (GimpValueArray *args,
+ gboolean full_copy)
+{
+ GPParam *params;
+ gint length;
+ gint i;
+
+ g_return_val_if_fail (args != NULL, NULL);
+
+ params = g_new0 (GPParam, gimp_value_array_length (args));
+
+ length = gimp_value_array_length (args);
+
+ for (i = 0; i < length; i++)
+ {
+ GValue *value = gimp_value_array_index (args, i);
+
+ params[i].type =
+ gimp_pdb_compat_arg_type_from_gtype (G_VALUE_TYPE (value));
+
+ switch (params[i].type)
+ {
+ case GIMP_PDB_INT32:
+ if (G_VALUE_HOLDS_INT (value))
+ params[i].data.d_int32 = g_value_get_int (value);
+ else if (G_VALUE_HOLDS_UINT (value))
+ params[i].data.d_int32 = g_value_get_uint (value);
+ else if (G_VALUE_HOLDS_ENUM (value))
+ params[i].data.d_int32 = g_value_get_enum (value);
+ else if (G_VALUE_HOLDS_BOOLEAN (value))
+ params[i].data.d_int32 = g_value_get_boolean (value);
+ else
+ {
+ g_printerr ("%s: unhandled GIMP_PDB_INT32 type: %s\n",
+ G_STRFUNC, g_type_name (G_VALUE_TYPE (value)));
+ g_return_val_if_reached (params);
+ }
+ break;
+
+ case GIMP_PDB_INT16:
+ params[i].data.d_int16 = g_value_get_int (value);
+ break;
+
+ case GIMP_PDB_INT8:
+ params[i].data.d_int8 = g_value_get_uint (value);
+ break;
+
+ case GIMP_PDB_FLOAT:
+ params[i].data.d_float = g_value_get_double (value);
+ break;
+
+ case GIMP_PDB_STRING:
+ if (full_copy)
+ params[i].data.d_string = g_value_dup_string (value);
+ else
+ params[i].data.d_string = (gchar *) g_value_get_string (value);
+ break;
+
+ case GIMP_PDB_INT32ARRAY:
+ if (full_copy)
+ params[i].data.d_int32array = gimp_value_dup_int32array (value);
+ else
+ params[i].data.d_int32array = (gint32 *) gimp_value_get_int32array (value);
+ break;
+
+ case GIMP_PDB_INT16ARRAY:
+ if (full_copy)
+ params[i].data.d_int16array = gimp_value_dup_int16array (value);
+ else
+ params[i].data.d_int16array = (gint16 *) gimp_value_get_int16array (value);
+ break;
+
+ case GIMP_PDB_INT8ARRAY:
+ if (full_copy)
+ params[i].data.d_int8array = gimp_value_dup_int8array (value);
+ else
+ params[i].data.d_int8array = (guint8 *) gimp_value_get_int8array (value);
+ break;
+
+ case GIMP_PDB_FLOATARRAY:
+ if (full_copy)
+ params[i].data.d_floatarray = gimp_value_dup_floatarray (value);
+ else
+ params[i].data.d_floatarray = (gdouble *) gimp_value_get_floatarray (value);
+ break;
+
+ case GIMP_PDB_STRINGARRAY:
+ if (full_copy)
+ params[i].data.d_stringarray = gimp_value_dup_stringarray (value);
+ else
+ params[i].data.d_stringarray = (gchar **) gimp_value_get_stringarray (value);
+ break;
+
+ case GIMP_PDB_COLOR:
+ gimp_value_get_rgb (value, &params[i].data.d_color);
+ break;
+
+ case GIMP_PDB_ITEM:
+ params[i].data.d_item = g_value_get_int (value);
+ break;
+
+ case GIMP_PDB_DISPLAY:
+ params[i].data.d_display = g_value_get_int (value);
+ break;
+
+ case GIMP_PDB_IMAGE:
+ params[i].data.d_image = g_value_get_int (value);
+ break;
+
+ case GIMP_PDB_LAYER:
+ params[i].data.d_layer = g_value_get_int (value);
+ break;
+
+ case GIMP_PDB_CHANNEL:
+ params[i].data.d_channel = g_value_get_int (value);
+ break;
+
+ case GIMP_PDB_DRAWABLE:
+ params[i].data.d_drawable = g_value_get_int (value);
+ break;
+
+ case GIMP_PDB_SELECTION:
+ params[i].data.d_selection = g_value_get_int (value);
+ break;
+
+ case GIMP_PDB_COLORARRAY:
+ if (full_copy)
+ params[i].data.d_colorarray = gimp_value_dup_colorarray (value);
+ else
+ params[i].data.d_colorarray = (GimpRGB *) gimp_value_get_colorarray (value);
+ break;
+
+ case GIMP_PDB_VECTORS:
+ params[i].data.d_vectors = g_value_get_int (value);
+ break;
+
+ case GIMP_PDB_PARASITE:
+ {
+ GimpParasite *parasite = (full_copy ?
+ g_value_dup_boxed (value) :
+ g_value_get_boxed (value));
+
+ if (parasite)
+ {
+ params[i].data.d_parasite.name = parasite->name;
+ params[i].data.d_parasite.flags = parasite->flags;
+ params[i].data.d_parasite.size = parasite->size;
+ params[i].data.d_parasite.data = parasite->data;
+
+ if (full_copy)
+ {
+ parasite->name = NULL;
+ parasite->flags = 0;
+ parasite->size = 0;
+ parasite->data = NULL;
+
+ gimp_parasite_free (parasite);
+ }
+ }
+ else
+ {
+ params[i].data.d_parasite.name = NULL;
+ params[i].data.d_parasite.flags = 0;
+ params[i].data.d_parasite.size = 0;
+ params[i].data.d_parasite.data = NULL;
+ }
+ }
+ break;
+
+ case GIMP_PDB_STATUS:
+ params[i].data.d_status = g_value_get_enum (value);
+ break;
+
+ case GIMP_PDB_END:
+ break;
+ }
+ }
+
+ return params;
+}
diff --git a/app/plug-in/plug-in-params.h b/app/plug-in/plug-in-params.h
new file mode 100644
index 0000000..0b8df27
--- /dev/null
+++ b/app/plug-in/plug-in-params.h
@@ -0,0 +1,32 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PLUG_IN_PARAMS_H__
+#define __PLUG_IN_PARAMS_H__
+
+
+GimpValueArray * plug_in_params_to_args (GParamSpec **pspecs,
+ gint n_pspecs,
+ GPParam *params,
+ gint n_params,
+ gboolean return_values,
+ gboolean full_copy);
+GPParam * plug_in_args_to_params (GimpValueArray *args,
+ gboolean full_copy);
+
+
+#endif /* __PLUG_IN_PARAMS_H__ */
diff --git a/app/plug-in/plug-in-rc.c b/app/plug-in/plug-in-rc.c
new file mode 100644
index 0000000..42cfe3d
--- /dev/null
+++ b/app/plug-in/plug-in-rc.c
@@ -0,0 +1,1137 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * plug-in-rc.c
+ * Copyright (C) 2001 Sven Neumann <sven@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpbase/gimpprotocol.h"
+#include "libgimpconfig/gimpconfig.h"
+
+#include "plug-in-types.h"
+
+#include "core/gimp.h"
+
+#include "pdb/gimp-pdb-compat.h"
+
+#include "gimpplugindef.h"
+#include "gimppluginprocedure.h"
+#include "plug-in-rc.h"
+
+#include "gimp-intl.h"
+
+
+#define PLUG_IN_RC_FILE_VERSION 5
+
+
+/*
+ * All deserialize functions return G_TOKEN_LEFT_PAREN on success,
+ * or the GTokenType they would have expected but didn't get,
+ * or G_TOKEN_ERROR if the function already set an error itself.
+ */
+
+static GTokenType plug_in_def_deserialize (Gimp *gimp,
+ GScanner *scanner,
+ GSList **plug_in_defs);
+static GTokenType plug_in_procedure_deserialize (GScanner *scanner,
+ Gimp *gimp,
+ GFile *file,
+ GimpPlugInProcedure **proc);
+static GTokenType plug_in_menu_path_deserialize (GScanner *scanner,
+ GimpPlugInProcedure *proc);
+static GTokenType plug_in_icon_deserialize (GScanner *scanner,
+ GimpPlugInProcedure *proc);
+static GTokenType plug_in_file_proc_deserialize (GScanner *scanner,
+ GimpPlugInProcedure *proc);
+static GTokenType plug_in_proc_arg_deserialize (GScanner *scanner,
+ Gimp *gimp,
+ GimpProcedure *procedure,
+ gboolean return_value);
+static GTokenType plug_in_locale_def_deserialize (GScanner *scanner,
+ GimpPlugInDef *plug_in_def);
+static GTokenType plug_in_help_def_deserialize (GScanner *scanner,
+ GimpPlugInDef *plug_in_def);
+static GTokenType plug_in_has_init_deserialize (GScanner *scanner,
+ GimpPlugInDef *plug_in_def);
+
+
+enum
+{
+ PROTOCOL_VERSION = 1,
+ FILE_VERSION,
+ PLUG_IN_DEF,
+ PROC_DEF,
+ LOCALE_DEF,
+ HELP_DEF,
+ HAS_INIT,
+ PROC_ARG,
+ MENU_PATH,
+ ICON,
+ LOAD_PROC,
+ SAVE_PROC,
+ EXTENSIONS,
+ PREFIXES,
+ MAGICS,
+ PRIORITY,
+ MIME_TYPES,
+ HANDLES_URI,
+ HANDLES_RAW,
+ THUMB_LOADER
+};
+
+
+GSList *
+plug_in_rc_parse (Gimp *gimp,
+ GFile *file,
+ GError **error)
+{
+ GScanner *scanner;
+ GEnumClass *enum_class;
+ GSList *plug_in_defs = NULL;
+ gint protocol_version = GIMP_PROTOCOL_VERSION;
+ gint file_version = PLUG_IN_RC_FILE_VERSION;
+ GTokenType token;
+
+ g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ scanner = gimp_scanner_new_gfile (file, error);
+
+ if (! scanner)
+ return NULL;
+
+ enum_class = g_type_class_ref (GIMP_TYPE_ICON_TYPE);
+
+ g_scanner_scope_add_symbol (scanner, 0,
+ "protocol-version",
+ GINT_TO_POINTER (PROTOCOL_VERSION));
+ g_scanner_scope_add_symbol (scanner, 0,
+ "file-version",
+ GINT_TO_POINTER (FILE_VERSION));
+ g_scanner_scope_add_symbol (scanner, 0,
+ "plug-in-def", GINT_TO_POINTER (PLUG_IN_DEF));
+
+ g_scanner_scope_add_symbol (scanner, PLUG_IN_DEF,
+ "proc-def", GINT_TO_POINTER (PROC_DEF));
+ g_scanner_scope_add_symbol (scanner, PLUG_IN_DEF,
+ "locale-def", GINT_TO_POINTER (LOCALE_DEF));
+ g_scanner_scope_add_symbol (scanner, PLUG_IN_DEF,
+ "help-def", GINT_TO_POINTER (HELP_DEF));
+ g_scanner_scope_add_symbol (scanner, PLUG_IN_DEF,
+ "has-init", GINT_TO_POINTER (HAS_INIT));
+ g_scanner_scope_add_symbol (scanner, PLUG_IN_DEF,
+ "proc-arg", GINT_TO_POINTER (PROC_ARG));
+ g_scanner_scope_add_symbol (scanner, PLUG_IN_DEF,
+ "menu-path", GINT_TO_POINTER (MENU_PATH));
+ g_scanner_scope_add_symbol (scanner, PLUG_IN_DEF,
+ "icon", GINT_TO_POINTER (ICON));
+ g_scanner_scope_add_symbol (scanner, PLUG_IN_DEF,
+ "load-proc", GINT_TO_POINTER (LOAD_PROC));
+ g_scanner_scope_add_symbol (scanner, PLUG_IN_DEF,
+ "save-proc", GINT_TO_POINTER (SAVE_PROC));
+
+ g_scanner_scope_add_symbol (scanner, LOAD_PROC,
+ "extensions", GINT_TO_POINTER (EXTENSIONS));
+ g_scanner_scope_add_symbol (scanner, LOAD_PROC,
+ "prefixes", GINT_TO_POINTER (PREFIXES));
+ g_scanner_scope_add_symbol (scanner, LOAD_PROC,
+ "magics", GINT_TO_POINTER (MAGICS));
+ g_scanner_scope_add_symbol (scanner, LOAD_PROC,
+ "priority", GINT_TO_POINTER (PRIORITY));
+ g_scanner_scope_add_symbol (scanner, LOAD_PROC,
+ "mime-types", GINT_TO_POINTER (MIME_TYPES));
+ g_scanner_scope_add_symbol (scanner, LOAD_PROC,
+ "handles-uri", GINT_TO_POINTER (HANDLES_URI));
+ g_scanner_scope_add_symbol (scanner, LOAD_PROC,
+ "handles-raw", GINT_TO_POINTER (HANDLES_RAW));
+ g_scanner_scope_add_symbol (scanner, LOAD_PROC,
+ "thumb-loader", GINT_TO_POINTER (THUMB_LOADER));
+
+ g_scanner_scope_add_symbol (scanner, SAVE_PROC,
+ "extensions", GINT_TO_POINTER (EXTENSIONS));
+ g_scanner_scope_add_symbol (scanner, SAVE_PROC,
+ "prefixes", GINT_TO_POINTER (PREFIXES));
+ g_scanner_scope_add_symbol (scanner, SAVE_PROC,
+ "priority", GINT_TO_POINTER (PRIORITY));
+ g_scanner_scope_add_symbol (scanner, SAVE_PROC,
+ "mime-types", GINT_TO_POINTER (MIME_TYPES));
+ g_scanner_scope_add_symbol (scanner, SAVE_PROC,
+ "handles-uri", GINT_TO_POINTER (HANDLES_URI));
+
+ token = G_TOKEN_LEFT_PAREN;
+
+ while (protocol_version == GIMP_PROTOCOL_VERSION &&
+ file_version == PLUG_IN_RC_FILE_VERSION &&
+ g_scanner_peek_next_token (scanner) == token)
+ {
+ token = g_scanner_get_next_token (scanner);
+
+ switch (token)
+ {
+ case G_TOKEN_LEFT_PAREN:
+ token = G_TOKEN_SYMBOL;
+ break;
+
+ case G_TOKEN_SYMBOL:
+ switch (GPOINTER_TO_INT (scanner->value.v_symbol))
+ {
+ case PROTOCOL_VERSION:
+ token = G_TOKEN_INT;
+ if (gimp_scanner_parse_int (scanner, &protocol_version))
+ token = G_TOKEN_RIGHT_PAREN;
+ break;
+
+ case FILE_VERSION:
+ token = G_TOKEN_INT;
+ if (gimp_scanner_parse_int (scanner, &file_version))
+ token = G_TOKEN_RIGHT_PAREN;
+ break;
+
+ case PLUG_IN_DEF:
+ g_scanner_set_scope (scanner, PLUG_IN_DEF);
+ token = plug_in_def_deserialize (gimp, scanner, &plug_in_defs);
+ g_scanner_set_scope (scanner, 0);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case G_TOKEN_RIGHT_PAREN:
+ token = G_TOKEN_LEFT_PAREN;
+ break;
+
+ default: /* do nothing */
+ break;
+ }
+ }
+
+ if (protocol_version != GIMP_PROTOCOL_VERSION ||
+ file_version != PLUG_IN_RC_FILE_VERSION ||
+ token != G_TOKEN_LEFT_PAREN)
+ {
+ if (protocol_version != GIMP_PROTOCOL_VERSION)
+ {
+ g_set_error (error,
+ GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_VERSION,
+ _("Skipping '%s': wrong GIMP protocol version."),
+ gimp_file_get_utf8_name (file));
+ }
+ else if (file_version != PLUG_IN_RC_FILE_VERSION)
+ {
+ g_set_error (error,
+ GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_VERSION,
+ _("Skipping '%s': wrong pluginrc file format version."),
+ gimp_file_get_utf8_name (file));
+ }
+ else if (token != G_TOKEN_ERROR)
+ {
+ g_scanner_get_next_token (scanner);
+ g_scanner_unexp_token (scanner, token, NULL, NULL, NULL,
+ _("fatal parse error"), TRUE);
+ }
+
+ g_slist_free_full (plug_in_defs, (GDestroyNotify) g_object_unref);
+ plug_in_defs = NULL;
+ }
+
+ g_type_class_unref (enum_class);
+
+ gimp_scanner_destroy (scanner);
+
+ return g_slist_reverse (plug_in_defs);
+}
+
+static GTokenType
+plug_in_def_deserialize (Gimp *gimp,
+ GScanner *scanner,
+ GSList **plug_in_defs)
+{
+ GimpPlugInDef *plug_in_def;
+ GimpPlugInProcedure *proc = NULL;
+ gchar *path;
+ GFile *file;
+ gint64 mtime;
+ GTokenType token;
+ GError *error = NULL;
+
+ if (! gimp_scanner_parse_string (scanner, &path))
+ return G_TOKEN_STRING;
+
+ if (! (path && *path))
+ {
+ g_scanner_error (scanner, "plug-in filename is empty");
+ return G_TOKEN_ERROR;
+ }
+
+ file = gimp_file_new_for_config_path (path, &error);
+ g_free (path);
+
+ if (! file)
+ {
+ g_scanner_error (scanner,
+ "unable to parse plug-in filename: %s",
+ error->message);
+ g_clear_error (&error);
+ return G_TOKEN_ERROR;
+ }
+
+ plug_in_def = gimp_plug_in_def_new (file);
+ g_object_unref (file);
+
+ if (! gimp_scanner_parse_int64 (scanner, &mtime))
+ {
+ g_object_unref (plug_in_def);
+ return G_TOKEN_INT;
+ }
+
+ plug_in_def->mtime = mtime;
+
+ token = G_TOKEN_LEFT_PAREN;
+
+ while (g_scanner_peek_next_token (scanner) == token)
+ {
+ token = g_scanner_get_next_token (scanner);
+
+ switch (token)
+ {
+ case G_TOKEN_LEFT_PAREN:
+ token = G_TOKEN_SYMBOL;
+ break;
+
+ case G_TOKEN_SYMBOL:
+ switch (GPOINTER_TO_INT (scanner->value.v_symbol))
+ {
+ case PROC_DEF:
+ token = plug_in_procedure_deserialize (scanner, gimp,
+ plug_in_def->file,
+ &proc);
+
+ if (token == G_TOKEN_LEFT_PAREN)
+ gimp_plug_in_def_add_procedure (plug_in_def, proc);
+
+ if (proc)
+ g_object_unref (proc);
+ break;
+
+ case LOCALE_DEF:
+ token = plug_in_locale_def_deserialize (scanner, plug_in_def);
+ break;
+
+ case HELP_DEF:
+ token = plug_in_help_def_deserialize (scanner, plug_in_def);
+ break;
+
+ case HAS_INIT:
+ token = plug_in_has_init_deserialize (scanner, plug_in_def);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case G_TOKEN_RIGHT_PAREN:
+ token = G_TOKEN_LEFT_PAREN;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (token == G_TOKEN_LEFT_PAREN)
+ {
+ token = G_TOKEN_RIGHT_PAREN;
+
+ if (gimp_scanner_parse_token (scanner, token))
+ {
+ *plug_in_defs = g_slist_prepend (*plug_in_defs, plug_in_def);
+ return G_TOKEN_LEFT_PAREN;
+ }
+ }
+
+ g_object_unref (plug_in_def);
+
+ return token;
+}
+
+static GTokenType
+plug_in_procedure_deserialize (GScanner *scanner,
+ Gimp *gimp,
+ GFile *file,
+ GimpPlugInProcedure **proc)
+{
+ GimpProcedure *procedure;
+ GTokenType token;
+ gchar *str;
+ gint proc_type;
+ gint n_args;
+ gint n_return_vals;
+ gint n_menu_paths;
+ gint i;
+
+ if (! gimp_scanner_parse_string (scanner, &str))
+ return G_TOKEN_STRING;
+
+ if (! (str && *str))
+ {
+ g_scanner_error (scanner, "procedure name is empty");
+ return G_TOKEN_ERROR;
+ }
+
+ if (! gimp_scanner_parse_int (scanner, &proc_type))
+ {
+ g_free (str);
+ return G_TOKEN_INT;
+ }
+
+ if (proc_type != GIMP_PLUGIN &&
+ proc_type != GIMP_EXTENSION)
+ {
+ g_free (str);
+ g_scanner_error (scanner, "procedure type %d is out of range",
+ proc_type);
+ return G_TOKEN_ERROR;
+ }
+
+ procedure = gimp_plug_in_procedure_new (proc_type, file);
+
+ *proc = GIMP_PLUG_IN_PROCEDURE (procedure);
+
+ gimp_object_take_name (GIMP_OBJECT (procedure),
+ gimp_canonicalize_identifier (str));
+
+ procedure->original_name = str;
+
+ if (! gimp_scanner_parse_string (scanner, &procedure->blurb))
+ return G_TOKEN_STRING;
+ if (! gimp_scanner_parse_string (scanner, &procedure->help))
+ return G_TOKEN_STRING;
+ if (! gimp_scanner_parse_string (scanner, &procedure->author))
+ return G_TOKEN_STRING;
+ if (! gimp_scanner_parse_string (scanner, &procedure->copyright))
+ return G_TOKEN_STRING;
+ if (! gimp_scanner_parse_string (scanner, &procedure->date))
+ return G_TOKEN_STRING;
+ if (! gimp_scanner_parse_string (scanner, &(*proc)->menu_label))
+ return G_TOKEN_STRING;
+
+ if (! gimp_scanner_parse_int (scanner, &n_menu_paths))
+ return G_TOKEN_INT;
+
+ for (i = 0; i < n_menu_paths; i++)
+ {
+ token = plug_in_menu_path_deserialize (scanner, *proc);
+ if (token != G_TOKEN_LEFT_PAREN)
+ return token;
+ }
+
+ token = plug_in_icon_deserialize (scanner, *proc);
+ if (token != G_TOKEN_LEFT_PAREN)
+ return token;
+
+ token = plug_in_file_proc_deserialize (scanner, *proc);
+ if (token != G_TOKEN_LEFT_PAREN)
+ return token;
+
+ if (! gimp_scanner_parse_string (scanner, &str))
+ return G_TOKEN_STRING;
+
+ gimp_plug_in_procedure_set_image_types (*proc, str);
+ g_free (str);
+
+ if (! gimp_scanner_parse_int (scanner, (gint *) &n_args))
+ return G_TOKEN_INT;
+ if (! gimp_scanner_parse_int (scanner, (gint *) &n_return_vals))
+ return G_TOKEN_INT;
+
+ for (i = 0; i < n_args; i++)
+ {
+ token = plug_in_proc_arg_deserialize (scanner, gimp, procedure, FALSE);
+ if (token != G_TOKEN_LEFT_PAREN)
+ return token;
+ }
+
+ for (i = 0; i < n_return_vals; i++)
+ {
+ token = plug_in_proc_arg_deserialize (scanner, gimp, procedure, TRUE);
+ if (token != G_TOKEN_LEFT_PAREN)
+ return token;
+ }
+
+ if (! gimp_scanner_parse_token (scanner, G_TOKEN_RIGHT_PAREN))
+ return G_TOKEN_RIGHT_PAREN;
+
+ return G_TOKEN_LEFT_PAREN;
+}
+
+static GTokenType
+plug_in_menu_path_deserialize (GScanner *scanner,
+ GimpPlugInProcedure *proc)
+{
+ gchar *menu_path;
+
+ if (! gimp_scanner_parse_token (scanner, G_TOKEN_LEFT_PAREN))
+ return G_TOKEN_LEFT_PAREN;
+
+ if (! gimp_scanner_parse_token (scanner, G_TOKEN_SYMBOL) ||
+ GPOINTER_TO_INT (scanner->value.v_symbol) != MENU_PATH)
+ return G_TOKEN_SYMBOL;
+
+ if (! gimp_scanner_parse_string (scanner, &menu_path))
+ return G_TOKEN_STRING;
+
+ proc->menu_paths = g_list_append (proc->menu_paths, menu_path);
+
+ if (! gimp_scanner_parse_token (scanner, G_TOKEN_RIGHT_PAREN))
+ return G_TOKEN_RIGHT_PAREN;
+
+ return G_TOKEN_LEFT_PAREN;
+}
+
+static GTokenType
+plug_in_icon_deserialize (GScanner *scanner,
+ GimpPlugInProcedure *proc)
+{
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+ GimpIconType icon_type;
+ gint icon_data_length;
+ gchar *icon_name;
+ guint8 *icon_data;
+
+ if (! gimp_scanner_parse_token (scanner, G_TOKEN_LEFT_PAREN))
+ return G_TOKEN_LEFT_PAREN;
+
+ if (! gimp_scanner_parse_token (scanner, G_TOKEN_SYMBOL) ||
+ GPOINTER_TO_INT (scanner->value.v_symbol) != ICON)
+ return G_TOKEN_SYMBOL;
+
+ enum_class = g_type_class_peek (GIMP_TYPE_ICON_TYPE);
+
+ switch (g_scanner_peek_next_token (scanner))
+ {
+ case G_TOKEN_IDENTIFIER:
+ g_scanner_get_next_token (scanner);
+
+ enum_value = g_enum_get_value_by_nick (G_ENUM_CLASS (enum_class),
+ scanner->value.v_identifier);
+ if (!enum_value)
+ enum_value = g_enum_get_value_by_name (G_ENUM_CLASS (enum_class),
+ scanner->value.v_identifier);
+
+ if (!enum_value)
+ {
+ g_scanner_error (scanner,
+ _("invalid value '%s' for icon type"),
+ scanner->value.v_identifier);
+ return G_TOKEN_NONE;
+ }
+ break;
+
+ case G_TOKEN_INT:
+ g_scanner_get_next_token (scanner);
+
+ enum_value = g_enum_get_value (enum_class,
+ (gint) scanner->value.v_int64);
+
+ if (!enum_value)
+ {
+ g_scanner_error (scanner,
+ _("invalid value '%ld' for icon type"),
+ (glong) scanner->value.v_int64);
+ return G_TOKEN_NONE;
+ }
+ break;
+
+ default:
+ return G_TOKEN_IDENTIFIER;
+ }
+
+ icon_type = enum_value->value;
+
+ if (! gimp_scanner_parse_int (scanner, &icon_data_length))
+ return G_TOKEN_INT;
+
+ switch (icon_type)
+ {
+ case GIMP_ICON_TYPE_ICON_NAME:
+ case GIMP_ICON_TYPE_IMAGE_FILE:
+ icon_data_length = -1;
+
+ if (! gimp_scanner_parse_string_no_validate (scanner, &icon_name))
+ return G_TOKEN_STRING;
+
+ icon_data = (guint8 *) icon_name;
+ break;
+
+ case GIMP_ICON_TYPE_INLINE_PIXBUF:
+ if (icon_data_length < 0)
+ return G_TOKEN_STRING;
+
+ if (! gimp_scanner_parse_data (scanner, icon_data_length, &icon_data))
+ return G_TOKEN_STRING;
+ break;
+ }
+
+ gimp_plug_in_procedure_take_icon (proc, icon_type,
+ icon_data, icon_data_length);
+
+ if (! gimp_scanner_parse_token (scanner, G_TOKEN_RIGHT_PAREN))
+ return G_TOKEN_RIGHT_PAREN;
+
+ return G_TOKEN_LEFT_PAREN;
+}
+
+static GTokenType
+plug_in_file_proc_deserialize (GScanner *scanner,
+ GimpPlugInProcedure *proc)
+{
+ GTokenType token;
+ gint symbol;
+
+ if (! gimp_scanner_parse_token (scanner, G_TOKEN_LEFT_PAREN))
+ return G_TOKEN_LEFT_PAREN;
+
+ if (! gimp_scanner_parse_token (scanner, G_TOKEN_SYMBOL))
+ return G_TOKEN_SYMBOL;
+
+ symbol = GPOINTER_TO_INT (scanner->value.v_symbol);
+ if (symbol != LOAD_PROC && symbol != SAVE_PROC)
+ return G_TOKEN_SYMBOL;
+
+ proc->file_proc = TRUE;
+
+ g_scanner_set_scope (scanner, symbol);
+
+ while (g_scanner_peek_next_token (scanner) == G_TOKEN_LEFT_PAREN)
+ {
+ token = g_scanner_get_next_token (scanner);
+
+ if (token != G_TOKEN_LEFT_PAREN)
+ return token;
+
+ if (! gimp_scanner_parse_token (scanner, G_TOKEN_SYMBOL))
+ return G_TOKEN_SYMBOL;
+
+ symbol = GPOINTER_TO_INT (scanner->value.v_symbol);
+
+ switch (symbol)
+ {
+ case EXTENSIONS:
+ {
+ gchar *extensions;
+
+ if (! gimp_scanner_parse_string (scanner, &extensions))
+ return G_TOKEN_STRING;
+
+ g_free (proc->extensions);
+ proc->extensions = extensions;
+ }
+ break;
+
+ case PREFIXES:
+ {
+ gchar *prefixes;
+
+ if (! gimp_scanner_parse_string (scanner, &prefixes))
+ return G_TOKEN_STRING;
+
+ g_free (proc->prefixes);
+ proc->prefixes = prefixes;
+ }
+ break;
+
+ case MAGICS:
+ {
+ gchar *magics;
+
+ if (! gimp_scanner_parse_string_no_validate (scanner, &magics))
+ return G_TOKEN_STRING;
+
+ g_free (proc->magics);
+ proc->magics = magics;
+ }
+ break;
+
+ case PRIORITY:
+ {
+ gint priority;
+
+ if (! gimp_scanner_parse_int (scanner, &priority))
+ return G_TOKEN_INT;
+
+ gimp_plug_in_procedure_set_priority (proc, priority);
+ }
+ break;
+
+ case MIME_TYPES:
+ {
+ gchar *mime_types;
+
+ if (! gimp_scanner_parse_string (scanner, &mime_types))
+ return G_TOKEN_STRING;
+
+ gimp_plug_in_procedure_set_mime_types (proc, mime_types);
+
+ g_free (mime_types);
+ }
+ break;
+
+ case HANDLES_URI:
+ gimp_plug_in_procedure_set_handles_uri (proc);
+ break;
+
+ case HANDLES_RAW:
+ gimp_plug_in_procedure_set_handles_raw (proc);
+ break;
+
+ case THUMB_LOADER:
+ {
+ gchar *thumb_loader;
+
+ if (! gimp_scanner_parse_string (scanner, &thumb_loader))
+ return G_TOKEN_STRING;
+
+ gimp_plug_in_procedure_set_thumb_loader (proc, thumb_loader);
+
+ g_free (thumb_loader);
+ }
+ break;
+
+ default:
+ return G_TOKEN_SYMBOL;
+ }
+ if (! gimp_scanner_parse_token (scanner, G_TOKEN_RIGHT_PAREN))
+ return G_TOKEN_RIGHT_PAREN;
+ }
+
+ if (! gimp_scanner_parse_token (scanner, G_TOKEN_RIGHT_PAREN))
+ return G_TOKEN_RIGHT_PAREN;
+
+ g_scanner_set_scope (scanner, PLUG_IN_DEF);
+
+ return G_TOKEN_LEFT_PAREN;
+}
+
+static GTokenType
+plug_in_proc_arg_deserialize (GScanner *scanner,
+ Gimp *gimp,
+ GimpProcedure *procedure,
+ gboolean return_value)
+{
+ GTokenType token;
+ gint arg_type;
+ gchar *name = NULL;
+ gchar *desc = NULL;
+ GParamSpec *pspec;
+
+ if (! gimp_scanner_parse_token (scanner, G_TOKEN_LEFT_PAREN))
+ {
+ token = G_TOKEN_LEFT_PAREN;
+ goto error;
+ }
+
+ if (! gimp_scanner_parse_token (scanner, G_TOKEN_SYMBOL) ||
+ GPOINTER_TO_INT (scanner->value.v_symbol) != PROC_ARG)
+ {
+ token = G_TOKEN_SYMBOL;
+ goto error;
+ }
+
+ if (! gimp_scanner_parse_int (scanner, (gint *) &arg_type))
+ {
+ token = G_TOKEN_INT;
+ goto error;
+ }
+ if (! gimp_scanner_parse_string (scanner, &name))
+ {
+ token = G_TOKEN_STRING;
+ goto error;
+ }
+ if (! gimp_scanner_parse_string (scanner, &desc))
+ {
+ token = G_TOKEN_STRING;
+ goto error;
+ }
+
+ if (! gimp_scanner_parse_token (scanner, G_TOKEN_RIGHT_PAREN))
+ {
+ token = G_TOKEN_RIGHT_PAREN;
+ goto error;
+ }
+
+ token = G_TOKEN_LEFT_PAREN;
+
+ pspec = gimp_pdb_compat_param_spec (gimp, arg_type, name, desc, NULL);
+
+ if (return_value)
+ gimp_procedure_add_return_value (procedure, pspec);
+ else
+ gimp_procedure_add_argument (procedure, pspec);
+
+ error:
+
+ g_free (name);
+ g_free (desc);
+
+ return token;
+}
+
+static GTokenType
+plug_in_locale_def_deserialize (GScanner *scanner,
+ GimpPlugInDef *plug_in_def)
+{
+ gchar *domain_name;
+ gchar *domain_path = NULL;
+ gchar *expanded_path = NULL;
+
+ if (! gimp_scanner_parse_string (scanner, &domain_name))
+ return G_TOKEN_STRING;
+
+ if (gimp_scanner_parse_string (scanner, &domain_path))
+ expanded_path = gimp_config_path_expand (domain_path, TRUE, NULL);
+
+ gimp_plug_in_def_set_locale_domain (plug_in_def, domain_name, expanded_path);
+
+ g_free (domain_name);
+ g_free (domain_path);
+ g_free (expanded_path);
+
+ if (! gimp_scanner_parse_token (scanner, G_TOKEN_RIGHT_PAREN))
+ return G_TOKEN_RIGHT_PAREN;
+
+ return G_TOKEN_LEFT_PAREN;
+}
+
+static GTokenType
+plug_in_help_def_deserialize (GScanner *scanner,
+ GimpPlugInDef *plug_in_def)
+{
+ gchar *domain_name;
+ gchar *domain_uri;
+
+ if (! gimp_scanner_parse_string (scanner, &domain_name))
+ return G_TOKEN_STRING;
+
+ if (! gimp_scanner_parse_string (scanner, &domain_uri))
+ domain_uri = NULL;
+
+ gimp_plug_in_def_set_help_domain (plug_in_def, domain_name, domain_uri);
+
+ g_free (domain_name);
+ g_free (domain_uri);
+
+ if (! gimp_scanner_parse_token (scanner, G_TOKEN_RIGHT_PAREN))
+ return G_TOKEN_RIGHT_PAREN;
+
+ return G_TOKEN_LEFT_PAREN;
+}
+
+static GTokenType
+plug_in_has_init_deserialize (GScanner *scanner,
+ GimpPlugInDef *plug_in_def)
+{
+ gimp_plug_in_def_set_has_init (plug_in_def, TRUE);
+
+ if (! gimp_scanner_parse_token (scanner, G_TOKEN_RIGHT_PAREN))
+ return G_TOKEN_RIGHT_PAREN;
+
+ return G_TOKEN_LEFT_PAREN;
+}
+
+
+/* serialize functions */
+
+gboolean
+plug_in_rc_write (GSList *plug_in_defs,
+ GFile *file,
+ GError **error)
+{
+ GimpConfigWriter *writer;
+ GEnumClass *enum_class;
+ GSList *list;
+
+ writer = gimp_config_writer_new_gfile (file,
+ FALSE,
+ "GIMP pluginrc\n\n"
+ "This file can safely be removed and "
+ "will be automatically regenerated by "
+ "querying the installed plug-ins.",
+ error);
+ if (!writer)
+ return FALSE;
+
+ enum_class = g_type_class_ref (GIMP_TYPE_ICON_TYPE);
+
+ gimp_config_writer_open (writer, "protocol-version");
+ gimp_config_writer_printf (writer, "%d", GIMP_PROTOCOL_VERSION);
+ gimp_config_writer_close (writer);
+
+ gimp_config_writer_open (writer, "file-version");
+ gimp_config_writer_printf (writer, "%d", PLUG_IN_RC_FILE_VERSION);
+ gimp_config_writer_close (writer);
+
+ gimp_config_writer_linefeed (writer);
+
+ for (list = plug_in_defs; list; list = list->next)
+ {
+ GimpPlugInDef *plug_in_def = list->data;
+
+ if (plug_in_def->procedures)
+ {
+ GSList *list2;
+ gchar *path;
+
+ path = gimp_file_get_config_path (plug_in_def->file, NULL);
+ if (! path)
+ continue;
+
+ gimp_config_writer_open (writer, "plug-in-def");
+ gimp_config_writer_string (writer, path);
+ gimp_config_writer_printf (writer, "%"G_GINT64_FORMAT,
+ plug_in_def->mtime);
+
+ g_free (path);
+
+ for (list2 = plug_in_def->procedures; list2; list2 = list2->next)
+ {
+ GimpPlugInProcedure *proc = list2->data;
+ GimpProcedure *procedure = GIMP_PROCEDURE (proc);
+ GEnumValue *enum_value;
+ GList *list3;
+ gint i;
+
+ if (proc->installed_during_init)
+ continue;
+
+ gimp_config_writer_open (writer, "proc-def");
+ gimp_config_writer_printf (writer, "\"%s\" %d",
+ procedure->original_name,
+ procedure->proc_type);
+ gimp_config_writer_linefeed (writer);
+ gimp_config_writer_string (writer, procedure->blurb);
+ gimp_config_writer_linefeed (writer);
+ gimp_config_writer_string (writer, procedure->help);
+ gimp_config_writer_linefeed (writer);
+ gimp_config_writer_string (writer, procedure->author);
+ gimp_config_writer_linefeed (writer);
+ gimp_config_writer_string (writer, procedure->copyright);
+ gimp_config_writer_linefeed (writer);
+ gimp_config_writer_string (writer, procedure->date);
+ gimp_config_writer_linefeed (writer);
+ gimp_config_writer_string (writer, proc->menu_label);
+ gimp_config_writer_linefeed (writer);
+
+ gimp_config_writer_printf (writer, "%d",
+ g_list_length (proc->menu_paths));
+ for (list3 = proc->menu_paths; list3; list3 = list3->next)
+ {
+ gimp_config_writer_open (writer, "menu-path");
+ gimp_config_writer_string (writer, list3->data);
+ gimp_config_writer_close (writer);
+ }
+
+ gimp_config_writer_open (writer, "icon");
+ enum_value = g_enum_get_value (enum_class, proc->icon_type);
+ gimp_config_writer_identifier (writer, enum_value->value_nick);
+ gimp_config_writer_printf (writer, "%d",
+ proc->icon_data_length);
+
+ switch (proc->icon_type)
+ {
+ case GIMP_ICON_TYPE_ICON_NAME:
+ case GIMP_ICON_TYPE_IMAGE_FILE:
+ gimp_config_writer_string (writer, (gchar *) proc->icon_data);
+ break;
+
+ case GIMP_ICON_TYPE_INLINE_PIXBUF:
+ gimp_config_writer_data (writer, proc->icon_data_length,
+ proc->icon_data);
+ break;
+ }
+
+ gimp_config_writer_close (writer);
+
+ if (proc->file_proc)
+ {
+ gimp_config_writer_open (writer,
+ proc->image_types ?
+ "save-proc" : "load-proc");
+
+ if (proc->extensions && *proc->extensions)
+ {
+ gimp_config_writer_open (writer, "extensions");
+ gimp_config_writer_string (writer, proc->extensions);
+ gimp_config_writer_close (writer);
+ }
+
+ if (proc->prefixes && *proc->prefixes)
+ {
+ gimp_config_writer_open (writer, "prefixes");
+ gimp_config_writer_string (writer, proc->prefixes);
+ gimp_config_writer_close (writer);
+ }
+
+ if (proc->magics && *proc->magics)
+ {
+ gimp_config_writer_open (writer, "magics");
+ gimp_config_writer_string (writer, proc->magics);
+ gimp_config_writer_close (writer);
+ }
+
+ if (proc->priority)
+ {
+ gimp_config_writer_open (writer, "priority");
+ gimp_config_writer_printf (writer, "%d", proc->priority);
+ gimp_config_writer_close (writer);
+ }
+
+ if (proc->mime_types && *proc->mime_types)
+ {
+ gimp_config_writer_open (writer, "mime-types");
+ gimp_config_writer_string (writer, proc->mime_types);
+ gimp_config_writer_close (writer);
+ }
+
+ if (proc->priority)
+ {
+ gimp_config_writer_open (writer, "priority");
+ gimp_config_writer_printf (writer, "%d", proc->priority);
+ gimp_config_writer_close (writer);
+ }
+
+ if (proc->handles_uri)
+ {
+ gimp_config_writer_open (writer, "handles-uri");
+ gimp_config_writer_close (writer);
+ }
+
+ if (proc->handles_raw && ! proc->image_types)
+ {
+ gimp_config_writer_open (writer, "handles-raw");
+ gimp_config_writer_close (writer);
+ }
+
+ if (proc->thumb_loader)
+ {
+ gimp_config_writer_open (writer, "thumb-loader");
+ gimp_config_writer_string (writer, proc->thumb_loader);
+ gimp_config_writer_close (writer);
+ }
+
+ gimp_config_writer_close (writer);
+ }
+
+ gimp_config_writer_linefeed (writer);
+
+ gimp_config_writer_string (writer, proc->image_types);
+ gimp_config_writer_linefeed (writer);
+
+ gimp_config_writer_printf (writer, "%d %d",
+ procedure->num_args,
+ procedure->num_values);
+
+ for (i = 0; i < procedure->num_args; i++)
+ {
+ GParamSpec *pspec = procedure->args[i];
+
+ gimp_config_writer_open (writer, "proc-arg");
+ gimp_config_writer_printf (writer, "%d",
+ gimp_pdb_compat_arg_type_from_gtype (G_PARAM_SPEC_VALUE_TYPE (pspec)));
+
+ gimp_config_writer_string (writer,
+ g_param_spec_get_name (pspec));
+ gimp_config_writer_string (writer,
+ g_param_spec_get_blurb (pspec));
+
+ gimp_config_writer_close (writer);
+ }
+
+ for (i = 0; i < procedure->num_values; i++)
+ {
+ GParamSpec *pspec = procedure->values[i];
+
+ gimp_config_writer_open (writer, "proc-arg");
+ gimp_config_writer_printf (writer, "%d",
+ gimp_pdb_compat_arg_type_from_gtype (G_PARAM_SPEC_VALUE_TYPE (pspec)));
+
+ gimp_config_writer_string (writer,
+ g_param_spec_get_name (pspec));
+ gimp_config_writer_string (writer,
+ g_param_spec_get_blurb (pspec));
+
+ gimp_config_writer_close (writer);
+ }
+
+ gimp_config_writer_close (writer);
+ }
+
+ if (plug_in_def->locale_domain_name)
+ {
+ gimp_config_writer_open (writer, "locale-def");
+ gimp_config_writer_string (writer,
+ plug_in_def->locale_domain_name);
+
+ if (plug_in_def->locale_domain_path)
+ {
+ path = gimp_config_path_unexpand (plug_in_def->locale_domain_path,
+ TRUE, NULL);
+ if (path)
+ {
+ gimp_config_writer_string (writer, path);
+ g_free (path);
+ }
+ }
+
+ gimp_config_writer_close (writer);
+ }
+
+ if (plug_in_def->help_domain_name)
+ {
+ gimp_config_writer_open (writer, "help-def");
+ gimp_config_writer_string (writer,
+ plug_in_def->help_domain_name);
+
+ if (plug_in_def->help_domain_uri)
+ gimp_config_writer_string (writer,
+ plug_in_def->help_domain_uri);
+
+ gimp_config_writer_close (writer);
+ }
+
+ if (plug_in_def->has_init)
+ {
+ gimp_config_writer_open (writer, "has-init");
+ gimp_config_writer_close (writer);
+ }
+
+ gimp_config_writer_close (writer);
+ }
+ }
+
+ g_type_class_unref (enum_class);
+
+ return gimp_config_writer_finish (writer, "end of pluginrc", error);
+}
diff --git a/app/plug-in/plug-in-rc.h b/app/plug-in/plug-in-rc.h
new file mode 100644
index 0000000..5a54b0b
--- /dev/null
+++ b/app/plug-in/plug-in-rc.h
@@ -0,0 +1,33 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * plug-in-rc.h
+ * Copyright (C) 2001 Sven Neumann <sven@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PLUG_IN_RC_H__
+#define __PLUG_IN_RC_H__
+
+
+GSList * plug_in_rc_parse (Gimp *gimp,
+ GFile *file,
+ GError **error);
+gboolean plug_in_rc_write (GSList *plug_in_defs,
+ GFile *file,
+ GError **error);
+
+
+#endif /* __PLUG_IN_RC_H__ */
diff --git a/app/plug-in/plug-in-types.h b/app/plug-in/plug-in-types.h
new file mode 100644
index 0000000..70d7ca0
--- /dev/null
+++ b/app/plug-in/plug-in-types.h
@@ -0,0 +1,40 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PLUG_IN_TYPES_H__
+#define __PLUG_IN_TYPES_H__
+
+
+#include "core/core-types.h"
+
+#include "plug-in/plug-in-enums.h"
+
+
+#define GIMP_PLUG_IN_TILE_WIDTH 128
+#define GIMP_PLUG_IN_TILE_HEIGHT 128
+
+
+typedef struct _GimpPlugIn GimpPlugIn;
+typedef struct _GimpPlugInDebug GimpPlugInDebug;
+typedef struct _GimpPlugInDef GimpPlugInDef;
+typedef struct _GimpPlugInManager GimpPlugInManager;
+typedef struct _GimpPlugInMenuBranch GimpPlugInMenuBranch;
+typedef struct _GimpPlugInProcFrame GimpPlugInProcFrame;
+typedef struct _GimpPlugInShm GimpPlugInShm;
+
+
+#endif /* __PLUG_IN_TYPES_H__ */